VirtualBox

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

Last change on this file since 65090 was 65090, checked in by vboxsync, 8 years ago

TestManager: More CSS tweaking,

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 71.2 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuimain.py 65090 2017-01-03 21:50:35Z vboxsync $
3
4"""
5Test Manager Core - WUI - The Main page.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2016 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: 65090 $"
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 ],
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">&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=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.sState == oCrit.ksState_Selected \
936 or len(oCrit.aoPossible) <= 2 \
937 or oCrit.fExpanded is True:
938 sClass = 'sf-collapsable';
939 sChar = '&#9660;';
940 else:
941 sClass = 'sf-expandable';
942 sChar = '&#9654;';
943
944 sHtml += u' <dt class="%s"><a href="javascript:void(0)" onclick="toggleCollapsableDtDd(this);">%s'\
945 u' %s</a></dt>\n' \
946 u' <dd class="%s">\n' \
947 u' <ul>\n' \
948 % (sClass, sChar, webutils.escapeElem(oCrit.sName), sClass);
949
950 for oDesc in oCrit.aoPossible:
951 fChecked = oDesc.oValue in oCrit.aoSelected;
952 sHtml += u' <li%s%s><input type="checkbox" name="%s" value="%s"%s/>%s%s</li>\n' \
953 % ( ' class="side-filter-irrelevant"' if oDesc.fIrrelevant else '',
954 ' title="%s"' % (webutils.escapeAttr(oDesc.sHover,) if oDesc.sHover is not None else ''),
955 oCrit.sVarNm, oDesc.oValue, ' checked' if fChecked else '',
956 webutils.escapeElem(oDesc.sDesc),
957 '<span class="side-filter-count"> [%u]</span>' % (oDesc.cTimes) if oDesc.cTimes is not None
958 else '', );
959
960 sHtml += u' </ul>\n' \
961 u' </dd>\n';
962
963 sHtml += u' </dl>\n';
964 sHtml += u' <input type="submit" value="Apply"/>\n';
965 sHtml += u'</div>\n';
966 return sHtml;
967
968 def _actionResultsUnGrouped(self):
969 """ Action wrapper. """
970 from testmanager.webui.wuitestresult import WuiGroupedResultList;
971 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
972 #return self._actionResultsListing(TestResultLogic, WuiGroupedResultList)?
973 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeNone,
974 TestResultLogic, TestResultFilter, WuiGroupedResultList);
975
976 def _actionResultsGroupedByTestGroup(self):
977 """ Action wrapper. """
978 from testmanager.webui.wuitestresult import WuiGroupedResultList;
979 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
980 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestGroup,
981 TestResultLogic, TestResultFilter, WuiGroupedResultList);
982
983 def _actionResultsGroupedByBuildRev(self):
984 """ Action wrapper. """
985 from testmanager.webui.wuitestresult import WuiGroupedResultList;
986 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
987 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildRev,
988 TestResultLogic, TestResultFilter, WuiGroupedResultList);
989
990 def _actionResultsGroupedByBuildCat(self):
991 """ Action wrapper. """
992 from testmanager.webui.wuitestresult import WuiGroupedResultList;
993 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
994 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeBuildCat,
995 TestResultLogic, TestResultFilter, WuiGroupedResultList);
996
997 def _actionResultsGroupedByTestBox(self):
998 """ Action wrapper. """
999 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1000 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1001 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestBox,
1002 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1003
1004 def _actionResultsGroupedByTestCase(self):
1005 """ Action wrapper. """
1006 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1007 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1008 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeTestCase,
1009 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1010
1011 def _actionResultsGroupedByOS(self):
1012 """ Action wrapper. """
1013 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1014 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1015 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeOS,
1016 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1017
1018 def _actionResultsGroupedByArch(self):
1019 """ Action wrapper. """
1020 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1021 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1022 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeArch,
1023 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1024
1025 def _actionResultsGroupedBySchedGroup(self):
1026 """ Action wrapper. """
1027 from testmanager.webui.wuitestresult import WuiGroupedResultList;
1028 from testmanager.core.testresults import TestResultLogic, TestResultFilter;
1029 return self._actionGroupedResultsListing(TestResultLogic.ksResultsGroupingTypeSchedGroup,
1030 TestResultLogic, TestResultFilter, WuiGroupedResultList);
1031
1032
1033 def _actionTestSetDetailsCommon(self, idTestSet):
1034 """Show test case execution result details."""
1035 from testmanager.core.build import BuildDataEx;
1036 from testmanager.core.testbox import TestBoxData;
1037 from testmanager.core.testcase import TestCaseDataEx;
1038 from testmanager.core.testcaseargs import TestCaseArgsDataEx;
1039 from testmanager.core.testgroup import TestGroupData;
1040 from testmanager.core.testresults import TestResultLogic;
1041 from testmanager.core.testset import TestSetData;
1042 from testmanager.webui.wuitestresult import WuiTestResult;
1043
1044 self._sTemplate = 'template-details.html';
1045 self._checkForUnknownParameters()
1046
1047 oTestSetData = TestSetData().initFromDbWithId(self._oDb, idTestSet);
1048 try:
1049 (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet);
1050 except TMTooManyRows:
1051 (oTestResultTree, _) = TestResultLogic(self._oDb).fetchResultTree(idTestSet, 2);
1052 oBuildDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuild, oTestSetData.tsCreated);
1053 try: oBuildValidationKitDataEx = BuildDataEx().initFromDbWithId(self._oDb, oTestSetData.idBuildTestSuite,
1054 oTestSetData.tsCreated);
1055 except: oBuildValidationKitDataEx = None;
1056 oTestBoxData = TestBoxData().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestBox);
1057 oTestGroupData = TestGroupData().initFromDbWithId(self._oDb, ## @todo This bogus time wise. Bad DB design?
1058 oTestSetData.idTestGroup, oTestSetData.tsCreated);
1059 oTestCaseDataEx = TestCaseDataEx().initFromDbWithGenId(self._oDb, oTestSetData.idGenTestCase,
1060 oTestSetData.tsConfig);
1061 oTestCaseArgsDataEx = TestCaseArgsDataEx().initFromDbWithGenIdEx(self._oDb, oTestSetData.idGenTestCaseArgs,
1062 oTestSetData.tsConfig);
1063
1064 oContent = WuiTestResult(oDisp = self, fnDPrint = self._oSrvGlue.dprint);
1065 (self._sPageTitle, self._sPageBody) = oContent.showTestCaseResultDetails(oTestResultTree,
1066 oTestSetData,
1067 oBuildDataEx,
1068 oBuildValidationKitDataEx,
1069 oTestBoxData,
1070 oTestGroupData,
1071 oTestCaseDataEx,
1072 oTestCaseArgsDataEx);
1073 return True
1074
1075 def _actionTestSetDetails(self):
1076 """Show test case execution result details."""
1077 from testmanager.core.testset import TestSetData;
1078
1079 idTestSet = self.getIntParam(TestSetData.ksParam_idTestSet);
1080 return self._actionTestSetDetailsCommon(idTestSet);
1081
1082 def _actionTestSetDetailsFromResult(self):
1083 """Show test case execution result details."""
1084 from testmanager.core.testresults import TestResultData;
1085 from testmanager.core.testset import TestSetData;
1086 idTestResult = self.getIntParam(TestSetData.ksParam_idTestResult);
1087 oTestResultData = TestResultData().initFromDbWithId(self._oDb, idTestResult);
1088 return self._actionTestSetDetailsCommon(oTestResultData.idTestSet);
1089
1090
1091 def _actionTestResultFailureAdd(self):
1092 """ Pro forma. """
1093 from testmanager.core.testresultfailures import TestResultFailureData;
1094 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1095 return self._actionGenericFormAdd(TestResultFailureData, WuiTestResultFailure);
1096
1097 def _actionTestResultFailureAddPost(self):
1098 """Add test result failure result"""
1099 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
1100 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1101 if self.ksParamRedirectTo not in self._dParams:
1102 raise WuiException('Missing parameter ' + self.ksParamRedirectTo);
1103
1104 return self._actionGenericFormAddPost(TestResultFailureData, TestResultFailureLogic,
1105 WuiTestResultFailure, self.ksActionResultsUnGrouped);
1106
1107 def _actionTestResultFailureDoRemove(self):
1108 """ Action wrapper. """
1109 from testmanager.core.testresultfailures import TestResultFailureData, TestResultFailureLogic;
1110 return self._actionGenericDoRemove(TestResultFailureLogic, TestResultFailureData.ksParam_idTestResult,
1111 self.ksActionResultsUnGrouped);
1112
1113 def _actionTestResultFailureDetails(self):
1114 """ Pro forma. """
1115 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
1116 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1117 return self._actionGenericFormDetails(TestResultFailureData, TestResultFailureLogic,
1118 WuiTestResultFailure, 'idTestResult');
1119
1120 def _actionTestResultFailureEdit(self):
1121 """ Pro forma. """
1122 from testmanager.core.testresultfailures import TestResultFailureData;
1123 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1124 return self._actionGenericFormEdit(TestResultFailureData, WuiTestResultFailure,
1125 TestResultFailureData.ksParam_idTestResult);
1126
1127 def _actionTestResultFailureEditPost(self):
1128 """Edit test result failure result"""
1129 from testmanager.core.testresultfailures import TestResultFailureLogic, TestResultFailureData;
1130 from testmanager.webui.wuitestresultfailure import WuiTestResultFailure;
1131 return self._actionGenericFormEditPost(TestResultFailureData, TestResultFailureLogic,
1132 WuiTestResultFailure, self.ksActionResultsUnGrouped);
1133
1134 def _actionViewLog(self):
1135 """
1136 Log viewer action.
1137 """
1138 from testmanager.core.testresults import TestResultLogic, TestResultFileDataEx;
1139 from testmanager.core.testset import TestSetData, TestSetLogic;
1140 from testmanager.webui.wuilogviewer import WuiLogViewer;
1141
1142 self._sTemplate = 'template-details.html'; ## @todo create new template (background color, etc)
1143 idTestSet = self.getIntParam(self.ksParamLogSetId, iMin = 1);
1144 idLogFile = self.getIntParam(self.ksParamLogFileId, iMin = 0, iDefault = 0);
1145 cbChunk = self.getIntParam(self.ksParamLogChunkSize, iMin = 256, iMax = 16777216, iDefault = 1024*1024);
1146 iChunk = self.getIntParam(self.ksParamLogChunkNo, iMin = 0,
1147 iMax = config.g_kcMbMaxMainLog * 1048576 / cbChunk, iDefault = 0);
1148 self._checkForUnknownParameters();
1149
1150 oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
1151 if idLogFile == 0:
1152 oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
1153 aoTimestamps = TestResultLogic(self._oDb).fetchTimestampsForLogViewer(idTestSet);
1154 else:
1155 oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idLogFile);
1156 aoTimestamps = [];
1157 if oTestFile.sMime not in [ 'text/plain',]:
1158 raise WuiException('The log view does not display files of type: %s' % (oTestFile.sMime,));
1159
1160 oContent = WuiLogViewer(oTestSet, oTestFile, cbChunk, iChunk, aoTimestamps,
1161 oDisp = self, fnDPrint = self._oSrvGlue.dprint);
1162 (self._sPageTitle, self._sPageBody) = oContent.show();
1163 return True;
1164
1165 def _actionGetFile(self):
1166 """
1167 Get file action.
1168 """
1169 from testmanager.core.testset import TestSetData, TestSetLogic;
1170 from testmanager.core.testresults import TestResultFileDataEx;
1171
1172 idTestSet = self.getIntParam(self.ksParamGetFileSetId, iMin = 1);
1173 idFile = self.getIntParam(self.ksParamGetFileId, iMin = 0, iDefault = 0);
1174 fDownloadIt = self.getBoolParam(self.ksParamGetFileDownloadIt, fDefault = True);
1175 self._checkForUnknownParameters();
1176
1177 #
1178 # Get the file info and open it.
1179 #
1180 oTestSet = TestSetData().initFromDbWithId(self._oDb, idTestSet);
1181 if idFile == 0:
1182 oTestFile = TestResultFileDataEx().initFakeMainLog(oTestSet);
1183 else:
1184 oTestFile = TestSetLogic(self._oDb).getFile(idTestSet, idFile);
1185
1186 (oFile, oSizeOrError, _) = oTestSet.openFile(oTestFile.sFile, 'rb');
1187 if oFile is None:
1188 raise Exception(oSizeOrError);
1189
1190 #
1191 # Send the file.
1192 #
1193 self._oSrvGlue.setHeaderField('Content-Type', oTestFile.getMimeWithEncoding());
1194 if fDownloadIt:
1195 self._oSrvGlue.setHeaderField('Content-Disposition', 'attachment; filename="TestSet-%d-%s"'
1196 % (idTestSet, oTestFile.sFile,));
1197 while True:
1198 abChunk = oFile.read(262144);
1199 if len(abChunk) == 0:
1200 break;
1201 self._oSrvGlue.writeRaw(abChunk);
1202 return self.ksDispatchRcAllDone;
1203
1204 def _actionGenericReport(self, oModelType, oFilterType, oReportType):
1205 """
1206 Generic report action.
1207 oReportType is a child of WuiReportContentBase.
1208 oFilterType is a child of ModelFilterBase.
1209 oModelType is a child of ReportModelBase.
1210 """
1211 from testmanager.core.report import ReportModelBase;
1212
1213 tsEffective = self.getEffectiveDateParam();
1214 cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 2, iMax = 99, iDefault = 7);
1215 cHoursPerPeriod = self.getIntParam(self.ksParamReportPeriodInHours, iMin = 1, iMax = 168, iDefault = 24);
1216 sSubject = self.getStringParam(self.ksParamReportSubject, ReportModelBase.kasSubjects,
1217 ReportModelBase.ksSubEverything);
1218 if sSubject == ReportModelBase.ksSubEverything:
1219 aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, aiDefaults = []);
1220 else:
1221 aidSubjects = self.getListOfIntParams(self.ksParamReportSubjectIds, iMin = 1);
1222 if aidSubjects is None:
1223 raise WuiException('Missing parameter %s' % (self.ksParamReportSubjectIds,));
1224 oFilter = oFilterType().initFromParams(self);
1225 self._checkForUnknownParameters();
1226
1227 dParams = \
1228 {
1229 self.ksParamEffectiveDate: tsEffective,
1230 self.ksParamReportPeriods: cPeriods,
1231 self.ksParamReportPeriodInHours: cHoursPerPeriod,
1232 self.ksParamReportSubject: sSubject,
1233 self.ksParamReportSubjectIds: aidSubjects,
1234 };
1235 ## @todo oFilter.
1236
1237 oModel = oModelType(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, aidSubjects, oFilter);
1238 oContent = oReportType(oModel, dParams, fSubReport = False, fnDPrint = self._oSrvGlue.dprint, oDisp = self);
1239 (self._sPageTitle, self._sPageBody) = oContent.show();
1240 sNavi = self._generateReportNavigation(tsEffective, cHoursPerPeriod, cPeriods);
1241 self._sPageBody = sNavi + self._sPageBody;
1242
1243 if hasattr(oFilter, 'kiBranches'):
1244 oFilter.aCriteria[oFilter.kiBranches].fExpanded = True;
1245 self._sPageFilter = self._generateResultFilter(oFilter, oModel, tsEffective, '%s hours' % (cHoursPerPeriod * cPeriods,));
1246 return True;
1247
1248 def _actionReportSummary(self):
1249 """ Action wrapper. """
1250 from testmanager.core.report import ReportLazyModel, ReportFilter;
1251 from testmanager.webui.wuireport import WuiReportSummary;
1252 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSummary);
1253
1254 def _actionReportRate(self):
1255 """ Action wrapper. """
1256 from testmanager.core.report import ReportLazyModel, ReportFilter;
1257 from testmanager.webui.wuireport import WuiReportSuccessRate;
1258 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportSuccessRate);
1259
1260 def _actionReportTestCaseFailures(self):
1261 """ Action wrapper. """
1262 from testmanager.core.report import ReportLazyModel, ReportFilter;
1263 from testmanager.webui.wuireport import WuiReportTestCaseFailures;
1264 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportTestCaseFailures);
1265
1266 def _actionReportFailureReasons(self):
1267 """ Action wrapper. """
1268 from testmanager.core.report import ReportLazyModel, ReportFilter;
1269 from testmanager.webui.wuireport import WuiReportFailureReasons;
1270 return self._actionGenericReport(ReportLazyModel, ReportFilter, WuiReportFailureReasons);
1271
1272 def _actionGraphWiz(self):
1273 """
1274 Graph wizard action.
1275 """
1276 from testmanager.core.report import ReportModelBase, ReportGraphModel;
1277 from testmanager.webui.wuigraphwiz import WuiGraphWiz;
1278 self._sTemplate = 'template-graphwiz.html';
1279
1280 tsEffective = self.getEffectiveDateParam();
1281 cPeriods = self.getIntParam(self.ksParamReportPeriods, iMin = 1, iMax = 1, iDefault = 1); # Not needed yet.
1282 sTmp = self.getStringParam(self.ksParamReportPeriodInHours, sDefault = '3 weeks');
1283 (cHoursPerPeriod, sError) = utils.parseIntervalHours(sTmp);
1284 if sError is not None: raise WuiException(sError);
1285 asSubjectIds = self.getListOfStrParams(self.ksParamReportSubjectIds);
1286 sSubject = self.getStringParam(self.ksParamReportSubject, [ReportModelBase.ksSubEverything],
1287 ReportModelBase.ksSubEverything); # dummy
1288 aidTestBoxes = self.getListOfIntParams(self.ksParamGraphWizTestBoxIds, iMin = 1, aiDefaults = []);
1289 aidBuildCats = self.getListOfIntParams(self.ksParamGraphWizBuildCatIds, iMin = 1, aiDefaults = []);
1290 aidTestCases = self.getListOfIntParams(self.ksParamGraphWizTestCaseIds, iMin = 1, aiDefaults = []);
1291 fSepTestVars = self.getBoolParam(self.ksParamGraphWizSepTestVars, fDefault = False);
1292
1293 enmGraphImpl = self.getStringParam(self.ksParamGraphWizImpl, asValidValues = self.kasGraphWizImplValid,
1294 sDefault = self.ksGraphWizImpl_Default);
1295 cx = self.getIntParam(self.ksParamGraphWizWidth, iMin = 128, iMax = 8192, iDefault = 1280);
1296 cy = self.getIntParam(self.ksParamGraphWizHeight, iMin = 128, iMax = 8192, iDefault = int(cx * 5 / 16) );
1297 cDotsPerInch = self.getIntParam(self.ksParamGraphWizDpi, iMin = 64, iMax = 512, iDefault = 96);
1298 cPtFont = self.getIntParam(self.ksParamGraphWizFontSize, iMin = 6, iMax = 32, iDefault = 8);
1299 fErrorBarY = self.getBoolParam(self.ksParamGraphWizErrorBarY, fDefault = False);
1300 cMaxErrorBarY = self.getIntParam(self.ksParamGraphWizMaxErrorBarY, iMin = 8, iMax = 9999999, iDefault = 18);
1301 cMaxPerGraph = self.getIntParam(self.ksParamGraphWizMaxPerGraph, iMin = 1, iMax = 24, iDefault = 8);
1302 fXkcdStyle = self.getBoolParam(self.ksParamGraphWizXkcdStyle, fDefault = False);
1303 fTabular = self.getBoolParam(self.ksParamGraphWizTabular, fDefault = False);
1304 idSrcTestSet = self.getIntParam(self.ksParamGraphWizSrcTestSetId, iDefault = None);
1305 self._checkForUnknownParameters();
1306
1307 dParams = \
1308 {
1309 self.ksParamEffectiveDate: tsEffective,
1310 self.ksParamReportPeriods: cPeriods,
1311 self.ksParamReportPeriodInHours: cHoursPerPeriod,
1312 self.ksParamReportSubject: sSubject,
1313 self.ksParamReportSubjectIds: asSubjectIds,
1314 self.ksParamGraphWizTestBoxIds: aidTestBoxes,
1315 self.ksParamGraphWizBuildCatIds: aidBuildCats,
1316 self.ksParamGraphWizTestCaseIds: aidTestCases,
1317 self.ksParamGraphWizSepTestVars: fSepTestVars,
1318
1319 self.ksParamGraphWizImpl: enmGraphImpl,
1320 self.ksParamGraphWizWidth: cx,
1321 self.ksParamGraphWizHeight: cy,
1322 self.ksParamGraphWizDpi: cDotsPerInch,
1323 self.ksParamGraphWizFontSize: cPtFont,
1324 self.ksParamGraphWizErrorBarY: fErrorBarY,
1325 self.ksParamGraphWizMaxErrorBarY: cMaxErrorBarY,
1326 self.ksParamGraphWizMaxPerGraph: cMaxPerGraph,
1327 self.ksParamGraphWizXkcdStyle: fXkcdStyle,
1328 self.ksParamGraphWizTabular: fTabular,
1329 self.ksParamGraphWizSrcTestSetId: idSrcTestSet,
1330 };
1331
1332 oModel = ReportGraphModel(self._oDb, tsEffective, cPeriods, cHoursPerPeriod, sSubject, asSubjectIds,
1333 aidTestBoxes, aidBuildCats, aidTestCases, fSepTestVars);
1334 oContent = WuiGraphWiz(oModel, dParams, fSubReport = False, fnDPrint = self._oSrvGlue.dprint, oDisp = self);
1335 (self._sPageTitle, self._sPageBody) = oContent.show();
1336 return True;
1337
1338 def _actionVcsHistoryTooltip(self):
1339 """
1340 Version control system history.
1341 """
1342 from testmanager.webui.wuivcshistory import WuiVcsHistoryTooltip;
1343 from testmanager.core.vcsrevisions import VcsRevisionLogic;
1344
1345 self._sTemplate = 'template-tooltip.html';
1346 iRevision = self.getIntParam(self.ksParamVcsHistoryRevision, iMin = 0, iMax = 999999999);
1347 sRepository = self.getStringParam(self.ksParamVcsHistoryRepository);
1348 cEntries = self.getIntParam(self.ksParamVcsHistoryEntries, iMin = 1, iMax = 1024, iDefault = 8);
1349 self._checkForUnknownParameters();
1350
1351 aoEntries = VcsRevisionLogic(self._oDb).fetchTimeline(sRepository, iRevision, cEntries);
1352 oContent = WuiVcsHistoryTooltip(aoEntries, sRepository, iRevision, cEntries,
1353 fnDPrint = self._oSrvGlue.dprint, oDisp = self);
1354 (self._sPageTitle, self._sPageBody) = oContent.show();
1355 return True;
1356
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