# -*- coding: utf-8 -*- # $Id: wuimain.py $ """ Test Manager Core - WUI - The Main page. """ __copyright__ = \ """ Copyright (C) 2012-2023 Oracle and/or its affiliates. This file is part of VirtualBox base platform packages, as available from https://www.virtualbox.org. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, in version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . The contents of this file may alternatively be used under the terms of the Common Development and Distribution License Version 1.0 (CDDL), a copy of it is provided in the "COPYING.CDDL" file included in the VirtualBox distribution, in which case the provisions of the CDDL are applicable instead of those of the GPL. You may elect to license modified versions of this file under the terms and conditions of either the GPL or the CDDL or both. SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 """ __version__ = "$Revision: 155244 $" # Standard Python imports. # Validation Kit imports. from testmanager import config; from testmanager.core.base import TMExceptionBase, TMTooManyRows; from testmanager.webui.wuibase import WuiDispatcherBase, WuiException; from testmanager.webui.wuicontentbase import WuiTmLink; from common import webutils, utils; class WuiMain(WuiDispatcherBase): """ WUI Main page. Note! All cylic dependency avoiance stuff goes here in the dispatcher code, not in the action specific code. This keeps the uglyness in one place and reduces load time dependencies in the more critical code path. """ ## The name of the script. ksScriptName = 'index.py' ## @name Actions ## @{ ksActionResultsUnGrouped = 'ResultsUnGrouped' ksActionResultsGroupedBySchedGroup = 'ResultsGroupedBySchedGroup' ksActionResultsGroupedByTestGroup = 'ResultsGroupedByTestGroup' ksActionResultsGroupedByBuildRev = 'ResultsGroupedByBuildRev' ksActionResultsGroupedByBuildCat = 'ResultsGroupedByBuildCat' ksActionResultsGroupedByTestBox = 'ResultsGroupedByTestBox' ksActionResultsGroupedByTestCase = 'ResultsGroupedByTestCase' ksActionResultsGroupedByOS = 'ResultsGroupedByOS' ksActionResultsGroupedByArch = 'ResultsGroupedByArch' ksActionTestSetDetails = 'TestSetDetails'; ksActionTestResultDetails = ksActionTestSetDetails; ksActionTestSetDetailsFromResult = 'TestSetDetailsFromResult' ksActionTestResultFailureDetails = 'TestResultFailureDetails' ksActionTestResultFailureAdd = 'TestResultFailureAdd' ksActionTestResultFailureAddPost = 'TestResultFailureAddPost' ksActionTestResultFailureEdit = 'TestResultFailureEdit' ksActionTestResultFailureEditPost = 'TestResultFailureEditPost' ksActionTestResultFailureDoRemove = 'TestResultFailureDoRemove' ksActionViewLog = 'ViewLog' ksActionGetFile = 'GetFile' ksActionReportSummary = 'ReportSummary'; ksActionReportRate = 'ReportRate'; ksActionReportTestCaseFailures = 'ReportTestCaseFailures'; ksActionReportTestBoxFailures = 'ReportTestBoxFailures'; ksActionReportFailureReasons = 'ReportFailureReasons'; ksActionGraphWiz = 'GraphWiz'; ksActionVcsHistoryTooltip = 'VcsHistoryTooltip'; ##< Hardcoded in common.js. ## @} ## @name Standard report parameters ## @{ ksParamReportPeriods = 'cPeriods'; ksParamReportPeriodInHours = 'cHoursPerPeriod'; ksParamReportSubject = 'sSubject'; ksParamReportSubjectIds = 'SubjectIds'; ## @} ## @name Graph Wizard parameters ## Common parameters: ksParamReportPeriods, ksParamReportPeriodInHours, ksParamReportSubjectIds, ## ksParamReportSubject, ksParamEffectivePeriod, and ksParamEffectiveDate. ## @{ ksParamGraphWizTestBoxIds = 'aidTestBoxes'; ksParamGraphWizBuildCatIds = 'aidBuildCats'; ksParamGraphWizTestCaseIds = 'aidTestCases'; ksParamGraphWizSepTestVars = 'fSepTestVars'; ksParamGraphWizImpl = 'enmImpl'; ksParamGraphWizWidth = 'cx'; ksParamGraphWizHeight = 'cy'; ksParamGraphWizDpi = 'dpi'; ksParamGraphWizFontSize = 'cPtFont'; ksParamGraphWizErrorBarY = 'fErrorBarY'; ksParamGraphWizMaxErrorBarY = 'cMaxErrorBarY'; ksParamGraphWizMaxPerGraph = 'cMaxPerGraph'; ksParamGraphWizXkcdStyle = 'fXkcdStyle'; ksParamGraphWizTabular = 'fTabular'; ksParamGraphWizSrcTestSetId = 'idSrcTestSet'; ## @} ## @name Graph implementations values for ksParamGraphWizImpl. ## @{ ksGraphWizImpl_Default = 'default'; ksGraphWizImpl_Matplotlib = 'matplotlib'; ksGraphWizImpl_Charts = 'charts'; kasGraphWizImplValid = [ ksGraphWizImpl_Default, ksGraphWizImpl_Matplotlib, ksGraphWizImpl_Charts]; kaasGraphWizImplCombo = [ ( ksGraphWizImpl_Default, 'Default' ), ( ksGraphWizImpl_Matplotlib, 'Matplotlib (server)' ), ( ksGraphWizImpl_Charts, 'Google Charts (client)'), ]; ## @} ## @name Log Viewer parameters. ## @{ ksParamLogSetId = 'LogViewer_idTestSet'; ksParamLogFileId = 'LogViewer_idFile'; ksParamLogChunkSize = 'LogViewer_cbChunk'; ksParamLogChunkNo = 'LogViewer_iChunk'; ## @} ## @name File getter parameters. ## @{ ksParamGetFileSetId = 'GetFile_idTestSet'; ksParamGetFileId = 'GetFile_idFile'; ksParamGetFileDownloadIt = 'GetFile_fDownloadIt'; ## @} ## @name VCS history parameters. ## @{ ksParamVcsHistoryRepository = 'repo'; ksParamVcsHistoryRevision = 'rev'; ksParamVcsHistoryEntries = 'cEntries'; ## @} ## @name Test result listing parameters. ## @{ ## If this param is specified, then show only results for this member when results grouped by some parameter. ksParamGroupMemberId = 'GroupMemberId' ## Optional parameter for indicating whether to restrict the listing to failures only. ksParamOnlyFailures = 'OnlyFailures'; ## The sheriff parameter for getting failures needing a reason or two assigned to them. ksParamOnlyNeedingReason = 'OnlyNeedingReason'; ## Result listing sorting. ksParamTestResultsSortBy = 'enmSortBy' ## @} ## Effective time period. one of the first column values in kaoResultPeriods. ksParamEffectivePeriod = 'sEffectivePeriod' ## Test result period values. kaoResultPeriods = [ ( '1 hour', '1 hour', 1 ), ( '2 hours', '2 hours', 2 ), ( '3 hours', '3 hours', 3 ), ( '6 hours', '6 hours', 6 ), ( '12 hours', '12 hours', 12 ), ( '1 day', '1 day', 24 ), ( '2 days', '2 days', 48 ), ( '3 days', '3 days', 72 ), ( '1 week', '1 week', 168 ), ( '2 weeks', '2 weeks', 336 ), ( '3 weeks', '3 weeks', 504 ), ( '1 month', '1 month', 31 * 24 ), # The approx hour count varies with the start date. ( '2 months', '2 months', (31 + 31) * 24 ), # Using maximum values. ( '3 months', '3 months', (31 + 30 + 31) * 24 ), ( '6 months', '6 months', (31 + 31 + 30 + 31 + 30 + 31) * 24 ), ( '1 year', '1 year', 365 * 24 ), ]; ## The default test result period. ksResultPeriodDefault = '6 hours'; def __init__(self, oSrvGlue): WuiDispatcherBase.__init__(self, oSrvGlue, self.ksScriptName); self._sTemplate = 'template.html' # # Populate the action dispatcher dictionary. # Lambda is forbidden because of readability, speed and reducing number of imports. # self._dDispatch[self.ksActionResultsUnGrouped] = self._actionResultsUnGrouped; self._dDispatch[self.ksActionResultsGroupedByTestGroup] = self._actionResultsGroupedByTestGroup; self._dDispatch[self.ksActionResultsGroupedByBuildRev] = self._actionResultsGroupedByBuildRev; self._dDispatch[self.ksActionResultsGroupedByBuildCat] = self._actionResultsGroupedByBuildCat; self._dDispatch[self.ksActionResultsGroupedByTestBox] = self._actionResultsGroupedByTestBox; self._dDispatch[self.ksActionResultsGroupedByTestCase] = self._actionResultsGroupedByTestCase; self._dDispatch[self.ksActionResultsGroupedByOS] = self._actionResultsGroupedByOS; self._dDispatch[self.ksActionResultsGroupedByArch] = self._actionResultsGroupedByArch; self._dDispatch[self.ksActionResultsGroupedBySchedGroup] = self._actionResultsGroupedBySchedGroup; self._dDispatch[self.ksActionTestSetDetails] = self._actionTestSetDetails; self._dDispatch[self.ksActionTestSetDetailsFromResult] = self._actionTestSetDetailsFromResult; self._dDispatch[self.ksActionTestResultFailureAdd] = self._actionTestResultFailureAdd; self._dDispatch[self.ksActionTestResultFailureAddPost] = self._actionTestResultFailureAddPost; self._dDispatch[self.ksActionTestResultFailureDetails] = self._actionTestResultFailureDetails; self._dDispatch[self.ksActionTestResultFailureDoRemove] = self._actionTestResultFailureDoRemove; self._dDispatch[self.ksActionTestResultFailureEdit] = self._actionTestResultFailureEdit; self._dDispatch[self.ksActionTestResultFailureEditPost] = self._actionTestResultFailureEditPost; self._dDispatch[self.ksActionViewLog] = self._actionViewLog; self._dDispatch[self.ksActionGetFile] = self._actionGetFile; self._dDispatch[self.ksActionReportSummary] = self._actionReportSummary; self._dDispatch[self.ksActionReportRate] = self._actionReportRate; self._dDispatch[self.ksActionReportTestCaseFailures] = self._actionReportTestCaseFailures; self._dDispatch[self.ksActionReportFailureReasons] = self._actionReportFailureReasons; self._dDispatch[self.ksActionGraphWiz] = self._actionGraphWiz; self._dDispatch[self.ksActionVcsHistoryTooltip] = self._actionVcsHistoryTooltip; # Legacy. self._dDispatch['TestResultDetails'] = self._dDispatch[self.ksActionTestSetDetails]; # # Popupate the menus. # # Additional URL parameters keeping for time navigation. sExtraTimeNav = '' dCurParams = oSrvGlue.getParameters() if dCurParams is not None: for sExtraParam in [ self.ksParamItemsPerPage, self.ksParamEffectiveDate, self.ksParamEffectivePeriod, ]: if sExtraParam in dCurParams: sExtraTimeNav += '&%s' % (webutils.encodeUrlParams({sExtraParam: dCurParams[sExtraParam]}),) # Additional URL parameters for reports sExtraReports = ''; if dCurParams is not None: for sExtraParam in [ self.ksParamReportPeriods, self.ksParamReportPeriodInHours, self.ksParamEffectiveDate, ]: if sExtraParam in dCurParams: sExtraReports += '&%s' % (webutils.encodeUrlParams({sExtraParam: dCurParams[sExtraParam]}),) # Shorthand to keep within margins. sActUrlBase = self._sActionUrlBase; sOnlyFailures = '&%s%s' % ( webutils.encodeUrlParams({self.ksParamOnlyFailures: True}), sExtraTimeNav, ); sSheriff = '&%s%s' % ( webutils.encodeUrlParams({self.ksParamOnlyNeedingReason: True}), sExtraTimeNav, ); self._aaoMenus = \ [ [ 'Sheriff', sActUrlBase + self.ksActionResultsUnGrouped + sSheriff, [ [ 'Grouped by', None ], [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sSheriff, False ], [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sSheriff, False ], [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sSheriff, False ], [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sSheriff, False ], [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sSheriff, False ], [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sSheriff, False ], [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sSheriff, False ], [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sSheriff, False ], [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sSheriff, False ], ] ], [ 'Reports', sActUrlBase + self.ksActionReportSummary, [ [ 'Summary', sActUrlBase + self.ksActionReportSummary + sExtraReports, False ], [ 'Success rate', sActUrlBase + self.ksActionReportRate + sExtraReports, False ], [ 'Test case failures', sActUrlBase + self.ksActionReportTestCaseFailures + sExtraReports, False ], [ 'Testbox failures', sActUrlBase + self.ksActionReportTestBoxFailures + sExtraReports, False ], [ 'Failure reasons', sActUrlBase + self.ksActionReportFailureReasons + sExtraReports, False ], ] ], [ 'Test Results', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav, [ [ 'Grouped by', None ], [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav, False ], [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sExtraTimeNav, False ], [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sExtraTimeNav, False ], [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sExtraTimeNav, False ], [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sExtraTimeNav, False ], [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sExtraTimeNav, False ], [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sExtraTimeNav, False ], [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sExtraTimeNav, False ], [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sExtraTimeNav, False ], ] ], [ 'Test Failures', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures, [ [ 'Grouped by', None ], [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures, False ], [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sOnlyFailures, False ], [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sOnlyFailures, False ], [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sOnlyFailures, False ], [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sOnlyFailures, False ], [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sOnlyFailures, False ], [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sOnlyFailures, False ], [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sOnlyFailures, False ], [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sOnlyFailures, False ], ] ], [ '> Admin', 'admin.py?' + webutils.encodeUrlParams(self._dDbgParams), [] ], ]; # # Overriding parent methods. # def _generatePage(self): """Override parent handler in order to change page title.""" if self._sPageTitle is not None: self._sPageTitle = 'Test Results - ' + self._sPageTitle return WuiDispatcherBase._generatePage(self) def _actionDefault(self): """Show the default admin page.""" from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; self._sAction = self.ksActionResultsUnGrouped return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _isMenuMatch(self, sMenuUrl, sActionParam): if super(WuiMain, self)._isMenuMatch(sMenuUrl, sActionParam): fOnlyNeedingReason = self.getBoolParam(self.ksParamOnlyNeedingReason, fDefault = False); if fOnlyNeedingReason: return (sMenuUrl.find(self.ksParamOnlyNeedingReason) > 0); fOnlyFailures = self.getBoolParam(self.ksParamOnlyFailures, fDefault = False); return (sMenuUrl.find(self.ksParamOnlyFailures) > 0) == fOnlyFailures \ and sMenuUrl.find(self.ksParamOnlyNeedingReason) < 0; return False; # # Navigation bar stuff # def _generateSortBySelector(self, dParams, sPreamble, sPostamble): """ Generate HTML code for the sort by selector. """ from testmanager.core.testresults import TestResultLogic; if self.ksParamTestResultsSortBy in dParams: enmResultSortBy = dParams[self.ksParamTestResultsSortBy]; del dParams[self.ksParamTestResultsSortBy]; else: enmResultSortBy = TestResultLogic.ksResultsSortByRunningAndStart; sHtmlSortBy = '
Sort by\n'; sHtmlSortBy += sPreamble; sHtmlSortBy += '\n \n'; sHtmlSortBy += sPostamble; sHtmlSortBy += '\n
\n' return sHtmlSortBy; def _generateStatusSelector(self, dParams, fOnlyFailures): """ Generate HTML code for the status code selector. Currently very simple. """ dParams[self.ksParamOnlyFailures] = not fOnlyFailures; return WuiTmLink('Show all results' if fOnlyFailures else 'Only show failed tests', '', dParams, fBracketed = False).toHtml(); def _generateTimeWalker(self, dParams, tsEffective, sCurPeriod): """ Generates HTML code for walking back and forth in time. """ # Have to do some math here. :-/ if tsEffective is None: self._oDb.execute('SELECT CURRENT_TIMESTAMP - \'' + sCurPeriod + '\'::interval'); tsNext = None; tsPrev = self._oDb.fetchOne()[0]; else: self._oDb.execute('SELECT %s::TIMESTAMP - \'' + sCurPeriod + '\'::interval,\n' ' %s::TIMESTAMP + \'' + sCurPeriod + '\'::interval', (tsEffective, tsEffective,)); tsPrev, tsNext = self._oDb.fetchOne(); # Forget about page No when changing a period if WuiDispatcherBase.ksParamPageNo in dParams: del dParams[WuiDispatcherBase.ksParamPageNo] # Format. dParams[WuiDispatcherBase.ksParamEffectiveDate] = str(tsPrev); sPrev = '<<  ' \ % (webutils.encodeUrlParams(dParams),); if tsNext is not None: dParams[WuiDispatcherBase.ksParamEffectiveDate] = str(tsNext); sNext = '  >>' \ % (webutils.encodeUrlParams(dParams),); else: sNext = '  >>'; from testmanager.webui.wuicontentbase import WuiListContentBase; ## @todo move to better place. return WuiListContentBase.generateTimeNavigation('top', self.getParameters(), self.getEffectiveDateParam(), sPrev, sNext, False); def _generateResultPeriodSelector(self, dParams, sCurPeriod): """ Generate HTML code for result period selector. """ if self.ksParamEffectivePeriod in dParams: del dParams[self.ksParamEffectivePeriod]; # Forget about page No when changing a period if WuiDispatcherBase.ksParamPageNo in dParams: del dParams[WuiDispatcherBase.ksParamPageNo] sHtmlPeriodSelector = '
\n' sHtmlPeriodSelector += ' Period is\n' sHtmlPeriodSelector += ' \n' \ '
\n' return sHtmlPeriodSelector def _generateGroupContentSelector(self, aoGroupMembers, iCurrentMember, sAltAction): """ Generate HTML code for group content selector. """ dParams = self.getParameters() if self.ksParamGroupMemberId in dParams: del dParams[self.ksParamGroupMemberId] if sAltAction is not None: if self.ksParamAction in dParams: del dParams[self.ksParamAction]; dParams[self.ksParamAction] = sAltAction; sHtmlSelector = '
\n' sHtmlSelector += ' \n' \ '
\n' return sHtmlSelector def _generatePagesSelector(self, dParams, cItems, cItemsPerPage, iPage): """ Generate HTML code for pages (1, 2, 3 ... N) selector """ if WuiDispatcherBase.ksParamPageNo in dParams: del dParams[WuiDispatcherBase.ksParamPageNo] sHrefPtr = '%s' cNumOfPages = (cItems + cItemsPerPage - 1) // cItemsPerPage; cPagesToDisplay = 10 cPagesRangeStart = iPage - cPagesToDisplay // 2 \ if not iPage - cPagesToDisplay / 2 < 0 else 0 cPagesRangeEnd = cPagesRangeStart + cPagesToDisplay \ if not cPagesRangeStart + cPagesToDisplay > cNumOfPages else cNumOfPages # Adjust pages range if cNumOfPages < cPagesToDisplay: cPagesRangeStart = 0 cPagesRangeEnd = cNumOfPages # 1 2 3 4... sHtmlPager = ' \n'.join(sHrefPtr % (x, str(x + 1)) if x != iPage else str(x + 1) for x in range(cPagesRangeStart, cPagesRangeEnd)) if cPagesRangeStart > 0: sHtmlPager = '%s  ...  \n' % (sHrefPtr % (0, str(1))) + sHtmlPager if cPagesRangeEnd < cNumOfPages: sHtmlPager += ' ... %s\n' % (sHrefPtr % (cNumOfPages, str(cNumOfPages + 1))) # Prev/Next (using << >> because « and » are too tiny). if iPage > 0: dParams[WuiDispatcherBase.ksParamPageNo] = iPage - 1 sHtmlPager = ('<<  \n' % (webutils.encodeUrlParams(dParams), )) \ + sHtmlPager; else: sHtmlPager = '<<  \n' + sHtmlPager if iPage + 1 < cNumOfPages: dParams[WuiDispatcherBase.ksParamPageNo] = iPage + 1 sHtmlPager += '\n  >>\n' % (webutils.encodeUrlParams(dParams),) else: sHtmlPager += '\n  >>\n' return sHtmlPager def _generateItemPerPageSelector(self, dParams, cItemsPerPage): """ Generate HTML code for items per page selector Note! Modifies dParams! """ from testmanager.webui.wuicontentbase import WuiListContentBase; ## @todo move to better place. return WuiListContentBase.generateItemPerPageSelector('top', dParams, cItemsPerPage); def _generateResultNavigation(self, cItems, cItemsPerPage, iPage, tsEffective, sCurPeriod, fOnlyFailures, sHtmlMemberSelector): """ Make custom time navigation bar for the results. """ # Generate the elements. sHtmlStatusSelector = self._generateStatusSelector(self.getParameters(), fOnlyFailures); sHtmlSortBySelector = self._generateSortBySelector(self.getParameters(), '', sHtmlStatusSelector); sHtmlPeriodSelector = self._generateResultPeriodSelector(self.getParameters(), sCurPeriod) sHtmlTimeWalker = self._generateTimeWalker(self.getParameters(), tsEffective, sCurPeriod); if cItems > 0: sHtmlPager = self._generatePagesSelector(self.getParameters(), cItems, cItemsPerPage, iPage) sHtmlItemsPerPageSelector = self._generateItemPerPageSelector(self.getParameters(), cItemsPerPage) else: sHtmlPager = '' sHtmlItemsPerPageSelector = '' # Generate navigation bar sHtml = '\n' \ '\n' \ ' \n' \ ' ' \ ' \n' \ '\n' \ '\n' \ ' \n' \ ' \n' \ ' \n'\ '\n' \ '
' + sHtmlMemberSelector + '' + sHtmlTimeWalker + '\n' + sHtmlPeriodSelector + '
' + sHtmlSortBySelector + '\n' + sHtmlPager + '\n' + sHtmlItemsPerPageSelector + '
\n' return sHtml def _generateReportNavigation(self, tsEffective, cHoursPerPeriod, cPeriods): """ Make time navigation bar for the reports. """ # The period length selector. dParams = self.getParameters(); if WuiMain.ksParamReportPeriodInHours in dParams: del dParams[WuiMain.ksParamReportPeriodInHours]; sHtmlPeriodLength = ''; sHtmlPeriodLength += '
\n' \ ' Period length \n' \ '
\n' # The period count selector. dParams = self.getParameters(); if WuiMain.ksParamReportPeriods in dParams: del dParams[WuiMain.ksParamReportPeriods]; sHtmlCountOfPeriods = ''; sHtmlCountOfPeriods += '
\n' \ ' Periods \n' \ '
\n' # The time walker. sHtmlTimeWalker = self._generateTimeWalker(self.getParameters(), tsEffective, '%d hours' % (cHoursPerPeriod)); # Combine them all. sHtml = '\n' \ ' \n' \ ' \n' \ ' ' \ ' \n' \ ' \n' \ '
\n' + sHtmlPeriodLength + '\n' + sHtmlTimeWalker + '\n' + sHtmlCountOfPeriods + '
\n'; return sHtml; # # The rest of stuff # def _actionGroupedResultsListing( #pylint: disable=too-many-locals self, enmResultsGroupingType, oResultsLogicType, oResultFilterType, oResultsListContentType): """ Override generic listing action. oResultsLogicType implements getEntriesCount, fetchResultsForListing and more. oResultFilterType is a child of ModelFilterBase. oResultsListContentType is a child of WuiListContentBase. """ from testmanager.core.testresults import TestResultLogic; cItemsPerPage = self.getIntParam(self.ksParamItemsPerPage, iMin = 2, iMax = 9999, iDefault = 128); iPage = self.getIntParam(self.ksParamPageNo, iMin = 0, iMax = 999999, iDefault = 0); tsEffective = self.getEffectiveDateParam(); iGroupMemberId = self.getIntParam(self.ksParamGroupMemberId, iMin = -1, iMax = 999999, iDefault = -1); fOnlyFailures = self.getBoolParam(self.ksParamOnlyFailures, fDefault = False); fOnlyNeedingReason = self.getBoolParam(self.ksParamOnlyNeedingReason, fDefault = False); enmResultSortBy = self.getStringParam(self.ksParamTestResultsSortBy, asValidValues = TestResultLogic.kasResultsSortBy, sDefault = TestResultLogic.ksResultsSortByRunningAndStart); oFilter = oResultFilterType().initFromParams(self); # Get testing results period and validate it asValidValues = [x for (x, _, _) in self.kaoResultPeriods] sCurPeriod = self.getStringParam(self.ksParamEffectivePeriod, asValidValues = asValidValues, sDefault = self.ksResultPeriodDefault) assert sCurPeriod != ''; # Impossible! self._checkForUnknownParameters() # # Fetch the group members. # # If no grouping is selected, we'll fill the grouping combo with # testboxes just to avoid having completely useless combo box. # oTrLogic = TestResultLogic(self._oDb); sAltSelectorAction = None; if enmResultsGroupingType in (TestResultLogic.ksResultsGroupingTypeNone, TestResultLogic.ksResultsGroupingTypeTestBox,): aoTmp = oTrLogic.getTestBoxes(tsNow = tsEffective, sPeriod = sCurPeriod) aoGroupMembers = sorted(list({(x.idTestBox, '%s (%s)' % (x.sName, str(x.ip))) for x in aoTmp }), reverse = False, key = lambda asData: asData[1]) if enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestBox: self._sPageTitle = 'Grouped by Test Box'; else: self._sPageTitle = 'Ungrouped results'; sAltSelectorAction = self.ksActionResultsGroupedByTestBox; aoGroupMembers.insert(0, [None, None]); # The "All" member. elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestGroup: aoTmp = oTrLogic.getTestGroups(tsNow = tsEffective, sPeriod = sCurPeriod); aoGroupMembers = sorted(list({ (x.idTestGroup, x.sName ) for x in aoTmp }), reverse = False, key = lambda asData: asData[1]) self._sPageTitle = 'Grouped by Test Group' elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildRev: aoTmp = oTrLogic.getBuilds(tsNow = tsEffective, sPeriod = sCurPeriod) aoGroupMembers = sorted(list({ (x.iRevision, '%s.%d' % (x.oCat.sBranch, x.iRevision)) for x in aoTmp }), reverse = True, key = lambda asData: asData[0]) self._sPageTitle = 'Grouped by Build' elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildCat: aoTmp = oTrLogic.getBuildCategories(tsNow = tsEffective, sPeriod = sCurPeriod) aoGroupMembers = sorted(list({ (x.idBuildCategory, '%s / %s / %s / %s' % ( x.sProduct, x.sBranch, ', '.join(x.asOsArches), x.sType) ) for x in aoTmp }), reverse = True, key = lambda asData: asData[1]); self._sPageTitle = 'Grouped by Build Category' elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestCase: aoTmp = oTrLogic.getTestCases(tsNow = tsEffective, sPeriod = sCurPeriod) aoGroupMembers = sorted(list({ (x.idTestCase, '%s' % x.sName) for x in aoTmp }), reverse = False, key = lambda asData: asData[1]) self._sPageTitle = 'Grouped by Test Case' elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeOS: aoTmp = oTrLogic.getOSes(tsNow = tsEffective, sPeriod = sCurPeriod) aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]); self._sPageTitle = 'Grouped by OS' elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeArch: aoTmp = oTrLogic.getArchitectures(tsNow = tsEffective, sPeriod = sCurPeriod) aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]); self._sPageTitle = 'Grouped by Architecture' elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeSchedGroup: aoTmp = oTrLogic.getSchedGroups(tsNow = tsEffective, sPeriod = sCurPeriod) aoGroupMembers = sorted(list({ (x.idSchedGroup, '%s' % x.sName) for x in aoTmp }), reverse = False, key = lambda asData: asData[1]) self._sPageTitle = 'Grouped by Scheduling Group' else: raise TMExceptionBase('Unknown grouping type') _sPageBody = '' oContent = None cEntriesMax = 0 _dParams = self.getParameters() oResultLogic = oResultsLogicType(self._oDb); for idMember, sMemberName in aoGroupMembers: # # Count and fetch entries to be displayed. # # Skip group members that were not specified. if idMember != iGroupMemberId \ and ( (idMember is not None and enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeNone) or (iGroupMemberId > 0 and enmResultsGroupingType != TestResultLogic.ksResultsGroupingTypeNone) ): continue cEntries = oResultLogic.getEntriesCount(tsNow = tsEffective, sInterval = sCurPeriod, oFilter = oFilter, enmResultsGroupingType = enmResultsGroupingType, iResultsGroupingValue = idMember, fOnlyFailures = fOnlyFailures, fOnlyNeedingReason = fOnlyNeedingReason); if cEntries == 0: # Do not display empty groups continue aoEntries = oResultLogic.fetchResultsForListing(iPage * cItemsPerPage, cItemsPerPage, tsNow = tsEffective, sInterval = sCurPeriod, oFilter = oFilter, enmResultSortBy = enmResultSortBy, enmResultsGroupingType = enmResultsGroupingType, iResultsGroupingValue = idMember, fOnlyFailures = fOnlyFailures, fOnlyNeedingReason = fOnlyNeedingReason); cEntriesMax = max(cEntriesMax, cEntries) # # Format them. # oContent = oResultsListContentType(aoEntries, cEntries, iPage, cItemsPerPage, tsEffective, fnDPrint = self._oSrvGlue.dprint, oDisp = self) (_, sHtml) = oContent.show(fShowNavigation = False) if sMemberName is not None: _sPageBody += '' % (sLink, cEntries) _sPageBody += '' _sPageBody += '
' _dParams[self.ksParamGroupMemberId] = idMember sLink = WuiTmLink(sMemberName, '', _dParams, fBracketed = False).toHtml() _sPageBody += '

