예제 #1
0
 def makeImageLink(self, graphFile):
     image = HTMLgen.Image(filename=graphFile,
                           src=graphFile,
                           height=100,
                           width=150,
                           border=0)
     return HTMLgen.Href(graphFile, image)
예제 #2
0
 def getLinksToOverview(self, version, testName, extraVersion,
                        linkFromDetailsToOverview):
     links = HTMLgen.Container()
     for targetFile, linkName in linkFromDetailsToOverview:
         testId = makeTestId(version, extraVersion, testName)
         links.append(HTMLgen.Href(targetFile + "#" + testId, linkName))
     return links
예제 #3
0
 def generateExtraVersionHeader(self, extraVersion):
     bgColour = self.colourFinder.find("column_header_bg")
     extraVersionElement = HTMLgen.Container(HTMLgen.Name(extraVersion),
                                             extraVersion)
     columnHeader = HTMLgen.TH(extraVersionElement,
                               colspan=len(self.tags) + 1,
                               bgcolor=bgColour)
     return HTMLgen.TR(columnHeader)
예제 #4
0
 def generateSummaries(self, extraVersion=None):
     bgColour = self.colourFinder.find("column_header_bg")
     row = [HTMLgen.TD("Summary", bgcolor=bgColour)]
     for tag in self.tags:
         categoryHandler = self.categoryHandlers[tag]
         detailPageName = getDetailPageName(self.pageVersion, tag)
         summary = categoryHandler.generateHTMLSummary(
             detailPageName + "#" + self.version, extraVersion)
         row.append(HTMLgen.TD(summary, bgcolor=bgColour))
     return HTMLgen.TR(*row)
예제 #5
0
 def writeCommentListPage(self):
     filename = os.path.join(self.pageDir, "commentlist.html")
     plugins.log.info("Writing comment html page at " + filename + "...")
     doc = HTMLgen.SimpleDocument(
         title="All Comments",
         stylesheet="../javascript/commentliststyle.css",
         xhtml=True,
         meta='<meta charset="' + locale.getpreferredencoding() + '">')
     for scriptFile in ["jquery.js", "comment.js", "commentlist.js"]:
         doc.append(HTMLgen.Script(src="../javascript/" + scriptFile))
     doc.write(filename)
예제 #6
0
 def getHeaderLine(self, tests, version, linkFromDetailsToOverview):
     testName, state, extraVersion = tests[0]
     if len(tests) == 1:
         linksToOverview = self.getLinksToOverview(
             version, testName, extraVersion, linkFromDetailsToOverview)
         headerText = "TEST " + repr(state) + " " + testName + " ("
         container = HTMLgen.Container(headerText, linksToOverview)
         return HTMLgen.Heading(4, container, ")")
     else:
         headerText = str(len(tests)) + " TESTS " + repr(state)
         return HTMLgen.Heading(4, headerText)
예제 #7
0
    def generateHTMLSummary(self, detailPageRef, extraVersion=None):
        numTests, summaryData = self.getSummaryData(extraVersion)
        container = HTMLgen.Container()
        for cat, count in summaryData:
            summary = HTMLgen.Text(self.getDescription(cat, count))
            if cat == "success":
                container.append(summary)
            else:
                linkTarget = detailPageRef + getCategoryDescription(cat)[-1]
                container.append(HTMLgen.Href(linkTarget, summary))

        testCountSummary = HTMLgen.Text(self.getTestCountDescription(numTests))
        return HTMLgen.Container(testCountSummary, container)
예제 #8
0
 def generateJenkinsChanges(self, pageDir):
     cacheDir = os.path.join(os.path.dirname(pageDir), "jenkins_changes")
     bgColour = self.colourFinder.find("changes_header_bg")
     row = [HTMLgen.TD("Changes", bgcolor=bgColour)]
     hasData = False
     prevTag = None
     for tag in self.tags:
         allChanges = self.findJenkinsChanges(prevTag, tag, cacheDir)
         cont = HTMLgen.Container()
         aborted = False
         for i, (authorOrMessage, target, bugs) in enumerate(allChanges):
             if i:
                 cont.append(HTMLgen.BR())
             if target:
                 cont.append(HTMLgen.Href(target, authorOrMessage))
             else:
                 cont.append(HTMLgen.Font(authorOrMessage, color="red"))
                 aborted = "Aborted" in authorOrMessage
             for bugText, bugTarget in bugs:
                 cont.append(HTMLgen.Href(bugTarget, bugText))
             hasData = True
         row.append(HTMLgen.TD(cont, bgcolor=bgColour))
         if not aborted:
             prevTag = tag
     if hasData:
         return HTMLgen.TR(*row)
