Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

standardPlotter.py

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 

Generated on Fri Jul 21 13:26:32 2006 for LATTE R04-12-00 by doxygen 1.4.3