1 | # -*- coding: utf-8 -*-
|
---|
2 | # $Id: wuimain.py 65092 2017-01-04 02:25:27Z vboxsync $
|
---|
3 |
|
---|
4 | """
|
---|
5 | Test Manager Core - WUI - The Main page.
|
---|
6 | """
|
---|
7 |
|
---|
8 | __copyright__ = \
|
---|
9 | """
|
---|
10 | Copyright (C) 2012-2016 Oracle Corporation
|
---|
11 |
|
---|
12 | This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
13 | available from http://www.virtualbox.org. This file is free software;
|
---|
14 | you can redistribute it and/or modify it under the terms of the GNU
|
---|
15 | General Public License (GPL) as published by the Free Software
|
---|
16 | Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
17 | VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
18 | hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
19 |
|
---|
20 | The contents of this file may alternatively be used under the terms
|
---|
21 | of the Common Development and Distribution License Version 1.0
|
---|
22 | (CDDL) only, as it comes in the "COPYING.CDDL" file of the
|
---|
23 | VirtualBox OSE distribution, in which case the provisions of the
|
---|
24 | CDDL are applicable instead of those of the GPL.
|
---|
25 |
|
---|
26 | You may elect to license modified versions of this file under the
|
---|
27 | terms and conditions of either the GPL or the CDDL or both.
|
---|
28 | """
|
---|
29 | __version__ = "$Revision: 65092 $"
|
---|
30 |
|
---|
31 | # Standard Python imports.
|
---|
32 |
|
---|
33 | # Validation Kit imports.
|
---|
34 | from testmanager import config;
|
---|
35 | from testmanager.core.base import TMExceptionBase, TMTooManyRows;
|
---|
36 | from testmanager.webui.wuibase import WuiDispatcherBase, WuiException;
|
---|
37 | from testmanager.webui.wuicontentbase import WuiTmLink;
|
---|
38 | from common import webutils, utils;
|
---|
39 |
|
---|
40 |
|
---|
41 |
|
---|
42 | class WuiMain(WuiDispatcherBase):
|
---|
43 | """
|
---|
44 | WUI Main page.
|
---|
45 |
|
---|
46 | Note! All cylic dependency avoiance stuff goes here in the dispatcher code,
|
---|
47 | not in the action specific code. This keeps the uglyness in one place
|
---|
48 | and reduces load time dependencies in the more critical code path.
|
---|
49 | """
|
---|
50 |
|
---|
51 | ## The name of the script.
|
---|
52 | ksScriptName = 'index.py'
|
---|
53 |
|
---|
54 | ## @name Actions
|
---|
55 | ## @{
|
---|
56 | ksActionResultsUnGrouped = 'ResultsUnGrouped'
|
---|
57 | ksActionResultsGroupedBySchedGroup = 'ResultsGroupedBySchedGroup'
|
---|
58 | ksActionResultsGroupedByTestGroup = 'ResultsGroupedByTestGroup'
|
---|
59 | ksActionResultsGroupedByBuildRev = 'ResultsGroupedByBuildRev'
|
---|
60 | ksActionResultsGroupedByBuildCat = 'ResultsGroupedByBuildCat'
|
---|
61 | ksActionResultsGroupedByTestBox = 'ResultsGroupedByTestBox'
|
---|
62 | ksActionResultsGroupedByTestCase = 'ResultsGroupedByTestCase'
|
---|
63 | ksActionResultsGroupedByOS = 'ResultsGroupedByOS'
|
---|
64 | ksActionResultsGroupedByArch = 'ResultsGroupedByArch'
|
---|
65 | ksActionTestSetDetails = 'TestSetDetails';
|
---|
66 | ksActionTestResultDetails = ksActionTestSetDetails;
|
---|
67 | ksActionTestSetDetailsFromResult = 'TestSetDetailsFromResult'
|
---|
68 | ksActionTestResultFailureDetails = 'TestResultFailureDetails'
|
---|
69 | ksActionTestResultFailureAdd = 'TestResultFailureAdd'
|
---|
70 | ksActionTestResultFailureAddPost = 'TestResultFailureAddPost'
|
---|
71 | ksActionTestResultFailureEdit = 'TestResultFailureEdit'
|
---|
72 | ksActionTestResultFailureEditPost = 'TestResultFailureEditPost'
|
---|
73 | ksActionTestResultFailureDoRemove = 'TestResultFailureDoRemove'
|
---|
74 | ksActionViewLog = 'ViewLog'
|
---|
75 | ksActionGetFile = 'GetFile'
|
---|
76 | ksActionReportSummary = 'ReportSummary';
|
---|
77 | ksActionReportRate = 'ReportRate';
|
---|
78 | ksActionReportTestCaseFailures = 'ReportTestCaseFailures';
|
---|
79 | ksActionReportTestBoxFailures = 'ReportTestBoxFailures';
|
---|
80 | ksActionReportFailureReasons = 'ReportFailureReasons';
|
---|
81 | ksActionGraphWiz = 'GraphWiz';
|
---|
82 | ksActionVcsHistoryTooltip = 'VcsHistoryTooltip';
|
---|
83 | ## @}
|
---|
84 |
|
---|
85 | ## @name Standard report parameters
|
---|
86 | ## @{
|
---|
87 | ksParamReportPeriods = 'cPeriods';
|
---|
88 | ksParamReportPeriodInHours = 'cHoursPerPeriod';
|
---|
89 | ksParamReportSubject = 'sSubject';
|
---|
90 | ksParamReportSubjectIds = 'SubjectIds';
|
---|
91 | ## @}
|
---|
92 |
|
---|
93 | ## @name Graph Wizard parameters
|
---|
94 | ## Common parameters: ksParamReportPeriods, ksParamReportPeriodInHours, ksParamReportSubjectIds,
|
---|
95 | ## ksParamReportSubject, ksParamEffectivePeriod, and ksParamEffectiveDate.
|
---|
96 | ## @{
|
---|
97 | ksParamGraphWizTestBoxIds = 'aidTestBoxes';
|
---|
98 | ksParamGraphWizBuildCatIds = 'aidBuildCats';
|
---|
99 | ksParamGraphWizTestCaseIds = 'aidTestCases';
|
---|
100 | ksParamGraphWizSepTestVars = 'fSepTestVars';
|
---|
101 | ksParamGraphWizImpl = 'enmImpl';
|
---|
102 | ksParamGraphWizWidth = 'cx';
|
---|
103 | ksParamGraphWizHeight = 'cy';
|
---|
104 | ksParamGraphWizDpi = 'dpi';
|
---|
105 | ksParamGraphWizFontSize = 'cPtFont';
|
---|
106 | ksParamGraphWizErrorBarY = 'fErrorBarY';
|
---|
107 | ksParamGraphWizMaxErrorBarY = 'cMaxErrorBarY';
|
---|
108 | ksParamGraphWizMaxPerGraph = 'cMaxPerGraph';
|
---|
109 | ksParamGraphWizXkcdStyle = 'fXkcdStyle';
|
---|
110 | ksParamGraphWizTabular = 'fTabular';
|
---|
111 | ksParamGraphWizSrcTestSetId = 'idSrcTestSet';
|
---|
112 | ## @}
|
---|
113 |
|
---|
114 | ## @name Graph implementations values for ksParamGraphWizImpl.
|
---|
115 | ## @{
|
---|
116 | ksGraphWizImpl_Default = 'default';
|
---|
117 | ksGraphWizImpl_Matplotlib = 'matplotlib';
|
---|
118 | ksGraphWizImpl_Charts = 'charts';
|
---|
119 | kasGraphWizImplValid = [ ksGraphWizImpl_Default, ksGraphWizImpl_Matplotlib, ksGraphWizImpl_Charts];
|
---|
120 | kaasGraphWizImplCombo = [
|
---|
121 | ( ksGraphWizImpl_Default, 'Default' ),
|
---|
122 | ( ksGraphWizImpl_Matplotlib, 'Matplotlib (server)' ),
|
---|
123 | ( ksGraphWizImpl_Charts, 'Google Charts (client)'),
|
---|
124 | ];
|
---|
125 | ## @}
|
---|
126 |
|
---|
127 | ## @name Log Viewer parameters.
|
---|
128 | ## @{
|
---|
129 | ksParamLogSetId = 'LogViewer_idTestSet';
|
---|
130 | ksParamLogFileId = 'LogViewer_idFile';
|
---|
131 | ksParamLogChunkSize = 'LogViewer_cbChunk';
|
---|
132 | ksParamLogChunkNo = 'LogViewer_iChunk';
|
---|
133 | ## @}
|
---|
134 |
|
---|
135 | ## @name File getter parameters.
|
---|
136 | ## @{
|
---|
137 | ksParamGetFileSetId = 'GetFile_idTestSet';
|
---|
138 | ksParamGetFileId = 'GetFile_idFile';
|
---|
139 | ksParamGetFileDownloadIt = 'GetFile_fDownloadIt';
|
---|
140 | ## @}
|
---|
141 |
|
---|
142 | ## @name VCS history parameters.
|
---|
143 | ## @{
|
---|
144 | ksParamVcsHistoryRepository = 'repo';
|
---|
145 | ksParamVcsHistoryRevision = 'rev';
|
---|
146 | ksParamVcsHistoryEntries = 'cEntries';
|
---|
147 | ## @}
|
---|
148 |
|
---|
149 | ## @name Test result listing parameters.
|
---|
150 | ## @{
|
---|
151 | ## If this param is specified, then show only results for this member when results grouped by some parameter.
|
---|
152 | ksParamGroupMemberId = 'GroupMemberId'
|
---|
153 | ## Optional parameter for indicating whether to restrict the listing to failures only.
|
---|
154 | ksParamOnlyFailures = 'OnlyFailures';
|
---|
155 | ## The sheriff parameter for getting failures needing a reason or two assigned to them.
|
---|
156 | ksParamOnlyNeedingReason = 'OnlyNeedingReason';
|
---|
157 | ## Result listing sorting.
|
---|
158 | ksParamTestResultsSortBy = 'enmSortBy'
|
---|
159 | ## @}
|
---|
160 |
|
---|
161 | ## Effective time period. one of the first column values in kaoResultPeriods.
|
---|
162 | ksParamEffectivePeriod = 'sEffectivePeriod'
|
---|
163 |
|
---|
164 | ## Test result period values.
|
---|
165 | kaoResultPeriods = [
|
---|
166 | ( '1 hour', '1 hour', 1 ),
|
---|
167 | ( '2 hours', '2 hours', 2 ),
|
---|
168 | ( '3 hours', '3 hours', 3 ),
|
---|
169 | ( '6 hours', '6 hours', 6 ),
|
---|
170 | ( '12 hours', '12 hours', 12 ),
|
---|
171 |
|
---|
172 | ( '1 day', '1 day', 24 ),
|
---|
173 | ( '2 days', '2 days', 48 ),
|
---|
174 | ( '3 days', '3 days', 72 ),
|
---|
175 |
|
---|
176 | ( '1 week', '1 week', 168 ),
|
---|
177 | ( '2 weeks', '2 weeks', 336 ),
|
---|
178 | ( '3 weeks', '3 weeks', 504 ),
|
---|
179 |
|
---|
180 | ( '1 month', '1 month', 31 * 24 ), # The approx hour count varies with the start date.
|
---|
181 | ( '2 months', '2 months', (31 + 31) * 24 ), # Using maximum values.
|
---|
182 | ( '3 months', '3 months', (31 + 30 + 31) * 24 ),
|
---|
183 |
|
---|
184 | ( '6 months', '6 months', (31 + 31 + 30 + 31 + 30 + 31) * 24 ),
|
---|
185 |
|
---|
186 | ( '1 year', '1 year', 365 * 24 ),
|
---|
187 | ];
|
---|
188 | ## The default test result period.
|
---|
189 | ksResultPeriodDefault = '6 hours';
|
---|
190 |
|
---|
191 |
|
---|
192 |
|
---|
193 | def __init__(self, oSrvGlue):
|
---|
194 | WuiDispatcherBase.__init__(self, oSrvGlue, self.ksScriptName);
|
---|
195 | self._sTemplate = 'template.html'
|
---|
196 |
|
---|
197 | #
|
---|
198 | # Populate the action dispatcher dictionary.
|
---|
199 | # Lambda is forbidden because of readability, speed and reducing number of imports.
|
---|
200 | #
|
---|
201 | self._dDispatch[self.ksActionResultsUnGrouped] = self._actionResultsUnGrouped;
|
---|
202 | self._dDispatch[self.ksActionResultsGroupedByTestGroup] = self._actionResultsGroupedByTestGroup;
|
---|
203 | self._dDispatch[self.ksActionResultsGroupedByBuildRev] = self._actionResultsGroupedByBuildRev;
|
---|
204 | self._dDispatch[self.ksActionResultsGroupedByBuildCat] = self._actionResultsGroupedByBuildCat;
|
---|
205 | self._dDispatch[self.ksActionResultsGroupedByTestBox] = self._actionResultsGroupedByTestBox;
|
---|
206 | self._dDispatch[self.ksActionResultsGroupedByTestCase] = self._actionResultsGroupedByTestCase;
|
---|
207 | self._dDispatch[self.ksActionResultsGroupedByOS] = self._actionResultsGroupedByOS;
|
---|
208 | self._dDispatch[self.ksActionResultsGroupedByArch] = self._actionResultsGroupedByArch;
|
---|
209 | self._dDispatch[self.ksActionResultsGroupedBySchedGroup] = self._actionResultsGroupedBySchedGroup;
|
---|
210 |
|
---|
211 | self._dDispatch[self.ksActionTestSetDetails] = self._actionTestSetDetails;
|
---|
212 | self._dDispatch[self.ksActionTestSetDetailsFromResult] = self._actionTestSetDetailsFromResult;
|
---|
213 |
|
---|
214 | self._dDispatch[self.ksActionTestResultFailureAdd] = self._actionTestResultFailureAdd;
|
---|
215 | self._dDispatch[self.ksActionTestResultFailureAddPost] = self._actionTestResultFailureAddPost;
|
---|
216 | self._dDispatch[self.ksActionTestResultFailureDetails] = self._actionTestResultFailureDetails;
|
---|
217 | self._dDispatch[self.ksActionTestResultFailureDoRemove] = self._actionTestResultFailureDoRemove;
|
---|
218 | self._dDispatch[self.ksActionTestResultFailureEdit] = self._actionTestResultFailureEdit;
|
---|
219 | self._dDispatch[self.ksActionTestResultFailureEditPost] = self._actionTestResultFailureEditPost;
|
---|
220 |
|
---|
221 | self._dDispatch[self.ksActionViewLog] = self._actionViewLog;
|
---|
222 | self._dDispatch[self.ksActionGetFile] = self._actionGetFile;
|
---|
223 |
|
---|
224 | self._dDispatch[self.ksActionReportSummary] = self._actionReportSummary;
|
---|
225 | self._dDispatch[self.ksActionReportRate] = self._actionReportRate;
|
---|
226 | self._dDispatch[self.ksActionReportTestCaseFailures] = self._actionReportTestCaseFailures;
|
---|
227 | self._dDispatch[self.ksActionReportFailureReasons] = self._actionReportFailureReasons;
|
---|
228 | self._dDispatch[self.ksActionGraphWiz] = self._actionGraphWiz;
|
---|
229 |
|
---|
230 | self._dDispatch[self.ksActionVcsHistoryTooltip] = self._actionVcsHistoryTooltip;
|
---|
231 |
|
---|
232 | # Legacy.
|
---|
233 | self._dDispatch['TestResultDetails'] = self._dDispatch[self.ksActionTestSetDetails];
|
---|
234 |
|
---|
235 |
|
---|
236 | #
|
---|
237 | # Popupate the menus.
|
---|
238 | #
|
---|
239 |
|
---|
240 | # Additional URL parameters keeping for time navigation.
|
---|
241 | sExtraTimeNav = ''
|
---|
242 | dCurParams = oSrvGlue.getParameters()
|
---|
243 | if dCurParams is not None:
|
---|
244 | for sExtraParam in [ self.ksParamItemsPerPage, self.ksParamEffectiveDate, self.ksParamEffectivePeriod, ]:
|
---|
245 | if sExtraParam in dCurParams:
|
---|
246 | sExtraTimeNav += '&%s' % (webutils.encodeUrlParams({sExtraParam: dCurParams[sExtraParam]}),)
|
---|
247 |
|
---|
248 | # Additional URL parameters for reports
|
---|
249 | sExtraReports = '';
|
---|
250 | if dCurParams is not None:
|
---|
251 | for sExtraParam in [ self.ksParamReportPeriods, self.ksParamReportPeriodInHours, self.ksParamEffectiveDate, ]:
|
---|
252 | if sExtraParam in dCurParams:
|
---|
253 | sExtraReports += '&%s' % (webutils.encodeUrlParams({sExtraParam: dCurParams[sExtraParam]}),)
|
---|
254 |
|
---|
255 | # Shorthand to keep within margins.
|
---|
256 | sActUrlBase = self._sActionUrlBase;
|
---|
257 | sOnlyFailures = '&%s%s' % ( webutils.encodeUrlParams({self.ksParamOnlyFailures: True}), sExtraTimeNav, );
|
---|
258 | sSheriff = '&%s%s' % ( webutils.encodeUrlParams({self.ksParamOnlyNeedingReason: True}), sExtraTimeNav, );
|
---|
259 |
|
---|
260 | self._aaoMenus = \
|
---|
261 | [
|
---|
262 | [
|
---|
263 | 'Sheriff', sActUrlBase + self.ksActionResultsUnGrouped + sSheriff,
|
---|
264 | [
|
---|
265 | [ 'Grouped by', None ],
|
---|
266 | [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sSheriff ],
|
---|
267 | [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sSheriff ],
|
---|
268 | [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sSheriff ],
|
---|
269 | [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sSheriff ],
|
---|
270 | [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sSheriff ],
|
---|
271 | [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sSheriff ],
|
---|
272 | [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sSheriff ],
|
---|
273 | [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sSheriff ],
|
---|
274 | [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sSheriff ],
|
---|
275 | ]
|
---|
276 | ],
|
---|
277 | [
|
---|
278 | 'Reports', sActUrlBase + self.ksActionReportSummary,
|
---|
279 | [
|
---|
280 | [ 'Summary', sActUrlBase + self.ksActionReportSummary + sExtraReports ],
|
---|
281 | [ 'Success rate', sActUrlBase + self.ksActionReportRate + sExtraReports ],
|
---|
282 | [ 'Test case failures', sActUrlBase + self.ksActionReportTestCaseFailures + sExtraReports ],
|
---|
283 | [ 'Testbox failures', sActUrlBase + self.ksActionReportTestBoxFailures + sExtraReports ],
|
---|
284 | [ 'Failure reasons', sActUrlBase + self.ksActionReportFailureReasons + sExtraReports ],
|
---|
285 | ]
|
---|
286 | ],
|
---|
287 | [
|
---|
288 | 'Test Results', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav,
|
---|
289 | [
|
---|
290 | [ 'Grouped by', None ],
|
---|
291 | [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav ],
|
---|
292 | [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sExtraTimeNav ],
|
---|
293 | [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sExtraTimeNav ],
|
---|
294 | [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sExtraTimeNav ],
|
---|
295 | [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sExtraTimeNav ],
|
---|
296 | [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sExtraTimeNav ],
|
---|
297 | [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sExtraTimeNav ],
|
---|
298 | [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sExtraTimeNav ],
|
---|
299 | [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sExtraTimeNav ],
|
---|
300 | ]
|
---|
301 | ],
|
---|
302 | [
|
---|
303 | 'Test Failures', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures,
|
---|
304 | [
|
---|
305 | [ 'Grouped by', None ],
|
---|
306 | [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures ],
|
---|
307 | [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sOnlyFailures ],
|
---|
308 | [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sOnlyFailures ],
|
---|
309 | [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sOnlyFailures ],
|
---|
310 | [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sOnlyFailures ],
|
---|
311 | [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sOnlyFailures ],
|
---|
312 | [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sOnlyFailures ],
|
---|
313 | [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sOnlyFailures ],
|
---|
314 | [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sOnlyFailures ],
|
---|
315 | ]
|
---|
316 | ],
|
---|
317 | [
|
---|
318 | '> Admin', 'admin.py?' + webutils.encodeUrlParams(self._dDbgParams), []
|
---|
319 | ],
|
---|
320 | ];
|
---|
321 |
|
---|
322 |
|
---|
323 | #
|
---|
324 | # Overriding parent methods.
|
---|
325 | #
|
---|
326 |
|
---|
327 | def _generatePage(self):
|
---|
328 | """Override parent handler in order to change page title."""
|
---|
329 | if self._sPageTitle is not None:
|
---|
330 | self._sPageTitle = 'Test Results - ' + self._sPageTitle
|
---|
331 |
|
---|
332 | return WuiDispatcherBase._generatePage(self)
|
---|
333 |
|
---|
334 | def _actionDefault(self):
|
---|
335 | """Show the default admin page."""
|
---|
336 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
337 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
338 | self._sAction = self.ksActionResultsUnGrouped
|
---|
339 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone,
|
---|
340 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
341 |
|
---|
342 | def _isMenuMatch(self, sMenuUrl, sActionParam):
|
---|
343 | if super(WuiMain, self)._isMenuMatch(sMenuUrl, sActionParam):
|
---|
344 | fOnlyNeedingReason = self.getBoolParam(self.ksParamOnlyNeedingReason, fDefault = False);
|
---|
345 | if fOnlyNeedingReason:
|
---|
346 | return (sMenuUrl.find(self.ksParamOnlyNeedingReason) > 0);
|
---|
347 | fOnlyFailures = self.getBoolParam(self.ksParamOnlyFailures, fDefault = False);
|
---|
348 | return (sMenuUrl.find(self.ksParamOnlyFailures) > 0) == fOnlyFailures \
|
---|
349 | and sMenuUrl.find(self.ksParamOnlyNeedingReason) < 0;
|
---|
350 | return False;
|
---|
351 |
|
---|
352 |
|
---|
353 | #
|
---|
354 | # Navigation bar stuff
|
---|
355 | #
|
---|
356 |
|
---|
357 | def _generateSortBySelector(self, dParams, sPreamble, sPostamble):
|
---|
358 | """
|
---|
359 | Generate HTML code for the sort by selector.
|
---|
360 | """
|
---|
361 | from testmanager.core.testresults import TestResultLogic;
|
---|
362 |
|
---|
363 | if self.ksParamTestResultsSortBy in dParams:
|
---|
364 | enmResultSortBy = dParams[self.ksParamTestResultsSortBy];
|
---|
365 | del dParams[self.ksParamTestResultsSortBy];
|
---|
366 | else:
|
---|
367 | enmResultSortBy = TestResultLogic.ksResultsSortByRunningAndStart;
|
---|
368 |
|
---|
369 | sHtmlSortBy = '<form name="TimeForm" method="GET"> Sort by\n';
|
---|
370 | sHtmlSortBy += sPreamble;
|
---|
371 | sHtmlSortBy += '\n <select name="%s" onchange="window.location=' % (self.ksParamTestResultsSortBy,);
|
---|
372 | sHtmlSortBy += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), self.ksParamTestResultsSortBy)
|
---|
373 | sHtmlSortBy += 'this.options[this.selectedIndex].value;" title="Sorting by">\n'
|
---|
374 |
|
---|
375 | fSelected = False;
|
---|
376 | for enmCode, sTitle in TestResultLogic.kaasResultsSortByTitles:
|
---|
377 | if enmCode == enmResultSortBy:
|
---|
378 | fSelected = True;
|
---|
379 | sHtmlSortBy += ' <option value="%s"%s>%s</option>\n' \
|
---|
380 | % (enmCode, ' selected="selected"' if enmCode == enmResultSortBy else '', sTitle,);
|
---|
381 | assert fSelected;
|
---|
382 | sHtmlSortBy += ' </select>\n';
|
---|
383 | sHtmlSortBy += sPostamble;
|
---|
384 | sHtmlSortBy += '\n</form>\n'
|
---|
385 | return sHtmlSortBy;
|
---|
386 |
|
---|
387 | def _generateStatusSelector(self, dParams, fOnlyFailures):
|
---|
388 | """
|
---|
389 | Generate HTML code for the status code selector. Currently very simple.
|
---|
390 | """
|
---|
391 | dParams[self.ksParamOnlyFailures] = not fOnlyFailures;
|
---|
392 | return WuiTmLink('Show all results' if fOnlyFailures else 'Only show failed tests', '', dParams,
|
---|
393 | fBracketed = False).toHtml();
|
---|
394 |
|
---|
395 | def _generateTimeSelector(self, dParams, sPreamble, sPostamble):
|
---|
396 | """
|
---|
397 | Generate HTML code for time selector.
|
---|
398 | """
|
---|
399 |
|
---|
400 | if WuiDispatcherBase.ksParamEffectiveDate in dParams:
|
---|
401 | tsEffective = dParams[WuiDispatcherBase.ksParamEffectiveDate]
|
---|
402 | del dParams[WuiDispatcherBase.ksParamEffectiveDate]
|
---|
403 | else:
|
---|
404 | tsEffective = ''
|
---|
405 |
|
---|
406 | # Forget about page No when changing a period
|
---|
407 | if WuiDispatcherBase.ksParamPageNo in dParams:
|
---|
408 | del dParams[WuiDispatcherBase.ksParamPageNo]
|
---|
409 |
|
---|
410 | sHtmlTimeSelector = '<form name="TimeForm" method="GET">\n'
|
---|
411 | sHtmlTimeSelector += sPreamble;
|
---|
412 | sHtmlTimeSelector += '\n <select name="%s" onchange="window.location=' % WuiDispatcherBase.ksParamEffectiveDate
|
---|
413 | sHtmlTimeSelector += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), WuiDispatcherBase.ksParamEffectiveDate)
|
---|
414 | sHtmlTimeSelector += 'this.options[this.selectedIndex].value;" title="Effective date">\n'
|
---|
415 |
|
---|
416 | aoWayBackPoints = [
|
---|
417 | ('+0000-00-00 00:00:00.00', 'Now', ' title="Present Day. Present Time."'), # lain :)
|
---|
418 |
|
---|
419 | ('-0000-00-00 01:00:00.00', '1 hour ago', ''),
|
---|
420 | ('-0000-00-00 02:00:00.00', '2 hours ago', ''),
|
---|
421 | ('-0000-00-00 03:00:00.00', '3 hours ago', ''),
|
---|
422 |
|
---|
423 | ('-0000-00-01 00:00:00.00', '1 day ago', ''),
|
---|
424 | ('-0000-00-02 00:00:00.00', '2 days ago', ''),
|
---|
425 | ('-0000-00-03 00:00:00.00', '3 days ago', ''),
|
---|
426 |
|
---|
427 | ('-0000-00-07 00:00:00.00', '1 week ago', ''),
|
---|
428 | ('-0000-00-14 00:00:00.00', '2 weeks ago', ''),
|
---|
429 | ('-0000-00-21 00:00:00.00', '3 weeks ago', ''),
|
---|
430 |
|
---|
431 | ('-0000-01-00 00:00:00.00', '1 month ago', ''),
|
---|
432 | ('-0000-02-00 00:00:00.00', '2 months ago', ''),
|
---|
433 | ('-0000-03-00 00:00:00.00', '3 months ago', ''),
|
---|
434 | ('-0000-04-00 00:00:00.00', '4 months ago', ''),
|
---|
435 | ('-0000-05-00 00:00:00.00', '5 months ago', ''),
|
---|
436 | ('-0000-06-00 00:00:00.00', 'Half a year ago', ''),
|
---|
437 |
|
---|
438 | ('-0001-00-00 00:00:00.00', '1 year ago', ''),
|
---|
439 | ]
|
---|
440 | fSelected = False;
|
---|
441 | for sTimestamp, sWayBackPointCaption, sExtraAttrs in aoWayBackPoints:
|
---|
442 | if sTimestamp == tsEffective:
|
---|
443 | fSelected = True;
|
---|
444 | sHtmlTimeSelector += ' <option value="%s"%s%s>%s</option>\n' \
|
---|
445 | % (webutils.quoteUrl(sTimestamp),
|
---|
446 | ' selected="selected"' if sTimestamp == tsEffective else '',
|
---|
447 | sExtraAttrs, sWayBackPointCaption)
|
---|
448 | if not fSelected and tsEffective != '':
|
---|
449 | sHtmlTimeSelector += ' <option value="%s" selected>%s</option>\n' \
|
---|
450 | % (webutils.quoteUrl(tsEffective), tsEffective)
|
---|
451 |
|
---|
452 | sHtmlTimeSelector += ' </select>\n';
|
---|
453 | sHtmlTimeSelector += sPostamble;
|
---|
454 | sHtmlTimeSelector += '\n</form>\n'
|
---|
455 |
|
---|
456 | return sHtmlTimeSelector
|
---|
457 |
|
---|
458 | def _generateTimeWalker(self, dParams, tsEffective, sCurPeriod):
|
---|
459 | """
|
---|
460 | Generates HTML code for walking back and forth in time.
|
---|
461 | """
|
---|
462 | # Have to do some math here. :-/
|
---|
463 | if tsEffective is None:
|
---|
464 | self._oDb.execute('SELECT CURRENT_TIMESTAMP - \'' + sCurPeriod + '\'::interval');
|
---|
465 | tsNext = None;
|
---|
466 | tsPrev = self._oDb.fetchOne()[0];
|
---|
467 | else:
|
---|
468 | self._oDb.execute('SELECT %s::TIMESTAMP - \'' + sCurPeriod + '\'::interval,\n'
|
---|
469 | ' %s::TIMESTAMP + \'' + sCurPeriod + '\'::interval',
|
---|
470 | (tsEffective, tsEffective,));
|
---|
471 | tsPrev, tsNext = self._oDb.fetchOne();
|
---|
472 |
|
---|
473 | # Forget about page No when changing a period
|
---|
474 | if WuiDispatcherBase.ksParamPageNo in dParams:
|
---|
475 | del dParams[WuiDispatcherBase.ksParamPageNo]
|
---|
476 |
|
---|
477 | # Format.
|
---|
478 | dParams[WuiDispatcherBase.ksParamEffectiveDate] = str(tsPrev);
|
---|
479 | sPrev = '<a href="?%s" title="One period earlier"><<</a> ' \
|
---|
480 | % (webutils.encodeUrlParams(dParams),);
|
---|
481 |
|
---|
482 | if tsNext is not None:
|
---|
483 | dParams[WuiDispatcherBase.ksParamEffectiveDate] = str(tsNext);
|
---|
484 | sNext = ' <a href="?%s" title="One period later">>></a>' \
|
---|
485 | % (webutils.encodeUrlParams(dParams),);
|
---|
486 | else:
|
---|
487 | sNext = ' >>';
|
---|
488 |
|
---|
489 | return self._generateTimeSelector(self.getParameters(), sPrev, sNext);
|
---|
490 |
|
---|
491 | def _generateResultPeriodSelector(self, dParams, sCurPeriod):
|
---|
492 | """
|
---|
493 | Generate HTML code for result period selector.
|
---|
494 | """
|
---|
495 |
|
---|
496 | if self.ksParamEffectivePeriod in dParams:
|
---|
497 | del dParams[self.ksParamEffectivePeriod];
|
---|
498 |
|
---|
499 | # Forget about page No when changing a period
|
---|
500 | if WuiDispatcherBase.ksParamPageNo in dParams:
|
---|
501 | del dParams[WuiDispatcherBase.ksParamPageNo]
|
---|
502 |
|
---|
503 | sHtmlPeriodSelector = '<form name="PeriodForm" method="GET">\n'
|
---|
504 | sHtmlPeriodSelector += ' Period is\n'
|
---|
505 | sHtmlPeriodSelector += ' <select name="%s" onchange="window.location=' % self.ksParamEffectivePeriod
|
---|
506 | sHtmlPeriodSelector += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), self.ksParamEffectivePeriod)
|
---|
507 | sHtmlPeriodSelector += 'this.options[this.selectedIndex].value;">\n'
|
---|
508 |
|
---|
509 | for sPeriodValue, sPeriodCaption, _ in self.kaoResultPeriods:
|
---|
510 | sHtmlPeriodSelector += ' <option value="%s"%s>%s</option>\n' \
|
---|
511 | % (webutils.quoteUrl(sPeriodValue),
|
---|
512 | ' selected="selected"' if sPeriodValue == sCurPeriod else '',
|
---|
513 | sPeriodCaption)
|
---|
514 |
|
---|
515 | sHtmlPeriodSelector += ' </select>\n' \
|
---|
516 | '</form>\n'
|
---|
517 |
|
---|
518 | return sHtmlPeriodSelector
|
---|
519 |
|
---|
520 | def _generateGroupContentSelector(self, aoGroupMembers, iCurrentMember, sAltAction):
|
---|
521 | """
|
---|
522 | Generate HTML code for group content selector.
|
---|
523 | """
|
---|
524 |
|
---|
525 | dParams = self.getParameters()
|
---|
526 |
|
---|
527 | if self.ksParamGroupMemberId in dParams:
|
---|
528 | del dParams[self.ksParamGroupMemberId]
|
---|
529 |
|
---|
530 | if sAltAction is not None:
|
---|
531 | if self.ksParamAction in dParams:
|
---|
532 | del dParams[self.ksParamAction];
|
---|
533 | dParams[self.ksParamAction] = sAltAction;
|
---|
534 |
|
---|
535 | sHtmlSelector = '<form name="GroupContentForm" method="GET">\n'
|
---|
536 | sHtmlSelector += ' <select name="%s" onchange="window.location=' % self.ksParamGroupMemberId
|
---|
537 | sHtmlSelector += '\'?%s&%s=\' + ' % (webutils.encodeUrlParams(dParams), self.ksParamGroupMemberId)
|
---|
538 | sHtmlSelector += 'this.options[this.selectedIndex].value;">\n'
|
---|
539 |
|
---|
540 | sHtmlSelector += '<option value="-1">All</option>\n'
|
---|
541 |
|
---|
542 | for iGroupMemberId, sGroupMemberName in aoGroupMembers:
|
---|
543 | if iGroupMemberId is not None:
|
---|
544 | sHtmlSelector += ' <option value="%s"%s>%s</option>\n' \
|
---|
545 | % (iGroupMemberId,
|
---|
546 | ' selected="selected"' if iGroupMemberId == iCurrentMember else '',
|
---|
547 | sGroupMemberName)
|
---|
548 |
|
---|
549 | sHtmlSelector += ' </select>\n' \
|
---|
550 | '</form>\n'
|
---|
551 |
|
---|
552 | return sHtmlSelector
|
---|
553 |
|
---|
554 | def _generatePagesSelector(self, dParams, cItems, cItemsPerPage, iPage):
|
---|
555 | """
|
---|
556 | Generate HTML code for pages (1, 2, 3 ... N) selector
|
---|
557 | """
|
---|
558 |
|
---|
559 | if WuiDispatcherBase.ksParamPageNo in dParams:
|
---|
560 | del dParams[WuiDispatcherBase.ksParamPageNo]
|
---|
561 |
|
---|
562 | sHrefPtr = '<a href="?%s&%s=' % (webutils.encodeUrlParams(dParams).replace('%', '%%'),
|
---|
563 | WuiDispatcherBase.ksParamPageNo)
|
---|
564 | sHrefPtr += '%d">%s</a>'
|
---|
565 |
|
---|
566 | cNumOfPages = (cItems + cItemsPerPage - 1) / cItemsPerPage;
|
---|
567 | cPagesToDisplay = 10
|
---|
568 | cPagesRangeStart = iPage - cPagesToDisplay / 2 \
|
---|
569 | if not iPage - cPagesToDisplay / 2 < 0 else 0
|
---|
570 | cPagesRangeEnd = cPagesRangeStart + cPagesToDisplay \
|
---|
571 | if not cPagesRangeStart + cPagesToDisplay > cNumOfPages else cNumOfPages
|
---|
572 | # Adjust pages range
|
---|
573 | if cNumOfPages < cPagesToDisplay:
|
---|
574 | cPagesRangeStart = 0
|
---|
575 | cPagesRangeEnd = cNumOfPages
|
---|
576 |
|
---|
577 | # 1 2 3 4...
|
---|
578 | sHtmlPager = ' \n'.join(sHrefPtr % (x, str(x + 1)) if x != iPage else str(x + 1)
|
---|
579 | for x in range(cPagesRangeStart, cPagesRangeEnd))
|
---|
580 | if cPagesRangeStart > 0:
|
---|
581 | sHtmlPager = '%s ... \n' % (sHrefPtr % (0, str(1))) + sHtmlPager
|
---|
582 | if cPagesRangeEnd < cNumOfPages:
|
---|
583 | sHtmlPager += ' ... %s\n' % (sHrefPtr % (cNumOfPages, str(cNumOfPages + 1)))
|
---|
584 |
|
---|
585 | # Prev/Next (using << >> because « and » are too tiny).
|
---|
586 | if iPage > 0:
|
---|
587 | dParams[WuiDispatcherBase.ksParamPageNo] = iPage - 1
|
---|
588 | sHtmlPager = ('<a title="Previous page" href="?%s"><<</a> \n'
|
---|
589 | % (webutils.encodeUrlParams(dParams), )) \
|
---|
590 | + sHtmlPager;
|
---|
591 | else:
|
---|
592 | sHtmlPager = '<< \n' + sHtmlPager
|
---|
593 |
|
---|
594 | if iPage + 1 < cNumOfPages:
|
---|
595 | dParams[WuiDispatcherBase.ksParamPageNo] = iPage + 1
|
---|
596 | sHtmlPager += '\n <a title="Next page" href="?%s">>></a>\n' % (webutils.encodeUrlParams(dParams),)
|
---|
597 | else:
|
---|
598 | sHtmlPager += '\n >>\n'
|
---|
599 |
|
---|
600 | return sHtmlPager
|
---|
601 |
|
---|
602 | def _generateItemPerPageSelector(self, dParams, cItemsPerPage):
|
---|
603 | """
|
---|
604 | Generate HTML code for items per page selector
|
---|
605 | """
|
---|
606 |
|
---|
607 | if WuiDispatcherBase.ksParamItemsPerPage in dParams:
|
---|
608 | del dParams[WuiDispatcherBase.ksParamItemsPerPage]
|
---|
609 |
|
---|
610 | # Forced reset of the page number
|
---|
611 | dParams[WuiDispatcherBase.ksParamPageNo] = 0
|
---|
612 | sHtmlItemsPerPageSelector = '<form name="AgesPerPageForm" method="GET">\n' \
|
---|
613 | ' Max <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
|
---|
614 | 'this.options[this.selectedIndex].value;" title="Max items per page">\n' \
|
---|
615 | % (WuiDispatcherBase.ksParamItemsPerPage,
|
---|
616 | webutils.encodeUrlParams(dParams),
|
---|
617 | WuiDispatcherBase.ksParamItemsPerPage)
|
---|
618 |
|
---|
619 | aiItemsPerPage = [16, 32, 64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096];
|
---|
620 | for iItemsPerPage in aiItemsPerPage:
|
---|
621 | sHtmlItemsPerPageSelector += ' <option value="%d" %s>%d</option>\n' \
|
---|
622 | % (iItemsPerPage,
|
---|
623 | 'selected="selected"' if iItemsPerPage == cItemsPerPage else '',
|
---|
624 | iItemsPerPage)
|
---|
625 | sHtmlItemsPerPageSelector += ' </select> items per page\n' \
|
---|
626 | '</form>\n'
|
---|
627 |
|
---|
628 | return sHtmlItemsPerPageSelector
|
---|
629 |
|
---|
630 | def _generateResultNavigation(self, cItems, cItemsPerPage, iPage, tsEffective, sCurPeriod, fOnlyFailures,
|
---|
631 | sHtmlMemberSelector):
|
---|
632 | """ Make custom time navigation bar for the results. """
|
---|
633 |
|
---|
634 | # Generate the elements.
|
---|
635 | sHtmlStatusSelector = self._generateStatusSelector(self.getParameters(), fOnlyFailures);
|
---|
636 | sHtmlSortBySelector = self._generateSortBySelector(self.getParameters(), '', sHtmlStatusSelector);
|
---|
637 | sHtmlPeriodSelector = self._generateResultPeriodSelector(self.getParameters(), sCurPeriod)
|
---|
638 | sHtmlTimeWalker = self._generateTimeWalker(self.getParameters(), tsEffective, sCurPeriod);
|
---|
639 |
|
---|
640 | if cItems > 0:
|
---|
641 | sHtmlPager = self._generatePagesSelector(self.getParameters(), cItems, cItemsPerPage, iPage)
|
---|
642 | sHtmlItemsPerPageSelector = self._generateItemPerPageSelector(self.getParameters(), cItemsPerPage)
|
---|
643 | else:
|
---|
644 | sHtmlPager = ''
|
---|
645 | sHtmlItemsPerPageSelector = ''
|
---|
646 |
|
---|
647 | # Generate navigation bar
|
---|
648 | sHtml = '<table width=100%>\n' \
|
---|
649 | '<tr>\n' \
|
---|
650 | ' <td width=30%>' + sHtmlMemberSelector + '</td>\n' \
|
---|
651 | ' <td width=40% align=center>' + sHtmlTimeWalker + '</td>' \
|
---|
652 | ' <td width=30% align=right>\n' + sHtmlPeriodSelector + '</td>\n' \
|
---|
653 | '</tr>\n' \
|
---|
654 | '<tr>\n' \
|
---|
655 | ' <td width=30%>' + sHtmlSortBySelector + '</td>\n' \
|
---|
656 | ' <td width=40% align=center>\n' + sHtmlPager + '</td>\n' \
|
---|
657 | ' <td width=30% align=right>\n' + sHtmlItemsPerPageSelector + '</td>\n'\
|
---|
658 | '</tr>\n' \
|
---|
659 | '</table>\n'
|
---|
660 |
|
---|
661 | return sHtml
|
---|
662 |
|
---|
663 | def _generateReportNavigation(self, tsEffective, cHoursPerPeriod, cPeriods):
|
---|
664 | """ Make time navigation bar for the reports. """
|
---|
665 |
|
---|
666 | # The period length selector.
|
---|
667 | dParams = self.getParameters();
|
---|
668 | if WuiMain.ksParamReportPeriodInHours in dParams:
|
---|
669 | del dParams[WuiMain.ksParamReportPeriodInHours];
|
---|
670 | sHtmlPeriodLength = '';
|
---|
671 | sHtmlPeriodLength += '<form name="ReportPeriodInHoursForm" method="GET">\n' \
|
---|
672 | ' Period length <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
|
---|
673 | 'this.options[this.selectedIndex].value;" title="Statistics period length in hours.">\n' \
|
---|
674 | % (WuiMain.ksParamReportPeriodInHours,
|
---|
675 | webutils.encodeUrlParams(dParams),
|
---|
676 | WuiMain.ksParamReportPeriodInHours)
|
---|
677 | for cHours in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 18, 24, 48, 72, 96, 120, 144, 168 ]:
|
---|
678 | sHtmlPeriodLength += ' <option value="%d"%s>%d hour%s</option>\n' \
|
---|
679 | % (cHours, 'selected="selected"' if cHours == cHoursPerPeriod else '', cHours,
|
---|
680 | 's' if cHours > 1 else '');
|
---|
681 | sHtmlPeriodLength += ' </select>\n' \
|
---|
682 | '</form>\n'
|
---|
683 |
|
---|
684 | # The period count selector.
|
---|
685 | dParams = self.getParameters();
|
---|
686 | if WuiMain.ksParamReportPeriods in dParams:
|
---|
687 | del dParams[WuiMain.ksParamReportPeriods];
|
---|
688 | sHtmlCountOfPeriods = '';
|
---|
689 | sHtmlCountOfPeriods += '<form name="ReportPeriodsForm" method="GET">\n' \
|
---|
690 | ' Periods <select name="%s" onchange="window.location=\'?%s&%s=\' + ' \
|
---|
691 | 'this.options[this.selectedIndex].value;" title="Statistics periods to report.">\n' \
|
---|
692 | % (WuiMain.ksParamReportPeriods,
|
---|
693 | webutils.encodeUrlParams(dParams),
|
---|
694 | WuiMain.ksParamReportPeriods)
|
---|
695 | for cCurPeriods in range(2, 43):
|
---|
696 | sHtmlCountOfPeriods += ' <option value="%d"%s>%d</option>\n' \
|
---|
697 | % (cCurPeriods, 'selected="selected"' if cCurPeriods == cPeriods else '', cCurPeriods);
|
---|
698 | sHtmlCountOfPeriods += ' </select>\n' \
|
---|
699 | '</form>\n'
|
---|
700 |
|
---|
701 | # The time walker.
|
---|
702 | sHtmlTimeWalker = self._generateTimeWalker(self.getParameters(), tsEffective, '%d hours' % (cHoursPerPeriod));
|
---|
703 |
|
---|
704 | # Combine them all.
|
---|
705 | sHtml = '<table width=100%>\n' \
|
---|
706 | ' <tr>\n' \
|
---|
707 | ' <td width=30% align="center">\n' + sHtmlPeriodLength + '</td>\n' \
|
---|
708 | ' <td width=40% align="center">\n' + sHtmlTimeWalker + '</td>' \
|
---|
709 | ' <td width=30% align="center">\n' + sHtmlCountOfPeriods + '</td>\n' \
|
---|
710 | ' </tr>\n' \
|
---|
711 | '</table>\n';
|
---|
712 | return sHtml;
|
---|
713 |
|
---|
714 |
|
---|
715 | #
|
---|
716 | # The rest of stuff
|
---|
717 | #
|
---|
718 |
|
---|
719 | def _actionGroupedResultsListing( #pylint: disable=R0914
|
---|
720 | self,
|
---|
721 | enmResultsGroupingType,
|
---|
722 | oResultsLogicType,
|
---|
723 | oResultFilterType,
|
---|
724 | oResultsListContentType):
|
---|
725 | """
|
---|
726 | Override generic listing action.
|
---|
727 |
|
---|
728 | oResultsLogicType implements getEntriesCount, fetchResultsForListing and more.
|
---|
729 | oResultFilterType is a child of ModelFilterBase.
|
---|
730 | oResultsListContentType is a child of WuiListContentBase.
|
---|
731 | """
|
---|
732 | from testmanager.core.testresults import TestResultLogic;
|
---|
733 |
|
---|
734 | cItemsPerPage = self.getIntParam(self.ksParamItemsPerPage, iMin = 2, iMax = 9999, iDefault = 128);
|
---|
735 | iPage = self.getIntParam(self.ksParamPageNo, iMin = 0, iMax = 999999, iDefault = 0);
|
---|
736 | tsEffective = self.getEffectiveDateParam();
|
---|
737 | iGroupMemberId = self.getIntParam(self.ksParamGroupMemberId, iMin = -1, iMax = 999999, iDefault = -1);
|
---|
738 | fOnlyFailures = self.getBoolParam(self.ksParamOnlyFailures, fDefault = False);
|
---|
739 | fOnlyNeedingReason = self.getBoolParam(self.ksParamOnlyNeedingReason, fDefault = False);
|
---|
740 | enmResultSortBy = self.getStringParam(self.ksParamTestResultsSortBy,
|
---|
741 | asValidValues = TestResultLogic.kasResultsSortBy,
|
---|
742 | sDefault = TestResultLogic.ksResultsSortByRunningAndStart);
|
---|
743 | oFilter = oResultFilterType().initFromParams(self);
|
---|
744 |
|
---|
745 | # Get testing results period and validate it
|
---|
746 | asValidValues = [x for (x, _, _) in self.kaoResultPeriods]
|
---|
747 | sCurPeriod = self.getStringParam(self.ksParamEffectivePeriod, asValidValues = asValidValues,
|
---|
748 | sDefault = self.ksResultPeriodDefault)
|
---|
749 | assert sCurPeriod != ''; # Impossible!
|
---|
750 |
|
---|
751 | self._checkForUnknownParameters()
|
---|
752 |
|
---|
753 | #
|
---|
754 | # Fetch the group members.
|
---|
755 | #
|
---|
756 | # If no grouping is selected, we'll fill the grouping combo with
|
---|
757 | # testboxes just to avoid having completely useless combo box.
|
---|
758 | #
|
---|
759 | oTrLogic = TestResultLogic(self._oDb);
|
---|
760 | sAltSelectorAction = None;
|
---|
761 | if enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeNone \
|
---|
762 | or enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestBox:
|
---|
763 | aoTmp = oTrLogic.getTestBoxes(tsNow = tsEffective, sPeriod = sCurPeriod)
|
---|
764 | aoGroupMembers = sorted(list(set([ (x.idTestBox, '%s (%s)' % (x.sName, str(x.ip))) for x in aoTmp ])),
|
---|
765 | reverse = False, key = lambda asData: asData[1])
|
---|
766 |
|
---|
767 | if enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestBox:
|
---|
768 | self._sPageTitle = 'Grouped by Test Box';
|
---|
769 | else:
|
---|
770 | self._sPageTitle = 'Ungrouped results';
|
---|
771 | sAltSelectorAction = self.ksActionResultsGroupedByTestBox;
|
---|
772 | aoGroupMembers.insert(0, [None, None]); # The "All" member.
|
---|
773 |
|
---|
774 | elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestGroup:
|
---|
775 | aoTmp = oTrLogic.getTestGroups(tsNow = tsEffective, sPeriod = sCurPeriod);
|
---|
776 | aoGroupMembers = sorted(list(set([ (x.idTestGroup, x.sName ) for x in aoTmp ])),
|
---|
777 | reverse = False, key = lambda asData: asData[1])
|
---|
778 | self._sPageTitle = 'Grouped by Test Group'
|
---|
779 |
|
---|
780 | elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildRev:
|
---|
781 | aoTmp = oTrLogic.getBuilds(tsNow = tsEffective, sPeriod = sCurPeriod)
|
---|
782 | aoGroupMembers = sorted(list(set([ (x.iRevision, '%s.%d' % (x.oCat.sBranch, x.iRevision)) for x in aoTmp ])),
|
---|
783 | reverse = True, key = lambda asData: asData[0])
|
---|
784 | self._sPageTitle = 'Grouped by Build'
|
---|
785 |
|
---|
786 | elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildCat:
|
---|
787 | aoTmp = oTrLogic.getBuildCategories(tsNow = tsEffective, sPeriod = sCurPeriod)
|
---|
788 | aoGroupMembers = sorted(list(set([ ( x.idBuildCategory, '%s / %s / %s / %s'
|
---|
789 | % ( x.sProduct, x.sBranch, ', '.join(x.asOsArches), x.sType) )
|
---|
790 | for x in aoTmp ])),
|
---|
791 | reverse = True, key = lambda asData: asData[1]);
|
---|
792 | self._sPageTitle = 'Grouped by Build Category'
|
---|
793 |
|
---|
794 | elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestCase:
|
---|
795 | aoTmp = oTrLogic.getTestCases(tsNow = tsEffective, sPeriod = sCurPeriod)
|
---|
796 | aoGroupMembers = sorted(list(set([ (x.idTestCase, '%s' % x.sName) for x in aoTmp ])),
|
---|
797 | reverse = False, key = lambda asData: asData[1])
|
---|
798 | self._sPageTitle = 'Grouped by Test Case'
|
---|
799 |
|
---|
800 | elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeOS:
|
---|
801 | aoTmp = oTrLogic.getOSes(tsNow = tsEffective, sPeriod = sCurPeriod)
|
---|
802 | aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]);
|
---|
803 | self._sPageTitle = 'Grouped by OS'
|
---|
804 |
|
---|
805 | elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeArch:
|
---|
806 | aoTmp = oTrLogic.getArchitectures(tsNow = tsEffective, sPeriod = sCurPeriod)
|
---|
807 | aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]);
|
---|
808 | self._sPageTitle = 'Grouped by Architecture'
|
---|
809 |
|
---|
810 | elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeSchedGroup:
|
---|
811 | aoTmp = oTrLogic.getSchedGroups(tsNow = tsEffective, sPeriod = sCurPeriod)
|
---|
812 | aoGroupMembers = sorted(list(set([ (x.idSchedGroup, '%s' % x.sName) for x in aoTmp ])),
|
---|
813 | reverse = False, key = lambda asData: asData[1])
|
---|
814 | self._sPageTitle = 'Grouped by Scheduling Group'
|
---|
815 |
|
---|
816 | else:
|
---|
817 | raise TMExceptionBase('Unknown grouping type')
|
---|
818 |
|
---|
819 | _sPageBody = ''
|
---|
820 | oContent = None
|
---|
821 | cEntriesMax = 0
|
---|
822 | _dParams = self.getParameters()
|
---|
823 | oResultLogic = oResultsLogicType(self._oDb);
|
---|
824 | for idMember, sMemberName in aoGroupMembers:
|
---|
825 | #
|
---|
826 | # Count and fetch entries to be displayed.
|
---|
827 | #
|
---|
828 |
|
---|
829 | # Skip group members that were not specified.
|
---|
830 | if idMember != iGroupMemberId \
|
---|
831 | and ( (idMember is not None and enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeNone)
|
---|
832 | or (iGroupMemberId > 0 and enmResultsGroupingType != TestResultLogic.ksResultsGroupingTypeNone) ):
|
---|
833 | continue
|
---|
834 |
|
---|
835 | cEntries = oResultLogic.getEntriesCount(tsNow = tsEffective,
|
---|
836 | sInterval = sCurPeriod,
|
---|
837 | oFilter = oFilter,
|
---|
838 | enmResultsGroupingType = enmResultsGroupingType,
|
---|
839 | iResultsGroupingValue = idMember,
|
---|
840 | fOnlyFailures = fOnlyFailures,
|
---|
841 | fOnlyNeedingReason = fOnlyNeedingReason);
|
---|
842 | if cEntries == 0: # Do not display empty groups
|
---|
843 | continue
|
---|
844 | aoEntries = oResultLogic.fetchResultsForListing(iPage * cItemsPerPage,
|
---|
845 | cItemsPerPage,
|
---|
846 | tsNow = tsEffective,
|
---|
847 | sInterval = sCurPeriod,
|
---|
848 | oFilter = oFilter,
|
---|
849 | enmResultSortBy = enmResultSortBy,
|
---|
850 | enmResultsGroupingType = enmResultsGroupingType,
|
---|
851 | iResultsGroupingValue = idMember,
|
---|
852 | fOnlyFailures = fOnlyFailures,
|
---|
853 | fOnlyNeedingReason = fOnlyNeedingReason);
|
---|
854 | cEntriesMax = max(cEntriesMax, cEntries)
|
---|
855 |
|
---|
856 | #
|
---|
857 | # Format them.
|
---|
858 | #
|
---|
859 | oContent = oResultsListContentType(aoEntries,
|
---|
860 | cEntries,
|
---|
861 | iPage,
|
---|
862 | cItemsPerPage,
|
---|
863 | tsEffective,
|
---|
864 | fnDPrint = self._oSrvGlue.dprint,
|
---|
865 | oDisp = self)
|
---|
866 |
|
---|
867 | (_, sHtml) = oContent.show(fShowNavigation = False)
|
---|
868 | if sMemberName is not None:
|
---|
869 | _sPageBody += '<table width=100%><tr><td>'
|
---|
870 |
|
---|
871 | _dParams[self.ksParamGroupMemberId] = idMember
|
---|
872 | sLink = WuiTmLink(sMemberName, '', _dParams, fBracketed = False).toHtml()
|
---|
873 |
|
---|
874 | _sPageBody += '<h2>%s (%d)</h2></td>' % (sLink, cEntries)
|
---|
875 | _sPageBody += '<td><br></td>'
|
---|
876 | _sPageBody += '</tr></table>'
|
---|
877 | _sPageBody += sHtml
|
---|
878 | _sPageBody += '<br>'
|
---|
879 |
|
---|
880 | #
|
---|
881 | # Complete the page by slapping navigation controls at the top and
|
---|
882 | # bottom of it.
|
---|
883 | #
|
---|
884 | sHtmlNavigation = self._generateResultNavigation(cEntriesMax, cItemsPerPage, iPage,
|
---|
885 | tsEffective, sCurPeriod, fOnlyFailures,
|
---|
886 | self._generateGroupContentSelector(aoGroupMembers, iGroupMemberId,
|
---|
887 | sAltSelectorAction));
|
---|
888 | if cEntriesMax > 0:
|
---|
889 | self._sPageBody = sHtmlNavigation + _sPageBody + sHtmlNavigation;
|
---|
890 | else:
|
---|
891 | self._sPageBody = sHtmlNavigation + '<p align="center"><i>No data to display</i></p>\n';
|
---|
892 |
|
---|
893 | #
|
---|
894 | # Now, generate a filter control panel for the side bar.
|
---|
895 | #
|
---|
896 | if hasattr(oFilter, 'kiBranches'):
|
---|
897 | oFilter.aCriteria[oFilter.kiBranches].fExpanded = True;
|
---|
898 | if hasattr(oFilter, 'kiTestStatus'):
|
---|
899 | oFilter.aCriteria[oFilter.kiTestStatus].fExpanded = True;
|
---|
900 | self._sPageFilter = self._generateResultFilter(oFilter, oResultLogic, tsEffective, sCurPeriod,
|
---|
901 | enmResultsGroupingType = enmResultsGroupingType,
|
---|
902 | aoGroupMembers = aoGroupMembers,
|
---|
903 | fOnlyFailures = fOnlyFailures,
|
---|
904 | fOnlyNeedingReason = fOnlyNeedingReason);
|
---|
905 | return True;
|
---|
906 |
|
---|
907 | def _generateResultFilter(self, oFilter, oResultLogic, tsNow, sPeriod, enmResultsGroupingType = None, aoGroupMembers = None,
|
---|
908 | fOnlyFailures = False, fOnlyNeedingReason = False):
|
---|
909 | """
|
---|
910 | Generates the result filter for the left hand side.
|
---|
911 | """
|
---|
912 | _ = enmResultsGroupingType; _ = aoGroupMembers; _ = fOnlyFailures; _ = fOnlyNeedingReason;
|
---|
913 | oResultLogic.fetchPossibleFilterOptions(oFilter, tsNow, sPeriod)
|
---|
914 |
|
---|
915 | # Add non-filter parameters as hidden fields so we can use 'GET' and have URLs to bookmark.
|
---|
916 | self._dSideMenuFormAttrs['method'] = 'GET';
|
---|
917 | sHtml = u'';
|
---|
918 | for sKey, oValue in self._oSrvGlue.getParameters().iteritems():
|
---|
919 | if len(sKey) > 3:
|
---|
920 | if hasattr(oValue, 'startswith'):
|
---|
921 | sHtml += u'<input type="hidden" name="%s" value="%s"/>\n' \
|
---|
922 | % (webutils.escapeAttr(sKey), webutils.escapeAttr(oValue),);
|
---|
923 | else:
|
---|
924 | for oSubValue in oValue:
|
---|
925 | sHtml += u'<input type="hidden" name="%s" value="%s"/>\n' \
|
---|
926 | % (webutils.escapeAttr(sKey), webutils.escapeAttr(oSubValue),);
|
---|
927 |
|
---|
928 | # Generate the filter panel.
|
---|
929 | sHtml += u'<div id="side-filters">\n' \
|
---|
930 | u' <p>Filters</p>\n' \
|
---|
931 | u' <dl>\n';
|
---|
932 |
|
---|
933 | for oCrit in oFilter.aCriteria:
|
---|
934 | if len(oCrit.aoPossible) > 0:
|
---|
935 | if ( oCrit.oSub is None \
|
---|
936 | and ( oCrit.sState == oCrit.ksState_Selected \
|
---|
937 | or len(oCrit.aoPossible) <= 2)) \
|
---|
938 | or ( oCrit.oSub is not None \
|
---|
939 | and ( oCrit.sState == oCrit.ksState_Selected \
|
---|
940 | or oCrit.oSub.sState == oCrit.ksState_Selected \
|
---|
941 | or (len(oCrit.aoPossible) <= 2 and len(oCrit.oSub.aoPossible) <= 2))) \
|
---|
942 | or oCrit.fExpanded is True:
|
---|
943 | sClass = 'sf-collapsible';
|
---|
944 | sChar = '▼';
|
---|
945 | else:
|
---|
946 | sClass = 'sf-expandable';
|
---|
947 | sChar = '▶';
|
---|
948 |
|
---|
949 | sHtml += u' <dt class="%s"><a href="javascript:void(0)" onclick="toggleCollapsibleDtDd(this);">%s'\
|
---|
950 | u' %s</a></dt>\n' \
|
---|
951 | u' <dd class="%s">\n' \
|
---|
952 | u' <ul>\n' \
|
---|
953 | % (sClass, sChar, webutils.escapeElem(oCrit.sName), sClass);
|
---|
954 |
|
---|
955 | for oDesc in oCrit.aoPossible:
|
---|
956 | fChecked = oDesc.oValue in oCrit.aoSelected;
|
---|
957 | sHtml += u' <li%s%s><label><input type="checkbox" name="%s" value="%s"%s%s/>%s%s</label>\n' \
|
---|
958 | % ( ' class="side-filter-irrelevant"' if oDesc.fIrrelevant else '',
|
---|
959 | (' title="%s"' % (webutils.escapeAttr(oDesc.sHover,)) if oDesc.sHover is not None else ''),
|
---|
960 | oCrit.sVarNm,
|
---|
961 | oDesc.oValue,
|
---|
962 | ' checked' if fChecked else '',
|
---|
963 | ' onclick="toggleCollapsibleCheckbox(this);"' if oDesc.aoSubs is not None else '',
|
---|
964 | webutils.escapeElem(oDesc.sDesc),
|
---|
965 | '<span class="side-filter-count"> [%u]</span>' % (oDesc.cTimes) if oDesc.cTimes is not None
|
---|
966 | else '', );
|
---|
967 | if oDesc.aoSubs is not None:
|
---|
968 | sHtml += u' <ul class="sf-checkbox-%s">\n' % ('collapsible' if fChecked else 'expandable', );
|
---|
969 | for oSubDesc in oDesc.aoSubs:
|
---|
970 | fSubChecked = oSubDesc.oValue in oCrit.oSub.aoSelected;
|
---|
971 | sHtml += u' <li%s%s><label><input type="checkbox" name="%s" value="%s"%s/>%s%s</label>\n' \
|
---|
972 | % ( ' class="side-filter-irrelevant"' if oSubDesc.fIrrelevant else '',
|
---|
973 | ' title="%s"' % ( webutils.escapeAttr(oSubDesc.sHover,) if oSubDesc.sHover is not None
|
---|
974 | else ''),
|
---|
975 | oCrit.oSub.sVarNm, oSubDesc.oValue, ' checked' if fSubChecked else '',
|
---|
976 | webutils.escapeElem(oSubDesc.sDesc),
|
---|
977 | '<span class="side-filter-count"> [%u]</span>' % (oSubDesc.cTimes)
|
---|
978 | if oSubDesc.cTimes is not None else '', );
|
---|
979 |
|
---|
980 | sHtml += u' </ul>\n';
|
---|
981 | sHtml += u' </li>';
|
---|
982 | sHtml += u' </ul>\n' \
|
---|
983 | u' </dd>\n';
|
---|
984 |
|
---|
985 | sHtml += u' </dl>\n';
|
---|
986 | sHtml += u' <input type="submit" value="Apply"/>\n';
|
---|
987 | sHtml += u' <input type="reset" value="Reset"/>\n';
|
---|
988 | sHtml += u'</div>\n';
|
---|
989 | return sHtml;
|
---|
990 |
|
---|
991 | def _actionResultsUnGrouped(self):
|
---|
992 | """ Action wrapper. """
|
---|
993 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
994 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
995 | #return self._actionResultsListing(TestResultLogic, WuiGroupedResultList)?
|
---|
996 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone,
|
---|
997 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
998 |
|
---|
999 | def _actionResultsGroupedByTestGroup(self):
|
---|
1000 | """ Action wrapper. """
|
---|
1001 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1002 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1003 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestGroup,
|
---|
1004 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1005 |
|
---|
1006 | def _actionResultsGroupedByBuildRev(self):
|
---|
1007 | """ Action wrapper. """
|
---|
1008 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1009 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1010 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildRev,
|
---|
1011 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1012 |
|
---|
1013 | def _actionResultsGroupedByBuildCat(self):
|
---|
1014 | """ Action wrapper. """
|
---|
1015 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1016 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1017 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildCat,
|
---|
1018 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1019 |
|
---|
1020 | def _actionResultsGroupedByTestBox(self):
|
---|
1021 | """ Action wrapper. """
|
---|
1022 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1023 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1024 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestBox,
|
---|
1025 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1026 |
|
---|
1027 | def _actionResultsGroupedByTestCase(self):
|
---|
1028 | """ Action wrapper. """
|
---|
1029 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1030 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1031 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestCase,
|
---|
1032 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1033 |
|
---|
1034 | def _actionResultsGroupedByOS(self):
|
---|
1035 | """ Action wrapper. """
|
---|
1036 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1037 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1038 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeOS,
|
---|
1039 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1040 |
|
---|
1041 | def _actionResultsGroupedByArch(self):
|
---|
1042 | """ Action wrapper. """
|
---|
1043 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1044 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1045 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeArch,
|
---|
1046 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1047 |
|
---|
1048 | def _actionResultsGroupedBySchedGroup(self):
|
---|
1049 | """ Action wrapper. """
|
---|
1050 | from testmanager.webui.wuitestresult import WuiGroupedResultList;
|
---|
1051 | from testmanager.core.testresults import TestResultLogic, TestResultFilter;
|
---|
1052 | return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeSchedGroup,
|
---|
1053 | TestResultLogic, TestResultFilter, WuiGroupedResultList);
|
---|
1054 |
|
---|
1055 |
|
---|
1056 | def _actionTestSetDetailsCommon(self, idTestSet):
|
---|
1057 | """Show test case execution result details."""
|
---|
1058 | from testmanager.core.build import BuildDataEx;
|
---|
1059 | from testmanager.core.testbox import TestBoxData;
|
---|
1060 | from testmanager.core.testcase import TestCaseDataEx;
|
---|
1061 | from testmanager.core.testcaseargs import TestCaseArgsDataEx;
|
---|
1062 | from testmanager.core.testgroup import TestGroupData;
|
---|
1063 | from testmanager.core.testresults import TestResultLogic;
|
---|
1064 | from testmanager.core.testset import TestSetData;
|
---|
1065 | from testmanager.webui.wuitestresult import WuiTestResult;
|
---|
1066 |
|
---|
1067 | self._sTemplate = 'template-details.html';
|
---|
1068 | self._checkForUnknownParameters()
|
---|
1069 |
|
---|
1070 | oTestSetData = TestSetData().initFromDbWithId(self._oDb, idTestSet);
|
---|
1071 | try:
|
---|
1072 | (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet);
|
---|
1073 | except TMTooManyRows:
|
---|
1074 | (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet, 2);
|
---|
1075 | oBuildDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuild, oTestSetData.tsCreated);
|
---|
1076 | try: oBuildValidationKitDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuildTestSuite,
|
---|
1077 | oTestSetData.tsCreated);
|
---|
1078 | except: oBuildValidationKitDataEx = None;
|
---|
1079 | oTestBoxData = TestBoxData().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestBox);
|
---|
1080 | oTestGroupData = TestGroupData().initFromDbWithId(self._oDb, ## @todo This bogus time wise. Bad DB design?
|
---|
1081 | oTestSetData.idTestGroup, oTestSetData.tsCreated);
|
---|
1082 | oTestCaseDataEx = TestCaseDataEx().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestCase,
|
---|
1083 | oTestSetData.tsConfig);
|
---|
1084 | oTestCaseArgsDataEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(self._oDb, oTestSetData.idGenTestCaseArgs,
|
---|
1085 | oTestSetData.tsConfig);
|
---|
1086 |
|
---|
1087 | oContent = WuiTestResult(oDisp = self, fnDPrint = self._oSrvGlue.dprint);
|
---|
1088 | (self._sPageTitle, self._sPageBody) = oContent.showTestCaseResultDetails(oTestResultTree,
|
---|
1089 | oTestSetData,
|
---|
1090 | oBuildDataEx,
|
---|
1091 | oBuildValidationKitDataEx,
|
---|
1092 | oTestBoxData,
|
---|
1093 | oTestGroupData,
|
---|
1094 | oTestCaseDataEx,
|
---|
1095 | oTestCaseArgsDataEx);
|
---|
1096 | return True
|
---|
1097 |
|
---|
1098 | def _actionTestSetDetails(self):
|
---|
1099 | """Show test case execution result details."""
|
---|
1100 | from testmanager.core.testset import TestSetData;
|
---|
1101 |
|
---|
1102 | idTestSet = self.getIntParam(TestSetData.ksParam_idTestSet);
|
---|
1103 | return self._actionTestSetDetailsCommon(idTestSet);
|
---|
1104 |
|
---|
1105 | def _actionTestSetDetailsFromResult(self):
|
---|
1106 | """Show test case execution result details."""
|
---|
1107 | from testmanager.core.testresults import TestResultData;
|
---|
1108 | from testmanager.core.testset import TestSetData;
|
---|
1109 | idTestResult = self.getIntParam(TestSetData.ksParam_idTestResult);
|
---|
1110 | oTestResultData = TestResultData().initFromDbWithId(self._oDb, idTestResult);
|
---|
1111 | return self._actionTestSetDetailsCommon(oTestResultData.idTestSet);
|
---|
1112 |
|
---|
1113 |
|
---|
1114 | def _actionTestResultFailureAdd(self):
|
---|
1115 | """ Pro forma. """
|
---|
1116 | from testmanager.core.testresultfailures import TestResultFailureData;
|
---|
1117 | from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
|
---|
1118 | return self._actionGenericFormAdd(TestResultFailureData, WuiTestResultFailure);
|
---|
1119 |
|
---|
1120 | def _actionTestResultFailureAddPost(self):
|
---|
1121 | """Add test result failure result"""
|
---|
1122 | from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
|
---|
1123 | from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
|
---|
1124 | if self.ksParamRedirectTo not in self._dParams:
|
---|
1125 | raise WuiException('Missing parameter ' + self.ksParamRedirectTo);
|
---|
1126 |
|
---|
1127 | return self._actionGenericFormAddPost(TestResultFailureData, TestResultFailureLogic,
|
---|
1128 | WuiTestResultFailure, self.ksActionResultsUnGrouped);
|
---|
1129 |
|
---|
1130 | def _actionTestResultFailureDoRemove(self):
|
---|
1131 | """ Action wrapper. """
|
---|
1132 | from testmanager.core.testresultfailures import TestResultFailureData, TestResultFailureLogic;
|
---|
1133 | return self._actionGenericDoRemove(TestResultFailureLogic, TestResultFailureData.ksParam_idTestResult,
|
---|
1134 | self.ksActionResultsUnGrouped);
|
---|
1135 |
|
---|
1136 | def _actionTestResultFailureDetails(self):
|
---|
1137 | """ Pro forma. """
|
---|
1138 | from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
|
---|
1139 | from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
|
---|
1140 | return self._actionGenericFormDetails(TestResultFailureData, TestResultFailureLogic,
|
---|
1141 | WuiTestResultFailure, 'idTestResult');
|
---|
1142 |
|
---|
1143 | def _actionTestResultFailureEdit(self):
|
---|
1144 | """ Pro forma. """
|
---|
1145 | from testmanager.core.testresultfailures import TestResultFailureData;
|
---|
1146 | from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
|
---|
1147 | return self._actionGenericFormEdit(TestResultFailureData, WuiTestResultFailure,
|
---|
1148 | TestResultFailureData.ksParam_idTestResult);
|
---|
1149 |
|
---|
1150 | def _actionTestResultFailureEditPost(self):
|
---|
1151 | """Edit test result failure result"""
|
---|
1152 | from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
|
---|
1153 | from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
|
---|
1154 | return self._actionGenericFormEditPost(TestResultFailureData, TestResultFailureLogic,
|
---|
1155 | WuiTestResultFailure, self.ksActionResultsUnGrouped);
|
---|
1156 |
|
---|
1157 | def _actionViewLog(self):
|
---|
1158 | """
|
---|
1159 | Log viewer action.
|
---|
1160 | """
|
---|
1161 | from testmanager.core.testresults import TestResultLogic, TestResultFileDataEx;
|
---|
1162 | from testmanager.core.testset import TestSetData, TestSetLogic;
|
---|
1163 | from testmanager.webui.wuilogviewer import WuiLogViewer;
|
---|
1164 |
|
---|
1165 | self._sTemplate = 'template-details.html'; ## @todo create new template (background color, etc)
|
---|
1166 | idTestSet = self.getIntParam(self.ksParamLogSetId, iMin = 1);
|
---|
1167 | idLogFile = self.getIntParam(self.ksParamLogFileId, iMin = 0, iDefault = 0);
|
---|
1168 | cbChunk = self.getIntParam(self.ksParamLogChunkSize, iMin = 256, iMax = 16777216, iDefault = 1024*1024);
|
---|
1169 | iChunk = self.getIntParam(self.ksParamLogChunkNo, iMin = 0,
|
---|
1170 | iMax = config.g_kcMbMaxMainLog * 1048576 / cbChunk, iDefault = 0);
|
---|
1171 | self._checkForUnknownParameters();
|
---|
1172 |
|
---|
1173 | oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
|
---|
1174 | if idLogFile == 0:
|
---|
1175 | oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
|
---|
1176 | aoTimestamps = TestResultLogic(self._oDb).fetchTimestampsForLogViewer(idTestSet);
|
---|
1177 | else:
|
---|
1178 | oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idLogFile);
|
---|
1179 | aoTimestamps = [];
|
---|
1180 | if oTestFile.sMime not in [ 'text/plain',]:
|
---|
1181 | raise WuiException('The log view does not display files of type: %s' % (oTestFile.sMime,));
|
---|
1182 |
|
---|
1183 | oContent = WuiLogViewer(oTestSet, oTestFile, cbChunk, iChunk, aoTimestamps,
|
---|
1184 | oDisp = self, fnDPrint = self._oSrvGlue.dprint);
|
---|
1185 | (self._sPageTitle, self._sPageBody) = oContent.show();
|
---|
1186 | return True;
|
---|
1187 |
|
---|
1188 | def _actionGetFile(self):
|
---|
1189 | """
|
---|
1190 | Get file action.
|
---|
1191 | """
|
---|
1192 | from testmanager.core.testset import TestSetData, TestSetLogic;
|
---|
1193 | from testmanager.core.testresults import TestResultFileDataEx;
|
---|
1194 |
|
---|
1195 | idTestSet = self.getIntParam(self.ksParamGetFileSetId, iMin = 1);
|
---|
1196 | idFile = self.getIntParam(self.ksParamGetFileId, iMin = 0, iDefault = 0);
|
---|
1197 | fDownloadIt = self.getBoolParam(self.ksParamGetFileDownloadIt, fDefault = True);
|
---|
1198 | self._checkForUnknownParameters();
|
---|
1199 |
|
---|
1200 | #
|
---|
1201 | # Get the file info and open it.
|
---|
1202 | #
|
---|
1203 | oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
|
---|
1204 | if idFile == 0:
|
---|
1205 | oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
|
---|
1206 | else:
|
---|
1207 | oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idFile);
|
---|
1208 |
|
---|
1209 | (oFile, oSizeOrError, _) = oTestSet.openFile(oTestFile.sFile, 'rb');
|
---|
1210 | if oFile is None:
|
---|
1211 | raise Exception(oSizeOrError);
|
---|
1212 |
|
---|
1213 | #
|
---|
1214 | # Send the file.
|
---|
1215 | #
|
---|
1216 | self._oSrvGlue.setHeaderField('Content-Type', oTestFile.getMimeWithEncoding());
|
---|
1217 | if fDownloadIt:
|
---|
1218 | self._oSrvGlue.setHeaderField('Content-Disposition', 'attachment; filename="TestSet-%d-%s"'
|
---|
1219 | % (idTestSet, oTestFile.sFile,));
|
---|
1220 | while True:
|
---|
1221 | abChunk = oFile.read(262144);
|
---|
1222 | if len(abChunk) == 0:
|
---|
1223 | break;
|
---|
1224 | self._oSrvGlue.writeRaw(abChunk);
|
---|
1225 | return self.ksDispatchRcAllDone;
|
---|
1226 |
|
---|
1227 | def _actionGenericReport(self, oModelType, oFilterType, oReportType):
|
---|
1228 | """
|
---|
1229 | Generic report action.
|
---|
1230 | oReportType is a child of WuiReportContentBase.
|
---|
1231 | oFilterType is a child of ModelFilterBase.
|
---|
1232 | oModelType is a child of ReportModelBase.
|
---|
1233 | """
|
---|
1234 | from testmanager.core.report import ReportModelBase;
|
---|
1235 |
|
---|
1236 | tsEffective = self.getEffectiveDateParam();
|
---|
1237 | cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 2, iMax = 99, iDefault = 7);
|
---|
1238 | cHoursPerPeriod = self.getIntParam(self.ksParamReportPeriodInHours, iMin = 1, iMax = 168, iDefault = 24);
|
---|
1239 | sSubject = self.getStringParam(self.ksParamReportSubject, ReportModelBase.kasSubjects,
|
---|
1240 | ReportModelBase.ksSubEverything);
|
---|
1241 | if sSubject == ReportModelBase.ksSubEverything:
|
---|
1242 | aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, aiDefaults = []);
|
---|
1243 | else:
|
---|
1244 | aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, iMin = 1);
|
---|
1245 | if aidSubjects is None:
|
---|
1246 | raise WuiException('Missing parameter %s' % (self.ksParamReportSubjectIds,));
|
---|
1247 | oFilter = oFilterType().initFromParams(self);
|
---|
1248 | self._checkForUnknownParameters();
|
---|
1249 |
|
---|
1250 | dParams = \
|
---|
1251 | {
|
---|
1252 | self.ksParamEffectiveDate: tsEffective,
|
---|
1253 | self.ksParamReportPeriods: cPeriods,
|
---|
1254 | self.ksParamReportPeriodInHours: cHoursPerPeriod,
|
---|
1255 | self.ksParamReportSubject: sSubject,
|
---|
1256 | self.ksParamReportSubjectIds: aidSubjects,
|
---|
1257 | };
|
---|
1258 | ## @todo oFilter.
|
---|
1259 |
|
---|
1260 | oModel = oModelType(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, oFilter);
|
---|
1261 | oContent = oReportType(oModel, dParams, fSubReport = False, fnDPrint = self._oSrvGlue.dprint, oDisp = self);
|
---|
1262 | (self._sPageTitle, self._sPageBody) = oContent.show();
|
---|
1263 | sNavi = self._generateReportNavigation(tsEffective, cHoursPerPeriod, cPeriods);
|
---|
1264 | self._sPageBody = sNavi + self._sPageBody;
|
---|
1265 |
|
---|
1266 | if hasattr(oFilter, 'kiBranches'):
|
---|
1267 | oFilter.aCriteria[oFilter.kiBranches].fExpanded = True;
|
---|
1268 | self._sPageFilter = self._generateResultFilter(oFilter, oModel, tsEffective, '%s hours' % (cHoursPerPeriod * cPeriods,));
|
---|
1269 | return True;
|
---|
1270 |
|
---|
1271 | def _actionReportSummary(self):
|
---|
1272 | """ Action wrapper. """
|
---|
1273 | from testmanager.core.report import ReportLazyModel, ReportFilter;
|
---|
1274 | from testmanager.webui.wuireport import WuiReportSummary;
|
---|
1275 | return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSummary);
|
---|
1276 |
|
---|
1277 | def _actionReportRate(self):
|
---|
1278 | """ Action wrapper. """
|
---|
1279 | from testmanager.core.report import ReportLazyModel, ReportFilter;
|
---|
1280 | from testmanager.webui.wuireport import WuiReportSuccessRate;
|
---|
1281 | return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSuccessRate);
|
---|
1282 |
|
---|
1283 | def _actionReportTestCaseFailures(self):
|
---|
1284 | """ Action wrapper. """
|
---|
1285 | from testmanager.core.report import ReportLazyModel, ReportFilter;
|
---|
1286 | from testmanager.webui.wuireport import WuiReportTestCaseFailures;
|
---|
1287 | return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportTestCaseFailures);
|
---|
1288 |
|
---|
1289 | def _actionReportFailureReasons(self):
|
---|
1290 | """ Action wrapper. """
|
---|
1291 | from testmanager.core.report import ReportLazyModel, ReportFilter;
|
---|
1292 | from testmanager.webui.wuireport import WuiReportFailureReasons;
|
---|
1293 | return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportFailureReasons);
|
---|
1294 |
|
---|
1295 | def _actionGraphWiz(self):
|
---|
1296 | """
|
---|
1297 | Graph wizard action.
|
---|
1298 | """
|
---|
1299 | from testmanager.core.report import ReportModelBase, ReportGraphModel;
|
---|
1300 | from testmanager.webui.wuigraphwiz import WuiGraphWiz;
|
---|
1301 | self._sTemplate = 'template-graphwiz.html';
|
---|
1302 |
|
---|
1303 | tsEffective = self.getEffectiveDateParam();
|
---|
1304 | cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 1, iMax = 1, iDefault = 1); # Not needed yet.
|
---|
1305 | sTmp = self.getStringParam(self.ksParamReportPeriodInHours, sDefault = '3 weeks');
|
---|
1306 | (cHoursPerPeriod, sError) = utils.parseIntervalHours(sTmp);
|
---|
1307 | if sError is not None: raise WuiException(sError);
|
---|
1308 | asSubjectIds = self.getListOfStrParams(self.ksParamReportSubjectIds);
|
---|
1309 | sSubject = self.getStringParam(self.ksParamReportSubject, [ReportModelBase.ksSubEverything],
|
---|
1310 | ReportModelBase.ksSubEverything); # dummy
|
---|
1311 | aidTestBoxes = self.getListOfIntParams(self.ksParamGraphWizTestBoxIds, iMin = 1, aiDefaults = []);
|
---|
1312 | aidBuildCats = self.getListOfIntParams(self.ksParamGraphWizBuildCatIds, iMin = 1, aiDefaults = []);
|
---|
1313 | aidTestCases = self.getListOfIntParams(self.ksParamGraphWizTestCaseIds, iMin = 1, aiDefaults = []);
|
---|
1314 | fSepTestVars = self.getBoolParam(self.ksParamGraphWizSepTestVars, fDefault = False);
|
---|
1315 |
|
---|
1316 | enmGraphImpl = self.getStringParam(self.ksParamGraphWizImpl, asValidValues = self.kasGraphWizImplValid,
|
---|
1317 | sDefault = self.ksGraphWizImpl_Default);
|
---|
1318 | cx = self.getIntParam(self.ksParamGraphWizWidth, iMin = 128, iMax = 8192, iDefault = 1280);
|
---|
1319 | cy = self.getIntParam(self.ksParamGraphWizHeight, iMin = 128, iMax = 8192, iDefault = int(cx * 5 / 16) );
|
---|
1320 | cDotsPerInch = self.getIntParam(self.ksParamGraphWizDpi, iMin = 64, iMax = 512, iDefault = 96);
|
---|
1321 | cPtFont = self.getIntParam(self.ksParamGraphWizFontSize, iMin = 6, iMax = 32, iDefault = 8);
|
---|
1322 | fErrorBarY = self.getBoolParam(self.ksParamGraphWizErrorBarY, fDefault = False);
|
---|
1323 | cMaxErrorBarY = self.getIntParam(self.ksParamGraphWizMaxErrorBarY, iMin = 8, iMax = 9999999, iDefault = 18);
|
---|
1324 | cMaxPerGraph = self.getIntParam(self.ksParamGraphWizMaxPerGraph, iMin = 1, iMax = 24, iDefault = 8);
|
---|
1325 | fXkcdStyle = self.getBoolParam(self.ksParamGraphWizXkcdStyle, fDefault = False);
|
---|
1326 | fTabular = self.getBoolParam(self.ksParamGraphWizTabular, fDefault = False);
|
---|
1327 | idSrcTestSet = self.getIntParam(self.ksParamGraphWizSrcTestSetId, iDefault = None);
|
---|
1328 | self._checkForUnknownParameters();
|
---|
1329 |
|
---|
1330 | dParams = \
|
---|
1331 | {
|
---|
1332 | self.ksParamEffectiveDate: tsEffective,
|
---|
1333 | self.ksParamReportPeriods: cPeriods,
|
---|
1334 | self.ksParamReportPeriodInHours: cHoursPerPeriod,
|
---|
1335 | self.ksParamReportSubject: sSubject,
|
---|
1336 | self.ksParamReportSubjectIds: asSubjectIds,
|
---|
1337 | self.ksParamGraphWizTestBoxIds: aidTestBoxes,
|
---|
1338 | self.ksParamGraphWizBuildCatIds: aidBuildCats,
|
---|
1339 | self.ksParamGraphWizTestCaseIds: aidTestCases,
|
---|
1340 | self.ksParamGraphWizSepTestVars: fSepTestVars,
|
---|
1341 |
|
---|
1342 | self.ksParamGraphWizImpl: enmGraphImpl,
|
---|
1343 | self.ksParamGraphWizWidth: cx,
|
---|
1344 | self.ksParamGraphWizHeight: cy,
|
---|
1345 | self.ksParamGraphWizDpi: cDotsPerInch,
|
---|
1346 | self.ksParamGraphWizFontSize: cPtFont,
|
---|
1347 | self.ksParamGraphWizErrorBarY: fErrorBarY,
|
---|
1348 | self.ksParamGraphWizMaxErrorBarY: cMaxErrorBarY,
|
---|
1349 | self.ksParamGraphWizMaxPerGraph: cMaxPerGraph,
|
---|
1350 | self.ksParamGraphWizXkcdStyle: fXkcdStyle,
|
---|
1351 | self.ksParamGraphWizTabular: fTabular,
|
---|
1352 | self.ksParamGraphWizSrcTestSetId: idSrcTestSet,
|
---|
1353 | };
|
---|
1354 |
|
---|
1355 | oModel = ReportGraphModel(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, asSubjectIds,
|
---|
1356 | aidTestBoxes, aidBuildCats, aidTestCases, fSepTestVars);
|
---|
1357 | oContent = WuiGraphWiz(oModel, dParams, fSubReport = False, fnDPrint = self._oSrvGlue.dprint, oDisp = self);
|
---|
1358 | (self._sPageTitle, self._sPageBody) = oContent.show();
|
---|
1359 | return True;
|
---|
1360 |
|
---|
1361 | def _actionVcsHistoryTooltip(self):
|
---|
1362 | """
|
---|
1363 | Version control system history.
|
---|
1364 | """
|
---|
1365 | from testmanager.webui.wuivcshistory import WuiVcsHistoryTooltip;
|
---|
1366 | from testmanager.core.vcsrevisions import VcsRevisionLogic;
|
---|
1367 |
|
---|
1368 | self._sTemplate = 'template-tooltip.html';
|
---|
1369 | iRevision = self.getIntParam(self.ksParamVcsHistoryRevision, iMin = 0, iMax = 999999999);
|
---|
1370 | sRepository = self.getStringParam(self.ksParamVcsHistoryRepository);
|
---|
1371 | cEntries = self.getIntParam(self.ksParamVcsHistoryEntries, iMin = 1, iMax = 1024, iDefault = 8);
|
---|
1372 | self._checkForUnknownParameters();
|
---|
1373 |
|
---|
1374 | aoEntries = VcsRevisionLogic(self._oDb).fetchTimeline(sRepository, iRevision, cEntries);
|
---|
1375 | oContent = WuiVcsHistoryTooltip(aoEntries, sRepository, iRevision, cEntries,
|
---|
1376 | fnDPrint = self._oSrvGlue.dprint, oDisp = self);
|
---|
1377 | (self._sPageTitle, self._sPageBody) = oContent.show();
|
---|
1378 | return True;
|
---|
1379 |
|
---|