Toggle line numbers
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:Zoom.Quiet@gmail.com>}
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