00001 #!/usr/local/bin/python
00002 #
00003 # Copyright 2002
00004 # by
00005 # The Board of Trustees of the
00006 # Leland Stanford Junior University.
00007 # All rights reserved.
00008 #
00009
00010 __facility__ = "Online"
00011 __abstract__ = "Test report generator class for RunControl"
00012 __author__ = "S. Tuvi <stuvi@SLAC.Stanford.edu> SLAC - GLAST LAT I&T/Online"
00013 __date__ = ("$Date: 2005/08/31 20:58:05 $").split(' ')[1]
00014 __version__ = "$Revision: 2.5 $"
00015 __release__ = "$Name: R04-12-00 $"
00016 __credits__ = "SLAC"
00017
00018 import LATTE.copyright_SLAC
00019
00020 import Pyana
00021 from xml.dom import Node
00022 #from xml.dom import implementation
00023 #from xml.dom.ext import PrettyPrint
00024 #from xml.dom.ext.reader import Sax2
00025 #from xml.dom.minidom import getDOMImplementation, parseString, Node
00026 from Ft.Xml.Domlette import NonvalidatingReader, PrettyPrint, implementation
00027
00028 from LATTE.database.gSchemaConfig import LAT_NAMESPACE
00029
00030 from cStringIO import StringIO
00031
00032 class rcTestReport(object):
00033 """\brief Test Report class for RunControl.
00034
00035 This class provides a mechanism to create test reports in a well formed (XML) format using
00036 the provided report building methods and lets the user transform it to HTML format by providing
00037 an XSLT stylesheet template. A third-party tool called Pyana is used to do the transformation.
00038 A sample template (reportStyle.xsl.sample) is provided in the RunControl directory. For more
00039 information on XSLT see the following links:
00040
00041 <p><a href="http:
00042 <p><a href="http:
00043
00044 """
00045
00046 def __init__(self):
00047 self.__reports = []
00048
00049 def initReport(self, title):
00050 """ Initializes the report class and provides a title for the report
00051
00052 \param title The title that will show in the window title of the HTML browser
00053 """
00054 #self.__doc = implementation.createDocument(None,None,None)
00055 #self.__doc = getDOMImplementation().createDocument(None,None,None)
00056 self.__doc = implementation.createDocument(None,None,None)
00057 self.__root = self.__doc.createElementNS(LAT_NAMESPACE, "TestReport")
00058 self.__doc.appendChild(self.__root)
00059 titleNode = self.__doc.createElementNS(LAT_NAMESPACE, "Title")
00060 data = self.__doc.createTextNode(title)
00061 titleNode.appendChild(data)
00062 self.__root.appendChild(titleNode)
00063 self.__maxHeadingLine = 0
00064 self.__maxSectionId = 0
00065 self.__maxTableId = 0
00066 self.__sectionNodes = {}
00067 self.__tableNodes = {}
00068
00069 def addHTML(self, htmlString, section=None):
00070 """ Adds an arbitrary HTML fragment to the end of the report or a section
00071
00072 \param htmlString HTML fragment.
00073 \param section Optional section id that this HTML will be written to.
00074 If no section is specified then HTML is added to the
00075 end of the report.
00076 """
00077 htmlDom = self.__createHTMLDom(htmlString)
00078 if section is None:
00079 self.__root.appendChild(htmlDom)
00080 else:
00081 sectionNode = self.__sectionNodes[section]
00082 sectionNode.appendChild(htmlDom)
00083
00084 def addHeading(self, text):
00085 """ Adds a heading to the report
00086
00087 \param text Heading text
00088 """
00089 heading = self.findTag(self.__root, 'Heading')
00090 if heading is None:
00091 heading = self.__doc.createElementNS(LAT_NAMESPACE, 'Heading')
00092 self.__root.appendChild(heading)
00093 self.__maxHeadingLine += 1
00094 line = self.__doc.createElementNS(LAT_NAMESPACE, 'Line')
00095 line.setAttributeNS(LAT_NAMESPACE, 'no', str(self.__maxHeadingLine))
00096 line.appendChild(self.__createHTMLDom(text))
00097 heading.appendChild(line)
00098
00099 def addSection(self, caption, insert=1):
00100 """ Adds a section to the report and returns the section id.
00101
00102 \param caption Section caption
00103 \param insert 0 = Do not insert the section into the report
00104 1 = Insert the section into the report (default)
00105 \return Section id.
00106 """
00107 self.__maxSectionId += 1
00108 sectionNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Section')
00109 sectionNode.setAttributeNS(LAT_NAMESPACE, 'id', str(self.__maxSectionId))
00110 sectionNode.appendChild(self.__createHTMLNode('Caption',caption))
00111
00112 if insert == 1:
00113 self.__root.appendChild(sectionNode)
00114 self.__sectionNodes[self.__maxSectionId] = sectionNode
00115 return self.__maxSectionId
00116
00117 def insertSection(self, sectionId, beforeSectionId=None):
00118 """ Inserts a previously added section (with insert=0)
00119 to the specified position in the report.
00120
00121 \param sectionId Id of the section to be inserted
00122 \param beforeSectionId Id of the section that will come after the section specified
00123 by \a sectionId. If it is None then it is appended to the end.
00124 Defaults to None.
00125 """
00126 if beforeSectionId is not None:
00127 self.__root.insertBefore(self.__sectionNodes[sectionId], self.__sectionNodes[beforeSectionId])
00128 else:
00129 self.__root.appendChild(self.__sectionNodes[sectionId])
00130
00131 def addSectionItem(self, sectionId, label, text, url=''):
00132 """ Adds a section item.
00133 Section items are entries with a label and text and an optional URL.
00134
00135 \param sectionId Id of the section that this item will be inserted in.
00136 \param label Item label
00137 \param text Item text
00138 \param url Item URL
00139 """
00140 sectionNode = self.__sectionNodes[sectionId]
00141 itemNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Item')
00142 itemNode.appendChild(self.__createHTMLNode('Label',label))
00143 itemNode.appendChild(self.__createHTMLNode('Text',text))
00144 if (url != '') & (url != None):
00145 linkNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Link')
00146 data = self.__doc.createTextNode(url)
00147 linkNode.appendChild(data)
00148 itemNode.appendChild(linkNode)
00149 sectionNode.appendChild(itemNode)
00150
00151 def addSectionImage(self, sectionId, caption, url, width='', height=''):
00152 """ Adds an image to a section. Images can have a caption,
00153 and an optional width and height. The url parameter specifies the
00154 location of the image.
00155
00156 \param sectionId Id of the section that this image will be inserted in.
00157 \param caption Image caption
00158 \param url Image source URL
00159 \param width Optional width of the image
00160 \param height Optional height of the image
00161 """
00162 sectionNode = self.__sectionNodes[sectionId]
00163 imageNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Image')
00164 imageNode.appendChild(self.__createHTMLNode('Caption',caption))
00165 linkNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Link')
00166 data = self.__doc.createTextNode(url)
00167 linkNode.appendChild(data)
00168 imageNode.appendChild(linkNode)
00169 if width != '':
00170 widthNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Width')
00171 data = self.__doc.createTextNode(str(width))
00172 widthNode.appendChild(data)
00173 imageNode.appendChild(widthNode)
00174 if height != '':
00175 heightNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Height')
00176 data = self.__doc.createTextNode(str(height))
00177 heightNode.appendChild(data)
00178 imageNode.appendChild(heightNode)
00179 sectionNode.appendChild(imageNode)
00180
00181
00182 def addSectionTable(self, sectionId, border='1', width=''):
00183 """ Adds a table to a section. Border and width are optional
00184
00185 \param sectionId Id of the section that this image will be inserted in.
00186 \param border Specifies whether the table will have a border or not. Default value is 1.
00187 \param width Specifies the width of the border in percentage relative to the width of the page.
00188
00189 \return Table id.
00190 """
00191 self.__maxTableId += 1
00192 sectionNode = self.__sectionNodes[sectionId]
00193 tableNode = self.__doc.createElementNS(LAT_NAMESPACE, 'Table')
00194 if border != '':
00195 tableNode.setAttributeNS(LAT_NAMESPACE, 'border', border)
00196 if width != '':
00197 tableNode.setAttributeNS(LAT_NAMESPACE, 'width', width)
00198 sectionNode.appendChild(tableNode)
00199 self.__tableNodes[self.__maxTableId] = tableNode
00200 return self.__maxTableId
00201
00202 def addTableHeader(self, tableId, text, align=''):
00203 """ Adds one column header to the table.
00204
00205 \param tableId Id of the table where column headers are being added.
00206 \param text Column header text.
00207 \param align Optional text alignment.
00208 """
00209 tableNode = self.__tableNodes[tableId]
00210 tr = self.findTag(tableNode, 'TR')
00211 if tr is None:
00212 tr = self.__doc.createElementNS(LAT_NAMESPACE, 'TR')
00213 tableNode.appendChild(tr)
00214 th = self.__doc.createElementNS(LAT_NAMESPACE, 'TH')
00215 if align != '':
00216 th.setAttributeNS(LAT_NAMESPACE, 'align', align)
00217 #data = self.__doc.createTextNode(text)
00218 #th.appendChild(data)
00219 th.appendChild(self.__createHTMLDom(text))
00220 tr.appendChild(th)
00221
00222 def beginTableRow(self, tableId):
00223 """ Starts a table row.
00224
00225 \param tableId Id of the table where the row is being added.
00226 """
00227 tableNode = self.__tableNodes[tableId]
00228 tr = self.__doc.createElementNS(LAT_NAMESPACE, 'TR')
00229 tableNode.appendChild(tr)
00230 self.__currentTableRow = tr
00231
00232 def addTableData(self, text, url='', align='', width=''):
00233 """ Adds a data column to the table row that was started by the last
00234 beginTableRow() call.
00235
00236 \param text Text of the data column.
00237 \param url Optional. If the text refers to a hyperlink provide the URL here.
00238 \param align Optional text alignment.
00239 \param width Optional column width in percentage.
00240 """
00241 tr = self.__currentTableRow
00242 if tr is None:
00243 raise RuntimeError, "No current row"
00244 else:
00245 td = self.__doc.createElementNS(LAT_NAMESPACE, 'TD')
00246 if align != '':
00247 td.setAttributeNS(LAT_NAMESPACE, 'align', align)
00248 if width != '':
00249 td.setAttributeNS(LAT_NAMESPACE, 'width', align)
00250 if url != '':
00251 href = self.__doc.createElementNS(LAT_NAMESPACE, 'A')
00252 href.setAttributeNS(LAT_NAMESPACE, 'href',url)
00253 data = self.__createHTMLDom(str(text))
00254 if url != '':
00255 href.appendChild(data)
00256 data = href
00257 td.appendChild(data)
00258 tr.appendChild(td)
00259
00260
00261 def findTag(self, node, tagName):
00262 tNode = None
00263 for n in self.getChildNodes(node):
00264 if n.tagName == tagName:
00265 tNode = n
00266 break
00267 return tNode
00268
00269 def xmlToString(self):
00270 xmlStr = StringIO()
00271 PrettyPrint(self.__doc, xmlStr)
00272 #self.__doc.writexml(xmlStr)
00273 return xmlStr.getvalue()
00274
00275 def transformToFile(self, xslFileName, htmlFileName):
00276 """ Based on the input template and output filename parameters
00277 creates an HTML report.
00278
00279 \param xslFileName Input XSLT template file name.
00280 \param htmlFileName Output HTML filename.
00281 """
00282 xslFile = file(xslFileName)
00283 htmlFile = file(htmlFileName, 'w+')
00284 Pyana.transform2Writer(self.xmlToString(), xslFile, htmlFile)
00285 self.__reports.append(htmlFileName)
00286
00287 def getReports(self):
00288 return self.__reports
00289
00290 def getChildNodes(self, node):
00291 childNodes = []
00292 for n in node.childNodes:
00293 if (n.nodeType == Node.TEXT_NODE and \
00294 (n.data[0] == '\n' or n.data[0].strip() == '')) \
00295 or n.nodeType == Node.COMMENT_NODE:
00296 pass
00297 else:
00298 childNodes.append(n)
00299 return childNodes
00300
00301 ##private
00302 def __createHTMLDom(self, text):
00303 return self.__createHTMLNode('cdatanode', text)
00304
00305 def __createHTMLNode(self, nodeName, text):
00306 htmlString = '<'+nodeName+'>' + text + '</'+nodeName+'>'
00307 #htmlDoc = Sax2.FromXml(htmlString).documentElement
00308 htmlDoc = NonvalidatingReader.parseString(htmlString, 'urn:dummy').documentElement
00309 return self.__doc.importNode(htmlDoc,1)