예제 #9
0
 def getFilterScripts(self, pageColours):
     finder = ColourFinder(self.getConfigValue)
     rowHeaderColour = finder.find("row_header_bg")
     successColour = finder.find("success_bg")
     # Always put green at the start, we often want to filter that
     sortedColours = sorted(pageColours,
                            key=lambda c: (c != successColour, c))
     scriptCode = "var TEST_ROW_HEADER_COLOR = " + repr(rowHeaderColour) + ";\n" + \
                  "var Colors = " + repr(sortedColours) + ";"
     return [
         HTMLgen.Script(code=scriptCode),
         HTMLgen.Script(src="../javascript/jquery.js"),
         HTMLgen.Script(src="../javascript/filter.js"),
         HTMLgen.Script(src="../javascript/comment.js")
     ]
예제 #10
0
    def generateTestRows(self, testName, extraVersion, results):
        bgColour = self.colourFinder.find("row_header_bg")
        testId = makeTestId(self.version, extraVersion, testName)
        description = self.descriptionInfo.get(testName, "")
        container = HTMLgen.Container(HTMLgen.Name(testId), testName)
        rows = []
        testRow = [
            HTMLgen.TD(container,
                       bgcolor=bgColour,
                       title=self.escapeForHtml(description))
        ]

        # Don't add empty rows to the table
        foundData = False
        bgcol = None
        for tag in self.tags:
            cellContent, bgcol, hasData = self.generateTestCell(
                tag, testName, testId, results)
            testRow.append(HTMLgen.TD(cellContent, bgcolor=bgcol))
            foundData |= hasData

        if foundData:
            # We only filter based on the final column
            self.usedColours.add(bgcol)
            rows.append(HTMLgen.TR(*testRow))
        else:
            return rows

        for resourceName in self.resourceNames:
            foundData = False
            resourceRow = [
                HTMLgen.TD(HTMLgen.Emphasis("(" + resourceName + ")"),
                           align="right")
            ]
            for tag in self.tags:
                cellContent, bgcol, hasData = self.generateTestCell(
                    tag, testName, testId, results, resourceName)
                resourceRow.append(
                    HTMLgen.TD(HTMLgen.Emphasis(cellContent),
                               bgcolor=bgcol,
                               align="right"))
                foundData |= hasData

            if foundData:
                rows.append(HTMLgen.TR(*resourceRow))
        return rows
예제 #11
0
 def addVersionSection(self, version, categoryHandler,
                       linkFromDetailsToOverview):
     self.totalCategoryHandler.update(categoryHandler)
     container = HTMLgen.Container()
     container.append(HTMLgen.HR())
     container.append(self.getSummaryHeading(version, categoryHandler))
     for desc, testInfo in categoryHandler.getTestsWithDescriptions():
         fullDescription = self.getFullDescription(
             testInfo, version, linkFromDetailsToOverview)
         if fullDescription:
             container.append(HTMLgen.Name(version + desc))
             container.append(
                 HTMLgen.Heading(
                     3, "Detailed information for the tests that " + desc +
                     ":"))
             container.append(fullDescription)
     self.versionSections.append(container)
예제 #12
0
 def createPage(self):
     style = "body,td {color: #000000;font-size: 11px;font-family: Helvetica;} th {color: #000000;font-size: 13px;font-family: Helvetica;}"
     title = "Test results for " + self.pageTitle
     return HTMLgen.SimpleDocument(title=title,
                                   style=style,
                                   xhtml=True,
                                   meta='<meta charset="' +
                                   locale.getpreferredencoding() + '">')
예제 #13
0
 def getFullDescription(self, tests, version, linkFromDetailsToOverview):
     freeTextData = self.getFreeTextData(tests)
     if len(freeTextData) == 0:
         return
     fullText = HTMLgen.Container()
     for freeText, tests in freeTextData:
         tests.sort(key=lambda info: info[0])
         for testName, _, extraVersion in tests:
             fullText.append(
                 HTMLgen.Name(makeTestId(version, extraVersion, testName)))
         fullText.append(
             self.getHeaderLine(tests, version, linkFromDetailsToOverview))
         self.appendFreeText(fullText, freeText)
         if len(tests) > 1:
             for line in self.getTestLines(tests, version,
                                           linkFromDetailsToOverview):
                 fullText.append(line)
     return fullText
