| import email.Parser |
| import time |
| import re |
| import sys |
| import getopt |
| |
| youngest = 0 |
| oldest = 0 |
| merged_branches=[] |
| integrated_branches=[] |
| projects = {} |
| integrationMatch = re.compile ( r"200[0-9]/[0-9]+/[0-9]+ [0-9]+:[0-9]+:[0-9]+" ) |
| version = re.compile ( r"[0-9]+\.[0-9]+[.0-9]*") |
| cws_branch = re.compile ( r"CWS ([a-zA-Z0-9]+)" ) |
| cws_tag = re.compile ( r"Tag:[ ]+([_a-zA-Z0-9]+)" ) |
| bugMatch = re.compile ( "[#]?[i]?[0-9][0-9][0-9][0-9]+[#]?" ) |
| projectMatch = re.compile ( "http://([-a-zA-Z0-9]+)\.openoffice.org" ) |
| #oldLineChangeMatch = re.compile ( r"^ [1-9]+\.[0-9.]+[ ]*\+([0-9]+) -([0-9]+) " ) |
| lineChangeMatch = re.compile ( "\+([0-9]+) -([0-9]+)" ) |
| mergedBranch = re.compile ( "CWS ([a-zA-Z0-9]+)" ) |
| developers = [] |
| noOfCommits = 0 |
| moOfNewFiles = 0 |
| noOfLinesAdded = 0 |
| noOfLinesRemoved = 0 |
| verbose = False |
| week = None |
| suppressFilesOn = {} |
| summarizeIntegration = False |
| |
| names = { |
| "ab":"Andreas Bregas", |
| "abi":"Andreas Bille", |
| "as":"Andreas Schluens", |
| "af":"Andre Fischer", |
| "ama":"Andreas Martens", |
| "armin":"Armin Theissen", |
| "aw":"Armin Weiss", |
| "bc":"Behrend Cornelius", |
| "bm":"Bjoern Mielke", |
| "cdt":"Claus Dieter Thoele", |
| "cd": "Carsten Driesner", |
| "cj":"Christian Jansen", |
| "cl":"Christian Lippka", |
| "cn":"Christoph Neumann?", |
| "cmc":"Caolán McNamara", |
| "cp":"Christof Pintaske", |
| "dfoster":"Duncan Foster", |
| "dbo":"Daniel Boelzle", |
| "dv": "Dirk Voelzke", |
| "dr":"Daniel Rentz", |
| "dvo":"Daniel Vogelheim", |
| "dsherwin":"Darragh Sherwin", |
| "er":"Eike Rathke", |
| "fa":"Dan Williams", |
| "fme":"Frank Meies", |
| "ft":"Falko Tesch", |
| "fs":"Frank Schoenheit", |
| "gh":"Gregor Hartmann", |
| "gt":"Gunnar Timm?", |
| "hbrinkm":"Henning Brinkmann", |
| "hdu":"Herbert Duerr", |
| "hjs":"Hans-Joachim Lankenau", |
| "haggai":"Chris Halls", |
| "hr":"Jens-Heiner Rechtien", |
| "hro":"Hennes Rohling", |
| "is":"Ingo Schmidt", |
| "khendricks":"Kevin B. Hendricks", |
| "idi":"Ivo Hinkelmann", |
| "ihi":"Ivo Hinkelmann", |
| "iha":"Ingrid Halama", |
| "jl":"Joachim Lingner", |
| "jb":"Joerg Barfurth", |
| "jbu":"Joerg Budischewski", |
| "jmarmion":"John Marmion", |
| "jsc":"Juergen Schmidt", |
| "kso":"Kai Sommerfeld", |
| "ka":"Kai Ahrens", |
| "khong":"Karl Hong", |
| "kr":"Kay Ramme", |
| "kz":"Kurt Zenker", |
| "lla":"Lars Langhans", |
| "lo":"Lars Oppermann", |
| "louis":"Louis Suarez-Potts", |
| "mav":"Mikhail Voitenko", |
| "mba":"Mathias Bauer", |
| "mh":"Martin Hollmichel", |
| "mhu":"Matthias Huetsch", |
| "mi":"Michael Hoennig", |
| "mib":"Michael Brauer", |
| "mindyliu":"Mindy Liu", |
| "mexx":"Mustafa?", |
| "mmi":"Michael Mi", |
| "mt":"Malte Timmermann", |
| "mmaher":"Martin Maher", |
| "mmeeks":"Michael Meeks", |
| "mmp":"Matthias Mueller-Prove", |
| "mrauch":"Michael Rauch", |
| "msc":"Marc Schwanz?", |
| "nn":"Niklas Nebel", |
| "npower":"Noel Power", |
| "obo":"Oliver Bolte", |
| "obr":"Oliver Braun", |
| "od":"Oliver Düsterhoff", |
| "oj":"Ocke Janssen", |
| "os":"Oliver Specht", |
| "pb":"Peter Burow", |
| "pjanik":"Pavel Janík", |
| "pl":"Philipp Lohmann", |
| "richard":"Richard Holt", |
| "rholt":"Richard Holt", |
| "rpiterman":"Ron Piterman", |
| "rt":"Rüdiger Timm", |
| "sab":"Sascha Ballach", |
| "sj":"Sven Jacobi", |
| "svesik":"Sander Vesik", |
| "st":"Stefan Taxhet", |
| "sb":"Stephan Bergmann", |
| "ssa":"Stephan Schaefer", |
| "ssmith":"Sarah Smith", |
| "stx12":"Stefan Taxhet", |
| "sw":"Stephan Wunderlich", |
| "tbe":"Thomas Behrens", |
| "thb":"Thorsten Behrens", |
| "toconnor":"Tomas O'Connor", |
| "tra":"Tino Rachui", |
| "tv":"Tom Verbeek", |
| "tl":"Thomas Lange", |
| "va":"Volker Ahrend", |
| "vg":"Vladimir Glazounov", |
| "vq":"Volker Quetschke", |
| "waratah":"Ken Foskey", |
| "yl146652":"Wind Li" |
| } |
| |
| projectNames = { |
| "api": "API", |
| "framework": "Application Framework", |
| "tools": "Build Tools and Environment", |
| "dba": "Database Access", |
| #"documentation":"Documentation", |
| "external": "External", |
| "graphics": "Graphic Applications", |
| "gsl": "Graphic System Layer", |
| "installation": "Installation", |
| "lingucomponent":"Lingucomponent", |
| #"i18n": "Internationalization", |
| "l10n": "Localization", |
| #"marketing": "Marketing", |
| "porting": "Porting", |
| #"qa": "Quality Assurance", |
| "sc": "Spreadsheet", |
| "ucb": "Universal Content Broker", |
| "udk": "UNO Development Kit / Component Technology", |
| "ui": "User Interface", |
| "util": "Utilities", |
| #"website": "Website", |
| "sw": "Word Processing", |
| "xml": "XML File Formats", |
| } |
| |
| class CVSLog: |
| def __init__( self, log ): |
| self.msg = parser.parsestr ( log ) |
| payload = self.msg.get_payload() |
| if isinstance(payload,list): |
| print >>sys.stderr, "found no payload, will try to use the log"# for:", log |
| print >>sys.stderr, payload |
| lines = log |
| for i in range(len(lines)): |
| if lines[i].startswith(" User"): |
| break |
| self.lines = lines[:i] |
| print >>sys.stderr, "lines set to",self.lines |
| else: |
| self.lines = payload.split("\n") |
| #print "no of lines=", len(self.lines) |
| self.currLine = 0 |
| self.comment = "" |
| self.URLs = [] |
| self.files ="" |
| self.tag = "" |
| self.change = None |
| self.mergedBranch = None |
| self.process() |
| def currentLine( self ): |
| return self.lines [ self.currLine ] |
| def nextLine( self ): |
| line = self.lines [ self.currLine ] |
| self.currLine += 1 |
| return line |
| def finished( self ): |
| #print self.currLine, len(self.lines) |
| #print self.lines[self.currLine] |
| if len(self.lines) == self.currLine+1 or self.lines[self.currLine].find("To unsubscribe") == 0: |
| #print "finished" |
| return True |
| return False |
| def extractAndRemoveTags(self, line): |
| tag = line.find ( "Tag:" ) |
| if tag != -1 : |
| #tag = re.sub(".*Tag:[ ]*", "", line.strip() ).split()[0] |
| tagAndFile = re.sub(".*Tag:[ ]*", "", line.strip() ) |
| #print tagAndFile |
| if tagAndFile == "": |
| line = self.nextLine() |
| tagAndFile = line.strip() |
| |
| tagL = tagAndFile.split() |
| #print tagL |
| tag = tagL[0] |
| #print tag |
| if self.tag and self.tag != tag: |
| print >>sys.stderr, "OhOh: can there really be different tags in a checkin?" |
| self.tag = tag |
| return line |
| def processAffectedFiles(self, line): |
| pass |
| def processAffectedTagsAndFiles(self, line): |
| lines = [] |
| while not self.finished(): |
| # line = self.extractAndRemoveTags( line ) |
| lines.append ( line .strip() + " " ) |
| line = self.nextLine() |
| if line.find ( "Log:" ) != -1 : |
| break |
| tagsAndFiles = "".join(lines) |
| match = cws_tag.search ( tagsAndFiles ) |
| if match : |
| while match: |
| self.tag = match.group(1) |
| #print >>sys.stderr, "tag:", self.tag, "\nlines:", tagsAndFiles |
| tagsAndFiles = tagsAndFiles.replace ( "Tag: " +self.tag, "") |
| #print >>sys.stderr, "after:", tagsAndFiles |
| match = cws_tag.search ( tagsAndFiles ) |
| # else: |
| # #print >>sys.stderr, "no tag:", tagsAndFiles |
| # pass |
| if tagsAndFiles.find("Added:") != -1 : |
| self.change = "added" |
| tagsAndFiles = re.sub ("Added:","", tagsAndFiles) |
| if tagsAndFiles.find("Removed:") != -1 : |
| self.change = "removed" |
| self.files = re.sub ("Removed:","", tagsAndFiles).strip() |
| self.comment = tagsAndFiles |
| if tagsAndFiles.find("Modified:") != -1 : |
| self.change = "modified" |
| tagsAndFiles = re.sub ("Modified:","", tagsAndFiles) |
| if tagsAndFiles.find("New directory") != -1 : |
| self.change = "added" |
| tagsAndFiles = re.sub ("- New directory","", tagsAndFiles) |
| # self.file = line.split(" - New")[0].strip() |
| self.files = tagsAndFiles.strip() |
| #print >>sys.stderr, "new directory added:", self.files,"\n", tagsAndFiles |
| |
| if tagsAndFiles.find("New") != -1 : |
| self.change = "added" |
| |
| #tagsAndFiles = re.sub ( " Tag:[ ]+[a-zA-Z0-9_]+","/", tagsAndFiles).strip() |
| #self.files = tagsAndFiles |
| #print >>sys.stderr, "tags and files:", tagsAndFiles |
| # while not self.finished(): |
| # line = self.extractAndRemoveTags( line ) |
| # self.processAffectedFiles ( line ) |
| # line = self.nextLine() |
| # if line.find ( "Log:" ) != -1 : |
| # return line |
| return line |
| def processUser(self, line): |
| self.user = line.split ( "User: " )[1].strip() |
| def processLogMessage(self, line): |
| integration = False |
| comments = [] |
| comments.append ( "<span class='comment'>" ) |
| while not self.finished(): |
| #remove version nos as in RESYNC: (1.7-1.8); FILE MERGED |
| if integration or line.find ( "INTEGRATION" ) != -1 : |
| match = cws_branch.search ( line ) |
| shorttag = "" |
| if match : |
| if not ( match.group(1) in merged_branches): |
| merged_branches .append ( match.group(1) ) |
| shorttag = match.group(1) |
| if self.change == "removed": |
| comments.append ( "removed " + self.comment ) |
| self.change = 'integrated' |
| comments.append ( "<a name='merged_%s'></a>" % ( shorttag, ) ) |
| if not shorttag in integrated_branches: |
| integrated_branches.append ( shorttag ) |
| integration = True |
| #print "before regex", line |
| line = line.replace( "INTEGRATION:", " ") |
| line = integrationMatch.sub("",line) |
| line = version.sub ( "", line ) |
| line = line.replace ( "();", "" ) |
| line = line.replace ( "(); FILE MERGED", " by " ) |
| line = line.replace ( "FILE MERGED", "" ) |
| line = line.replace ( "FILE ADDED", "" ) |
| line = line.replace ( "RESYNC:;", "" ) |
| if summarizeIntegration : |
| match = mergedBranch.match ( line ) |
| if match: |
| self.mergedBranch = match.group(1) |
| line = line.replace ( "CWS", "" ) |
| #print "integration file changes:", line |
| line = re.sub("RESYNC: (.*);","RESYNC:;", line ) |
| line = re.sub("<","<", line ) |
| line = re.sub(">",">", line ) |
| comments.append ( line.replace("Log:", "" ) + " " ) |
| line = self.nextLine() |
| if line.find ( "Revision Changes Path" ) != -1 : |
| #print >>sys.stderr, "FILE? ", line |
| break |
| if line.find ( "File " ) != -1 : |
| #print >>sys.stderr, "FILE? ", line |
| break |
| if integration and not self.mergedBranch: |
| print >> sys.stderr, "Some problem with getting the CWS tag" |
| self.printLog() |
| sys.exit ( -1 ) |
| comments.append ( "</span>" ) |
| self.comment = "".join ( comments ) |
| #print >> sys.stderr, "comments are", self.comments |
| #print >> sys.stderr, "comments came from", comments |
| return line |
| def addChangeURLs(self, line): |
| while not self.finished(): |
| if line.find( "http://") != -1 and line.find( "?") != -1: |
| self.URLs .append ( line.strip() ) |
| line = self.nextLine() |
| if line.find ( "Index:" ) != -1 \ |
| or line.find ( "To unsubscribe" ) != -1: |
| return line |
| def process ( self ): |
| while not self.finished(): |
| line = self.nextLine() |
| #line = re.sub("&","&", line ) |
| #print line |
| if line.find ( "User:") != -1 : |
| self.processUser ( line ) |
| elif line.find("Added:") != -1 or line.find("Removed:") != -1 or \ |
| line.find("Modified:") != -1 or line.find("New directory") != -1: |
| line = self.processAffectedTagsAndFiles(line) |
| if not self.finished() and line.find ( "Log:" ) != -1 : |
| line = self.processLogMessage ( line ) |
| match = lineChangeMatch.match ( line ) |
| if match: |
| global noOfCommits |
| global noOfLinesAdded |
| global noOfLinesRemoved |
| noOfCommits += 1 |
| noOfLinesAdded += int ( match.group(1) ) |
| noOfLinesRemoved += int ( match.group(2) ) |
| |
| if not self.finished() and ( line.find ( "diff?r1" ) != -1 or \ |
| line.find("?rev=") != -1 ): |
| line = self.addChangeURLs ( line ) |
| break |
| def printLog(self): |
| print >>sys.stderr, "changed by :[", self.user, "]" |
| print >>sys.stderr, "changed tag:", self.tag |
| if self.change == None: |
| print >>sys.stderr, "probelm with this payload :", self.lines |
| else: |
| print >>sys.stderr, "type :[", self.change, "]" |
| print >>sys.stderr, "comment :", self.comment |
| print >>sys.stderr, "URLs :", self.URLs |
| print >>sys.stderr, "file :", self.files |
| print >>sys.stderr, "" |
| |
| class CVSLog2: |
| def __init__( self, log ): |
| self.msg = parser.parsestr ( log ) |
| payload = self.msg.get_payload() |
| if isinstance(payload,list): |
| print >>sys.stderr, "found no payload, will try to use the log"# for:", log |
| print >>sys.stderr, payload |
| lines = log |
| for i in range(len(lines)): |
| if lines[i].startswith(" User"): |
| break |
| self.lines = lines[:i] |
| print >>sys.stderr, "lines set to",self.lines |
| else: |
| self.lines = payload.split("\n") |
| if verbose: |
| print >>sys.stderr, self.lines |
| #print "no of lines=", len(self.lines) |
| self.currLine = 0 |
| self.comment = "" |
| self.URLs = [] |
| self.files ="" |
| self.tag = "" |
| self.change = None |
| self.mergedBranch = None |
| self.process() |
| def currentLine( self ): |
| return self.lines [ self.currLine ] |
| def nextLine( self ): |
| line = self.lines [ self.currLine ] |
| self.currLine += 1 |
| return line |
| def finished( self ): |
| if len(self.lines) == self.currLine+1 or self.lines[self.currLine].find("To unsubscribe") == 0: |
| if verbose: print >>sys.stderr, "finished" |
| return True |
| return False |
| def processTag(self, line): |
| self.tag = re.sub(".*Tag:[ ]*", "", line.strip() ) |
| def processTypeOfChange ( self, line ): |
| if line.startswith ( "Modified" ): |
| self.change = "modified" |
| elif line.startswith ( "Removed" ): |
| self.change = "removed" |
| elif line.startswith ( "Added" ): |
| self.change = "added" |
| def processUser(self, line): |
| self.user = line.split ( "User: " )[1].strip() |
| def processLogMessage(self, line): |
| integration = False |
| line = self.nextLine() |
| comments = [] |
| comments.append ( "<span class='comment'>" ) |
| while not self.finished(): |
| #remove version nos as in RESYNC: (1.7-1.8); FILE MERGED |
| if integration or line.find ( "INTEGRATION" ) != -1 : |
| match = cws_branch.search ( line ) |
| shorttag = "" |
| if match : |
| if not ( match.group(1) in merged_branches): |
| merged_branches .append ( match.group(1) ) |
| shorttag = match.group(1) |
| if self.change == "removed": |
| comments.append ( "removed " + self.comment ) |
| self.change = 'integrated' |
| comments.append ( "<a name='merged_%s'></a>" % ( shorttag, ) ) |
| if not shorttag in integrated_branches: |
| integrated_branches.append ( shorttag ) |
| integration = True |
| #print "before regex", line |
| line = line.replace( "INTEGRATION:", " ") |
| line = integrationMatch.sub("",line) |
| line = version.sub ( "", line ) |
| line = line.replace ( "();", "" ) |
| line = line.replace ( "(); FILE MERGED", " by " ) |
| line = line.replace ( "FILE MERGED", "" ) |
| line = line.replace ( "FILE ADDED", "" ) |
| line = line.replace ( "RESYNC:;", "" ) |
| if summarizeIntegration : |
| #print >>sys.stderr, "summarizing: [%s]" % ( line, ) |
| match = mergedBranch.search ( line ) |
| if match: |
| self.mergedBranch = match.group(1) |
| #else: |
| #print >>sys.stderr, "no match", line |
| line = line.replace ( "CWS", "" ) |
| #print "integration file changes:", line |
| if self.change == "added" and line.startswith( " Directory" ): |
| line = re.sub ( "added to the repository", "", line ) |
| line = re.sub ( "/cvs/", "", line ) |
| elif self.change == "added" and line.startswith( " --> Using" ): |
| line = re.sub ( "--> Using per-directory sticky", " using ", line ) |
| line = re.sub("RESYNC: (.*);","RESYNC:;", line ) |
| line = re.sub("<","<", line ) |
| line = re.sub(">",">", line ) |
| comments.append ( line ) |
| line = self.nextLine() |
| if line.startswith ( "File Changes:" ) : |
| #print >>sys.stderr, "FILE? ", line |
| break |
| comments.append ( "</span>" ) |
| self.comment = "".join ( comments ) |
| if integration and summarizeIntegration and not self.mergedBranch: |
| print >> sys.stderr, "Some problem with getting the CWS tag:", comments |
| self.printLog() |
| #sys.exit ( -1 ) |
| #print >> sys.stderr, "comments are", self.comments |
| #print >> sys.stderr, "comments came from", comments |
| return line |
| def processChangedFiles(self, line): |
| addDir = None |
| while not self.finished(): |
| if line.startswith ( "Url: "): |
| url = line.replace( "Url: ", "" ) |
| self.URLs.append ( url ) |
| #print >> sys.stderr, "DELTA? ", line |
| if line.startswith ( "Delta lines:"): |
| #print >> sys.stderr, "DELTA:", line |
| match = lineChangeMatch.search ( line ) |
| if match: |
| #print >> sys.stderr, "---DELTA!", line |
| global noOfCommits |
| global noOfLinesAdded |
| global noOfLinesRemoved |
| noOfCommits += 1 |
| noOfLinesAdded += int ( match.group(1) ) |
| noOfLinesRemoved += int ( match.group(2) ) |
| #print >> sys.stderr, "noOfCommits = ", noOfCommits |
| #print >> sys.stderr, "noOfLinesAdded = ", noOfLinesAdded |
| if line.startswith ( "Directory:" ): |
| addDir = line.replace ( "Directory: ", "" ) |
| if line.startswith ( "File [removed]" ): |
| self.files += line.replace ( "File [removed]:", "" ) |
| if self.change == "added" and line.startswith ( "Directory [added]:" ): |
| self.files = addDir |
| if self.change == "removed" and line.startswith ( "Directory [removed]:" ): |
| self.files = addDir |
| line = self.nextLine() |
| |
| def process ( self ): |
| while not self.finished(): |
| line = self.nextLine() |
| #line = re.sub("&","&", line ) |
| #print line |
| if line.find ( "Tag:") != -1 : |
| self.processTag ( line ) |
| elif line.find ( "User:") != -1 : |
| self.processUser ( line ) |
| elif line.find("Added:") != -1 or line.find("Removed:") != -1 or \ |
| line.find("Modified:") != -1 or line.find("New directory") != -1: |
| self.processTypeOfChange(line) |
| elif line.find ( "Log:" ) != -1: |
| line = self.processLogMessage ( line ) |
| |
| if not self.finished() and line.find ( "File Changes:" ) != -1 : |
| line = self.processChangedFiles ( line ) |
| |
| def printLog(self): |
| print >>sys.stderr, "changed by :[", self.user, "]" |
| print >>sys.stderr, "changed tag:", self.tag |
| if self.change == None: |
| print >>sys.stderr, "probelm with this payload :", self.lines |
| else: |
| print >>sys.stderr, "type :[", self.change, "]" |
| print >>sys.stderr, "comment :", self.comment |
| print >>sys.stderr, "URLs :", self.URLs |
| print >>sys.stderr, "file :", self.files |
| print >>sys.stderr, "" |
| |
| def processMessage ( message ): |
| headers = parser.parsestr ( message, True ) |
| if verbose: print >>sys.stderr, "************\nsubject =", headers[ "Subject" ] |
| |
| #### if headers["Subject"] and headers[ "Subject" ].find ( "CVS update" ) != -1 : |
| #if ( message.find ( "Subject: CVS update" ) != -1 ) or \ |
| if headers["Subject"] and headers[ "Subject" ].find ( "CVS update" ) != -1: |
| #convert from format : 21 Oct 2003 08:24:30 -0000 |
| #to the tuple : (2003, 10, 21, 8, 53, 54, 1, 294, 0) |
| msgTime = time.strptime ( headers[ "Date" ], "%d %b %Y %H:%M:%S -0000" ) |
| global oldest |
| global youngest |
| |
| realMsgTime = time.mktime(msgTime) |
| if realMsgTime < oldest or oldest == 0: |
| oldest = realMsgTime |
| if realMsgTime > youngest or youngest == 0: |
| youngest = realMsgTime |
| dayOfYear = 300 ### time.strftime ( "%j", msgTime ) |
| #print "checking this date", msgTime, ": ", sinceDay, "=? ", int(dayOfYear) |
| if 1: #int(dayOfYear) >= sinceDay: |
| #print "process this \n", message |
| log = CVSLog2 ( message ) |
| if verbose: |
| log.printLog() |
| createAll = False |
| if not logs .has_key ( log.tag ): |
| logs [ log.tag ] = {} |
| createAll = True |
| |
| if summarizeIntegration and log.change=="integrated": |
| log.comment = "<a name='merged_%s'></a><span class='comment'>%s</span>" % \ |
| ( log.mergedBranch, log.mergedBranch ) |
| log.comment += "[ <a href='%s#%s'>description</a>]" % ( |
| "http://development.openoffice.org/releases/OOo_2_0_timetable.html", \ |
| log.mergedBranch, ) |
| |
| if not log.user in developers: |
| developers.append ( log.user ) |
| |
| if createAll or not logs [ log.tag ].has_key( log.user ): |
| logs [ log.tag ][log.user] = {} |
| createAll = True |
| if createAll or not logs [ log.tag ][log.user].has_key( log.change ): |
| logs [ log.tag ][log.user][log.change] = {} |
| createAll = True |
| if createAll or not logs [ log.tag ][log.user][log.change].has_key( log.comment ): |
| logs [ log.tag ][log.user][log.change][log.comment] = [] |
| if len(log.URLs) : |
| #print >>sys.stderr, "files:", log.URLs |
| logs [ log.tag ] [ log.user ] [ log.change ] [ log.comment ] += log.URLs |
| if log.change=="integrated" or ( log.change=="modified" and \ |
| ( log.comment.find(u"RESYNC:; FILE MERGED") != -1 ) ): |
| pass |
| else: |
| for url in log.URLs: |
| match = projectMatch.search ( url ) |
| if not projects.has_key ( match.group(1) ): |
| projects[match.group(1)] = [] |
| if log.tag in projects[match.group(1)] : pass |
| else: |
| projects[match.group(1)].append ( log.tag ) |
| else: |
| if log.change != "removed" and len(log.files) == 0: |
| print >>sys.stderr, "no files and no URLs!!" |
| print >>sys.stderr, "message=", message |
| log.printLog() |
| logs [ log.tag ] [ log.user ] [ log.change ] [ log.comment ].append( log.files ) |
| |
| def replaceWithIZLink( match): |
| iz = match.group() #match.string [ match.start():match.end()] |
| shortIz = iz |
| shortIz = shortIz.replace( "i", "") |
| shortIz = shortIz.replace( "#", "") |
| shortIz = shortIz.replace( "#", "") |
| sIz = int(shortIz) |
| if sIz == 2000 or sIz == 2002 or sIz == 2003 or sIz == 2004 or sIz >= 100000: |
| return iz |
| #return "<a href='http://www.openoffice.org/issues/show_bug.cgi?id=" + \ |
| # shortIz + "'> " + iz + "</a>" |
| return "<a href='http://www.openoffice.org/issues/show_bug.cgi?id=%s'> %s</a>" \ |
| % ( shortIz, iz ) |
| |
| def getFullName ( abbrev ): |
| name = abbrev.upper() + "?" |
| try: |
| if not ( abbrev in names ) : |
| print >> sys.stderr, "Could not find full name for", abbrev |
| else: |
| name = names [ abbrev ].encode("latin-1") |
| except: |
| print >>sys.stderr, "did you forget to put a \"u\" infront of the full name for :",abbrev,"?" |
| return name |
| |
| def getProjectName ( proj ): |
| name = proj |
| if not ( proj in projectNames ) : |
| print >> sys.stderr, "Could not find project name for", proj |
| else: |
| name = projectNames [ proj ] |
| projectNames [ proj ] = None |
| return name |
| |
| def processBranchChanges( grouping, tag, branchChanges ): |
| for user, changes2 in branchChanges.items(): |
| if tag == "": tag = "HEAD" |
| merged = "" |
| css_tag = "branch" |
| if grouping: |
| for mbranch in merged_branches: |
| if len(tag) and tag.find ( mbranch ) != -1: |
| merged = " [ <a href='#merged_%s'>and it was merged</a> ]" \ |
| % ( mbranch, ) |
| css_tag = "mergedbranch" |
| |
| print "\n<a name='%s'></a><a name='%s_%s'></a><div class=\"%s\">%s was changed by %s(%s)%s</div>" % \ |
| ( tag, tag, user, css_tag, tag, getFullName(user), user, merged ) |
| mergedBranches = {} # "merged_branch": ( file1, file2 , etc ) |
| for change, changes3 in changes2.items(): |
| # if change == "integrated" and summarizeIntegration : |
| # # build summary of files on branches here |
| # for comment, elements in changes3.items(): |
| # pass |
| # |
| # else: |
| for comment, elements in changes3.items(): |
| comment = bugMatch.sub( replaceWithIZLink, comment ) |
| # print >>sys.stderr, "comment is ", comment |
| print "<div class='%s'>%s : %s" % ( change, change, comment ) |
| print "<div class=\"file-list\">" |
| if len (elements) : |
| files={} |
| fileNo = 0 |
| for i in elements: |
| fileNo += 1 |
| if i.find ( "http:" ) == -1: |
| cleanest = i.strip().replace ( " ", "/" ) |
| else: |
| clean = re.sub("http://.*/browse/","", i ) |
| cleaner = re.sub("\.diff\?.*","", clean ) |
| cleanest = re.sub("\?.*","", cleaner ) |
| if verbose: |
| print >>sys.stderr, "stripped:", cleanest |
| #dirE = re.sub("/[a-zA-Z0-9_.]*","",cleanest) |
| file = re.sub(".*/","",cleanest) |
| dirE = cleanest [ :-len(file)] |
| if verbose: |
| print >>sys.stderr, "branch=",tag, "dir=", dirE, " and file=", file |
| if not files.has_key( dirE ): files [ dirE ] = [] |
| if i.find ( "http:" ) == -1: |
| files [ dirE ].append ( file ) |
| else: |
| if suppressFilesOn .has_key( tag ) and suppressFilesOn [ tag ].has_key(user) \ |
| and file.find ( suppressFilesOn [ tag ][user] ) != -1 : |
| |
| files [ dirE ].append ( "<a href='%s'>%s</a>" % ( i, fileNo )) |
| else: |
| files [ dirE ].append ( "<a href='%s'>%s</a>" % ( i, file )) |
| |
| if change=="removed" or ( change=="modified" and \ |
| ( comment.find(u"RESYNC:; FILE MERGED") != -1 ) ): |
| #print >>sys.stderr, change, files |
| print " files from:" |
| for d,fs in files.items(): |
| if len(fs) == 1: |
| print "%s%s" % ( d, fs[0] ) |
| else: |
| print "%s:" %( d, ) |
| for f in fs: |
| print "%s," %( f, ) |
| else: |
| for d,fs in files.items(): |
| if len(fs) == 1: |
| print "\t\t%s%s" % ( d, fs[0] ) |
| else: |
| print "\t\t", d, " : ", |
| for i in range(len(fs)-1): |
| print "%s," % ( fs[i],), |
| print "%s ;" % ( fs[len(fs) -1],) |
| |
| #print "<br>" |
| else: |
| print "\t\t", |
| for i in elements: |
| clean = re.sub("http://.*/browse/","", i ) |
| cleaner = re.sub("\.diff\?.*","", clean ) |
| cleanest = re.sub("\?.*","", cleaner ) |
| #dirE = re.sub("/[a-zA-Z0-9_.]*","",cleanest) |
| #file = re.sub(".*/","",cleanest) |
| if len ( cleaner ) == 0: |
| cleanest = re.sub("\?rev=.*","", clean ) |
| print "<a href='%s'> %s</a>" % (i, cleanest) |
| if len(elements) > 1: ", ", |
| #print "<br>" |
| print "</div>" |
| print "</div>" |
| print "<p>" |
| |
| def printIndexOfBranches ( startsWith ): |
| cws = [] |
| for tag, changes in logs.items() : |
| if tag.startswith ( startsWith ): |
| cws.append ( tag ) |
| cws.sort() |
| grouping = "x" |
| for tag in cws: |
| if not tag.startswith ( grouping ): |
| grouping = tag [ :tag.rfind("_") ] |
| print "</td></tr><tr><td class='group'>",grouping, "</td><td>" |
| shorttag = tag.replace ( grouping, "" )[1:] |
| if shorttag in merged_branches: |
| css_tag = "mergedbranch" |
| else: |
| css_tag = "branch" |
| print "<a class='index_%s' href='#%s'>%s</a> " % ( css_tag, tag, shorttag ) |
| return cws |
| |
| def printIndexOfIntegratedBranches (): |
| if len(integrated_branches): |
| integrated_branches.sort() |
| for tag in integrated_branches: |
| print "<a class='index_mergedbranch' href='#merged_%s'>%s</a> " % ( tag, tag ) |
| else: |
| print " none " |
| |
| def printBranchList(): |
| print "<a name='branch_index'></a><H2>Branches with changes this week</H2>" |
| print "<small><center>( from ", oldestMsg, "<br>to", youngestMsg, ")</center></small><br>" |
| |
| print "<table class='branch_index' cellspacing='0'>" |
| print "<colgroup><col class='branch_group'/><col class='branch_names'/></colgroup><tr>" |
| print "<th>Group</th><th>Branch" |
| |
| ws = printIndexOfBranches ( "cws_" ) |
| mws = printIndexOfBranches ( "mws_" ) |
| ws.extend ( mws ) |
| |
| print "</td></tr><tr><td class='group'> merged branches </td><td>" |
| printIndexOfIntegratedBranches () |
| print "</td></tr><tr><td class='group'> misc </td><td>" |
| |
| for tag, changes in logs.items() : |
| if not tag in ws: |
| print "<a href='#%s'>%s</a> " % ( tag, tag ) |
| |
| # print "<a href='#HEAD' style='word-spacing: 1em;'>HEAD and other branches</a> " |
| print " <a href='#HEAD'>HEAD and other branches</a> " |
| print "</td></tr></table>" |
| |
| def printProjectBranch( tag, shorttag ): |
| if shorttag in merged_branches: |
| css_tag = "mergedbranch" |
| else: |
| css_tag = "branch" |
| print "<a class='index_%s' href='#%s'>%s</a> " % ( css_tag, tag, shorttag ) |
| |
| def printProjectList(): |
| print "<a name='project_index'></a><H2>Projects which were changed</H2>" |
| print "<small>(excluding integration and resyncing)</small><br>" |
| print "<table class='project_index' cellspacing='0'>" |
| print "<colgroup><col class='project'/><col class='branch_group'/><col class='branch_names'/></colgroup>" |
| print "<tr><th class='project_col'>Project<br>(linked to the project page)</th>" |
| print "<th>Branch Group</th><th class='branch_col'>Branch<br>" |
| print "(linked to the branch changes below)</th></tr>" |
| |
| childWS = re.compile ( r"cws_([a-zA-Z0-9]+)_([a-zA-Z0-9]+)" ) |
| masterWS = re.compile ( r"mws_([a-zA-Z0-9]+)" ) |
| projkeys = projects.keys() |
| projkeys.sort() |
| for proj in projkeys: |
| miscTags = [] |
| mwsTags = [] |
| cwsTags = {} |
| for tag in projects[ proj ]: |
| if len(tag) == 0: tag = "HEAD" |
| match = childWS.search ( tag ) |
| if match : |
| if not cwsTags . has_key ( match.group(1) ) : |
| cwsTags [ match.group(1) ] = [] |
| cwsTags [ match.group(1) ].append ( ( match.group(2), tag ) ) |
| else: |
| match = masterWS.search ( tag ) |
| if match : |
| mwsTags.append( ( match.group(1), tag ) ) |
| else: |
| miscTags.append ( tag ) |
| #cwsTags.sort() |
| branch_groups = (len ( mwsTags ) != 0 ) + len ( cwsTags ) + ( len( miscTags )!=0 ) |
| print "<tr><td rowspan='%i'><a class='project' href='http://%s.openoffice.org'>%s (aka %s)</a></td>" \ |
| % ( branch_groups, proj, getProjectName(proj), proj ) |
| firstbranchOfProject = True |
| if len(cwsTags) > 0: |
| for group in cwsTags.keys(): |
| if not firstbranchOfProject : |
| print "<tr>" |
| firstbranchOfProject = False |
| print "<td>",group,"</td>" |
| tags = cwsTags [ group ] |
| tags.sort() |
| print "<td>" |
| for tag in tags: |
| printProjectBranch ( tag[1], tag[0] ) |
| print "</td></tr>" |
| |
| if len(mwsTags) > 0: |
| if not firstbranchOfProject : |
| print "<tr>" |
| firstbranchOfProject = False |
| print "<td>master</td><td>" |
| for shortTag,tag in mwsTags: |
| printProjectBranch ( tag, shortTag ) |
| print "</td></tr>" |
| |
| if len(miscTags) > 0: |
| if not firstbranchOfProject : |
| print "<tr>" |
| firstbranchOfProject = False |
| miscTags.sort() |
| print "<td>misc<td>" |
| for tag in miscTags: |
| printProjectBranch ( tag, tag ) |
| print "</td></tr>" |
| |
| print "</table>" |
| |
| def usage(): |
| print >>sys.stderr, sys.argv[0], " usage:" |
| print >>sys.stderr, sys.argv[0], "[-h] [-v] [-z] [-s suppress-condition>] -c <CVS log file> -w weekNo -i intro " |
| print >>sys.stderr, "where :" |
| print >>sys.stderr, "\t -h or --help : prints this message" |
| print >>sys.stderr, "\t -v or --verbose : prints more verbose messages" |
| print >>sys.stderr, "\t -z or --summarizeintegration : summarizes the integration messages" |
| print >>sys.stderr, "\t -c or --cvslog= : takes an argument which contains the cvs log to digest" |
| print >>sys.stderr, "\t -w or --week= : takes an argument which is the week number in question (1-53) " |
| print >>sys.stderr, "\t -o or --output= : output file name" |
| print >>sys.stderr, "\t -i or --intro= : takes an argument which contains the hand written introduction " + \ |
| "to the digest e.g. the Issuezilla statistics" |
| print >>sys.stderr, "\t -s or --suppressFilesOn= : takes an argument which lists what files to suppress" |
| print >>sys.stderr, "\t\t\t e.g. -s \"{'cws_srx645_alphaart':{'mmeeks':'.bmp'},}\" causes the files" + \ |
| "on the branch cws_srx645_alphaart modified by mmeeks which contain the string .bmp to be numbered only" |
| |
| try: |
| opts,args = getopt.getopt(sys.argv[1:],"hvzw:i:c:s:o:", |
| ["help","verbose","summarizeintegration","output=","week=","intro=","cvslog=","suppressFilesOn="]) |
| except: |
| usage() |
| sys.exit(-1) |
| |
| intro = None |
| cvslogName = None |
| outputFilename = None |
| |
| now = time.time() |
| nowTime = time.gmtime(now) |
| since = time.strftime ( "%j", nowTime ) |
| sinceDay = int(since) - 300 |
| |
| cvsYear = time.strftime( "cvs%Y_", time.gmtime(now) ) |
| |
| for o,a in opts: |
| if verbose: |
| print >>sys.stderr, o, "=", a |
| if o in ( "-v", "--verbose" ): |
| print >>sys.stderr, "turning on verbose" |
| verbose = True |
| if o in ( "-h", "--help" ): |
| usage() |
| sys.exit ( 0 ) |
| if o in ( "-w", "--week" ): |
| week = a |
| if not cvslogName : cvslogName = "cvs" + a + ".log" |
| if not intro: intro = "intro" + a + ".html" |
| if not outputFilename: outputFilename = cvsYear + a + ".html" |
| if o in ( "-i", "--intro" ): |
| intro = a |
| if o in ( "-o", "--output" ): |
| outputFilename= a |
| if o in ( "-c", "--cvslog" ): |
| cvslogName = a |
| if o in ( "-z", "--summarizeintegration" ): |
| summarizeIntegration = True |
| if o in ( "-s", "--suppress" ): |
| suppressFilesOn = eval ( a ) |
| |
| sys.stdout = open("out.txt", "w") |
| |
| if not cvslogName or not week or not intro : |
| missing = "unknown" |
| if not cvslogName : |
| missing = "cvslog" |
| if not week : |
| missing = "week" |
| if not intro : |
| missing = "intro" |
| print >>sys.stderr, "ERROR:missing input parameter", missing, "!" |
| usage() |
| sys.exit(-1) |
| |
| if outputFilename: |
| sys.stdout = open( outputFilename, "w") |
| |
| cvslog=open(cvslogName) |
| parser = email.Parser.Parser() |
| |
| maybeNewMessageNext = False |
| message = "" |
| |
| |
| logs={} |
| #print "processing", sys.argv[1] |
| i = 0 |
| message = [] |
| while 1: |
| line= cvslog.readline() |
| if i > 1000 and not verbose: |
| i = 0 |
| sys.stderr.write( "." ) |
| if verbose ==2 : |
| print line, |
| if not line: |
| break |
| if maybeNewMessageNext and ( line.startswith("From" ) or line.startswith("Path:" ) ): |
| if verbose: |
| print "process this message" |
| processMessage ( "".join ( message ) ) |
| message = [] |
| if line == "\n": |
| maybeNewMessageNext = True |
| else: |
| maybeNewMessageNext = False |
| message.append ( line ) |
| i += 1 |
| sys.stderr.write( "\n" ) |
| |
| if len(message): |
| processMessage ( "".join ( message ) ) |
| |
| |
| #print "printing formatted log" |
| print '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' |
| print '<html><head>' |
| print ' <meta HTTP-EQUIV="content-type" CONTENT="text/html; charset=UTF-8">' |
| #print ' <META name="generator" content="HTML Tidy for Linux/x86 (vers 1st November 2003), see www.w3.org">' |
| print ' <style type="text/css">@import url("cvs.css"); </style>' |
| print ' <TITLE>Development digest : Week', week, '</TITLE>' |
| print '</head>' |
| print '<body><div class="digest"><div class="leadin">' |
| |
| if len(sys.argv) >2 and sys.argv[2]: |
| print open ( intro ).read() |
| |
| print "<p><a name='cvs_stats'></a><H2>CVS statistics</h2>" |
| print noOfCommits, "commits by", len(developers), "developers resulted in" |
| print noOfLinesAdded, "lines added, and", noOfLinesRemoved, "lines removed.<p>" |
| |
| oldestMsg = time.strftime( "%H:%M %A %B %d %Y UTC (week %W)", time.gmtime(oldest) ) |
| forWeek = time.strftime( "%W", time.gmtime(oldest) ) |
| youngestMsg = time.strftime( "%H:%M %A %B %d %Y UTC (week %W)", time.gmtime(youngest) ) |
| |
| printBranchList() |
| print "</div><p>" |
| printProjectList() |
| |
| |
| print "<a name='details'></a><H2>Detailed changes on each branch</H2>" |
| |
| for tag, changes in logs.items() : |
| if tag.startswith ( "cws_" ): |
| processBranchChanges( "cws_", tag, changes ) |
| logs[tag]=None |
| |
| for tag, changes in logs.items() : |
| if tag.startswith ( "mws_" ): |
| processBranchChanges( "mws_", tag, changes ) |
| logs[tag]=None |
| |
| print "<a name='HEAD'></a>" |
| for tag, changes in logs.items() : |
| if changes: |
| processBranchChanges( None, tag, changes ) |
| |
| print "</div></div></body></html>" |
| |
| for proj,fullName in projectNames.items(): |
| if fullName: |
| print >>sys.stderr, "Warning: suspiciously no updates for project %s (aka %s)" % ( fullName, proj ) |