ITK Release 4/MigrationGuideDeveloperTutorial/InitializeXMLGuide

From KitwarePublic
Jump to navigationJump to search

<source lang="python">

  1. InitializeXMLGuide.py
  2. This script will parse the Git commits on the current branch and initializes
  3. an XML migration guide document with the apropriate content.

import os.path import sys import subprocess

  1. Helper Functions #
  1. strip the prefix from the string in order

def stripPrefix(string, prefix):

 if string[0:len(prefix)] == prefix:
   return string[len(prefix):len(string)]
 else:
   return string
  1. strip the suffix from the string in order

def stripSuffix(string, suffix):

 if string[len(string)-len(suffix):len(string)] == suffix:
   return string[0:len(string)-len(suffix)]
 else:
   return string
  1. test the start of a string

def startsWith(string, prefix):

 return (string.find(prefix) == 0)
  1. test the end of a string

def endsWith(string, suffix):

 return (string.rfind(suffix) == len(string) - len(suffix))
  1. get the output of an external command

def runCommand(command):

 args = command.split()
 process = subprocess.Popen(args, stdout = subprocess.PIPE)
 while process.returncode == None:
   process.poll()
 return process.communicate()[0]
  1. add proper number of indents

def getIndent(indent):

 if indent:
   return "  "
 else:
   return ""
  1. sub out protected characters