예제 #14
0
 def getTestLines(self, tests, version, linkFromDetailsToOverview):
     lines = []
     for testName, _, extraVersion in tests:
         linksToOverview = self.getLinksToOverview(
             version, testName, extraVersion, linkFromDetailsToOverview)
         headerText = testName + " ("
         container = HTMLgen.Container(headerText, linksToOverview, ")<br>")
         lines.append(container)
     return lines
예제 #15
0
 def generateTestCell(self,
                      tag,
                      testName,
                      testId,
                      results,
                      resourceName=""):
     state = results.get(tag)
     cellText, success, fgcol, bgcol = self.getCellData(state, resourceName)
     cellContent = HTMLgen.Font(cellText, color=fgcol)
     if success:
         return cellContent, bgcol, cellText != "N/A"
     else:
         linkTarget = getDetailPageName(self.pageVersion,
                                        tag) + "#" + testId
         tooltip = "'" + testName + "' failure for " + getDisplayText(tag)
         return HTMLgen.Href(linkTarget,
                             cellContent,
                             title=tooltip,
                             style="color:black"), bgcol, True
예제 #16
0
 def appendFreeText(self, fullText, freeText):
     freeText = freeText.replace("<", "&lt;").replace(">", "&gt;")
     linkMarker = "URL=http"
     if linkMarker in freeText:
         currFreeText = ""
         for line in freeText.splitlines():
             if linkMarker in line:
                 fullText.append(
                     HTMLgen.RawText("<PRE>" + currFreeText.strip() +
                                     "</PRE>"))
                 currFreeText = ""
                 words = line.strip().split()
                 linkTarget = words[-1][4:]  # strip off the URL=
                 newLine = " ".join(words[:-1])
                 fullText.append(HTMLgen.Href(linkTarget, newLine))
                 fullText.append(HTMLgen.BR())
             else:
                 currFreeText += line + "\n"
     else:
         currFreeText = freeText
     if currFreeText:
         fullText.append(HTMLgen.RawText("<PRE>" + currFreeText + "</PRE>"))
예제 #17
0
    def generate(self, loggedTests, pageDir, repositoryDirs):
        table = HTMLgen.TableLite(border=0,
                                  cellpadding=4,
                                  cellspacing=2,
                                  width="100%")
        table.append(self.generateTableHead(repositoryDirs))
        table.append(self.generateSummaries())
        if os.getenv("JENKINS_URL"):
            changeRow = self.generateJenkinsChanges(pageDir)
            if changeRow:
                table.append(changeRow)
        hasRows = False
        for extraVersion, testInfo in list(loggedTests.items()):
            currRows = []
            for test in sorted(testInfo.keys()):
                results = testInfo[test]
                rows = self.generateTestRows(test, extraVersion, results)
                if rows:
                    currRows += rows

            if len(currRows) == 0:
                continue
            else:
                hasRows = True

            # Add an extra line in the table only if there are several versions.
            if len(loggedTests) > 1:
                fullVersion = self.version
                if extraVersion:
                    fullVersion += "." + extraVersion
                table.append(self.generateExtraVersionHeader(fullVersion))
                table.append(self.generateSummaries(extraVersion))

            for row in currRows:
                table.append(row)

        if hasRows:
            table.append(HTMLgen.BR())
            return table
