00001 #!/usr/local/bin/python 00002 # 00003 # Copyright 2003 00004 # by 00005 # The Board of Trustees of the 00006 # Leland Stanford Junior University. 00007 # All rights reserved. 00008 # 00009 00010 00011 __facility__ = "Online" 00012 __abstract__ = "LDF Plotter for consumer" 00013 __author__ = "Lester Miller" 00014 __date__ = "11/18/2003" 00015 __version__ = "$Revision: 1.10 $" 00016 __credits__ = "SLAC" 00017 00018 import LATTE.copyright_SLAC 00019 import sihippo 00020 import os 00021 import time 00022 import Numeric as numarray 00023 import string 00024 import ConfigParser 00025 import logging as myLog 00026 from LATTE.consumer.standardPlotterGuiImpl import standardPlotterGuiImpl 00027 import qt 00028 from LATTE.runcontrol.rcTestReport import rcTestReport 00029 00030 class standardPlotter(object): 00031 def __init__(self,lci,gui=None): 00032 self.gui = gui 00033 self.lci = lci 00034 00035 def initializePlotter(self): 00036 # get configuration file from GUI 00037 if self.gui is not None: 00038 self.configGui = self.gui.createGUI(standardPlotterGuiImpl, self.gui, 'test1', 1) 00039 configFile = self.gui.execGUImethod(self.configGui.getConfigFile,self.configFile) 00040 else: 00041 self.configGui = standardPlotterGuiImpl(self, 'test1', 1) 00042 configFile = self.configGui.getConfigFile(self.configFile) 00043 if configFile is None: 00044 raise AssertionError,'User Cancelled: No config file given' 00045 self.setConfigFile(configFile) 00046 00047 def setDataType(self,dataType): 00048 self.dataType = dataType 00049 00050 def setConfigFile(self,configFile): 00051 myLog.info("Configuring with file %s"%(configFile)) 00052 self.configFile = os.path.expandvars(configFile) 00053 # lci owns ntuples and numarrays 00054 self.lci.ntDict = {} 00055 self.lci.numarrayDict = {} 00056 # next we find what's been requested from config 00057 self.histDict = {} 00058 self.limits = {} 00059 self.cuts = {} 00060 self.options = {} 00061 self.ntRequests = {} 00062 self.plotRequests = {} 00063 self.limitRequests = {} 00064 self.cutRequests = {} 00065 self.getRequests() 00066 # these are the options fields 00067 self.reportPath = None 00068 if self.gui is not None: 00069 self.reportPath = self.gui.preferences()["reportdir"] 00070 self.reportPath = os.path.expandvars(self.reportPath) 00071 elif 'reportpath' in self.options: 00072 self.reportPath = self.options['reportpath'] 00073 self.reportPath = os.path.expandvars(self.reportPath) 00074 if not os.path.isabs(self.reportPath): 00075 self.reportPath = os.path.join(os.getcwd(),self.reportPath) 00076 self.checkInterval = 1000 00077 if 'checkinterval' in self.options: 00078 self.checkInterval = eval(self.options['checkinterval']) 00079 self.plotUpdateRate = 2 00080 if 'plotupdaterate' in self.options: 00081 self.plotUpdateRate = eval(self.options['plotupdaterate']) 00082 self.dataType = 'All' 00083 if 'datatype' in self.options: 00084 self.dataType = self.options['datatype'] 00085 pedSubtract = True 00086 self.lci.acdPeds = None 00087 self.lci.calPeds = None 00088 if 'pedsubtract' in self.options: 00089 pedSubtract = eval(self.options['pedsubtract']) 00090 if pedSubtract: 00091 self.readPedestals() 00092 if 'pedsparsify' in self.options: 00093 self.lci.sparsify = eval(self.options['pedsparsify']) 00094 # init hippo as requested 00095 if self.gui is not None: 00096 self.gui.execGUImethod(self.initHippo) 00097 else: 00098 self.initHippo() 00099 00100 def getName(self): 00101 return 'standardPlotter' 00102 00103 def initHippo(self): 00104 self.lci.nEvtSeen=0 00105 # setup with canvas 00106 ntc = sihippo.NTupleController.instance() 00107 wc = sihippo.WindowController.instance() 00108 self.canvas = wc.currentCanvas() 00109 if self.canvas is None: 00110 wc.createInspector() 00111 self.window = sihippo.CanvasWindow() 00112 self.window.show() 00113 self.canvas = wc.currentCanvas() 00114 self.canvas.clear() 00115 self.dc = sihippo.DisplayController.instance() 00116 self.canvas.show() 00117 self.qtTimer = qt.QTime.currentTime() 00118 qt.QTime.start(self.qtTimer) 00119 # book ntuples 00120 for ntuple in self.ntRequests.keys(): 00121 ntupleColumns = self.lci.getNtupleDefinition(ntuple) 00122 if ntupleColumns is not None: 00123 if self.ntRequests[ntuple]>0: 00124 cNt = ntc.createCircularBuffer( ntupleColumns ) 00125 cNt.reserve ( self.ntRequests[ntuple] ) 00126 else: 00127 cNt = ntc.createNTuple( ntupleColumns ) 00128 cNt.setName(ntuple) 00129 cNt.setTitle(ntuple) 00130 cNt.setIntervalEnabled ( 1 ) 00131 cNt.setIntervalCount ( 1000000 ) 00132 self.lci.ntDict[ntuple] = cNt 00133 else: 00134 myLog.warn('StandardPlotter:initHippo: Unknown ntuple \'%s\' in requested ntuples'%(ntuple)) 00135 # book plots 00136 # expect that the config comes in form 00137 # Ntuple name, Plot Type, Plot Title, column list [, xbinlow xbinhi xbinwidth, ybinlow ybinhi ybinwidth] 00138 for plot in self.plotRequests.keys(): 00139 declarationList = string.split(self.plotRequests[plot],',') 00140 if len(declarationList)>=4: 00141 ntupleName = string.strip(declarationList[0]).lower() 00142 if ntupleName in self.lci.ntDict: 00143 cNt = self.lci.ntDict[ntupleName] 00144 hColumnList = string.split(declarationList[3]) 00145 hColumns = [] 00146 cNtColumns = self.lci.getNtupleDefinition(ntupleName) 00147 foundColumns = True 00148 for column in hColumnList: 00149 normalColumn = string.strip(column).lower() 00150 if normalColumn in cNtColumns: 00151 hColumns.append(normalColumn) 00152 else: 00153 foundColumns = False 00154 myLog.warn('StandardPlotter:initHippo: Requested plot \'%s\' has column \'%s\' not in ntuple \'%s\' with columns %s'%(plot,normalColumn,ntupleName,cNtColumns)) 00155 if foundColumns: 00156 # finally have everything we need to declare the plot 00157 hType = string.strip(declarationList[1]) 00158 hTitle = string.strip(declarationList[2]) 00159 cHist = self.dc.createDisplay(hType,cNt,hColumns) 00160 cHist.setTitle(hTitle) 00161 # check for binning information 00162 if len(declarationList)>4: 00163 # x 00164 xBinPars = string.split(declarationList[4]) 00165 if len(xBinPars)==3: 00166 xLower = float(xBinPars[0]) 00167 xUpper = float(xBinPars[1]) 00168 xWidth = float(xBinPars[2]) 00169 if xUpper>xLower: 00170 cHist.setRange('x', xLower , xUpper ) 00171 if xWidth>0: 00172 cHist.setBinWidth('x', xWidth) 00173 else: 00174 myLog.warn('StandardPlotter:initHippo: Requested plot \'%s\' ignoring extra binning parameters for x, saw %s'%(plot,xBinPars)) 00175 if len(declarationList)>5: 00176 # y 00177 yBinPars = string.split(declarationList[5]) 00178 if len(yBinPars)==3: 00179 yLower = float(yBinPars[0]) 00180 yUpper = float(yBinPars[1]) 00181 yWidth = float(yBinPars[2]) 00182 if yUpper>yLower: 00183 cHist.setRange('y', yLower , yUpper ) 00184 if yWidth>0: 00185 cHist.setBinWidth('y', yWidth) 00186 else: 00187 myLog.warn('StandardPlotter:initHippo: Requested plot \'%s\' ignoring extra binning parameters for y, saw %s'%(plot,yBinPars)) 00188 self.histDict[plot]=cHist 00189 self.canvas.addDisplay(cHist) 00190 else: 00191 myLog.warn('StandardPlotter:initHippo: Requested plot \'%s\' error in declaration: ntuple \'%s\' unknown or not requested'%\ 00192 (plot,ntupleName)) 00193 else: 00194 myLog.warn('StandardPlotter:initHippo: Requested plot \'%s\' error in declaration:\n expect: Ntuple name, Plot Type, Plot Title, column list, [lowerx upperx binwidthx, lowery uppery binwidthy]\n got: %s'%(plot,declarationList)) 00195 # now apply the cuts 00196 for (plot,variable) in self.cuts.keys(): 00197 if not plot in self.histDict: 00198 myLog.warn('StandardPlotter:initHippo: Requested cut on plot \'%s\' variable \'%s\' but plot not requested'%(plot,variable)) 00199 continue 00200 ntuple = self.getPlotNtuple(plot) 00201 if not ntuple in self.lci.ntDict: 00202 myLog.warn('StandardPlotter:initHippo: Requested cut on plot \'%s\' variable \'%s\' but ntuple not \'%s\' not requested'%(plot,variable,ntuple)) 00203 continue 00204 if not variable in self.lci.getNtupleDefinition(ntuple): 00205 myLog.warn('StandardPlotter:initHippo: Requested cut on plot \'%s\' variable \'%s\' but ntuple \'%s\' has variables: %s'%\ 00206 (plot,variable,ntuple,self.lci.getNtupleDefinition(ntuple))) 00207 continue 00208 cc = sihippo.CutController.instance() 00209 (cutLower,cutUpper) = self.cuts[(plot,variable)] 00210 cut = cc.addCut ( self.histDict[plot], variable ) 00211 cut.setCutRange ( cutLower,cutUpper ) 00212 00213 def clearPlots(self): 00214 for nt in self.lci.ntDict.values(): 00215 nt.clear() 00216 self.lci.nEvtSeen=0 00217 self.forceUpdatePlots() 00218 00219 def updatePlots(self): 00220 if qt.QTime.elapsed(self.qtTimer)>int(1000/self.plotUpdateRate): 00221 self.forceUpdatePlots() 00222 qt.QTime.restart(self.qtTimer) 00223 00224 def forceUpdatePlots(self): 00225 for nt in self.lci.ntDict.values(): 00226 nt.setIntervalEnabled(0) 00227 nt.setIntervalEnabled(1) 00228 00229 def getPlotColumns(self,plot): 00230 result = [] 00231 if plot in self.histDict and plot in self.plotRequests: 00232 declarationList = string.split(self.plotRequests[plot],',') 00233 if len(declarationList)>=4: 00234 hColumnList = string.split(declarationList[3]) 00235 for column in hColumnList: 00236 result.append(string.strip(column).lower()) 00237 return result 00238 def getPlotNtuple(self,plot): 00239 result = '' 00240 if plot in self.histDict and plot in self.plotRequests: 00241 declarationList = string.split(self.plotRequests[plot],',') 00242 if len(declarationList)>=4: 00243 result = string.strip(declarationList[0]).lower() 00244 return result 00245 def getPlotTitle(self,plot): 00246 result = '' 00247 if plot in self.histDict and plot in self.plotRequests: 00248 declarationList = string.split(self.plotRequests[plot],',') 00249 if len(declarationList)>=4: 00250 result = string.strip(declarationList[2]) 00251 return result 00252 def getPlotCuts(self,plot): 00253 result = [] 00254 if plot in self.histDict and plot in self.plotRequests: 00255 for (plotCut,variable) in self.cuts.keys(): 00256 if plot == plotCut: 00257 (cutLower,cutUpper) = self.cuts[(plot,variable)] 00258 result.append((variable,cutLower,cutUpper)) 00259 return result 00260 def getPlotType(self,plot): 00261 result = '' 00262 if plot in self.histDict and plot in self.plotRequests: 00263 declarationList = string.split(self.plotRequests[plot],',') 00264 if len(declarationList)>=4: 00265 result = string.strip(declarationList[1]) 00266 return result 00267 00268 def getRequests(self): 00269 # try reading from a config file 00270 if self.configFile is not None: 00271 parser = ConfigParser.ConfigParser() 00272 if os.path.exists(self.configFile): 00273 parser.read(self.configFile) 00274 sections = parser.sections() 00275 for section in sections: 00276 options = parser.options(section) 00277 # don't evaluate plots, cuts or limits, those are parsed 00278 if section == 'plots': 00279 for option in options: 00280 self.plotRequests[option] = parser.get(section,option) 00281 elif section == 'limits': 00282 for option in options: 00283 self.limitRequests[option] = parser.get(section,option) 00284 elif section == 'cuts': 00285 for option in options: 00286 self.cutRequests[option] = parser.get(section,option) 00287 elif section == 'ntuples': 00288 for option in options: 00289 self.ntRequests[option] = eval(parser.get(section,option)) 00290 elif section == 'options': 00291 for option in options: 00292 self.options[option] = parser.get(section,option) 00293 else: 00294 myLog.warn("%s.getRequests: unrecognized section \'%s\'\n" %(self.getName(),section)) 00295 self.parseLimits() 00296 self.parseCuts() 00297 00298 def parseLimits(self): 00299 for limit in self.limitRequests.values(): 00300 # limits are plot name, lower upper sigma 00301 # TKRocc_ped = TKRocc, 0.0 1.5 00302 declarationList = string.split(limit,',') 00303 if len(declarationList)>=2: 00304 plotName = string.strip(declarationList[0]).lower() 00305 limitPars = string.split(declarationList[1]) 00306 if len(limitPars)>=2: 00307 limitLower = float(limitPars[0]) 00308 limitUpper = float(limitPars[1]) 00309 if len(limitPars)>=3: 00310 limitWidth = float(limitPars[2]) 00311 else: 00312 limitWidth = -1. 00313 self.limits[plotName] = (limitLower,limitUpper,limitWidth) 00314 else: 00315 myLog.warn("%s.parseLimits: error in limit parameters for \'%s\' need two or three numbers saw %s\n" %(self.getName(),limit,limitPars)) 00316 else: 00317 myLog.warn("%s.parseLimits: error in limit declaration of \'%s\':\n expect: Plot name, lowerLimit upperLimit [rmsLimit]\n got: %s\n"\ 00318 %(self.getName(),limit,declarationList)) 00319 00320 def parseCuts(self): 00321 for cut in self.cutRequests.values(): 00322 # cuts are plot name, ntuple variable, cut value low high 00323 # TKRocc_ped = TKRocc, tower, -0.5 0.5 00324 declarationList = string.split(cut,',') 00325 if len(declarationList)>=3: 00326 plotName = string.strip(declarationList[0]).lower() 00327 plotVariable = string.strip(declarationList[1]).lower() 00328 cutPars = string.split(declarationList[2]) 00329 if len(cutPars)>=2: 00330 cutLower = float(cutPars[0]) 00331 cutUpper = float(cutPars[1]) 00332 self.cuts[(plotName,plotVariable)] = (cutLower,cutUpper) 00333 else: 00334 myLog.warn("%s.parseCuts: error in cut parameters for \'%s\' need two numbers saw %s\n" %(self.getName(),cut,cutPars)) 00335 else: 00336 myLog.warn("%s.parseCuts: error in cut declaration of \'%s\':\n expect: Plot name, Data Type, lowerCut upperCut\n got: %s\n"\ 00337 %(self.getName(),cut,declarationList)) 00338 00339 def readPedestals(self): 00340 if 'ANCILLARY_ROOT' in os.environ: 00341 ifname = os.path.join(os.environ['ANCILLARY_ROOT'],'Consumer/acdPeds.txt') 00342 try: 00343 infile = open(ifname) 00344 except: 00345 return 00346 self.lci.acdPeds = numarray.zeros((12,18,2,2)) 00347 for line in infile.xreadlines(): 00348 if line[0]=='!': 00349 continue 00350 # cable channel range mean rms 00351 entries = string.split(line) 00352 if len(entries)==5: 00353 self.lci.acdPeds[int(entries[0]),int(entries[1]),int(entries[2]),0] = float(entries[3]) 00354 self.lci.acdPeds[int(entries[0]),int(entries[1]),int(entries[2]),1] = float(entries[4]) 00355 infile.close() 00356 myLog.info('StandardPlotter:readPedestals: Read in ACD pedestals from %s'%(ifname)) 00357 00358 ifname = os.path.join(os.environ['ANCILLARY_ROOT'],'Consumer/calPeds.txt') 00359 try: 00360 infile = open(ifname) 00361 except: 00362 return 00363 self.lci.calPeds = numarray.zeros((16,8,12,2,4,2)) 00364 for line in infile.xreadlines(): 00365 if line[0]=='!': 00366 continue 00367 # tower layer column end range mean rms 00368 entries = string.split(line) 00369 if len(entries)==7: 00370 self.lci.calPeds[int(entries[0]),int(entries[1]),int(entries[2]),int(entries[3]),int(entries[4]),0] = float(entries[5]) 00371 self.lci.calPeds[int(entries[0]),int(entries[1]),int(entries[2]),int(entries[3]),int(entries[4]),1] = float(entries[6]) 00372 infile.close() 00373 myLog.info('StandardPlotter:readPedestals: Read in CAL pedestals from %s'%(ifname)) 00374 00375 def checkLimit(self,plot,limit): 00376 # check the given limit against the given plot 00377 # returns a tuple of (limitColumnName,limitLabelList,limitList,errorLabelList,errorList) 00378 limitLabelList = [] 00379 limitFailList = [] 00380 errorLabelList = [] 00381 errorFailList = [] 00382 originalLimitColumnName = None 00383 if plot in self.histDict: 00384 # get columns wanted from plot declaration 00385 plotColumns = self.getPlotColumns(plot) 00386 xColumnName = None 00387 yColumnName = None 00388 errorColumnName = None 00389 limitColumnName = plotColumns[len(plotColumns)-1] # limit column is always last or entries/bin 00390 originalLimitColumnName = limitColumnName 00391 if len(plotColumns)>=1: 00392 xColumnName = plotColumns[0] 00393 if len(plotColumns)>=2: 00394 yColumnName = plotColumns[1] 00395 # match these to the plot ntuple columns 00396 xColumn = None 00397 yColumn = None 00398 limitColumn = None 00399 errorColumn = None 00400 cHist = self.histDict[plot] 00401 histNtuple = cHist.createNTuple() 00402 for columnIndex in range(histNtuple.columns()): 00403 columnName = histNtuple.getLabelAt(columnIndex) 00404 if columnName == xColumnName: 00405 xColumn = histNtuple.getColumn(columnName) 00406 if columnName == yColumnName: 00407 yColumn = histNtuple.getColumn(columnName) 00408 if columnName == limitColumnName: 00409 limitColumn = histNtuple.getColumn(columnName) 00410 if columnName == 'Error': 00411 errorColumn = histNtuple.getColumn(columnName) 00412 errorColumnName = columnName 00413 # special case of Histogram or 2Dhist: limit column is entries per bin 00414 if columnName == 'Density' or columnName == 'Entries / bin': 00415 limitColumn = histNtuple.getColumn(columnName) 00416 limitColumnName = columnName 00417 # if the limit is same as one of the dependent variables, remove them 00418 if limitColumnName == yColumnName: 00419 yColumn = None 00420 yColumnName = None 00421 # if the y column doesn't exist, put a dummy column in place 00422 if yColumn is None: 00423 yColumn = numarray.zeros((len(limitColumn))) 00424 # now we have the Label Lists 00425 limitLabelList = [xColumnName,yColumnName,limitColumnName] 00426 (limitLower,limitUpper,limitWidth) = limit 00427 # check this limit 00428 # if we're not ring buffered, assume the limits are rates in histogram bins 00429 if limitColumnName == 'Density' or limitColumnName == 'Entries / bin': 00430 if self.ntRequests[self.getPlotNtuple(plot)]<=0: 00431 limitUpper *= self.lci.nEvtSeen 00432 limitLower *= self.lci.nEvtSeen 00433 for binIndex in range(len(limitColumn)): 00434 if limitColumn[binIndex]<limitLower or limitColumn[binIndex]>limitUpper: 00435 limitFailList.append([xColumn[binIndex],yColumn[binIndex],limitColumn[binIndex]]) 00436 # next the width if applicable 00437 if limitWidth>0 and errorColumn is not None: 00438 errorLabelList = [xColumnName,yColumnName,errorColumnName] 00439 for binIndex in range(len(errorColumn)): 00440 if errorColumn[binIndex]>limitWidth: 00441 errorFailList.append([xColumn[binIndex],yColumn[binIndex],errorColumn[binIndex]]) 00442 else: 00443 myLog.warn('%s.checkLimit: error in limit \'%s\': plot \'%s\' unknown or not requested. Known plots: %s\n'\ 00444 %(self.getName(),limit,plot,self.histDict.keys())) 00445 00446 return (originalLimitColumnName,limitLabelList,limitFailList,errorLabelList,errorFailList) 00447 00448 def reportResults(self): 00449 if self.reportPath is not None and self.getRunId() != '': 00450 self.startReport() 00451 self.reportLimits() 00452 self.reportPlots() 00453 reportName = self.getName() + "_" + self.getRunId() + ".html" 00454 myLog.info('Creating report %s'%(reportName)) 00455 self.writeReport(os.path.join(self.reportPath,reportName)) 00456 00457 00458 def startSummary(self): 00459 self.summarySection = self.report.addSection('Summary of Results',insert=0) 00460 configFileName = os.path.basename(self.configFile) 00461 itemString = "<a href = '%s'>%s</a>"%(configFileName,configFileName) 00462 self.report.addSectionItem(self.summarySection,'Plotter Configuration File',itemString) 00463 self.addExportedFile(self.configFile,delete = False) 00464 # a table of results 00465 self.summaryTable = self.report.addSectionTable(self.summarySection) 00466 self.report.beginTableRow(self.summaryTable) 00467 self.report.addTableHeader(self.summaryTable,'Plot Name') 00468 self.report.addTableHeader(self.summaryTable,'Data Type') 00469 self.report.addTableHeader(self.summaryTable,'Limit Type') 00470 self.report.addTableHeader(self.summaryTable,'Status') 00471 00472 def reportLimits(self): 00473 sectionID = None 00474 for plot in self.limits.keys(): 00475 if plot in self.histDict: 00476 cLimit = self.limits[plot] 00477 # check ringBuffer for completion 00478 if self.ntRequests[self.getPlotNtuple(plot)]>0 and self.lci.ntDict[self.getPlotNtuple(plot)].rows()<self.ntRequests[self.getPlotNtuple(plot)]: 00479 # append this to the summary table 00480 (limitLower,limitUpper,limitWidth) = cLimit 00481 status = 'INSUFFICIENT' 00482 if limitWidth>0: 00483 limitType = 'range/sigma limit' 00484 else: 00485 limitType = 'range limit' 00486 if self.getCompletionStatusStr()=='PASSED': 00487 self.setCompletionStatus(self.COMPL_STATUS_PASSED_CONDITIONALLY) 00488 self.addSummaryTableRow(plot,limitType,status) 00489 continue 00490 (limitName,limitLabelList,limitFailList,errorLabelList,errorFailList) = self.checkLimit(plot,cLimit) 00491 if limitName is not None: 00492 # print out a plot section 00493 itemStartString = 'Limit Check for Plot:' 00494 if sectionID is None: 00495 sectionID = self.report.addSection('%s Data Limit Checks'%(self.dataType)) 00496 if self.firstBodySection is None: 00497 self.firstBodySection = sectionID 00498 self.reportPlotItem(sectionID,plot,itemStartString) 00499 # get limit descriptor 00500 (limitLower,limitUpper,limitWidth) = cLimit 00501 xName = limitLabelList[0] 00502 yName = limitLabelList[1] 00503 zName = limitLabelList[2] 00504 limitDesc = self.lci.getColumnDescription(self.getPlotNtuple(plot),limitName) 00505 if zName == 'Density' or zName == 'Entries / bin': 00506 if self.ntRequests[self.getPlotNtuple(plot)]>0: 00507 limitDesc = 'Entries per bin' 00508 else: 00509 limitDesc = 'Entries per bin per event' 00510 itemString = '%s between %f and %f'%(limitDesc,limitLower,limitUpper) 00511 self.report.addSectionItem(sectionID,'Limit',itemString) 00512 # check this limit 00513 status = 'PASSED' 00514 if len(limitFailList)>0: 00515 status = 'FAILED' 00516 self.setCompletionStatus(self.COMPL_STATUS_FAILED) 00517 self.report.addSectionItem(sectionID,'Status',self.report.statusString(status)) 00518 # append this to the summary table 00519 self.addSummaryTableRow(plot,'range limit',status) 00520 # list out failures 00521 self.listFailed(limitLabelList,limitFailList,plot,limitDesc,sectionID) 00522 # next the error limit if requested 00523 if len(errorLabelList)>0: 00524 itemString = '%s sigma less than %f'%(limitDesc,limitWidth) 00525 self.report.addSectionItem(sectionID,'Limit',itemString) 00526 # check this limit 00527 status = 'PASSED' 00528 if len(errorFailList)>0: 00529 status = 'FAILED' 00530 self.setCompletionStatus(self.COMPL_STATUS_FAILED) 00531 self.report.addSectionItem(sectionID,'Status',self.report.statusString(status)) 00532 # append this to the summary table 00533 self.addSummaryTableRow(plot,'sigma limit',status) 00534 # list out failures 00535 self.listFailed(errorLabelList,errorFailList,plot,limitDesc,sectionID) 00536 return sectionID 00537 00538 def addSummaryTableRow(self,plot,limitType,status): 00539 if self.summarySection is None: 00540 self.startSummary() 00541 self.report.beginTableRow(self.summaryTable) 00542 self.report.addTableData(self.getPlotTitle(plot)) 00543 self.report.addTableData(self.dataType) 00544 self.report.addTableData(limitType) 00545 self.report.addTableData(self.report.statusString(status)) 00546 00547 def reportPlotItem(self,sectionID,plot,itemStartString): 00548 self.reportedPlots.append((plot,self.dataType)) 00549 if self.gui is not None: 00550 pngFile = self.gui.execGUImethod(self.capturePlot,plot) 00551 else: 00552 pngFile = self.capturePlot(plot) 00553 axisLabelString = '' 00554 first = True 00555 for label in self.getPlotColumns(plot): 00556 if first: 00557 axisLabelString+=self.lci.getColumnDescription(self.getPlotNtuple(plot),label) 00558 first = False 00559 else: 00560 axisLabelString+=' <b>Vs.</b> '+self.lci.getColumnDescription(self.getPlotNtuple(plot),label) 00561 itemString = '<hr/>%s %s'%(itemStartString,self.getPlotTitle(plot)) 00562 self.report.addSectionImage(sectionID,itemString,pngFile) 00563 self.report.addSectionItem(sectionID,'Plot Type',self.getPlotType(plot)) 00564 self.report.addSectionItem(sectionID,'Plot Axes',axisLabelString) 00565 if self.ntRequests[self.getPlotNtuple(plot)]>0: 00566 ntEntries = self.lci.ntDict[self.getPlotNtuple(plot)].rows() 00567 bufferSize = self.ntRequests[self.getPlotNtuple(plot)] 00568 if ntEntries < bufferSize: 00569 eventItemString = '%s (Ring Buffer has %d of %d rows filled)'\ 00570 %(str(self.lci.nEvtSeen),ntEntries,bufferSize) 00571 else: 00572 eventItemString = '%s (Ring Buffer of %d is full)'\ 00573 %(str(self.lci.nEvtSeen),bufferSize) 00574 else: 00575 eventItemString = str(self.lci.nEvtSeen) 00576 self.report.addSectionItem(sectionID,'Number of Events',eventItemString) 00577 cutList = self.getPlotCuts(plot) 00578 if len(cutList)>0: 00579 itemString = '<ul>' 00580 for (variable,low,high) in cutList: 00581 itemString+='<li>%s between %f and %f</li>'%(self.lci.getColumnDescription(self.getPlotNtuple(plot),variable),low,high) 00582 itemString += '</ul>' 00583 self.report.addSectionItem(sectionID,'Cuts Applied',itemString) 00584 00585 def listFailed(self,names,failList,plot,limitDesc,sectionID): 00586 xName = names[0] 00587 yName = names[1] 00588 zName = names[2] 00589 if (len(failList)==0): return 00590 # make this a table 00591 tableID = self.report.addSectionTable(sectionID) 00592 self.report.beginTableRow(tableID) 00593 self.report.addTableHeader(tableID,self.lci.getColumnDescription(self.getPlotNtuple(plot),xName)) 00594 if yName is not None: 00595 self.report.addTableHeader(tableID,self.lci.getColumnDescription(self.getPlotNtuple(plot),yName)) 00596 if zName == 'Error': 00597 self.report.addTableHeader(tableID,limitDesc+' sigma') 00598 else: 00599 self.report.addTableHeader(tableID,limitDesc) 00600 for binIndex in range(len(failList)): 00601 self.report.beginTableRow(tableID) 00602 entry = failList[binIndex] 00603 if xName == 'chanindex': 00604 self.report.addTableData('%s (Channel %d)'%(self.lci.getChanIndexDescription(self.getPlotNtuple(plot),entry[0]),entry[0])) 00605 else: 00606 self.report.addTableData('%0.1f'%(entry[0])) 00607 if yName is not None: 00608 if yName == 'chanindex': 00609 self.report.addTableData('%s (Channel %d)'%(self.lci.getChanIndexDescription(self.getPlotNtuple(plot),entry[1]),entry[1])) 00610 else: 00611 self.report.addTableData('%0.1f'%(entry[1])) 00612 # value compared depends on if its per event or not 00613 limitValueString = '%f'%(entry[2]) 00614 if zName == 'Density' or zName == 'Entries / bin': 00615 if self.ntRequests[self.getPlotNtuple(plot)]<=0: 00616 if self.lci.nEvtSeen>0: 00617 limitValueString = '%f'%(entry[2]/self.lci.nEvtSeen) 00618 else: 00619 limitValueString = '0.0' 00620 self.report.addTableData(limitValueString) 00621 00622 def capturePlot(self,plot): 00623 plotFileName = plot+"_"+self.dataType+ "_" + self.getRunId() + ".png" 00624 plotFileFull = os.path.join(self.reportPath,plotFileName) 00625 self.canvas.saveAsImage(self.histDict[plot], plotFileFull) 00626 self.addExportedFile(plotFileFull, delete=True) 00627 return plotFileName 00628 00629 def startReport(self): 00630 self.report = standardTestReport() 00631 self.summarySection = None 00632 self.firstBodySection = None 00633 self.reportedPlots = [] 00634 00635 def writeReport(self, reportFileName): 00636 # write out plots and report on noticed anomalies 00637 if self.gui is not None: 00638 testInfoSection = self.report.writeTestInformation(Operator = self.gui.preferences()["operator"], 00639 Site = self.gui.preferences()["lastsite"], 00640 TestName = self.getName(), 00641 runId = self.getRunId(), 00642 Device = self.gui.preferences()["lastinstrumenttype"], 00643 Phase = self.gui.preferences()["lastphase"], 00644 Status = self.getCompletionStatusStr() ) 00645 else: 00646 testInfoSection = self.report.writeTestInformation(TestName = self.getName(), 00647 runId = self.getRunId(), 00648 Status = self.getCompletionStatusStr() ) 00649 if self.firstBodySection is not None: 00650 self.report.insertSection(testInfoSection,self.firstBodySection) 00651 if self.summarySection is not None: 00652 self.report.insertSection(self.summarySection,self.firstBodySection) 00653 else: 00654 self.report.insertSection(testInfoSection) 00655 if self.summarySection is not None: 00656 self.report.insertSection(self.summarySection) 00657 import os 00658 self.report.closeTestReport(reportFileName) 00659 self.addExportedFile(reportFileName,delete = True) 00660 00661 def reportPlots(self): 00662 if len(self.reportedPlots)>0: 00663 sectionString = 'Additional %s Data Plots'%(self.dataType) 00664 else: 00665 sectionString = '%s Data Plots'%(self.dataType) 00666 sectionID = self.report.addSection(sectionString) 00667 if self.firstBodySection is None: 00668 self.firstBodySection = sectionID 00669 for plot in self.histDict: 00670 if not (plot,self.dataType) in self.reportedPlots: 00671 itemStartString = 'Plot:' 00672 self.reportPlotItem(sectionID,plot,itemStartString) 00673 00674 class standardTestReport(rcTestReport): 00675 """ 00676 This class provide a standard Test Report HTML file 00677 for plotter results. 00678 """ 00679 def __init__(self): 00680 "Init the TestReport and Write Heading" 00681 rcTestReport.__init__(self) 00682 rcTestReport.initReport(self,title='Plotter Report') 00683 rcTestReport.addHTML(self,""" 00684 <STYLE TYPE="text/css"> 00685 H1 { font-size: x-large; color: blue } 00686 H2 { font-size: large; color: blue } 00687 H3 { font-size: medium; color: blue } 00688 </STYLE> 00689 """) 00690 rcTestReport.addHeading(self,'GLAST LAT Integration and Test') 00691 rcTestReport.addHeading(self,'Test Report') 00692 00693 def writeTestInformation(self, runId, TestName=None, Operator=None, Site=None, Device=None, Phase=None, Status=None): 00694 """Write standard information about test routine. 00695 00696 \param 00697 Operator : name of the operator who perform the test 00698 \param 00699 Site : test site 00700 \param 00701 TestName : test name 00702 \param 00703 runID : run ID given by RunControl 00704 \param 00705 Device : device under test (i.e. MCM, tray, stack, tower) 00706 """ 00707 infoPage = rcTestReport.addSection(self, 'Test identification',insert=0) 00708 rcTestReport.addSectionItem(self, infoPage, 'Date', time.asctime() ) 00709 rcTestReport.addSectionItem(self, infoPage, 'Run ID', runId) 00710 rcTestReport.addSectionItem(self, infoPage, 'Application', TestName ) 00711 if Site is not None: 00712 rcTestReport.addSectionItem(self, infoPage, 'Site', Site, ) 00713 if Operator is not None: 00714 rcTestReport.addSectionItem(self, infoPage, 'Operator', Operator, ) 00715 if Device is not None: 00716 rcTestReport.addSectionItem(self, infoPage, 'Instrument Type', Device ) 00717 if Phase is not None: 00718 rcTestReport.addSectionItem(self, infoPage, 'Phase', Phase ) 00719 if Status is not None: 00720 rcTestReport.addSectionItem(self, infoPage, 'Status', self.statusString(Status) ) 00721 return infoPage 00722 00723 def statusString(self, status): 00724 """Write test status (Passed/Failed or Aborted) in the report 00725 \param 00726 status : Test status ('Passed', 'Failed' or 'Aborted' if script is stopped before end). 00727 """ 00728 if status.upper() == 'PASSED': 00729 string = self.getColorText('Passed', 'green', Bold=True) 00730 elif status.upper() == 'PASSED_CONDITIONALLY': 00731 string = self.getColorText('Passed Conditionally', 'yellow', Bold=True) 00732 elif status.upper() == 'FAILED': 00733 string = self.getColorText('Failed', 'red', Bold=True) 00734 elif status.upper() == 'ABORTED': 00735 string = self.getColorText('Aborted', 'yellow', Bold=True) 00736 elif status.upper() == 'UNDEFINED': 00737 string = self.getColorText('Undefined', 'yellow', Bold=True) 00738 elif status.upper() == 'INSUFFICIENT': 00739 string = self.getColorText('Insufficient Data', 'yellow', Bold=True) 00740 else: 00741 myLog.warn("%s is an invalid status in statusString" % (status)) 00742 return 00743 return string 00744 00745 def getColorText(self, Text, Color='black', Bold=False): 00746 ColorTable = { 'red' : '#FF0000', 00747 'green' : '#33CC33', 00748 'yellow': '#FFAA33', 00749 'black' : '#000000' 00750 } 00751 if ColorTable.has_key(Color): 00752 Color = ColorTable[Color] 00753 string = '<font color ="' + Color + '">' + Text + '</font>' 00754 if Bold: 00755 string = '<b>' + string + '</b>' 00756 return string 00757 00758 def closeTestReport(self, FileName): 00759 "Close the HTML file and save it" 00760 rcTestReport.transformToFile(self, os.environ.get('ONLINE_ROOT') + "/LATTE/consumer/reportStyle.xsl", FileName) 00761 00762