def prepXMLString(string):

 out = string.replace("<", "<")
 out = out.replace("&", "&")
 out = out.replace(">", ">")
 out = out.replace('"', """)
 return out.replace("'", "'")
  1. add an element to the XML file string

def addXMLElement(xmlString, elementName, elementText, indent=False, comment = ""):

 # comment
 if comment != "":
   xmlString = xmlString + getIndent(indent) + "\n"
 # opening tag
 xmlString = xmlString + getIndent(indent) + "<" + elementName + ">\n"
 # body
 for line in elementText.splitlines():
   xmlString = xmlString + getIndent(indent) + "  " + line + "\n"
 # closing tag
 return xmlString + getIndent(indent) + "</" + elementName + ">\n\n"
  1. add spaces for camel case string

def addCamelCaseSpaces(string):

 firstLetter = True
 out = ""
 for letter in string:
   if not firstLetter and letter.isupper():
     out = out + " " + letter
   else:
     out = out + letter
   firstLetter = False
 return out


  1. Main execution point #

if __name__ == '__main__':

 #
 # Get a unique XMLFileName from the user
 #
 # get directory info
 migrationDir = sys.path[0]
 baseDir = stripSuffix(migrationDir, "/Migration")
 # get XMLFileName (loop until name is unique)
 uniqueName = False
 XMLFileName = ""
 XMLFilePath = ""
 print "Please enter a unique name for the XML document"
 while uniqueName == False:
   # get the user's input
   nameCandidate = raw_input(">> ")
   if not endsWith(nameCandidate, ".xml"):
     nameCandidate = nameCandidate + ".xml"
   pathCandidate = migrationDir + "/" + nameCandidate
   # check uniqueness
   if not os.path.exists(pathCandidate):
     uniqueName = True
     XMLFileName = nameCandidate
     XMLFilePath = pathCandidate
   else:
     print '"' + nameCandidate + '" already exists.  Please specify a differnt name'
 # split the name for the title tag
 titleText = addCamelCaseSpaces(stripSuffix(XMLFileName, ".xml"))
 #
 # Get list of commits on current branch
 #
 # Get the current branch name
 HEADfilename = baseDir + "/.git/HEAD"
 try:
   HEADfile = open(HEADfilename, "r")
 except IOError:
   print "I/O error: Could not open .git/HEAD"
   sys.exit()
 branchName = HEADfile.readline()
 branchName = stripPrefix(branchName, "ref: refs/heads/")
 branchName = stripSuffix(branchName, "\n")
 # parse file in .git/logs/refs/heads/BRANCH_NAME
 branchLogFilename = baseDir + "/.git/logs/refs/heads/" + branchName
 try:
   branchLogFile = open(branchLogFilename, "r")
 except IOError:
   print "I/O error: Could not open branch log file"
   sys.exit()
 # grab the commit lines, but ignore ammended ones
 commitList = []
 for line in branchLogFile.readlines():
   if line.find("commit (amend):") == -1:
     commitList.append(line.split()[1])
   else:
     commitList.pop()
     commitList.append(line.split()[1])
 #
 # Parse each commit's log
 #
 descriptionText = ""
 changeIdText = ""
 sampleCodeOldText = ""
 sampleCodeNewText = ""
 changedFileList = []
 exampleAndTestChangedFileList = []
 firstCommit = commitList[0];
 commitList.remove(firstCommit)
 for commit in commitList:
   # get the log for the commit
   logCommand = "git log " + commit + " -n1 --stat"
   log = runCommand(logCommand)
   descriptionText = descriptionText + "---- " + commit + " ----\n"
   for line in log.splitlines():
     # commit message lines and change id lines
     if startsWith(line, "  "):
       if startsWith(line.strip(), "Change-Id: "):
         changeId = stripPrefix(line.strip(), "Change-Id: ")
         changeIdText = changeIdText + changeId + "\n"
       else:
         descriptionText = descriptionText + line.strip() + '\n'
     # changed file lines
     elif startsWith(line, " "):
       splits = line.split("|")
       if len(splits) == 2:
         filename = splits[0].strip()
         if not filename in changedFileList:
           changedFileList.append(filename)
         if startsWith(filename, "Examples") or startsWith(filename, "Testing"):
           exampleAndTestChangedFileList.append(filename)


 #
 # parse the diff of each Example or Testing file
 #
 for filename in exampleAndTestChangedFileList:
   # Add filename
   sampleCodeOldText = sampleCodeOldText + "---- " + filename + " ----\n"
   sampleCodeNewText = sampleCodeNewText + "---- " + filename + " ----\n"
   # get the log for the commit
   fullPath = baseDir + "/" + filename
   diffCommand = "git diff " + firstCommit + " " + commitList[len(commitList)-1] \
     + " -- " + fullPath
   diff = runCommand(diffCommand)
   # parse lines into old and new
   for line in diff.splitlines():
     # old line
     if (not startsWith(line, "--")) and startsWith(line, "-"):
       sampleCodeOldText = sampleCodeOldText + line.lstrip("-").strip() + "\n"
     # new line
     elif (not startsWith(line, "++")) and startsWith(line, "+"):
       sampleCodeNewText = sampleCodeNewText + line.lstrip("+").strip() + "\n"


 #
 # Create XMl file text
 #
 xmlString = '<?xml version="1.0" encoding="UTF-8"?>\n\n'
 changeElementBody = ""
 # <Title> element
 titleComment = "Title for the online migration page"
 changeElementBody = addXMLElement(changeElementBody, "Title", titleText, True, titleComment)
 # <Description> element
 descriptionComment = "Plain text description of the change\n-->Extracted from git commit messages"
 changeElementBody = \
   addXMLElement(changeElementBody, "Description",\
   prepXMLString(des# InitializeXMLGuide.py
  1. This script will parse the Git commits on the current branch and initializes
  2. an XML migration guide document with the apropriate content.

import os.path import sys import subprocess

  1. Helper Functions #
  1. strip the prefix from the string in order

def stripPrefix(string, prefix):

 if string[0:len(prefix)] == prefix:
   return string[len(prefix):len(string)]
 else:
   return string
  1. strip the suffix from the string in order

def stripSuffix(string, suffix):

 if string[len(string)-len(suffix):len(string)] == suffix:
   return string[0:len(string)-len(suffix)]
 else:
   return string
  1. test the start of a string

def startsWith(string, prefix):

 return (string.find(prefix) == 0)
  1. test the end of a string

def endsWith(string, suffix):

 return (string.rfind(suffix) == len(string) - len(suffix))
  1. get the output of an external command

def runCommand(command):

 args = command.split()
 process = subprocess.Popen(args, stdout = subprocess.PIPE)
 while process.returncode == None:
   process.poll()
 return process.communicate()[0]
  1. add proper number of indents

def getIndent(indent):

 if indent:
   return "  "
 else:
   return ""
  1. sub out protected characters

def prepXMLString(string):

 out = string.replace("<", "<")
 out = out.replace("&", "&")
 out = out.replace(">", ">")
 out = out.replace('"', """)
 return out.replace("'", "'")
  1. add an element to the XML file string

def addXMLElement(xmlString, elementName, elementText, indent=False, comment = ""):

 # comment
 if comment != "":
   xmlString = xmlString + getIndent(indent) + "\n"
 # opening tag
 xmlString = xmlString + getIndent(indent) + "<" + elementName + ">\n"
 # body
 for line in elementText.splitlines():
   xmlString = xmlString + getIndent(indent) + "  " + line + "\n"
 # closing tag
 return xmlString + getIndent(indent) + "</" + elementName + ">\n\n"
  1. add spaces for camel case string