예제 #18
0
    def addTable(self, page, resourceNames, categoryHandlers, version,
                 loggedTests, selector, tableHeader, filePath, graphHeading,
                 repositoryDirs):
        graphDirname, graphFileRef = self.getGraphFileParts(filePath, version)
        testTable = self.getTestTable(self.getConfigValue, resourceNames,
                                      self.descriptionInfo,
                                      selector.selectedTags, categoryHandlers,
                                      self.pageVersion, version,
                                      os.path.join(graphDirname, graphFileRef))
        table = testTable.generate(loggedTests, self.pageDir, repositoryDirs)
        if table:
            cells = []
            if tableHeader:
                page.append(HTMLgen.HR())
                cells.append(self.makeTableHeaderCell(tableHeader))

            graphLink = None
            fullPath = os.path.abspath(os.path.join(graphDirname,
                                                    graphFileRef))
            if testTable.generateGraph(fullPath, graphHeading):
                graphLink = self.makeImageLink(graphFileRef.replace("\\", "/"))
                cells.append(HTMLgen.TD(graphLink))

            if len(cells):
                row = HTMLgen.TR(*cells)
                initialTable = HTMLgen.TableLite(align="center")
                initialTable.append(row)
                page.append(initialTable)

            extraVersions = list(loggedTests.keys())[1:]
            if len(extraVersions) > 0:
                page.append(testTable.generateExtraVersionLinks(extraVersions))

            page.append(table)
            return True, graphLink, testTable.usedColours
        else:
            return False, None, []
예제 #19
0
 def __init__(self, tag, pageTitle, pageSubTitles):
     tagText = getDisplayText(tag)
     pageDetailTitle = "Detailed test results for " + pageTitle + ": " + tagText
     self.document = HTMLgen.SimpleDocument(
         title=TitleWithDateStamp(pageDetailTitle),
         meta='<meta charset="' + locale.getpreferredencoding() + '">')
     headerText = tagText + " - detailed test results for " + pageTitle
     self.document.append(HTMLgen.Heading(1, headerText, align='center'))
     for subTitle, command in pageSubTitles:
         self.document.append(HTMLgen.Center(HTMLgen.Emphasis(subTitle)))
         self.document.append(
             HTMLgen.Center(
                 HTMLgen.Paragraph(command, style='font-family:monospace')))
     self.totalCategoryHandler = CategoryHandler()
     self.versionSections = []
