| Classes | Job Modules | Data Objects | Services | Algorithms | Tools | Packages | Directories | Tracs |

In This Package:

xmlIndex.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Generate an index of root file contents
00004 #
00005 # Usage:
00006 #   % xmlIndex.py histograms.root index.xml
00007 
00008 indexDtd = """
00009  <!DOCTYPE diagnostic_index [ 
00010 
00011  <!ELEMENT diagnostic_index (run*)>
00012  <!ELEMENT run (runnumber,runindex?,detector*,figure*)>
00013  <!ELEMENT runnumber (#PCDATA)>
00014  <!ELEMENT runindex (#PCDATA)>
00015  <!ELEMENT detector (detname, figure*, channel*)>
00016  <!ELEMENT detname (#PCDATA)>
00017  <!ELEMENT channel (channelname, figure*)>
00018  <!ELEMENT channelname (#PCDATA)>
00019  <!ELEMENT figure (path,figname,rootPath,figtitle)>
00020  <!ELEMENT path (#PCDATA)>
00021  <!ELEMENT figname (#PCDATA)>
00022  <!ELEMENT rootPath (#PCDATA)>
00023  <!ELEMENT figtitle (#PCDATA)>
00024 
00025  <!ATTLIST run id ID #REQUIRED> 
00026  <!ATTLIST detector id ID #REQUIRED> 
00027  <!ATTLIST channel id ID #REQUIRED>
00028  <!ATTLIST figure id ID #REQUIRED> 
00029  
00030  ]>
00031 """
00032 
00033 
00034 def writeXml(outFile, currentIndex):
00035     """Write current index to output file"""
00036     xmlfile = open(outFile,'w')
00037     if not xmlfile:
00038         print "Failed to open XML index output file: ",outFile
00039         return
00040     xmlfile.write( '<?xml version="1.0" ?>\n' )
00041     global indexDtd
00042     xmlfile.write( indexDtd )
00043     prettyXml = currentIndex.toprettyxml(" ")
00044     lines = prettyXml.split("\n")
00045     prettierLines = []
00046     closeText=False
00047     for line in lines:
00048         stLine = line.strip()
00049         if len(stLine)==0: continue
00050         if closeText and stLine[0:2]=='</':
00051             prettierLines[-1] += stLine
00052             closeText=False
00053             continue
00054         if stLine[0]=='<':
00055             prettierLines.append(line)
00056             continue
00057         # Found text line, add to previous block
00058         prettierLines[-1] += stLine
00059         closeText=True
00060     xmlfile.write( '\n'.join(prettierLines) )
00061     xmlfile.close()
00062     return
00063 
00064 
00065 class XmlIndexGenerator:
00066     """XML Index Generator Class"""
00067     def __init__(self, filename, rootPath, fileRoot, xmlIndex, pathReplace):
00068         "Constructor"
00069         self.filename = filename
00070         self.rootPath = rootPath
00071         self.fileRoot = fileRoot
00072         self.xmlIndex = xmlIndex
00073         self.pathReplace = pathReplace
00074         self.file = None
00075         self.format = "png"
00076         self.xml = None
00077         self.rootElem = None
00078         return
00079 
00080     def process(self):
00081         """Generate XML"""
00082         if not self.openFile():
00083             return
00084         self.xmlOpen()
00085         self.processPath("diagnostics",None)
00086         self.xmlClose()
00087         return
00088         
00089     def openFile(self):
00090         "Open root file"
00091         import ROOT
00092         rootFile = ROOT.TFile(self.filename)
00093         if not rootFile:
00094             print "Failed to open root file:",filename
00095             return False
00096         self.file = rootFile
00097         return True
00098 
00099     def processPath(self, itemPath, parentElem):
00100         """Process a path in the current root file"""
00101         #print "Processing:",itemPath
00102         filePath = self.fileRoot + "/" + itemPath
00103         content = self.file.Get(filePath);
00104         if not content:
00105             print "Failed to get item at: ", itemPath
00106             return
00107         if (content.IsA().InheritsFrom("TH1")
00108             or content.IsA().InheritsFrom("TGraph")):
00109             # Found a figure.  Index it.
00110             self.indexFigure(content, itemPath, parentElem)
00111         elif content.IsA().InheritsFrom("TDirectory"):
00112             # Found a directory.  Index it
00113             newElem = None
00114             newElem = self.tryDiagnostics(itemPath, parentElem)
00115             if not newElem:
00116                 newElem = self.tryRun(itemPath, parentElem)
00117             if not newElem:
00118                 newElem = self.tryDetector(itemPath, parentElem)
00119             if not newElem:
00120                 newElem = self.tryChannel(itemPath, parentElem)
00121             if not newElem:
00122                 print "Will not index path: ", itemPath
00123                 return
00124             keylist = content.GetListOfKeys()
00125             for key in keylist:
00126                 dirItem = key.ReadObj()
00127                 dirItemPath = None
00128                 if len(itemPath)>0:
00129                     dirItemPath = itemPath+"/"+dirItem.GetName()
00130                 else:
00131                     dirItemPath = dirItem.GetName()
00132                 self.processPath(dirItemPath, newElem)
00133         else:
00134             # Won't handle this object
00135             print "Not handing %s of type %s" % (filepath,content.ClassName())
00136         return
00137 
00138     def xmlOpen(self):
00139         """Create XML index header"""
00140         import xml.dom.minidom
00141         self.xml = xml.dom.minidom.Document()
00142         return
00143 
00144     def xmlClose(self):
00145         """Close XML index, write to output file"""
00146         writeXml(self.xmlIndex, self.rootElem)
00147         return
00148 
00149     def indexFigure(self, item, itemPath, parentElem):
00150         """Check path and add to XML index"""
00151         if parentElem.localName in ['run','detector','channel']: 
00152             # Add figure to parent
00153             figPath = itemPath + '.' + self.format
00154             # Replace figure path if needed
00155             if self.pathReplace:
00156                 for key in self.pathReplace.keys():
00157                     figPath = figPath.replace(key,self.pathReplace[key])
00158             parentId = parentElem.attributes['id'].value
00159             figElem = self.createXmlNode('figure',
00160                                          {'figname':item.GetName(),
00161                                           'path':figPath,
00162                                           'figtitle':item.GetTitle(),
00163                                           'rootPath':self.rootPath},
00164                                          {'id':parentId+'_'+item.GetName()})
00165             parentElem.appendChild(figElem)
00166         return figElem
00167 
00168     def createXmlNode(self, name, children=None, attributes=None):
00169         newElem = self.xml.createElement(name)
00170         if children:
00171             for child in children.keys():
00172                 childElem = self.xml.createElement(child)
00173                 childContent = self.xml.createTextNode(children[child])
00174                 childElem.appendChild(childContent)
00175                 newElem.appendChild(childElem)
00176         if attributes:
00177             for attrName in attributes.keys():
00178                 newElem.setAttribute(attrName, attributes[attrName])
00179         return newElem
00180 
00181     def tryDiagnostics(self, itemPath, parentElem):
00182         """Check if path is in 'diagnostics' format"""
00183         if parentElem: return None
00184         pathParts = itemPath.split('/')
00185         if len(pathParts)!=1: return None
00186         if itemPath != 'diagnostics': return None
00187         self.rootElem = self.createXmlNode('diagnostic_index')
00188         return self.rootElem
00189 
00190     def tryRun(self, itemPath, parentElem):
00191         """Check if path is in 'run' format"""
00192         if parentElem.localName != 'diagnostic_index':
00193             return None
00194         pathParts = itemPath.split('/')
00195         if len(pathParts)!=2: return None
00196         runPart = pathParts[-1]
00197         if len(runPart)!=11: return None
00198         if not runPart.startswith("run_"): return None
00199         if not runPart[4:].isdigit(): return None
00200         runnumber = int(runPart[4:])
00201         runId = runPart
00202         if runnumber == 0:
00203             print "FIXME: sim data has runnumber == 0"
00204             runpathfix = self.pathReplace.keys()[0]
00205             runnumber = int(runpathfix[-7:])
00206             self.pathReplace = {runPart : self.pathReplace[runpathfix]}
00207             print "FIXME: replacing with runnumber ",runnumber
00208             runId = runpathfix
00209         runElem = self.createXmlNode('run',{'runnumber':str(runnumber)},
00210                                      {'id':runId})
00211         parentElem.appendChild(runElem)
00212         return runElem
00213 
00214     def tryDetector(self, itemPath, parentElem):
00215         """Check if path is in 'detector' format"""
00216         if parentElem.localName != 'run':
00217             return None
00218         pathParts = itemPath.split('/')
00219         if len(pathParts)!=3: return None
00220         detPart = pathParts[-1]
00221         if not detPart.startswith("detector_"): return None
00222         detectorId = parentElem.attributes.get('id').nodeValue + '_' + detPart
00223         detectorName = detPart[9:]
00224         detElem = self.createXmlNode('detector',{'detname':detectorName},
00225                                      {'id':detectorId})
00226         parentElem.appendChild(detElem)
00227         return detElem
00228 
00229     def tryChannel(self, itemPath, parentElem):
00230         """Check if path is in 'channel' format"""
00231         if parentElem.localName != 'detector':
00232             return None
00233         pathParts = itemPath.split('/')
00234         if len(pathParts)!=4: return None
00235         chanPart = pathParts[-1]
00236         if not chanPart.startswith("channel_"): return None
00237         channelId = parentElem.attributes.get('id').nodeValue + '_' + chanPart
00238         channelName = chanPart[8:]
00239         channelElem = self.createXmlNode('channel',
00240                                          {'channelname':channelName},
00241                                          {'id':channelId})
00242         parentElem.appendChild(channelElem)
00243         return channelElem
00244 
00245 class XmlIndexUpdater:
00246     """XML Index Updater class"""
00247     def __init__(self, oldIndex, indexUpdate, newIndex):
00248         "Constructor"
00249         self.oldIndex = oldIndex
00250         self.indexUpdate = indexUpdate
00251         self.newIndex = newIndex
00252         return
00253 
00254     def process(self):
00255         """Update an existing index with entries from new index"""
00256         import xml.dom.minidom
00257         currentXml = xml.dom.minidom.parse(self.oldIndex)
00258         xmlUpdate = xml.dom.minidom.parse(self.indexUpdate)
00259         self.removeWhitespace(currentXml)
00260         self.removeWhitespace(xmlUpdate)
00261         currentIndex = self.getIndex(currentXml)
00262         indexUpdate = self.getIndex(xmlUpdate)
00263         self.updateXml(currentIndex,indexUpdate)
00264         writeXml( self.newIndex, currentIndex )
00265         return True
00266 
00267     def getIndex(self, xmlDoc):
00268         """Get the diagnostics index from this XML document"""
00269         diagIndex = None
00270         for child in xmlDoc.childNodes:
00271             if (child.nodeType==child.ELEMENT_NODE
00272                 and child.localName == "diagnostic_index"):
00273                 diagIndex = child
00274                 break
00275         if not diagIndex:
00276             print "XML document has no diagnostic index"
00277         return diagIndex
00278 
00279     def updateXml(self, currentElement, updateElement):
00280         """Recursively update XML elements"""
00281         mergeChildren = {}
00282         simpleElements = {}
00283         firstIdElement = None
00284         for child in list(currentElement.childNodes):
00285             #print "processing child: ",child.toxml()
00286             if (child.nodeType==child.ELEMENT_NODE
00287                 and child.hasAttributes()
00288                 and child.hasAttribute('id')):
00289                 # Maintain attributes with IDs, will be updated
00290                 if not firstIdElement == None: firstIdElement = child
00291                 childId = child.attributes['id'].value
00292                 mergeChildren[childId] = child
00293             else:
00294                 simpleElements[child.localName]=child
00295         for child in list(updateElement.childNodes):
00296             if (child.nodeType==child.ELEMENT_NODE
00297                 and child.hasAttribute('id')):
00298                 childId = child.attributes['id'].value
00299                 if mergeChildren.has_key(childId):
00300                     # Merge elements with same IDs
00301                     self.updateXml(mergeChildren[childId], child)
00302                 else:
00303                     # Add element with new ID
00304                     # print "Adding new element: ",childId
00305                     currentElement.appendChild(child)
00306             else:
00307                 if(simpleElements.has_key(child.localName)):
00308                     # Replace this simple node
00309                     replaceChild = simpleElements[child.localName]
00310                     # print "Replacing simple element: ",child.localName
00311                     currentElement.replaceChild(child, replaceChild)
00312                     simpleElements[child.localName] = child
00313                 else:
00314                     # Add new simple node
00315                     # print "Adding new simple element: ",child.localName
00316                     currentElement.insertBefore(child, firstIdElement)
00317         return
00318 
00319     def removeWhitespace(self, xmlElement):
00320         """Recursively remove excess whitespace from 'pretty' xml"""
00321         for child in list(xmlElement.childNodes):
00322             if child.nodeType==child.TEXT_NODE:
00323                 cleanText = child.data.strip()
00324                 if cleanText=='':
00325                     xmlElement.removeChild(child)
00326                 else:
00327                     child.data = cleanText
00328             else:
00329                 self.removeWhitespace(child)
00330         return
00331 
00332 if __name__ == "__main__":
00333 
00334     fileRoot = "stats"
00335     xmlIndex = "index.xml"
00336     filename = None
00337     oldIndex = None
00338     indexUpdate = None
00339     rootPath = ""
00340     pathReplace = None
00341     import sys, getopt
00342     opts, args = getopt.getopt(sys.argv[1:],"f:o:p:r:i:u:")
00343     for opt, arg in opts:
00344         if opt == "-f":
00345             filename = arg
00346         elif opt == "-o":
00347             xmlIndex = arg
00348         elif opt == "-p":
00349             rootPath = arg
00350         elif opt == "-r":
00351             items = arg.split(',')
00352             if len(items)==2:
00353                 if pathReplace==None:
00354                     pathReplace={}
00355                 pathReplace[items[0].strip()] = items[1].strip()
00356         elif opt == "-i":
00357             oldIndex = arg
00358         elif opt == "-u":
00359             indexUpdate = arg
00360     if filename!=None and oldIndex==None and indexUpdate==None:
00361         # Generate new index
00362         print "Filename:  ",filename
00363         print "Root File Local Path:  ",rootPath
00364         print "XML Index: ",xmlIndex
00365         if pathReplace:
00366             print "Run Path Substitute: ",pathReplace
00367         xmlGen = XmlIndexGenerator(filename, rootPath, fileRoot, xmlIndex,
00368                                    pathReplace)
00369         xmlGen.process()
00370         print "Done with index generation"
00371     elif filename==None and oldIndex!=None and indexUpdate!=None:
00372         print "New XML Index: ",xmlIndex
00373         print "Previous XML Index: ",oldIndex
00374         print "Update XML fragment: ",indexUpdate
00375         xmlUpd = XmlIndexUpdater(oldIndex, indexUpdate, xmlIndex)
00376         xmlUpd.process()
00377         print "Done with index updating"
00378     else:
00379         print """ Usage:
00380         Generate a new index:
00381         % xmlIndex.py -f run001234.root -o run001234.xml -p ./root/run001245.root
00382           Options:
00383             -f: input root filename [REQUIRED]
00384             -o: output XML index filename
00385             -p: disk path of root file relative to main index
00386             -r: replace figure paths with substitute strings '-r old,new'
00387 
00388         Update an existing index:
00389         % xmlIndex.py -o newIndex.xml -i input.xml -u update.xml
00390           Options:
00391             -i: input XML filename [REQUIRED]
00392             -u: update XML filename [REQUIRED]
00393             -o: output XML index filename [DEFAULT: index.xml]
00394         """
00395 
00396     
00397     
| Classes | Job Modules | Data Objects | Services | Algorithms | Tools | Packages | Directories | Tracs |

Generated on Mon Apr 11 20:10:26 2011 for RunDiagnostics by doxygen 1.4.7