VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuimain.py@ 82968

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.6 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuimain.py 82968 2020-02-04 10:35:17Z vboxsync $
3
4"""
5Test Manager Core - WUI - The Main page.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2020 Oracle Corporation
11
12This file is part of VirtualBox Open Source Edition (OSE), as
13available from http://www.virtualbox.org. This file is free software;
14you can redistribute it and/or modify it under the terms of the GNU
15General Public License (GPL) as published by the Free Software
16Foundation, in version 2 as it comes in the "COPYING" file of the
17VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19
20The contents of this file may alternatively be used under the terms
21of the Common Development and Distribution License Version 1.0
22(CDDL) only, as it comes in the "COPYING.CDDL" file of the
23VirtualBox OSE distribution, in which case the provisions of the
24CDDL are applicable instead of those of the GPL.
25
26You may elect to license modified versions of this file under the
27terms and conditions of either the GPL or the CDDL or both.
28"""
29__version__ = "$Revision: 82968 $"
30
31# Standard Python imports.
32
33# Validation Kit imports.
34from testmanager import config;
35from testmanager.core.base import TMExceptionBase, TMTooManyRows;
36from testmanager.webui.wuibase import WuiDispatcherBase, WuiException;
37from testmanager.webui.wuicontentbase import WuiTmLink;
38from common import webutils, utils;
39
40
41
42class 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, False ],
267 [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sSheriff, False ],
268 [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sSheriff, False ],
269 [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sSheriff, False ],
270 [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sSheriff, False ],
271 [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sSheriff, False ],
272 [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sSheriff, False ],
273 [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sSheriff, False ],
274 [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sSheriff, False ],
275 ]
276 ],
277 [
278 'Reports', sActUrlBase + self.ksActionReportSummary,
279 [
280 [ 'Summary', sActUrlBase + self.ksActionReportSummary + sExtraReports, False ],
281 [ 'Success rate', sActUrlBase + self.ksActionReportRate + sExtraReports, False ],
282 [ 'Test case failures', sActUrlBase + self.ksActionReportTestCaseFailures + sExtraReports, False ],
283 [ 'Testbox failures', sActUrlBase + self.ksActionReportTestBoxFailures + sExtraReports, False ],
284 [ 'Failure reasons', sActUrlBase + self.ksActionReportFailureReasons + sExtraReports, False ],
285 ]
286 ],
287 [
288 'Test Results', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav,
289 [
290 [ 'Grouped by', None ],
291 [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sExtraTimeNav, False ],
292 [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sExtraTimeNav, False ],
293 [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sExtraTimeNav, False ],
294 [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sExtraTimeNav, False ],
295 [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sExtraTimeNav, False ],
296 [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sExtraTimeNav, False ],
297 [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sExtraTimeNav, False ],
298 [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sExtraTimeNav, False ],
299 [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sExtraTimeNav, False ],
300 ]
301 ],
302 [
303 'Test Failures', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures,
304 [
305 [ 'Grouped by', None ],
306 [ 'Ungrouped', sActUrlBase + self.ksActionResultsUnGrouped + sOnlyFailures, False ],
307 [ 'Sched group', sActUrlBase + self.ksActionResultsGroupedBySchedGroup + sOnlyFailures, False ],
308 [ 'Test group', sActUrlBase + self.ksActionResultsGroupedByTestGroup + sOnlyFailures, False ],
309 [ 'Test case', sActUrlBase + self.ksActionResultsGroupedByTestCase + sOnlyFailures, False ],
310 [ 'Testbox', sActUrlBase + self.ksActionResultsGroupedByTestBox + sOnlyFailures, False ],
311 [ 'OS', sActUrlBase + self.ksActionResultsGroupedByOS + sOnlyFailures, False ],
312 [ 'Architecture', sActUrlBase + self.ksActionResultsGroupedByArch + sOnlyFailures, False ],
313 [ 'Revision', sActUrlBase + self.ksActionResultsGroupedByBuildRev + sOnlyFailures, False ],
314 [ 'Build category', sActUrlBase + self.ksActionResultsGroupedByBuildCat + sOnlyFailures, False ],
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">&lt;&lt;</a>&nbsp;&nbsp;' \
480 % (webutils.encodeUrlParams(dParams),);
481
482 if tsNext is not None:
483 dParams[WuiDispatcherBase.ksParamEffectiveDate] = str(tsNext);
484 sNext = '&nbsp;&nbsp;<a href="?%s" title="One period later">&gt;&gt;</a>' \
485 % (webutils.encodeUrlParams(dParams),);
486 else:
487 sNext = '&nbsp;&nbsp;&gt;&gt;';
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 = '&nbsp;\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&nbsp; ... &nbsp;\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 &laquo; and &raquo are too tiny).
586 if iPage > 0:
587 dParams[WuiDispatcherBase.ksParamPageNo] = iPage - 1
588 sHtmlPager = ('<a title="Previous page" href="?%s">&lt;&lt;</a>&nbsp;&nbsp;\n'
589 % (webutils.encodeUrlParams(dParams), )) \
590 + sHtmlPager;
591 else:
592 sHtmlPager = '&lt;&lt;&nbsp;&nbsp;\n' + sHtmlPager
593
594 if iPage + 1 < cNumOfPages:
595 dParams[WuiDispatcherBase.ksParamPageNo] = iPage + 1
596 sHtmlPager += '\n&nbsp; <a title="Next page" href="?%s">&gt;&gt;</a>\n' % (webutils.encodeUrlParams(dParams),)
597 else:
598 sHtmlPager += '\n&nbsp; &gt;&gt;\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=too-many-locals
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 in (TestResultLogic.ksResultsGroupingTypeNone, TestResultLogic.ksResultsGroupingTypeTestBox,):
762 aoTmp = oTrLogic.getTestBoxes(tsNow = tsEffective, sPeriod = sCurPeriod)
763 aoGroupMembers = sorted(list({(x.idTestBox, '%s (%s)' % (x.sName, str(x.ip))) for x in aoTmp }),
764 reverse = False, key = lambda asData: asData[1])
765
766 if enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestBox:
767 self._sPageTitle = 'Grouped by Test Box';
768 else:
769 self._sPageTitle = 'Ungrouped results';
770 sAltSelectorAction = self.ksActionResultsGroupedByTestBox;
771 aoGroupMembers.insert(0, [None, None]); # The "All" member.
772
773 elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestGroup:
774 aoTmp = oTrLogic.getTestGroups(tsNow = tsEffective, sPeriod = sCurPeriod);
775 aoGroupMembers = sorted(list({ (x.idTestGroup, x.sName ) for x in aoTmp }),
776 reverse = False, key = lambda asData: asData[1])
777 self._sPageTitle = 'Grouped by Test Group'
778
779 elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildRev:
780 aoTmp = oTrLogic.getBuilds(tsNow = tsEffective, sPeriod = sCurPeriod)
781 aoGroupMembers = sorted(list({ (x.iRevision, '%s.%d' % (x.oCat.sBranch, x.iRevision)) for x in aoTmp }),
782 reverse = True, key = lambda asData: asData[0])
783 self._sPageTitle = 'Grouped by Build'
784
785 elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeBuildCat:
786 aoTmp = oTrLogic.getBuildCategories(tsNow = tsEffective, sPeriod = sCurPeriod)
787 aoGroupMembers = sorted(list({ (x.idBuildCategory,
788 '%s / %s / %s / %s' % ( x.sProduct, x.sBranch, ', '.join(x.asOsArches), x.sType) )
789 for x in aoTmp }),
790 reverse = True, key = lambda asData: asData[1]);
791 self._sPageTitle = 'Grouped by Build Category'
792
793 elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeTestCase:
794 aoTmp = oTrLogic.getTestCases(tsNow = tsEffective, sPeriod = sCurPeriod)
795 aoGroupMembers = sorted(list({ (x.idTestCase, '%s' % x.sName) for x in aoTmp }),
796 reverse = False, key = lambda asData: asData[1])
797 self._sPageTitle = 'Grouped by Test Case'
798
799 elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeOS:
800 aoTmp = oTrLogic.getOSes(tsNow = tsEffective, sPeriod = sCurPeriod)
801 aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]);
802 self._sPageTitle = 'Grouped by OS'
803
804 elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeArch:
805 aoTmp = oTrLogic.getArchitectures(tsNow = tsEffective, sPeriod = sCurPeriod)
806 aoGroupMembers = sorted(list(set(aoTmp)), reverse = False, key = lambda asData: asData[1]);
807 self._sPageTitle = 'Grouped by Architecture'
808
809 elif enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeSchedGroup:
810 aoTmp = oTrLogic.getSchedGroups(tsNow = tsEffective, sPeriod = sCurPeriod)
811 aoGroupMembers = sorted(list({ (x.idSchedGroup, '%s' % x.sName) for x in aoTmp }),
812 reverse = False, key = lambda asData: asData[1])
813 self._sPageTitle = 'Grouped by Scheduling Group'
814
815 else:
816 raise TMExceptionBase('Unknown grouping type')
817
818 _sPageBody = ''
819 oContent = None
820 cEntriesMax = 0
821 _dParams = self.getParameters()
822 oResultLogic = oResultsLogicType(self._oDb);
823 for idMember, sMemberName in aoGroupMembers:
824 #
825 # Count and fetch entries to be displayed.
826 #
827
828 # Skip group members that were not specified.
829 if idMember != iGroupMemberId \
830 and ( (idMember is not None and enmResultsGroupingType == TestResultLogic.ksResultsGroupingTypeNone)
831 or (iGroupMemberId > 0 and enmResultsGroupingType != TestResultLogic.ksResultsGroupingTypeNone) ):
832 continue
833
834 cEntries = oResultLogic.getEntriesCount(tsNow = tsEffective,
835 sInterval = sCurPeriod,
836 oFilter = oFilter,
837 enmResultsGroupingType = enmResultsGroupingType,
838 iResultsGroupingValue = idMember,
839 fOnlyFailures = fOnlyFailures,
840 fOnlyNeedingReason = fOnlyNeedingReason);
841 if cEntries == 0: # Do not display empty groups
842 continue
843 aoEntries = oResultLogic.fetchResultsForListing(iPage * cItemsPerPage,
844 cItemsPerPage,
845 tsNow = tsEffective,
846 sInterval = sCurPeriod,
847 oFilter = oFilter,
848 enmResultSortBy = enmResultSortBy,
849 enmResultsGroupingType = enmResultsGroupingType,
850 iResultsGroupingValue = idMember,
851 fOnlyFailures = fOnlyFailures,
852 fOnlyNeedingReason = fOnlyNeedingReason);
853 cEntriesMax = max(cEntriesMax, cEntries)
854
855 #
856 # Format them.
857 #
858 oContent = oResultsListContentType(aoEntries,
859 cEntries,
860 iPage,
861 cItemsPerPage,
862 tsEffective,
863 fnDPrint = self._oSrvGlue.dprint,
864 oDisp = self)
865
866 (_, sHtml) = oContent.show(fShowNavigation = False)
867 if sMemberName is not None:
868 _sPageBody += '<table width=100%><tr><td>'
869
870 _dParams[self.ksParamGroupMemberId] = idMember
871 sLink = WuiTmLink(sMemberName, '', _dParams, fBracketed = False).toHtml()
872
873 _sPageBody += '<h2>%s (%d)</h2></td>' % (sLink, cEntries)
874 _sPageBody += '<td><br></td>'
875 _sPageBody += '</tr></table>'
876 _sPageBody += sHtml
877 _sPageBody += '<br>'
878
879 #
880 # Complete the page by slapping navigation controls at the top and
881 # bottom of it.
882 #
883 sHtmlNavigation = self._generateResultNavigation(cEntriesMax, cItemsPerPage, iPage,
884 tsEffective, sCurPeriod, fOnlyFailures,
885 self._generateGroupContentSelector(aoGroupMembers, iGroupMemberId,
886 sAltSelectorAction));
887 if cEntriesMax > 0:
888 self._sPageBody = sHtmlNavigation + _sPageBody + sHtmlNavigation;
889 else:
890 self._sPageBody = sHtmlNavigation + '<p align="center"><i>No data to display</i></p>\n';
891
892 #
893 # Now, generate a filter control panel for the side bar.
894 #
895 if hasattr(oFilter, 'kiBranches'):
896 oFilter.aCriteria[oFilter.kiBranches].fExpanded = True;
897 if hasattr(oFilter, 'kiTestStatus'):
898 oFilter.aCriteria[oFilter.kiTestStatus].fExpanded = True;
899 self._sPageFilter = self._generateResultFilter(oFilter, oResultLogic, tsEffective, sCurPeriod,
900 enmResultsGroupingType = enmResultsGroupingType,
901 aoGroupMembers = aoGroupMembers,
902 fOnlyFailures = fOnlyFailures,
903 fOnlyNeedingReason = fOnlyNeedingReason);
904 return True;
905
906 def _generateResultFilter(self, oFilter, oResultLogic, tsNow, sPeriod, enmResultsGroupingType = None, aoGroupMembers = None,
907 fOnlyFailures = False, fOnlyNeedingReason = False):
908 """
909 Generates the result filter for the left hand side.
910 """
911 _ = enmResultsGroupingType; _ = aoGroupMembers; _ = fOnlyFailures; _ = fOnlyNeedingReason;
912 oResultLogic.fetchPossibleFilterOptions(oFilter, tsNow, sPeriod)
913
914 # Add non-filter parameters as hidden fields so we can use 'GET' and have URLs to bookmark.
915 self._dSideMenuFormAttrs['method'] = 'GET';
916 sHtml = u'';
917 for sKey, oValue in self._oSrvGlue.getParameters().items():
918 if len(sKey) > 3:
919 if hasattr(oValue, 'startswith'):
920 sHtml += u'<input type="hidden" name="%s" value="%s"/>\n' \
921 % (webutils.escapeAttr(sKey), webutils.escapeAttr(oValue),);
922 else:
923 for oSubValue in oValue:
924 sHtml += u'<input type="hidden" name="%s" value="%s"/>\n' \
925 % (webutils.escapeAttr(sKey), webutils.escapeAttr(oSubValue),);
926
927 # Generate the filter panel.
928 sHtml += u'<div id="side-filters">\n' \
929 u' <p>Filters' \
930 u' <span class="tm-side-filter-title-buttons"><input type="submit" value="Apply" />\n' \
931 u' <a href="javascript:toggleSidebarSize();" class="tm-sidebar-size-link">&#x00bb;&#x00bb;</a></span></p>\n';
932 sHtml += u' <dl>\n';
933 for oCrit in oFilter.aCriteria:
934 if oCrit.aoPossible or oCrit.sType == oCrit.ksType_Ranges:
935 if ( oCrit.oSub is None \
936 and ( oCrit.sState == oCrit.ksState_Selected \
937 or (len(oCrit.aoPossible) <= 2 and oCrit.sType != oCrit.ksType_Ranges))) \
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 = '&#9660;';
945 else:
946 sClass = 'sf-expandable';
947 sChar = '&#9654;';
948
949 sHtml += u' <dt class="%s"><a href="javascript:void(0)" onclick="toggleCollapsibleDtDd(this);">%s %s</a> ' \
950 % (sClass, sChar, webutils.escapeElem(oCrit.sName),);
951 sHtml += u'<span class="tm-side-filter-dt-buttons">';
952 if oCrit.sInvVarNm is not None:
953 sHtml += u'<input id="sf-union-%s" class="tm-side-filter-union-input" ' \
954 u'name="%s" value="1" type="checkbox"%s />' \
955 u'<label for="sf-union-%s" class="tm-side-filter-union-input"></label>' \
956 % ( oCrit.sInvVarNm, oCrit.sInvVarNm, ' checked' if oCrit.fInverted else '', oCrit.sInvVarNm,);
957 sHtml += u' <input type="submit" value="Apply" />';
958 sHtml += u'</span>';
959 sHtml += u'</dt>\n' \
960 u' <dd class="%s">\n' \
961 u' <ul>\n' \
962 % (sClass);
963
964 if oCrit.sType == oCrit.ksType_Ranges:
965 assert not oCrit.oSub;
966 assert not oCrit.aoPossible;
967 asValues = [];
968 for tRange in oCrit.aoSelected:
969 if tRange[0] == tRange[1]:
970 asValues.append('%s' % (tRange[0],));
971 else:
972 asValues.append('%s-%s' % (tRange[0] if tRange[0] is not None else 'inf',
973 tRange[1] if tRange[1] is not None else 'inf'));
974 sHtml += u' <li title="%s"><input type="text" name="%s" value="%s"/></li>\n' \
975 % ( webutils.escapeAttr('comma separate list of numerical ranges'), oCrit.sVarNm,
976 ', '.join(asValues), );
977 else:
978 for oDesc in oCrit.aoPossible:
979 fChecked = oDesc.oValue in oCrit.aoSelected;
980 sHtml += u' <li%s%s><label><input type="checkbox" name="%s" value="%s"%s%s/>%s%s</label>\n' \
981 % ( ' class="side-filter-irrelevant"' if oDesc.fIrrelevant else '',
982 (' title="%s"' % (webutils.escapeAttr(oDesc.sHover,)) if oDesc.sHover is not None else ''),
983 oCrit.sVarNm,
984 oDesc.oValue,
985 ' checked' if fChecked else '',
986 ' onclick="toggleCollapsibleCheckbox(this);"' if oDesc.aoSubs is not None else '',
987 webutils.escapeElem(oDesc.sDesc),
988 '<span class="side-filter-count"> [%u]</span>' % (oDesc.cTimes) if oDesc.cTimes is not None
989 else '', );
990 if oDesc.aoSubs is not None:
991 sHtml += u' <ul class="sf-checkbox-%s">\n' % ('collapsible' if fChecked else 'expandable', );
992 for oSubDesc in oDesc.aoSubs:
993 fSubChecked = oSubDesc.oValue in oCrit.oSub.aoSelected;
994 sHtml += u' <li%s%s><label><input type="checkbox" name="%s" value="%s"%s/>%s%s</label>\n' \
995 % ( ' class="side-filter-irrelevant"' if oSubDesc.fIrrelevant else '',
996 ' title="%s"' % ( webutils.escapeAttr(oSubDesc.sHover,) if oSubDesc.sHover is not None
997 else ''),
998 oCrit.oSub.sVarNm, oSubDesc.oValue, ' checked' if fSubChecked else '',
999 webutils.escapeElem(oSubDesc.sDesc),
1000 '<span class="side-filter-count"> [%u]</span>' % (oSubDesc.cTimes)
1001 if oSubDesc.cTimes is not None else '', );
1002
1003 sHtml += u' </ul>\n';
1004 sHtml += u' </li>';
1005
1006 sHtml += u' </ul>\n' \
1007 u' </dd>\n';
1008
1009 sHtml += u' </dl>\n';
1010 sHtml += u' <input type="submit" value="Apply"/>\n';
1011 sHtml += u' <input type="reset" value="Reset"/>\n';
1012 sHtml += u' <button type="button" onclick="clearForm(\'side-menu-form\');">Clear</button>\n';
1013 sHtml += u'</div>\n';
1014 return sHtml;
1015
1016 def _actionResultsUnGrouped(self):
1017 """ Action wrapper. """
1018 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1019 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1020 #return self._actionResultsListing(TestResultLogic, WuiGroupedResultList)?
1021 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone,
1022 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1023
1024 def _actionResultsGroupedByTestGroup(self):
1025 """ Action wrapper. """
1026 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1027 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1028 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestGroup,
1029 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1030
1031 def _actionResultsGroupedByBuildRev(self):
1032 """ Action wrapper. """
1033 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1034 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1035 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildRev,
1036 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1037
1038 def _actionResultsGroupedByBuildCat(self):
1039 """ Action wrapper. """
1040 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1041 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1042 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildCat,
1043 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1044
1045 def _actionResultsGroupedByTestBox(self):
1046 """ Action wrapper. """
1047 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1048 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1049 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestBox,
1050 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1051
1052 def _actionResultsGroupedByTestCase(self):
1053 """ Action wrapper. """
1054 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1055 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1056 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestCase,
1057 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1058
1059 def _actionResultsGroupedByOS(self):
1060 """ Action wrapper. """
1061 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1062 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1063 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeOS,
1064 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1065
1066 def _actionResultsGroupedByArch(self):
1067 """ Action wrapper. """
1068 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1069 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1070 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeArch,
1071 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1072
1073 def _actionResultsGroupedBySchedGroup(self):
1074 """ Action wrapper. """
1075 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1076 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1077 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeSchedGroup,
1078 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1079
1080
1081 def _actionTestSetDetailsCommon(self, idTestSet):
1082 """Show test case execution result details."""
1083 from testmanager.core.build import BuildDataEx;
1084 from testmanager.core.testbox import TestBoxData;
1085 from testmanager.core.testcase import TestCaseDataEx;
1086 from testmanager.core.testcaseargs import TestCaseArgsDataEx;
1087 from testmanager.core.testgroup import TestGroupData;
1088 from testmanager.core.testresults import TestResultLogic;
1089 from testmanager.core.testset import TestSetData;
1090 from testmanager.webui.wuitestresult import WuiTestResult;
1091
1092 self._sTemplate = 'template-details.html';
1093 self._checkForUnknownParameters()
1094
1095 oTestSetData = TestSetData().initFromDbWithId(self._oDb, idTestSet);
1096 try:
1097 (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet);
1098 except TMTooManyRows:
1099 (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet, 2);
1100 oBuildDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuild, oTestSetData.tsCreated);
1101 try: oBuildValidationKitDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuildTestSuite,
1102 oTestSetData.tsCreated);
1103 except: oBuildValidationKitDataEx = None;
1104 oTestBoxData = TestBoxData().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestBox);
1105 oTestGroupData = TestGroupData().initFromDbWithId(self._oDb, ## @todo This bogus time wise. Bad DB design?
1106 oTestSetData.idTestGroup, oTestSetData.tsCreated);
1107 oTestCaseDataEx = TestCaseDataEx().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestCase,
1108 oTestSetData.tsConfig);
1109 oTestCaseArgsDataEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(self._oDb, oTestSetData.idGenTestCaseArgs,
1110 oTestSetData.tsConfig);
1111
1112 oContent = WuiTestResult(oDisp = self, fnDPrint = self._oSrvGlue.dprint);
1113 (self._sPageTitle, self._sPageBody) = oContent.showTestCaseResultDetails(oTestResultTree,
1114 oTestSetData,
1115 oBuildDataEx,
1116 oBuildValidationKitDataEx,
1117 oTestBoxData,
1118 oTestGroupData,
1119 oTestCaseDataEx,
1120 oTestCaseArgsDataEx);
1121 return True
1122
1123 def _actionTestSetDetails(self):
1124 """Show test case execution result details."""
1125 from testmanager.core.testset import TestSetData;
1126
1127 idTestSet = self.getIntParam(TestSetData.ksParam_idTestSet);
1128 return self._actionTestSetDetailsCommon(idTestSet);
1129
1130 def _actionTestSetDetailsFromResult(self):
1131 """Show test case execution result details."""
1132 from testmanager.core.testresults import TestResultData;
1133 from testmanager.core.testset import TestSetData;
1134 idTestResult = self.getIntParam(TestSetData.ksParam_idTestResult);
1135 oTestResultData = TestResultData().initFromDbWithId(self._oDb, idTestResult);
1136 return self._actionTestSetDetailsCommon(oTestResultData.idTestSet);
1137
1138
1139 def _actionTestResultFailureAdd(self):
1140 """ Pro forma. """
1141 from testmanager.core.testresultfailures import TestResultFailureData;
1142 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1143 return self._actionGenericFormAdd(TestResultFailureData, WuiTestResultFailure);
1144
1145 def _actionTestResultFailureAddPost(self):
1146 """Add test result failure result"""
1147 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
1148 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1149 if self.ksParamRedirectTo not in self._dParams:
1150 raise WuiException('Missing parameter ' + self.ksParamRedirectTo);
1151
1152 return self._actionGenericFormAddPost(TestResultFailureData, TestResultFailureLogic,
1153 WuiTestResultFailure, self.ksActionResultsUnGrouped);
1154
1155 def _actionTestResultFailureDoRemove(self):
1156 """ Action wrapper. """
1157 from testmanager.core.testresultfailures import TestResultFailureData, TestResultFailureLogic;
1158 return self._actionGenericDoRemove(TestResultFailureLogic, TestResultFailureData.ksParam_idTestResult,
1159 self.ksActionResultsUnGrouped);
1160
1161 def _actionTestResultFailureDetails(self):
1162 """ Pro forma. """
1163 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
1164 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1165 return self._actionGenericFormDetails(TestResultFailureData, TestResultFailureLogic,
1166 WuiTestResultFailure, 'idTestResult');
1167
1168 def _actionTestResultFailureEdit(self):
1169 """ Pro forma. """
1170 from testmanager.core.testresultfailures import TestResultFailureData;
1171 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1172 return self._actionGenericFormEdit(TestResultFailureData, WuiTestResultFailure,
1173 TestResultFailureData.ksParam_idTestResult);
1174
1175 def _actionTestResultFailureEditPost(self):
1176 """Edit test result failure result"""
1177 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
1178 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1179 return self._actionGenericFormEditPost(TestResultFailureData, TestResultFailureLogic,
1180 WuiTestResultFailure, self.ksActionResultsUnGrouped);
1181
1182 def _actionViewLog(self):
1183 """
1184 Log viewer action.
1185 """
1186 from testmanager.core.testresults import TestResultLogic, TestResultFileDataEx;
1187 from testmanager.core.testset import TestSetData, TestSetLogic;
1188 from testmanager.webui.wuilogviewer import WuiLogViewer;
1189
1190 self._sTemplate = 'template-details.html'; ## @todo create new template (background color, etc)
1191 idTestSet = self.getIntParam(self.ksParamLogSetId, iMin = 1);
1192 idLogFile = self.getIntParam(self.ksParamLogFileId, iMin = 0, iDefault = 0);
1193 cbChunk = self.getIntParam(self.ksParamLogChunkSize, iMin = 256, iMax = 16777216, iDefault = 1024*1024);
1194 iChunk = self.getIntParam(self.ksParamLogChunkNo, iMin = 0,
1195 iMax = config.g_kcMbMaxMainLog * 1048576 / cbChunk, iDefault = 0);
1196 self._checkForUnknownParameters();
1197
1198 oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
1199 if idLogFile == 0:
1200 oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
1201 aoTimestamps = TestResultLogic(self._oDb).fetchTimestampsForLogViewer(idTestSet);
1202 else:
1203 oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idLogFile);
1204 aoTimestamps = [];
1205 if oTestFile.sMime not in [ 'text/plain',]:
1206 raise WuiException('The log view does not display files of type: %s' % (oTestFile.sMime,));
1207
1208 oContent = WuiLogViewer(oTestSet, oTestFile, cbChunk, iChunk, aoTimestamps,
1209 oDisp = self, fnDPrint = self._oSrvGlue.dprint);
1210 (self._sPageTitle, self._sPageBody) = oContent.show();
1211 return True;
1212
1213 def _actionGetFile(self):
1214 """
1215 Get file action.
1216 """
1217 from testmanager.core.testset import TestSetData, TestSetLogic;
1218 from testmanager.core.testresults import TestResultFileDataEx;
1219
1220 idTestSet = self.getIntParam(self.ksParamGetFileSetId, iMin = 1);
1221 idFile = self.getIntParam(self.ksParamGetFileId, iMin = 0, iDefault = 0);
1222 fDownloadIt = self.getBoolParam(self.ksParamGetFileDownloadIt, fDefault = True);
1223 self._checkForUnknownParameters();
1224
1225 #
1226 # Get the file info and open it.
1227 #
1228 oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
1229 if idFile == 0:
1230 oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
1231 else:
1232 oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idFile);
1233
1234 (oFile, oSizeOrError, _) = oTestSet.openFile(oTestFile.sFile, 'rb');
1235 if oFile is None:
1236 raise Exception(oSizeOrError);
1237
1238 #
1239 # Send the file.
1240 #
1241 self._oSrvGlue.setHeaderField('Content-Type', oTestFile.getMimeWithEncoding());
1242 if fDownloadIt:
1243 self._oSrvGlue.setHeaderField('Content-Disposition', 'attachment; filename="TestSet-%d-%s"'
1244 % (idTestSet, oTestFile.sFile,));
1245 while True:
1246 abChunk = oFile.read(262144);
1247 if not abChunk:
1248 break;
1249 self._oSrvGlue.writeRaw(abChunk);
1250 return self.ksDispatchRcAllDone;
1251
1252 def _actionGenericReport(self, oModelType, oFilterType, oReportType):
1253 """
1254 Generic report action.
1255 oReportType is a child of WuiReportContentBase.
1256 oFilterType is a child of ModelFilterBase.
1257 oModelType is a child of ReportModelBase.
1258 """
1259 from testmanager.core.report import ReportModelBase;
1260
1261 tsEffective = self.getEffectiveDateParam();
1262 cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 2, iMax = 99, iDefault = 7);
1263 cHoursPerPeriod = self.getIntParam(self.ksParamReportPeriodInHours, iMin = 1, iMax = 168, iDefault = 24);
1264 sSubject = self.getStringParam(self.ksParamReportSubject, ReportModelBase.kasSubjects,
1265 ReportModelBase.ksSubEverything);
1266 if sSubject == ReportModelBase.ksSubEverything:
1267 aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, aiDefaults = []);
1268 else:
1269 aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, iMin = 1);
1270 if aidSubjects is None:
1271 raise WuiException('Missing parameter %s' % (self.ksParamReportSubjectIds,));
1272
1273 aiSortColumnsDup = self.getListOfIntParams(self.ksParamSortColumns,
1274 iMin = -getattr(oReportType, 'kcMaxSortColumns', cPeriods) + 1,
1275 iMax = getattr(oReportType, 'kcMaxSortColumns', cPeriods), aiDefaults = []);
1276 aiSortColumns = [];
1277 for iSortColumn in aiSortColumnsDup:
1278 if iSortColumn not in aiSortColumns:
1279 aiSortColumns.append(iSortColumn);
1280
1281 oFilter = oFilterType().initFromParams(self);
1282 self._checkForUnknownParameters();
1283
1284 dParams = \
1285 {
1286 self.ksParamEffectiveDate: tsEffective,
1287 self.ksParamReportPeriods: cPeriods,
1288 self.ksParamReportPeriodInHours: cHoursPerPeriod,
1289 self.ksParamReportSubject: sSubject,
1290 self.ksParamReportSubjectIds: aidSubjects,
1291 };
1292 ## @todo oFilter.
1293
1294 oModel = oModelType(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, oFilter);
1295 oContent = oReportType(oModel, dParams, fSubReport = False, aiSortColumns = aiSortColumns,
1296 fnDPrint = self._oSrvGlue.dprint, oDisp = self);
1297 (self._sPageTitle, self._sPageBody) = oContent.show();
1298 sNavi = self._generateReportNavigation(tsEffective, cHoursPerPeriod, cPeriods);
1299 self._sPageBody = sNavi + self._sPageBody;
1300
1301 if hasattr(oFilter, 'kiBranches'):
1302 oFilter.aCriteria[oFilter.kiBranches].fExpanded = True;
1303 self._sPageFilter = self._generateResultFilter(oFilter, oModel, tsEffective, '%s hours' % (cHoursPerPeriod * cPeriods,));
1304 return True;
1305
1306 def _actionReportSummary(self):
1307 """ Action wrapper. """
1308 from testmanager.core.report import ReportLazyModel, ReportFilter;
1309 from testmanager.webui.wuireport import WuiReportSummary;
1310 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSummary);
1311
1312 def _actionReportRate(self):
1313 """ Action wrapper. """
1314 from testmanager.core.report import ReportLazyModel, ReportFilter;
1315 from testmanager.webui.wuireport import WuiReportSuccessRate;
1316 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSuccessRate);
1317
1318 def _actionReportTestCaseFailures(self):
1319 """ Action wrapper. """
1320 from testmanager.core.report import ReportLazyModel, ReportFilter;
1321 from testmanager.webui.wuireport import WuiReportTestCaseFailures;
1322 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportTestCaseFailures);
1323
1324 def _actionReportFailureReasons(self):
1325 """ Action wrapper. """
1326 from testmanager.core.report import ReportLazyModel, ReportFilter;
1327 from testmanager.webui.wuireport import WuiReportFailureReasons;
1328 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportFailureReasons);
1329
1330 def _actionGraphWiz(self):
1331 """
1332 Graph wizard action.
1333 """
1334 from testmanager.core.report import ReportModelBase, ReportGraphModel;
1335 from testmanager.webui.wuigraphwiz import WuiGraphWiz;
1336 self._sTemplate = 'template-graphwiz.html';
1337
1338 tsEffective = self.getEffectiveDateParam();
1339 cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 1, iMax = 1, iDefault = 1); # Not needed yet.
1340 sTmp = self.getStringParam(self.ksParamReportPeriodInHours, sDefault = '3 weeks');
1341 (cHoursPerPeriod, sError) = utils.parseIntervalHours(sTmp);
1342 if sError is not None: raise WuiException(sError);
1343 asSubjectIds = self.getListOfStrParams(self.ksParamReportSubjectIds);
1344 sSubject = self.getStringParam(self.ksParamReportSubject, [ReportModelBase.ksSubEverything],
1345 ReportModelBase.ksSubEverything); # dummy
1346 aidTestBoxes = self.getListOfIntParams(self.ksParamGraphWizTestBoxIds, iMin = 1, aiDefaults = []);
1347 aidBuildCats = self.getListOfIntParams(self.ksParamGraphWizBuildCatIds, iMin = 1, aiDefaults = []);
1348 aidTestCases = self.getListOfIntParams(self.ksParamGraphWizTestCaseIds, iMin = 1, aiDefaults = []);
1349 fSepTestVars = self.getBoolParam(self.ksParamGraphWizSepTestVars, fDefault = False);
1350
1351 enmGraphImpl = self.getStringParam(self.ksParamGraphWizImpl, asValidValues = self.kasGraphWizImplValid,
1352 sDefault = self.ksGraphWizImpl_Default);
1353 cx = self.getIntParam(self.ksParamGraphWizWidth, iMin = 128, iMax = 8192, iDefault = 1280);
1354 cy = self.getIntParam(self.ksParamGraphWizHeight, iMin = 128, iMax = 8192, iDefault = int(cx * 5 / 16) );
1355 cDotsPerInch = self.getIntParam(self.ksParamGraphWizDpi, iMin = 64, iMax = 512, iDefault = 96);
1356 cPtFont = self.getIntParam(self.ksParamGraphWizFontSize, iMin = 6, iMax = 32, iDefault = 8);
1357 fErrorBarY = self.getBoolParam(self.ksParamGraphWizErrorBarY, fDefault = False);
1358 cMaxErrorBarY = self.getIntParam(self.ksParamGraphWizMaxErrorBarY, iMin = 8, iMax = 9999999, iDefault = 18);
1359 cMaxPerGraph = self.getIntParam(self.ksParamGraphWizMaxPerGraph, iMin = 1, iMax = 24, iDefault = 8);
1360 fXkcdStyle = self.getBoolParam(self.ksParamGraphWizXkcdStyle, fDefault = False);
1361 fTabular = self.getBoolParam(self.ksParamGraphWizTabular, fDefault = False);
1362 idSrcTestSet = self.getIntParam(self.ksParamGraphWizSrcTestSetId, iDefault = None);
1363 self._checkForUnknownParameters();
1364
1365 dParams = \
1366 {
1367 self.ksParamEffectiveDate: tsEffective,
1368 self.ksParamReportPeriods: cPeriods,
1369 self.ksParamReportPeriodInHours: cHoursPerPeriod,
1370 self.ksParamReportSubject: sSubject,
1371 self.ksParamReportSubjectIds: asSubjectIds,
1372 self.ksParamGraphWizTestBoxIds: aidTestBoxes,
1373 self.ksParamGraphWizBuildCatIds: aidBuildCats,
1374 self.ksParamGraphWizTestCaseIds: aidTestCases,
1375 self.ksParamGraphWizSepTestVars: fSepTestVars,
1376
1377 self.ksParamGraphWizImpl: enmGraphImpl,
1378 self.ksParamGraphWizWidth: cx,
1379 self.ksParamGraphWizHeight: cy,
1380 self.ksParamGraphWizDpi: cDotsPerInch,
1381 self.ksParamGraphWizFontSize: cPtFont,
1382 self.ksParamGraphWizErrorBarY: fErrorBarY,
1383 self.ksParamGraphWizMaxErrorBarY: cMaxErrorBarY,
1384 self.ksParamGraphWizMaxPerGraph: cMaxPerGraph,
1385 self.ksParamGraphWizXkcdStyle: fXkcdStyle,
1386 self.ksParamGraphWizTabular: fTabular,
1387 self.ksParamGraphWizSrcTestSetId: idSrcTestSet,
1388 };
1389
1390 oModel = ReportGraphModel(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, asSubjectIds,
1391 aidTestBoxes, aidBuildCats, aidTestCases, fSepTestVars);
1392 oContent = WuiGraphWiz(oModel, dParams, fSubReport = False, fnDPrint = self._oSrvGlue.dprint, oDisp = self);
1393 (self._sPageTitle, self._sPageBody) = oContent.show();
1394 return True;
1395
1396 def _actionVcsHistoryTooltip(self):
1397 """
1398 Version control system history.
1399 """
1400 from testmanager.webui.wuivcshistory import WuiVcsHistoryTooltip;
1401 from testmanager.core.vcsrevisions import VcsRevisionLogic;
1402
1403 self._sTemplate = 'template-tooltip.html';
1404 iRevision = self.getIntParam(self.ksParamVcsHistoryRevision, iMin = 0, iMax = 999999999);
1405 sRepository = self.getStringParam(self.ksParamVcsHistoryRepository);
1406 cEntries = self.getIntParam(self.ksParamVcsHistoryEntries, iMin = 1, iMax = 1024, iDefault = 8);
1407 self._checkForUnknownParameters();
1408
1409 aoEntries = VcsRevisionLogic(self._oDb).fetchTimeline(sRepository, iRevision, cEntries);
1410 oContent = WuiVcsHistoryTooltip(aoEntries, sRepository, iRevision, cEntries,
1411 fnDPrint = self._oSrvGlue.dprint, oDisp = self);
1412 (self._sPageTitle, self._sPageBody) = oContent.show();
1413 return True;
1414
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette