00001
00002
00003
00004
00005
00006
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
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
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
00110 self.indexFigure(content, itemPath, parentElem)
00111 elif content.IsA().InheritsFrom("TDirectory"):
00112
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
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
00153 figPath = itemPath + '.' + self.format
00154
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
00286 if (child.nodeType==child.ELEMENT_NODE
00287 and child.hasAttributes()
00288 and child.hasAttribute('id')):
00289
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
00301 self.updateXml(mergeChildren[childId], child)
00302 else:
00303
00304
00305 currentElement.appendChild(child)
00306 else:
00307 if(simpleElements.has_key(child.localName)):
00308
00309 replaceChild = simpleElements[child.localName]
00310
00311 currentElement.replaceChild(child, replaceChild)
00312 simpleElements[child.localName] = child
00313 else:
00314
00315
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
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