예제 #20
0
    def generate(self, repositoryDirs, subPageNames, archiveUnused):
        minorVersionHeader = HTMLgen.Container()
        allMonthSelectors = set()
        latestMonth = None
        pageToGraphs = {}
        for version, repositoryDirInfo in list(repositoryDirs.items()):
            self.diag.info("Generating " + version)
            tagData, stateFiles, successFiles = self.findTestStateFilesAndTags(
                repositoryDirInfo)
            if len(stateFiles) > 0 or len(successFiles) > 0:
                tags = list(tagData.keys())
                tags.sort(key=self.tagSortKey)
                selectors = self.makeSelectors(subPageNames, tags)
                monthSelectors = SelectorByMonth.makeInstances(tags)
                allMonthSelectors.update(monthSelectors)
                allSelectors = selectors + list(reversed(monthSelectors))
                # If we already have month pages, we only regenerate the current one
                if len(self.getExistingMonthPages()) == 0:
                    selectors = allSelectors
                else:
                    currLatestMonthSel = monthSelectors[-1]
                    if latestMonth is None or currLatestMonthSel.linkName == latestMonth:
                        selectors.append(monthSelectors[-1])
                        latestMonth = currLatestMonthSel.linkName
                    selectedTags = set()
                    unusedTags = set(tags)
                    for selector in selectors:
                        currTags = set(selector.selectedTags)
                        selectedTags.update(currTags)
                        if archiveUnused:
                            unusedTags.difference_update(currTags)
                    tags = [t for t in tags if t in selectedTags]
                    if archiveUnused and unusedTags:
                        plugins.log.info(
                            "Automatic repository cleaning will now remove old data for the following runs:"
                        )
                        for tag in sorted(unusedTags, key=self.tagSortKey):
                            plugins.log.info("- " + tag)
                        plugins.log.info(
                            "(To disable automatic repository cleaning in future, please run with the --manualarchive flag when collating the HTML report.)"
                        )
                        self.removeUnused(unusedTags, tagData)

                loggedTests = OrderedDict()
                categoryHandlers = {}
                self.diag.info("Processing " + str(len(stateFiles)) +
                               " teststate files")
                relevantFiles = 0
                for stateFile, repository in stateFiles:
                    tag = self.getTagFromFile(stateFile)
                    if len(tags) == 0 or tag in tags:
                        relevantFiles += 1
                        testId, state, extraVersion = self.processTestStateFile(
                            stateFile, repository)
                        loggedTests.setdefault(extraVersion,
                                               OrderedDict()).setdefault(
                                                   testId,
                                                   OrderedDict())[tag] = state
                        categoryHandlers.setdefault(
                            tag, CategoryHandler()).registerInCategory(
                                testId, state.category, extraVersion, state)
                        if relevantFiles % 100 == 0:
                            self.diag.info("- Processed " +
                                           str(relevantFiles) +
                                           " files with matching tags so far")
                self.diag.info("Processed " + str(relevantFiles) +
                               " relevant teststate files")
                self.diag.info("Processing " + str(len(successFiles)) +
                               " success files")
                for successFile, repository in successFiles:
                    testId = self.getTestIdentifier(successFile, repository)
                    extraVersion = self.findExtraVersion(repository)
                    with open(successFile) as f:
                        fileTags = set()
                        for line in f:
                            parts = line.strip().split(" ", 1)
                            if len(parts) != 2:
                                continue
                            tag, text = parts
                            if tag in fileTags:
                                sys.stderr.write(
                                    "WARNING: more than one result present for tag '"
                                    + tag + "' in file " + successFile + "!\n")
                                sys.stderr.write("Ignoring later ones\n")
                                continue

                            fileTags.add(tag)
                            if len(tags) == 0 or tag in tags:
                                loggedTests.setdefault(
                                    extraVersion, OrderedDict()).setdefault(
                                        testId, OrderedDict())[tag] = text
                                categoryHandlers.setdefault(
                                    tag, CategoryHandler()).registerInCategory(
                                        testId, "success", extraVersion, text)
                self.diag.info("Processed " + str(len(successFiles)) +
                               " success files")
                versionToShow = self.removePageVersion(version)
                hasData = False
                for sel in selectors:
                    filePath = self.getPageFilePath(sel)
                    if filePath in self.pagesOverview:
                        page, pageColours = self.pagesOverview[filePath]
                    else:
                        page = self.createPage()
                        pageColours = set()
                        self.pagesOverview[filePath] = page, pageColours

                    tableHeader = self.getTableHeader(version, repositoryDirs)
                    heading = self.getHeading(versionToShow)
                    hasNewData, graphLink, tableColours = self.addTable(
                        page, self.resourceNames, categoryHandlers, version,
                        loggedTests, sel, tableHeader, filePath, heading,
                        repositoryDirInfo)
                    hasData |= hasNewData
                    pageColours.update(tableColours)
                    if graphLink:
                        pageToGraphs.setdefault(page, []).append(graphLink)

                if hasData and versionToShow:
                    link = HTMLgen.Href("#" + version, versionToShow)
                    minorVersionHeader.append(link)

                # put them in reverse order, most relevant first
                linkFromDetailsToOverview = [
                    sel.getLinkInfo(self.pageVersion) for sel in allSelectors
                ]
                for tag in tags:
                    details = self.pagesDetails.setdefault(
                        tag,
                        TestDetails(tag, self.pageTitle, self.pageSubTitles))
                    details.addVersionSection(version, categoryHandlers[tag],
                                              linkFromDetailsToOverview)

        selContainer = HTMLgen.Container()
        selectors = self.makeSelectors(subPageNames)
        for sel in selectors:
            target, linkName = sel.getLinkInfo(self.pageVersion)
            selContainer.append(HTMLgen.Href(target, linkName))

        monthContainer = HTMLgen.Container()
        if len(allMonthSelectors) == 1:
            # Don't want just one month, no navigation possible
            prevMonth = list(allMonthSelectors)[0].getPreviousMonthSelector()
            allMonthSelectors.add(prevMonth)

        for sel in sorted(allMonthSelectors, key=lambda s: s.sortKey()):
            target, linkName = sel.getLinkInfo(self.pageVersion)
            monthContainer.append(HTMLgen.Href(target, linkName))

        for page, pageColours in list(self.pagesOverview.values()):
            if len(monthContainer.contents) > 0:
                page.prepend(HTMLgen.Heading(2, monthContainer,
                                             align='center'))
            graphs = pageToGraphs.get(page)
            page.prepend(HTMLgen.Heading(2, selContainer, align='center'))
            if minorVersionHeader.contents:
                if not graphs is None and len(graphs) > 1:
                    page.prepend(HTMLgen.Heading(1, *graphs, align='center'))
                page.prepend(
                    HTMLgen.Heading(1, minorVersionHeader, align='center'))
            creationDate = TitleWithDateStamp("").__str__().strip()
            page.prepend(HTMLgen.Paragraph(creationDate, align="center"))
            page.prepend(HTMLgen.Heading(1, self.getHeading(), align='center'))
            if len(pageColours) > 0:
                page.prepend(HTMLgen.BR())
                page.prepend(HTMLgen.BR())
                page.script = self.getFilterScripts(pageColours)

        self.writePages()
