Author: mkupfer Date: Mon Apr 20 23:41:48 2009 New Revision: 40612
URL: http://svn.reactos.org/svn/reactos?rev=40612&view=rev Log: - changelog template generator for wiki changelog page - creates a complete changelog for the given xml file and try to put all entries in the best fitting section - the section file needs update to represent the whole wiki page structure - entries which don't fit (due to different paths) to a section are collected in "uncategorized" section
Added: trunk/tools/changelog/ trunk/tools/changelog/autocl.py (with props) trunk/tools/changelog/sections (with props)
Added: trunk/tools/changelog/autocl.py URL: http://svn.reactos.org/svn/reactos/trunk/tools/changelog/autocl.py?rev=40612... ============================================================================== --- trunk/tools/changelog/autocl.py (added) +++ trunk/tools/changelog/autocl.py [iso-8859-1] Mon Apr 20 23:41:48 2009 @@ -1,0 +1,172 @@ +#!/usr/bin/python + +# Creates basic changelog wiki page from svn log +# - argument is the generated xml log file with log entries +# created by 'svn log -v --xml -r revision1:revision2 repository' +# - needs internet access for retrieving the author names from wiki +# (http://www.reactos.org/wiki/index.php?title=Developer_Roles&action=edit) +# - needs a section file 'sections' +# this file contains all generated wiki-sections and the according paths +# to assign the log messages correctly +# +# (c) Matthias Kupfer 2009 + +import xml.sax # for the parser +import sys # for system functions +import string # for string operations +import re # for regular expressions +import time # for sorting +import urllib # for downloading the developer wiki page + +class XMLLogParser: + class XMLLogHandler(xml.sax.handler.ContentHandler): + __logentries = [] # log entries list + __logentry = {} + def startElement(self, name, attrs): + "Process all start tags." + self.__data = '' + attr = {} # temporary dictionary of attributes + for i in attrs.items(): # iterate over all attributes + tmp = i[1] # save attribute value + if tmp: # if value exists + tmp = tmp.encode('utf-8') # encode as utf-8 + attr[i[0].encode('utf-8')] = tmp # encode attribute name too + if name == 'logentry': + # save the log entry revision number (currently unused) + self.__logentry["revision"] = attr.get("revision") + elif name == 'paths': + # collect files and paths + self.__logentry["paths"] = [] # reset path + elif name == 'path': + # save file or path attributes + self.__pathattr = (attr.get("action"), + attr.get("copyfrom-path"), + attrs.get("copyfrom-rev")) # copy path attributes + def endElement(self, name): + "Process all end tags." + if name == 'logentry': + # append the created log entry and clear the temporary one + self.__logentries.append(self.__logentry) + self.__logentry = {} # clear current log entry + elif name == 'author': + # save the author name in the current log entry + self.__logentry["author"] = self.__data # copy author + elif name == 'date': + # extract timestamp and save it in the current log entry + m = re.match(r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):" \ + "(\d{2}).(\d{6})Z", self.__data) # regular expression for time + self.__logentry["time"] = time.mktime((int(m.group(1)), + int(m.group(2)), int(m.group(3)), int(m.group(4)), + int(m.group(5)), int(m.group(6)), -1, -1, -1)) + \ + (float("0."+m.group(7))) # build time entry + elif name == 'path': + # append a file or path entry to current log entry + self.__logentry["paths"].append((self.__data, self.__pathattr)) + elif name == 'msg': + # save message in current log entry + self.__logentry["msg"] = self.__data + def characters(self, ch): + "Save character data." + self.__data += ch.encode('utf-8') + def get_entry_list(self): + "Sort created list by revision in ascending order." + self.__logentries.sort(lambda x,y : cmp(y["revision"], x["revision"])) + return self.__logentries + __Handler = XMLLogHandler() # instance of content handler + get_entry_list = __Handler.get_entry_list + def __init__(self, file = None): + "Create a XML parser, set content handler and load the file." + self.parser = xml.sax.make_parser() # create a xml parser + self.parser.setContentHandler(self.__Handler) # set content handler + if file: + self.load(file) + def load(self, file): + "Parse a file." + self.parser.parse(file) # parse file + +def get_common_part(paths): + "returns the longest common path part of all file entries" + index = 0 # length + mypaths = [] # list of all paths + for i in paths: # extract all path from list + mypaths.append(i[0][0:string.rfind(i[0],'/')]) + if len(mypaths) == 1: # if only one path + return mypaths[0] # the return the whole path + else: + cmp = mypaths[0] # set first path for comparison + while index < len(cmp): + isequal = True + for i in mypaths[1:]: # for all remaining paths + if index >= len(i) or i[index] != cmp[index]: + isequal = False + break + if isequal == True: # if equal then next character + index += 1 + else: # else finish + break; + return cmp[0:index] + +def find_section(i,sections): + "find the best fitting (longest path) section" + section = "" # no section found + path = "" # no path + for k in sections.keys(): # iterate over all section paths + if i.startswith(k) and len(k) > len(path): # if i starts with path and + #is longer then the previous path then update path and section + section = sections[k] + path = k + return section # return wiki section name + +if __name__ == '__main__': + if len(sys.argv) != 2: + print "This script generates a template for the wiki changelog" + print + print "Syntax: %s <svn-log-xml-file>" % sys.argv[0] + print "log file should be created with:" + print "svn log -v --xml [-r revision1:revision2] repository > svn-log-xml-file" + exit(1) + sec_order = [] # list for the sections (to preserve) order + authors = {} # dictionary for commit name -> real names + sections = {} # dictionary for path -> sections + messages = {} # dictionary for section -> log messages + author = urllib.urlopen("http://www.reactos.org/wiki/index.php?title=Developer_Roles&action=edit%...) + for i in author: + m = re.match("^.*[[(.*)]].*||[^|]*||\s([^ ]*)\s||.*$",i) + if m: + authors[m.group(2)] = m.group(1) # fill author dictionary + section = open('sections').readlines() # read sections + for i in section: + m = re.match("^(/.*?) (.*)$",i) + if m: + sections[m.group(1)] = m.group(2) # fill section dictionary + sec_order.append(m.group(2)) # and section list + log = XMLLogParser(sys.argv[1]) # read log entries + for i in log.get_entry_list(): + lns = [] + lines = string.split(i['msg'],'\n'); + for k in lines: + m = re.match(r"^\s*[-*]*\s*(.*)\s*$",k) + if m: + l = string.strip(m.group(1)) + if len(l) > 0: + lns.append(l) + msg = string.join(lns) + author = authors[i['author']] + if not i.has_key('paths'): + print "Error: no path information found" + print "Did you really use the option '-v' in 'svn log'?" + print "Can't create template without path information" + exit(1) + path = get_common_part(i['paths']) + this_section = find_section(path,sections) + if messages.has_key(this_section): + messages[this_section].append((msg,author)) + else: + messages[this_section] = [(msg,author)] + for i in sec_order: + print i + if messages.has_key(i): + for k in messages[i]: + print '*',k[0],'([['+k[1]+']])' + +# vim: set ts=4 sw=4:
Propchange: trunk/tools/changelog/autocl.py ------------------------------------------------------------------------------ svn:eol-style = native
Propchange: trunk/tools/changelog/autocl.py ------------------------------------------------------------------------------ svn:executable = *
Added: trunk/tools/changelog/sections URL: http://svn.reactos.org/svn/reactos/trunk/tools/changelog/sections?rev=40612&... ============================================================================== --- trunk/tools/changelog/sections (added) +++ trunk/tools/changelog/sections [iso-8859-1] Mon Apr 20 23:41:48 2009 @@ -1,0 +1,42 @@ +This file contains the sections for the wiki page +every line that doens't start with '/' is ignored +the line consist of 2 parts: path wiki-header + +This file is fairly incomplete, please add path information step by step! + +/trunk/reactos/boot/freeldr == Bootloader (FreeLoader) == +/trunk/reactos/ntoskrnl == Kernel and Executive (NTOSKRNL) == +/trunk/reactos/ntoskrnl/cc === CC === +/trunk/reactos/ntoskrnl/cm === CM === +/trunk/reactos/ntoskrnl/dbgk === DBGK === +/trunk/reactos/ntoskrnl/ex === EX === +/trunk/reactos/ntoskrnl/io === IO === +/trunk/reactos/ntoskrnl/kd === KD === +/trunk/reactos/ntoskrnl/mm === MM === +/trunk/reactos/dll/cpl == CPL == +/trunk/reactos/subsystems == Subsystems == +/trunk/reactos/drivers == Kernel Mode Drivers == +/trunk/reactos/drivers/bus/acpi === ACPI === +/trunk/reactos/drivers/network/afd === AFD === +/trunk/reactos/drivers/bus/filesystems/cdfs === CDFS === +/trunk/reactos/drivers/bus/filesystems/ext2 === EXT2 === +/trunk/reactos/drivers/bus/filesystems/fastfat === FASTFAT === +/trunk/reactos/drivers/bus/filesystems/fs_rec === FS_REC === +/trunk/reactos/drivers/bus/filesystems/npfs === NPFS === +/trunk/reactos/drivers/bus/usb === USB === +/trunk/reactos/dll/ntdll == NT System Library (NTDLL) == +/trunk/reactos/dll/keyboard === Keyboard Layouts === +/trunk/reactos/base/setup == Setup == +/trunk/reactos/base/setup/usetup === USETUP === +/trunk/reactos/subsystems == Win32™ Personality == +/trunk/reactos/subsystems/win32/csrss === User mode subsystem (CSRSS) === +/trunk/reactos/subsystems/win32/win32k === Kernel-mode subsystem Server (Win32K) === +/trunk/reactos/dll/cpl == Control panel applets == +/trunk/reactos/dll/cpl/appwiz === APPWIZ === +/trunk/reactos/dll/cpl/desk === DESKTOP === +/trunk/reactos/dll/cpl/intl === INTL === +/trunk/reactos/dll/cpl/sysdm === SYSDM === +/trunk/reactos/dll/cpl/timedate === TIMEDATE === +/trunk/reactos/base/applications == Win32™ Applications == +/trunk/reactos/base/dll/win32 == Win32™ Libraries == +/trunk/reactos == uncategorized ==
Propchange: trunk/tools/changelog/sections ------------------------------------------------------------------------------ svn:eol-style = native