%s (%d)


' _sPageBody += sHtml _sPageBody += '
' # # Complete the page by slapping navigation controls at the top and # bottom of it. # sHtmlNavigation = self._generateResultNavigation(cEntriesMax, cItemsPerPage, iPage, tsEffective, sCurPeriod, fOnlyFailures, self._generateGroupContentSelector(aoGroupMembers, iGroupMemberId, sAltSelectorAction)); if cEntriesMax > 0: self._sPageBody = sHtmlNavigation + _sPageBody + sHtmlNavigation; else: self._sPageBody = sHtmlNavigation + '

No data to display

\n'; # # Now, generate a filter control panel for the side bar. # if hasattr(oFilter, 'kiBranches'): oFilter.aCriteria[oFilter.kiBranches].fExpanded = True; if hasattr(oFilter, 'kiTestStatus'): oFilter.aCriteria[oFilter.kiTestStatus].fExpanded = True; self._sPageFilter = self._generateResultFilter(oFilter, oResultLogic, tsEffective, sCurPeriod, enmResultsGroupingType = enmResultsGroupingType, aoGroupMembers = aoGroupMembers, fOnlyFailures = fOnlyFailures, fOnlyNeedingReason = fOnlyNeedingReason); return True; def _generateResultFilter(self, oFilter, oResultLogic, tsNow, sPeriod, enmResultsGroupingType = None, aoGroupMembers = None, fOnlyFailures = False, fOnlyNeedingReason = False): """ Generates the result filter for the left hand side. """ _ = enmResultsGroupingType; _ = aoGroupMembers; _ = fOnlyFailures; _ = fOnlyNeedingReason; oResultLogic.fetchPossibleFilterOptions(oFilter, tsNow, sPeriod) # Add non-filter parameters as hidden fields so we can use 'GET' and have URLs to bookmark. self._dSideMenuFormAttrs['method'] = 'GET'; sHtml = u''; for sKey, oValue in self._oSrvGlue.getParameters().items(): if len(sKey) > 3: if hasattr(oValue, 'startswith'): sHtml += u'\n' \ % (webutils.escapeAttr(sKey), webutils.escapeAttr(oValue),); else: for oSubValue in oValue: sHtml += u'\n' \ % (webutils.escapeAttr(sKey), webutils.escapeAttr(oSubValue),); # Generate the filter panel. sHtml += u'
\n' \ u'