예제 #21
0
 def getSummaryHeading(self, version, categoryHandler):
     return HTMLgen.Heading(
         2, version + ": " + categoryHandler.generateTextSummary())
예제 #22
0
 def makeTableHeaderCell(self, tableHeader):
     container = HTMLgen.Container()
     container.append(HTMLgen.Name(tableHeader))
     container.append(
         HTMLgen.U(HTMLgen.Heading(1, tableHeader, align='center')))
     return HTMLgen.TD(container)
예제 #23
0
 def generateTableHead(self, repositoryDirs):
     head = [HTMLgen.TH("Test")]
     ciUrl = os.getenv("JENKINS_URL") or os.getenv(
         "SYSTEM_TEAMFOUNDATIONSERVERURI")
     runNameDirs = self.getRunNameDirs(repositoryDirs) if ciUrl else []
     for tag in self.tags:
         tagColour = self.findTagColour(tag)
         linkTarget = getDetailPageName(self.pageVersion, tag)
         linkText = HTMLgen.Font(getDisplayText(tag), color=tagColour)
         buildNumber = self.getCiBuildNumber(tag)
         if ciUrl and buildNumber.isdigit():
             runEnv = getEnvironmentFromRunFiles(runNameDirs, tag)
             container = HTMLgen.Container()
             tooltip = jenkinschanges.getTimestamp(buildNumber)
             container.append(
                 HTMLgen.Href(linkTarget, linkText, title=tooltip))
             container.append(HTMLgen.BR())
             ciTitle, ciTarget = self.getCiLinkData(runEnv, buildNumber)
             ciText = HTMLgen.Emphasis(
                 HTMLgen.Font("(" + ciTitle + ")", size=1))
             container.append(HTMLgen.Href(ciTarget, ciText, title=tooltip))
             head.append(HTMLgen.TH(container))
         else:
             head.append(HTMLgen.TH(HTMLgen.Href(linkTarget, linkText)))
     heading = HTMLgen.TR()
     heading = heading + head
     return heading
예제 #24
0
 def generateTableHead(self, repositoryDirs):
     head = [HTMLgen.TH("Test")]
     jenkinsUrl = os.getenv("JENKINS_URL")
     runNameDirs = self.getRunNameDirs(repositoryDirs) if jenkinsUrl else []
     for tag in self.tags:
         tagColour = self.findTagColour(tag)
         linkTarget = getDetailPageName(self.pageVersion, tag)
         linkText = HTMLgen.Font(getDisplayText(tag), color=tagColour)
         buildNumber = self.getJenkinsBuildNumber(tag)
         if jenkinsUrl and buildNumber.isdigit():
             runEnv = getEnvironmentFromRunFiles(runNameDirs, tag)
             container = HTMLgen.Container()
             tooltip = jenkinschanges.getTimestamp(buildNumber)
             container.append(
                 HTMLgen.Href(linkTarget, linkText, title=tooltip))
             container.append(HTMLgen.BR())
             jobTarget = os.path.join(self.getRunEnv(runEnv,
                                                     "JENKINS_URL"), "job",
                                      self.getRunEnv(runEnv, "JOB_NAME"),
                                      buildNumber)
             jobText = HTMLgen.Emphasis(
                 HTMLgen.Font("(Jenkins " + buildNumber + ")", size=1))
             container.append(
                 HTMLgen.Href(jobTarget, jobText, title=tooltip))
             head.append(HTMLgen.TH(container))
         else:
             head.append(HTMLgen.TH(HTMLgen.Href(linkTarget, linkText)))
     heading = HTMLgen.TR()
     heading = heading + head
     return heading
예제 #25
0
 def generateExtraVersionLinks(self, extraVersions):
     cont = HTMLgen.Container()
     for extra in extraVersions:
         fullName = self.version + "." + extra
         cont.append(HTMLgen.Href("#" + fullName, extra))
     return HTMLgen.Heading(2, cont, align='center')