def addCamelCaseSpaces(string):

 firstLetter = True
 out = ""
 for letter in string:
   if not firstLetter and letter.isupper():
     out = out + " " + letter
   else:
     out = out + letter
   firstLetter = False
 return out


  1. Main execution point #

if __name__ == '__main__':

 #
 # Get a unique XMLFileName from the user
 #
 # get directory info
 migrationDir = sys.path[0]
 baseDir = stripSuffix(migrationDir, "/Migration")
 # get XMLFileName (loop until name is unique)
 uniqueName = False
 XMLFileName = ""
 XMLFilePath = ""
 print "Please enter a unique name for the XML document"
 while uniqueName == False:
   # get the user's input
   nameCandidate = raw_input(">> ")
   if not endsWith(nameCandidate, ".xml"):
     nameCandidate = nameCandidate + ".xml"
   pathCandidate = migrationDir + "/" + nameCandidate
   # check uniqueness
   if not os.path.exists(pathCandidate):
     uniqueName = True
     XMLFileName = nameCandidate
     XMLFilePath = pathCandidate
   else:
     print '"' + nameCandidate + '" already exists.  Please specify a differnt name'
 # split the name for the title tag
 titleText = addCamelCaseSpaces(stripSuffix(XMLFileName, ".xml"))
 #
 # Get list of commits on current branch
 #
 # Get the current branch name
 HEADfilename = baseDir + "/.git/HEAD"
 try:
   HEADfile = open(HEADfilename, "r")
 except IOError:
   print "I/O error: Could not open .git/HEAD"
   sys.exit()
 branchName = HEADfile.readline()
 branchName = stripPrefix(branchName, "ref: refs/heads/")
 branchName = stripSuffix(branchName, "\n")
 # parse file in .git/logs/refs/heads/BRANCH_NAME
 branchLogFilename = baseDir + "/.git/logs/refs/heads/" + branchName
 try:
   branchLogFile = open(branchLogFilename, "r")
 except IOError:
   print "I/O error: Could not open branch log file"
   sys.exit()
 # grab the commit lines, but ignore ammended ones
 commitList = []
 for line in branchLogFile.readlines():
   if line.find("commit (amend):") == -1:
     commitList.append(line.split()[1])
   else:
     commitList.pop()
     commitList.append(line.split()[1])
 #
 # Parse each commit's log
 #
 descriptionText = ""
 changeIdText = ""
 sampleCodeOldText = ""
 sampleCodeNewText = ""
 changedFileList = []
 exampleAndTestChangedFileList = []
 firstCommit = commitList[0];
 commitList.remove(firstCommit)
 for commit in commitList:
   # get the log for the commit
   logCommand = "git log " + commit + " -n1 --stat"
   log = runCommand(logCommand)
   descriptionText = descriptionText + "---- " + commit + " ----\n"
   for line in log.splitlines():
     # commit message lines and change id lines
     if startsWith(line, "  "):
       if startsWith(line.strip(), "Change-Id: "):
         changeId = stripPrefix(line.strip(), "Change-Id: ")
         changeIdText = changeIdText + changeId + "\n"
       else:
         descriptionText = descriptionText + line.strip() + '\n'
     # changed file lines
     elif startsWith(line, " "):
       splits = line.split("|")
       if len(splits) == 2:
         filename = splits[0].strip()
         if not filename in changedFileList:
           changedFileList.append(filename)
         if startsWith(filename, "Examples") or startsWith(filename, "Testing"):
           exampleAndTestChangedFileList.append(filename)


 #
 # parse the diff of each Example or Testing file
 #
 for filename in exampleAndTestChangedFileList:
   # Add filename
   sampleCodeOldText = sampleCodeOldText + "---- " + filename + " ----\n"
   sampleCodeNewText = sampleCodeNewText + "---- " + filename + " ----\n"
   # get the log for the commit
   fullPath = baseDir + "/" + filename
   diffCommand = "git diff " + firstCommit + " " + commitList[len(commitList)-1] \
     + " -- " + fullPath
   diff = runCommand(diffCommand)
   # parse lines into old and new
   for line in diff.splitlines():
     # old line
     if (not startsWith(line, "--")) and startsWith(line, "-"):
       sampleCodeOldText = sampleCodeOldText + line.lstrip("-").strip() + "\n"
     # new line
     elif (not startsWith(line, "++")) and startsWith(line, "+"):
       sampleCodeNewText = sampleCodeNewText + line.lstrip("+").strip() + "\n"


 #
 # Create XMl file text
 #
 xmlString = '<?xml version="1.0" encoding="UTF-8"?>\n\n'
 changeElementBody = ""
 # <Title> element
 titleComment = "Title for the online migration page"
 changeElementBody = addXMLElement(changeElementBody, "Title", titleText, True, titleComment)
 # <Description> element
 descriptionComment = "Plain text description of the change\n-->Extracted from git commit messages"
 changeElementBody = \
   addXMLElement(changeElementBody, "Description",\
   prepXMLString(descriptionText), True, descriptionComment)
 # <SampleCode> element
 sampleCodeComment = "Sample code snippets\n-->Extracted from git diff of changed files in Examples and Testing"
 sampleCodeElementBody = ""
 sampleCodeElementBody = \
   addXMLElement(sampleCodeElementBody, "Old", prepXMLString(sampleCodeOldText))
 sampleCodeElementBody = \
   addXMLElement(sampleCodeElementBody, "New", prepXMLString(sampleCodeNewText))
 changeElementBody = addXMLElement(changeElementBody, "SampleCode",\
                                   sampleCodeElementBody, True, sampleCodeComment)
 # <Gerrit-ChangeId> element
 changeIdComment = "The change-ids for all commits in the topic branch"
 changeElementBody = \
   addXMLElement(changeElementBody, "Gerrit-ChangeId",\
   changeIdText, True, changeIdComment)
 # <FileList> element
 fileListComment = "List of all changed files from the topic branch"
 fileListText = ""
 for f in changedFileList:
   fileListText = fileListText + f + "\n"
 changeElementBody = \
   addXMLElement(changeElementBody, "FileList",\
   fileListText, True, fileListComment)
 # <Change> element
 changeComment = "\n" + XMLFileName + "\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nTHIS FILE HAS BEEN AUTOMATICALLY GENERATED. EDIT IT BEFORE COMMITING\n<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"
 xmlString = addXMLElement(xmlString, "Change", changeElementBody, False, changeComment)
 xmlString = xmlString.strip()
 #
 # Save the file
 #
 try:
   xmlFile = open(XMLFilePath, "w")
   xmlFile.write(xmlString)
 except IOError:
   print "I/O Error: Could not write to " + XMLFilePath
   sys.exit()criptionText), True, descriptionComment)
 # <SampleCode> element
 sampleCodeComment = "Sample code snippets\n-->Extracted from git diff of changed files in Examples and Testing"
 sampleCodeElementBody = ""
 sampleCodeElementBody = \
   addXMLElement(sampleCodeElementBody, "Old", prepXMLString(sampleCodeOldText))
 sampleCodeElementBody = \
   addXMLElement(sampleCodeElementBody, "New", prepXMLString(sampleCodeNewText))
 changeElementBody = addXMLElement(changeElementBody, "SampleCode",\
                                   sampleCodeElementBody, True, sampleCodeComment)
 # <Gerrit-ChangeId> element
 changeIdComment = "The change-ids for all commits in the topic branch"
 changeElementBody = \
   addXMLElement(changeElementBody, "Gerrit-ChangeId",\
   changeIdText, True, changeIdComment)
 # <FileList> element
 fileListComment = "List of all changed files from the topic branch"
 fileListText = ""
 for f in changedFileList:
   fileListText = fileListText + f + "\n"
 changeElementBody = \
   addXMLElement(changeElementBody, "FileList",\
   fileListText, True, fileListComment)
 # <Change> element
 changeComment = "\n" + XMLFileName + "\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nTHIS FILE HAS BEEN AUTOMATICALLY GENERATED. EDIT IT BEFORE COMMITING\n<<<<<<<<<<<<<<<<<<<<<<<<<<<\n\n"
 xmlString = addXMLElement(xmlString, "Change", changeElementBody, False, changeComment)
 xmlString = xmlString.strip()
 #
 # Save the file
 #
 try:
   xmlFile = open(XMLFilePath, "w")
   xmlFile.write(xmlString)
 except IOError:
   print "I/O Error: Could not write to " + XMLFilePath
   sys.exit()

</source>