Filters' \ u' \n' \ u' »»

\n'; sHtml += u'
\n'; for oCrit in oFilter.aCriteria: if oCrit.aoPossible or oCrit.sType == oCrit.ksType_Ranges: if ( oCrit.oSub is None \ and ( oCrit.sState == oCrit.ksState_Selected \ or (len(oCrit.aoPossible) <= 2 and oCrit.sType != oCrit.ksType_Ranges))) \ or ( oCrit.oSub is not None \ and ( oCrit.sState == oCrit.ksState_Selected \ or oCrit.oSub.sState == oCrit.ksState_Selected \ or (len(oCrit.aoPossible) <= 2 and len(oCrit.oSub.aoPossible) <= 2))) \ or oCrit.fExpanded is True: sClass = 'sf-collapsible'; sChar = '▼'; else: sClass = 'sf-expandable'; sChar = '▶'; sHtml += u'
%s %s ' \ % (sClass, sChar, webutils.escapeElem(oCrit.sName),); sHtml += u''; if oCrit.sInvVarNm is not None: sHtml += u'' \ u'' \ % ( oCrit.sInvVarNm, oCrit.sInvVarNm, ' checked' if oCrit.fInverted else '', oCrit.sInvVarNm,); sHtml += u' '; sHtml += u''; sHtml += u'
\n' \ u'
\n' \ u'
    \n' \ % (sClass); if oCrit.sType == oCrit.ksType_Ranges: assert not oCrit.oSub; assert not oCrit.aoPossible; asValues = []; for tRange in oCrit.aoSelected: if tRange[0] == tRange[1]: asValues.append('%s' % (tRange[0],)); else: asValues.append('%s-%s' % (tRange[0] if tRange[0] is not None else 'inf', tRange[1] if tRange[1] is not None else 'inf')); sHtml += u'
  • \n' \ % ( webutils.escapeAttr('comma separate list of numerical ranges'), oCrit.sVarNm, ', '.join(asValues), ); else: for oDesc in oCrit.aoPossible: fChecked = oDesc.oValue in oCrit.aoSelected; sHtml += u' \n' \ % ( ' class="side-filter-irrelevant"' if oDesc.fIrrelevant else '', (' title="%s"' % (webutils.escapeAttr(oDesc.sHover,)) if oDesc.sHover is not None else ''), oCrit.sVarNm, oDesc.oValue, ' checked' if fChecked else '', ' onclick="toggleCollapsibleCheckbox(this);"' if oDesc.aoSubs is not None else '', webutils.escapeElem(oDesc.sDesc), ' [%u]' % (oDesc.cTimes) if oDesc.cTimes is not None else '', ); if oDesc.aoSubs is not None: sHtml += u'
      \n' % ('collapsible' if fChecked else 'expandable', ); for oSubDesc in oDesc.aoSubs: fSubChecked = oSubDesc.oValue in oCrit.oSub.aoSelected; sHtml += u' \n' \ % ( ' class="side-filter-irrelevant"' if oSubDesc.fIrrelevant else '', ' title="%s"' % ( webutils.escapeAttr(oSubDesc.sHover,) if oSubDesc.sHover is not None else ''), oCrit.oSub.sVarNm, oSubDesc.oValue, ' checked' if fSubChecked else '', webutils.escapeElem(oSubDesc.sDesc), ' [%u]' % (oSubDesc.cTimes) if oSubDesc.cTimes is not None else '', ); sHtml += u'
    \n'; sHtml += u' '; sHtml += u'
