1 #@+leo-ver=4
   2 #@+node:@file cvsanalyser.py
   3 #@verbatim
   4 #@ignore
   5 #@verbatim
   6 #@language python
   7 #@<< cvsanalyser declarations >>
   8 #@+node:<< cvsanalyser declarations >>
   9 # -*- coding: UTF-8 -*-
  10 """cvsanalyser CVS History analyser
  11     - 直接分析CVS History 记录
  12     - 取得统计数据!
  13 特别的 203 中的自动执行:
  14     - #su -l sinaop
  15     - 专门的用户中的 来维护
  16     - %crontab -e
  17     
  18 @version: $Id: cvsanalyser.py,v 1.10 2005/01/05 07:13:36 zhouqi Exp $
  19 @author: U{Zoom.Quiet<mailto:[email protected]>}
  20 @see: U{cvsdelta by Limodou<http://pyrecord.freezope.org/articles/doc2001082801/show>}
  21 """
  22 import sys,os,string
  23 import time,datetime
  24 from elementtree import ElementTree
  25 from elementtree.ElementTree import Element, SubElement, tostring
  26 
  27 import getopt
  28 import ConfigParser
  29 
  30 #@-node:<< cvsanalyser declarations >>
  31 #@nl
  32 #@+others
  33 #@+node:class CvsAnalyser
  34 class CvsAnalyser:
  35     """主类,集成所有处理
  36     """
  37     #@  @+others
  38     #@+node:__init__
  39     def __init__(self):
  40         """初始化参数
  41             - 通过 /var/log/cron 的据错邮件发现目录的严格性
  42             - 当前除了.conf 文件,要需要配置cfg 文件的读取路径!
  43         """
  44         self.cfg = ConfigParser.ConfigParser()
  45         try:
  46             # 必须正确指定配置文件路径在此
  47             self.cfg.read("/home/zoomq/share/statcvs/cvsanalyser.conf")
  48             #=============================
  49             #以下是运行脚本不需要修改
  50             self.cvsroot = self.cfg.get('Path','cvsroot')
  51             self.cvs = self.cfg.get('Target','cvs').split()
  52             self.tarea = int(self.cfg.get('Target','time'))
  53             self.viewcvs = self.cfg.get('Target','viewcvs')
  54             self.xml = self.cfg.get('Target','xml')
  55             self.xmlhead = """<?xml version='1.0' encoding='UTF-8'?>
  56             <!-- edited with elementtree (http://effbot.org/zone/element-index.htm) by Zoom.Quiet  -->
  57             <?xml-stylesheet type='text/xsl' href='historystat.xsl'?>
  58             """
  59             #self.cfg.get('Target','xmlhead')
  60 
  61             self.finder = "find %s -mtime -7 -type f | wc -l | awk '{print $1}'"
  62         except:
  63             print """config files:\n cvsanalyser.conf 
  64             not exist or bad!!!
  65             please check it....and try again!
  66             """
  67             return
  68         self.report = {}
  69         self.stime = (datetime.date.today()-datetime.timedelta(self.tarea)).isoformat()
  70         self.etime = datetime.date.today().isoformat()
  71         #print self.etime
  72     #@-node:__init__
  73     #@+node:__repr__
  74     def __repr__(self):
  75         """类自述定义,记录手册等等信息
  76         """
  77         return """
  78         使用手册:
  79         设置 cvsanalyser.conf 指定对应的 CVS root 的目录,和输出的XML文件,以及文件的编码约定
  80         cvsanalyser.py 将自动追查所有CVS模块的更新信息
  81         """
  82     #@nonl
  83     #@-node:__repr__
  84     #@+node:chkFiles
  85     def chkFiles(self):
  86         """逐一搜索有效目录获得History 文件列表
  87             - 好象没有必要? CVSROOT 目录是每个库的唯一!
  88         """
  89         files = []
  90         for key in self.cvs:
  91             files.append(key)
  92         return files
  93     #@nonl
  94     #@-node:chkFiles
  95     #@+node:analyseAll
  96     def analyseAll(self):
  97         """逐一处理所有模块
  98             - 使用Python 内建结构先建立数据映射
  99             - self.report{}:
 100                 - cvs 仓库名称:{}
 101                     - commits: ; files: ;
 102                     - mods:{}
 103                         - 模块名:{}
 104                         - commits: ; files: ;
 105                         - viewcvs:{} 链接到 ViewCVS 各文件的 下载,查看
 106         # 本地单一文件测试
 107             his = open("history","r")        
 108             hislis = his.readlines()
 109             print "-------------"
 110             print len(hislis)
 111             cvs="scm"
 112             self.report[cvs]=self.analyseDate(hislis)
 113             print "==============="
 114             #print self.report
 115             self.put2xml()
 116             return
 117         """
 118         cvsli = self.chkFiles()
 119         for cvs in cvsli:
 120             #print hisfile
 121             his = open(self.cvsroot+cvs+"/CVSROOT/history","r")
 122             hislis = his.readlines()
 123             print "-------------"
 124             print len(hislis)
 125             self.report[cvs]=self.analyseDate(hislis)
 126 
 127         #print self.report
 128         """成功的数据实例
 129         {'scm': {'files': 9, 'commits': 20
 130         , 'mods': {'scm': {'files': 9, 'commits': 20
 131         ,'viewcvs': {
 132             'CVSanalyser.leo': ('M', '41d50d27', 'zhouqi', 'scm/statcvs', '1.3',
 133      'CVSanalyser.leo')
 134             , 'weeklystat.py': ('M', '41d50d27', 'zhouqi', 'scm/statcvs',
 135      '1.4', 'weeklystat.py')
 136             , 'weeklystat.xsl': ('M', '41d4ec4c', 'zhouqi', 'scm/sta
 137     tcvs', '1.3', 'weeklystat.xsl')
 138             , 'cvsanalyser.conf': ('M', '41d50d27', 'zhouqi',
 139      'scm/statcvs', '1.2', 'cvsanalyser.conf')
 140             , 'statcvs.sh.py': ('M', '41d4ec4c', '
 141     zhouqi', 'scm/statcvs', '1.6', 'statcvs.sh.py')
 142             , 'weeklystat.xml': ('A', '41d0f6
 143     92', 'zhouqi', 'scm/statcvs', '1.1', 'weeklystat.xml')
 144             , 'style.css': ('A', '41d0
 145     f7b3', 'zhouqi', 'scm/statcvs', '1.1', 'style.css')
 146             , 'cvsanalyser.py': ('M', '41
 147     d50d27', 'zhouqi', 'scm/statcvs', '1.2', 'cvsanalyser.py')
 148             , 'sinaeye.ico': ('A',
 149      '41d0f7b3', 'zhouqi', 'scm/statcvs', '1.1', 'sinaeye.ico')
 150             }
 151         }
 152     }
 153     }}
 154         """
 155 
 156 
 157 
 158     #@nonl
 159     #@-node:analyseAll
 160     #@+node:analyseDate
 161     def analyseDate(self,historic):
 162         """根据日期进行统计
 163             - 是每个CVS 的针对性分析
 164         """
 165         rep = []
 166         #stat = 0
 167         for line in historic:
 168             info = self.analyseLine(line)
 169             #'%Y-%m-%d %H:%M:%S'
 170             sdate=time.strftime('%Y-%m-%d', time.localtime(int(info[1], 16)))
 171             if(sdate>=self.stime):
 172                 if(info[0]in"AMT"):
 173                     #stat+=1
 174                     rep.append(info)
 175         # 数组长度就是CVS 的总提交修改数!commits
 176 
 177         print "<<history in date right"
 178         print len(rep)
 179         return self.analyseAct(rep)
 180     #@-node:analyseDate
 181     #@+node:analyseAct
 182     def analyseAct(self,list):
 183         """根据行为分析
 184             - 关注 M|A 作为更新
 185             - T 作为版本标记行为
 186             - 统计不同模块中的更新情况!
 187                 - 变更次数
 188                 - 文件数            
 189             - 统计整个库的修改文件数
 190         """
 191         rep = {}
 192         rep["commits"]=0
 193         rep["files"]=0
 194         modict={}   #记录有更新的模板信息
 195         fdict={}   #记录有更新的文件    
 196         for line in list:
 197             if(line[0] in "AMT"):
 198                 rep["commits"]+=1
 199                 #print line
 200                 #模块处理
 201                 moudle = line[3].split("/")[0]
 202                 if(moudle in modict):
 203                     modict[moudle]["commits"]+=1
 204                     fdict=self.analyseFile(fdict,moudle,line)
 205                 else:
 206                     #新发现的模块
 207                     print "moudle:::"+moudle
 208                     modict[moudle]={}
 209                     modict[moudle]["commits"]=1
 210                     modict[moudle]["files"]=1
 211                     # 文件处理
 212                     fdict=self.analyseFile(fdict,moudle,line)
 213             else:
 214                 pass
 215         rep["mods"]=modict
 216         for mod in rep["mods"]:
 217             # 统计每模块的文件信息
 218             rep["mods"][mod]["viewcvs"]=fdict[mod]
 219             rep["mods"][mod]["files"]=len(fdict[mod])
 220             # 统计整个库的文件信息
 221             rep["files"]+=rep["mods"][mod]["files"]
 222         #rep["viewcvs"]=self.viewcvsFiles(fdict)
 223 
 224         """"
 225         for m in fdict:
 226             print len(fdict[m])
 227             for f in fdict[m]:
 228                 print fdict[m][f]
 229         """
 230         return rep
 231 
 232     #@nonl
 233     #@-node:analyseAct
 234     #@+node:analyseFile
 235     def analyseFile(self,fdict,mod,hisline):
 236         """使用专门的容器,累计分析文件信息
 237             - fdict 上级的文件统计容器
 238             - 当前的模块信息
 239             - 当前的文件History 行信息
 240         """
 241         file = hisline[5]
 242         if mod in fdict:
 243             if file in fdict[mod]:
 244                 fdict[mod][file]=hisline
 245             else:
 246                 fdict[mod][file]=hisline
 247         else:
 248             #创建新的模块文件信息容器
 249             fdict[mod]={}
 250             fdict[mod][file]=hisline
 251 
 252         return fdict
 253     #@nonl
 254     #@-node:analyseFile
 255     #@+node:viewcvsFiles
 256     def viewcvsFiles(self,hisline):
 257         """根据行为列表组合 ViewCVS 链接
 258             - 在线观看
 259             - 下载
 260         仅仅在输出XML等等其它文件时调用
 261         """
 262         url=""
 263 
 264 
 265         return url
 266 
 267 
 268 
 269     #@-node:viewcvsFiles
 270     #@+node:analyseLine
 271     def analyseLine(self,line):
 272         """分析日志行,分解得到信息元组
 273             - 操作类型 
 274             - 操作日期
 275             - 提交人
 276             - 模块
 277             - 版本
 278             - 文件名
 279         """
 280         fields=line.split('|')
 281         return (fields[0][0], fields[0][1:], fields[1],fields[3], fields[4], fields[5][:-1])
 282     #@-node:analyseLine
 283     #@+node:put2xml
 284     def put2xml(self):
 285         """汇出数据为XML文件!
 286             - 使用轻型XML操作库 Elements and Element Trees
 287             - @see: U{Elements<http://effbot.org/zone/element.htm>}
 288         """
 289         re = self.report
 290         zday = time.strftime("%Y-%m-%d",time.localtime(time.time()))
 291         print zday
 292         ztime = time.strftime("%X",time.localtime(time.time()))
 293         # 必须使用系统的绝对路径!否则无法 cron 自动执行
 294         tree = ElementTree.parse(self.xml).getroot()
 295         week = tree.findall("week")
 296         # 先判定是否要覆盖记录
 297         done = 0
 298         for node in week:
 299             if(zday==node.attrib["date"]):
 300                 #print node.attrib["date"]
 301                 done = 1
 302                 tree.remove(node)
 303                 break
 304         #tree.SubElement(tree, "week")
 305         if(1==done):
 306             print "recorded xml! new upgrade it!"
 307         else:
 308             print "need recording...will exp. as xml file"
 309         neweek = Element("week")
 310         neweek.set("date", zday)
 311         neweek.set("time", ztime)
 312         statcount = {'files':0
 313             ,'commits':0
 314             }
 315         for proj in re:
 316             #print self.report[proj]
 317             #print proj
 318             newcvs = SubElement(neweek, "cvs")
 319             newcvs.set("name", proj)
 320             newcvs.set("files", str(re[proj]["files"]))
 321             newcvs.set("commits", str(re[proj]["commits"]))
 322             statcount["files"]+=re[proj]["files"]
 323             statcount["commits"]+=re[proj]["commits"]
 324             for mod in re[proj]["mods"]:
 325                 module = SubElement(newcvs, "module")
 326                 module.set("name", mod)
 327                 module.set("files", str(re[proj]["mods"][mod]["files"]))
 328                 module.set("commits", str(re[proj]["mods"][mod]["commits"]))
 329                 for view in re[proj]["mods"][mod]["viewcvs"]:
 330                     file = Element("file")
 331                     #ViewCVS 处理
 332                     his = re[proj]["mods"][mod]["viewcvs"][view]
 333                     #print re[proj]["mods"][mod]["viewcvs"][view]        
 334                     file.set("name", view)
 335                     # 小心谨慎的拼合!
 336                     file.set("view", self.viewcvs+his[3]+"/"+his[5]+"?root="+proj+"&rev="+his[4]+"&view=auto")
 337                     file.set("load", self.viewcvs+"*checkout*/"+his[3]+"/"+his[5]+"?rev="+his[4])
 338                     module.append(file)
 339                 #print re[proj]["mods"][mod]
 340             neweek.set("commits", str(statcount["commits"]))
 341             neweek.set("files", str(statcount["files"]))
 342         tree.append(neweek)
 343         #ElementTree.ElementTree(tree).write("out.xml")
 344         code = tostring(tree)
 345         #print code
 346         print self.xml
 347         open(self.xml,"w").write(self.xmlhead+code)
 348     #@nonl
 349     #@-node:put2xml
 350     #@-others
 351 #@-node:class CvsAnalyser
 352 #@-others
 353 
 354 
 355 if __name__ == '__main__':
 356     """基本运行模式
 357     """
 358     week = CvsAnalyser()
 359     week.analyseAll()
 360     week.put2xml()
 361 #@nonl
 362 #@-node:@file cvsanalyser.py
 363 #@-leo