''' Created on Jun 18, 2013 @author: acaproni ''' from datetime import datetime import SvnLogEntry class TwikiChangeLog: ''' TwikiChangeLog writes the ChangeLog in Twiki format. We don't care of the changes for author (we do not want to blame anybody here) but we want to have the page with the following format: ---+ Title %TOC% ---------- ---++ Stats ---++ ChangeLog by date ---++ Change by module ---++ Change by file This class allows also to create the changes by developer but this is not part of the ChangeLog. It is useful for ACS staff to write the ReleaseNotes. ''' def __init__(self, entries): ''' Constructor entries: the list of svn logs to manipulate (i.e a list of SvnLogEntry objects) ''' self.entries=entries def printEntry(self,entry,outFile): ''' Format and print the passed entry entry: the entry (SvnLogEntry) to be formatted and printed outFile: the file to write the twiki page int ''' outFile.write("*"+entry.date+"* _"+entry.author+"_\n") for path in entry.files: outFile.write(" * "+path.action+" ="+path.path+"= ("+path.kind+")\n") outFile.write("
"+entry.msg.strip()+"
\n") outFile.write("-------\n") def printFileEntry(self, fileName, svnEntries,outFile): ''' Format and print a file entry that is potentially composed of several entry (SvnLogEntry) if the files has been changed and committed more then once. fileName: the name (path) of the file svnEntries: the list of svn entries (SvnLogEntry) outFile: the file to write the twiki page int ''' outFile.write("*"+fileName+"*\n") for entry in svnEntries: outFile.write("
\n") outFile.write(" * "+entry.date+" _"+entry.author+"_\n") outFile.write("
"+entry.msg.strip()+"
\n") outFile.write("
\n") outFile.write("-------\n") def printStats(self,outFile): ''' print some stats on the stdout outFile: the file to write the twiki page int ''' outFile.write("---++ Stats\n") outFile.write('*Total number of commits:'+str(len(self.entries))+'*\n\n') # Print the number of entries for author # It is the number of commit and not the number of files because each commit can # include more then one source file changesByAuthor={} # The number of files modified by each author (this is not the number of log entries) modifiedFilesForAuthor={} for entry in self.entries: if entry.author in changesByAuthor: changesByAuthor[entry.author]=changesByAuthor[entry.author]+1 else: changesByAuthor[entry.author]=1 if entry.author in modifiedFilesForAuthor: modifiedFilesForAuthor[entry.author]=modifiedFilesForAuthor[entry.author]+len(entry.files) else: modifiedFilesForAuthor[entry.author]=len(entry.files) authors=changesByAuthor.keys() authors.sort() outFile.write("| *Author* | *# Commits* | *# Changed sources* |\n") for author in authors: outFile.write("| "+author+" | "+str(changesByAuthor[author])+" | "+str(modifiedFilesForAuthor[author])+" |\n") def changeByDate(self,outFile): ''' Scans the entries to generate the entries by date. Note that this is the order of the entries returned by SVN so no check is done here but only formatting outFile: the file to write the twiki page int ''' outFile.write("---++ ChangeLog by date\n") for entry in self.entries: self.printEntry(entry,outFile) def changeByDeveloper(self,outFile): ''' Create the table with the changes done by each developer. This is not included in the ChangeLog but used internally by ACS to write the release notes. outFile: the file to write the twiki page int ''' # Scans all the entries (that are ordered by time) # and create a dictionary for each user outFile.write("---+ Changes by developer\n") outFile.write("%TOC%\n") outFile.write("-------------\n") userChanges={} for entry in self.entries: if entry.author not in userChanges: userChanges[entry.author]=[] userChanges[entry.author].append(entry) # Scans the list and generate the twiki page. authors=userChanges.keys() authors.sort() for author in authors: outFile.write("---++ "+author+"\n") for entry in userChanges[author]: self.printEntry(entry,outFile) def createTwikiPage(self,outFile): ''' Create the whole twiki page delegating to the other methods. outFile: the file to write the twiki page int ''' now=datetime.now() outFile.write("---+ SVN %NOP%ChangeLog\n") outFile.write("Automatically generated on "+now.isoformat()+"\n") outFile.write("%TOC%\n") outFile.write("----------\n\n") self.printStats(outFile) self.changeByDate(outFile) self.changeByFile(outFile) def changeByFile(self,outFile): ''' Generate the change log by file. Differently from CVS, each commit in SVN can involve more files at the same time outFile: the file to write the twiki page int ''' outFile.write("---++ ChangeLog by file\n") # Scans all the entries and produce a new entry for each of files it contain. # At the end of this pass there is a bigger list of entries each of which contains # only one file entriesWithOneFileOnly=[] for entry in self.entries: if len(entry.files)==1: entriesWithOneFileOnly.append(entry) else: # more files then split in more entries for filePath in entry.files: fileList=[] fileList.append(filePath) newLogEntry=SvnLogEntry.SvnLogEntry(entry.revision,entry.author,entry.date,fileList,entry.msg) entriesWithOneFileOnly.append(newLogEntry) # We create a dictionary with key the file name and value # the list of entries of such file name entriesByFileName={} for entry in entriesWithOneFileOnly: fileName=entry.files[0].path.strip() if fileName not in entriesByFileName: entriesByFileName[fileName]=[] entriesByFileName[fileName].append(entry) # Sorted list of file names sortedFileNames=entriesByFileName.keys() sortedFileNames.sort() print len(entriesByFileName),"entries without duplication" for f in sortedFileNames: self.printFileEntry(f,entriesByFileName[f],outFile)