\n' \ u'
\n'; sHtml += u'
\n'; sHtml += u' \n'; sHtml += u' \n'; sHtml += u' \n'; sHtml += u'
\n'; return sHtml; def _actionResultsUnGrouped(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; #return self._actionResultsListing(TestResultLogic, WuiGroupedResultList)? return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedByTestGroup(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestGroup, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedByBuildRev(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildRev, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedByBuildCat(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildCat, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedByTestBox(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestBox, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedByTestCase(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestCase, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedByOS(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeOS, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedByArch(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeArch, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionResultsGroupedBySchedGroup(self): """ Action wrapper. """ from testmanager.webui.wuitestresult import WuiGroupedResultList; from testmanager.core.testresults import TestResultLogic, TestResultFilter; return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeSchedGroup, TestResultLogic, TestResultFilter, WuiGroupedResultList); def _actionTestSetDetailsCommon(self, idTestSet): """Show test case execution result details.""" from testmanager.core.build import BuildDataEx; from testmanager.core.testbox import TestBoxData; from testmanager.core.testcase import TestCaseDataEx; from testmanager.core.testcaseargs import TestCaseArgsDataEx; from testmanager.core.testgroup import TestGroupData; from testmanager.core.testresults import TestResultLogic; from testmanager.core.testset import TestSetData; from testmanager.webui.wuitestresult import WuiTestResult; self._sTemplate = 'template-details.html'; self._checkForUnknownParameters() oTestSetData = TestSetData().initFromDbWithId(self._oDb, idTestSet); try: (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet); except TMTooManyRows: (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet, 2); oBuildDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuild, oTestSetData.tsCreated); try: oBuildValidationKitDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuildTestSuite, oTestSetData.tsCreated); except: oBuildValidationKitDataEx = None; oTestBoxData = TestBoxData().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestBox); oTestGroupData = TestGroupData().initFromDbWithId(self._oDb, ## @todo This bogus time wise. Bad DB design? oTestSetData.idTestGroup, oTestSetData.tsCreated); oTestCaseDataEx = TestCaseDataEx().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestCase, oTestSetData.tsConfig); oTestCaseArgsDataEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(self._oDb, oTestSetData.idGenTestCaseArgs, oTestSetData.tsConfig); oContent = WuiTestResult(oDisp = self, fnDPrint = self._oSrvGlue.dprint); (self._sPageTitle, self._sPageBody) = oContent.showTestCaseResultDetails(oTestResultTree, oTestSetData, oBuildDataEx, oBuildValidationKitDataEx, oTestBoxData, oTestGroupData, oTestCaseDataEx, oTestCaseArgsDataEx); return True def _actionTestSetDetails(self): """Show test case execution result details.""" from testmanager.core.testset import TestSetData; idTestSet = self.getIntParam(TestSetData.ksParam_idTestSet); return self._actionTestSetDetailsCommon(idTestSet); def _actionTestSetDetailsFromResult(self): """Show test case execution result details.""" from testmanager.core.testresults import TestResultData; from testmanager.core.testset import TestSetData; idTestResult = self.getIntParam(TestSetData.ksParam_idTestResult); oTestResultData = TestResultData().initFromDbWithId(self._oDb, idTestResult); return self._actionTestSetDetailsCommon(oTestResultData.idTestSet); def _actionTestResultFailureAdd(self): """ Pro forma. """ from testmanager.core.testresultfailures import TestResultFailureData; from testmanager.webui.wuitestresultfailure import WuiTestResultFailure; return self._actionGenericFormAdd(TestResultFailureData, WuiTestResultFailure); def _actionTestResultFailureAddPost(self): """Add test result failure result""" from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData; from testmanager.webui.wuitestresultfailure import WuiTestResultFailure; if self.ksParamRedirectTo not in self._dParams: raise WuiException('Missing parameter ' + self.ksParamRedirectTo); return self._actionGenericFormAddPost(TestResultFailureData, TestResultFailureLogic, WuiTestResultFailure, self.ksActionResultsUnGrouped); def _actionTestResultFailureDoRemove(self): """ Action wrapper. """ from testmanager.core.testresultfailures import TestResultFailureData, TestResultFailureLogic; return self._actionGenericDoRemove(TestResultFailureLogic, TestResultFailureData.ksParam_idTestResult, self.ksActionResultsUnGrouped); def _actionTestResultFailureDetails(self): """ Pro forma. """ from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData; from testmanager.webui.wuitestresultfailure import WuiTestResultFailure; return self._actionGenericFormDetails(TestResultFailureData, TestResultFailureLogic, WuiTestResultFailure, 'idTestResult'); def _actionTestResultFailureEdit(self): """ Pro forma. """ from testmanager.core.testresultfailures import TestResultFailureData; from testmanager.webui.wuitestresultfailure import WuiTestResultFailure; return self._actionGenericFormEdit(TestResultFailureData, WuiTestResultFailure, TestResultFailureData.ksParam_idTestResult); def _actionTestResultFailureEditPost(self): """Edit test result failure result""" from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData; from testmanager.webui.wuitestresultfailure import WuiTestResultFailure; return self._actionGenericFormEditPost(TestResultFailureData, TestResultFailureLogic, WuiTestResultFailure, self.ksActionResultsUnGrouped); def _actionViewLog(self): """ Log viewer action. """ from testmanager.core.testresults import TestResultLogic, TestResultFileDataEx; from testmanager.core.testset import TestSetData, TestSetLogic; from testmanager.webui.wuilogviewer import WuiLogViewer; self._sTemplate = 'template-details.html'; ## @todo create new template (background color, etc) idTestSet = self.getIntParam(self.ksParamLogSetId, iMin = 1); idLogFile = self.getIntParam(self.ksParamLogFileId, iMin = 0, iDefault = 0); cbChunk = self.getIntParam(self.ksParamLogChunkSize, iMin = 256, iMax = 16777216, iDefault = 1024*1024); iChunk = self.getIntParam(self.ksParamLogChunkNo, iMin = 0, iMax = config.g_kcMbMaxMainLog * 1048576 / cbChunk, iDefault = 0); self._checkForUnknownParameters(); oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet); if idLogFile == 0: oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet); aoTimestamps = TestResultLogic(self._oDb).fetchTimestampsForLogViewer(idTestSet); else: oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idLogFile); aoTimestamps = []; if oTestFile.sMime not in [ 'text/plain',]: raise WuiException('The log view does not display files of type: %s' % (oTestFile.sMime,)); oContent = WuiLogViewer(oTestSet, oTestFile, cbChunk, iChunk, aoTimestamps, oDisp = self, fnDPrint = self._oSrvGlue.dprint); (self._sPageTitle, self._sPageBody) = oContent.show(); return True; def _actionGetFile(self): """ Get file action. """ from testmanager.core.testset import TestSetData, TestSetLogic; from testmanager.core.testresults import TestResultFileDataEx; idTestSet = self.getIntParam(self.ksParamGetFileSetId, iMin = 1); idFile = self.getIntParam(self.ksParamGetFileId, iMin = 0, iDefault = 0); fDownloadIt = self.getBoolParam(self.ksParamGetFileDownloadIt, fDefault = True); self._checkForUnknownParameters(); # # Get the file info and open it. # oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet); if idFile == 0: oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet); else: oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idFile); (oFile, oSizeOrError, _) = oTestSet.openFile(oTestFile.sFile, 'rb'); if oFile is None: raise Exception(oSizeOrError); # # Send the file. # self._oSrvGlue.setHeaderField('Content-Type', oTestFile.getMimeWithEncoding()); if fDownloadIt: self._oSrvGlue.setHeaderField('Content-Disposition', 'attachment; filename="TestSet-%d-%s"' % (idTestSet, oTestFile.sFile,)); while True: abChunk = oFile.read(262144); if not abChunk: break; self._oSrvGlue.writeRaw(abChunk); return self.ksDispatchRcAllDone; def _actionGenericReport(self, oModelType, oFilterType, oReportType): """ Generic report action. oReportType is a child of WuiReportContentBase. oFilterType is a child of ModelFilterBase. oModelType is a child of ReportModelBase. """ from testmanager.core.report import ReportModelBase; tsEffective = self.getEffectiveDateParam(); cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 2, iMax = 99, iDefault = 7); cHoursPerPeriod = self.getIntParam(self.ksParamReportPeriodInHours, iMin = 1, iMax = 168, iDefault = 24); sSubject = self.getStringParam(self.ksParamReportSubject, ReportModelBase.kasSubjects, ReportModelBase.ksSubEverything); if sSubject == ReportModelBase.ksSubEverything: aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, aiDefaults = []); else: aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, iMin = 1); if aidSubjects is None: raise WuiException('Missing parameter %s' % (self.ksParamReportSubjectIds,)); aiSortColumnsDup = self.getListOfIntParams(self.ksParamSortColumns, iMin = -getattr(oReportType, 'kcMaxSortColumns', cPeriods) + 1, iMax = getattr(oReportType, 'kcMaxSortColumns', cPeriods), aiDefaults = []); aiSortColumns = []; for iSortColumn in aiSortColumnsDup: if iSortColumn not in aiSortColumns: aiSortColumns.append(iSortColumn); oFilter = oFilterType().initFromParams(self); self._checkForUnknownParameters(); dParams = \ { self.ksParamEffectiveDate: tsEffective, self.ksParamReportPeriods: cPeriods, self.ksParamReportPeriodInHours: cHoursPerPeriod, self.ksParamReportSubject: sSubject, self.ksParamReportSubjectIds: aidSubjects, }; ## @todo oFilter. oModel = oModelType(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, oFilter); oContent = oReportType(oModel, dParams, fSubReport = False, aiSortColumns = aiSortColumns, fnDPrint = self._oSrvGlue.dprint, oDisp = self); (self._sPageTitle, self._sPageBody) = oContent.show(); sNavi = self._generateReportNavigation(tsEffective, cHoursPerPeriod, cPeriods); self._sPageBody = sNavi + self._sPageBody; if hasattr(oFilter, 'kiBranches'): oFilter.aCriteria[oFilter.kiBranches].fExpanded = True; self._sPageFilter = self._generateResultFilter(oFilter, oModel, tsEffective, '%s hours' % (cHoursPerPeriod * cPeriods,)); return True; def _actionReportSummary(self): """ Action wrapper. """ from testmanager.core.report import ReportLazyModel, ReportFilter; from testmanager.webui.wuireport import WuiReportSummary; return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSummary); def _actionReportRate(self): """ Action wrapper. """ from testmanager.core.report import ReportLazyModel, ReportFilter; from testmanager.webui.wuireport import WuiReportSuccessRate; return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSuccessRate); def _actionReportTestCaseFailures(self): """ Action wrapper. """ from testmanager.core.report import ReportLazyModel, ReportFilter; from testmanager.webui.wuireport import WuiReportTestCaseFailures; return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportTestCaseFailures); def _actionReportFailureReasons(self): """ Action wrapper. """ from testmanager.core.report import ReportLazyModel, ReportFilter; from testmanager.webui.wuireport import WuiReportFailureReasons; return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportFailureReasons); def _actionGraphWiz(self): """ Graph wizard action. """ from testmanager.core.report import ReportModelBase, ReportGraphModel; from testmanager.webui.wuigraphwiz import WuiGraphWiz; self._sTemplate = 'template-graphwiz.html'; tsEffective = self.getEffectiveDateParam(); cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 1, iMax = 1, iDefault = 1); # Not needed yet. sTmp = self.getStringParam(self.ksParamReportPeriodInHours, sDefault = '3 weeks'); (cHoursPerPeriod, sError) = utils.parseIntervalHours(sTmp); if sError is not None: raise WuiException(sError); asSubjectIds = self.getListOfStrParams(self.ksParamReportSubjectIds); sSubject = self.getStringParam(self.ksParamReportSubject, [ReportModelBase.ksSubEverything], ReportModelBase.ksSubEverything); # dummy aidTestBoxes = self.getListOfIntParams(self.ksParamGraphWizTestBoxIds, iMin = 1, aiDefaults = []); aidBuildCats = self.getListOfIntParams(self.ksParamGraphWizBuildCatIds, iMin = 1, aiDefaults = []); aidTestCases = self.getListOfIntParams(self.ksParamGraphWizTestCaseIds, iMin = 1, aiDefaults = []); fSepTestVars = self.getBoolParam(self.ksParamGraphWizSepTestVars, fDefault = False); enmGraphImpl = self.getStringParam(self.ksParamGraphWizImpl, asValidValues = self.kasGraphWizImplValid, sDefault = self.ksGraphWizImpl_Default); cx = self.getIntParam(self.ksParamGraphWizWidth, iMin = 128, iMax = 8192, iDefault = 1280); cy = self.getIntParam(self.ksParamGraphWizHeight, iMin = 128, iMax = 8192, iDefault = int(cx * 5 / 16) ); cDotsPerInch = self.getIntParam(self.ksParamGraphWizDpi, iMin = 64, iMax = 512, iDefault = 96); cPtFont = self.getIntParam(self.ksParamGraphWizFontSize, iMin = 6, iMax = 32, iDefault = 8); fErrorBarY = self.getBoolParam(self.ksParamGraphWizErrorBarY, fDefault = False); cMaxErrorBarY = self.getIntParam(self.ksParamGraphWizMaxErrorBarY, iMin = 8, iMax = 9999999, iDefault = 18); cMaxPerGraph = self.getIntParam(self.ksParamGraphWizMaxPerGraph, iMin = 1, iMax = 24, iDefault = 8); fXkcdStyle = self.getBoolParam(self.ksParamGraphWizXkcdStyle, fDefault = False); fTabular = self.getBoolParam(self.ksParamGraphWizTabular, fDefault = False); idSrcTestSet = self.getIntParam(self.ksParamGraphWizSrcTestSetId, iDefault = None); self._checkForUnknownParameters(); dParams = \ { self.ksParamEffectiveDate: tsEffective, self.ksParamReportPeriods: cPeriods, self.ksParamReportPeriodInHours: cHoursPerPeriod, self.ksParamReportSubject: sSubject, self.ksParamReportSubjectIds: asSubjectIds, self.ksParamGraphWizTestBoxIds: aidTestBoxes, self.ksParamGraphWizBuildCatIds: aidBuildCats, self.ksParamGraphWizTestCaseIds: aidTestCases, self.ksParamGraphWizSepTestVars: fSepTestVars, self.ksParamGraphWizImpl: enmGraphImpl, self.ksParamGraphWizWidth: cx, self.ksParamGraphWizHeight: cy, self.ksParamGraphWizDpi: cDotsPerInch, self.ksParamGraphWizFontSize: cPtFont, self.ksParamGraphWizErrorBarY: fErrorBarY, self.ksParamGraphWizMaxErrorBarY: cMaxErrorBarY, self.ksParamGraphWizMaxPerGraph: cMaxPerGraph, self.ksParamGraphWizXkcdStyle: fXkcdStyle, self.ksParamGraphWizTabular: fTabular, self.ksParamGraphWizSrcTestSetId: idSrcTestSet, }; oModel = ReportGraphModel(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, asSubjectIds, aidTestBoxes, aidBuildCats, aidTestCases, fSepTestVars); oContent = WuiGraphWiz(oModel, dParams, fSubReport = False, fnDPrint = self._oSrvGlue.dprint, oDisp = self); (self._sPageTitle, self._sPageBody) = oContent.show(); return True; def _actionVcsHistoryTooltip(self): """ Version control system history. """ from testmanager.webui.wuivcshistory import WuiVcsHistoryTooltip; from testmanager.core.vcsrevisions import VcsRevisionLogic; self._sTemplate = 'template-tooltip.html'; iRevision = self.getIntParam(self.ksParamVcsHistoryRevision, iMin = 0, iMax = 999999999); sRepository = self.getStringParam(self.ksParamVcsHistoryRepository); cEntries = self.getIntParam(self.ksParamVcsHistoryEntries, iMin = 1, iMax = 1024, iDefault = 8); self._checkForUnknownParameters(); aoEntries = VcsRevisionLogic(self._oDb).fetchTimeline(sRepository, iRevision, cEntries); oContent = WuiVcsHistoryTooltip(aoEntries, sRepository, iRevision, cEntries, fnDPrint = self._oSrvGlue.dprint, oDisp = self); (self._sPageTitle, self._sPageBody) = oContent.show(); return True;