VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuigraphwiz.py@ 77922

Last change on this file since 77922 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuigraphwiz.py 76553 2019-01-01 01:45:53Z vboxsync $
3
4"""
5Test Manager WUI - Graph Wizard
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2019 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: 76553 $"
30
31# Python imports.
32import functools;
33
34# Validation Kit imports.
35from testmanager.webui.wuimain import WuiMain;
36from testmanager.webui.wuihlpgraph import WuiHlpLineGraphErrorbarY, WuiHlpGraphDataTableEx;
37from testmanager.webui.wuireport import WuiReportBase;
38
39from common import utils, webutils;
40from common import constants;
41
42
43class WuiGraphWiz(WuiReportBase):
44 """Construct a graph for analyzing test results (values) across builds and testboxes."""
45
46 ## @name Series name parts.
47 ## @{
48 kfSeriesName_TestBox = 1;
49 kfSeriesName_Product = 2;
50 kfSeriesName_Branch = 4;
51 kfSeriesName_BuildType = 8;
52 kfSeriesName_OsArchs = 16;
53 kfSeriesName_TestCase = 32;
54 kfSeriesName_TestCaseArgs = 64;
55 kfSeriesName_All = 127;
56 ## @}
57
58
59 def __init__(self, oModel, dParams, fSubReport = False, fnDPrint = None, oDisp = None):
60 WuiReportBase.__init__(self, oModel, dParams, fSubReport = fSubReport, fnDPrint = fnDPrint, oDisp = oDisp);
61
62 # Select graph implementation.
63 if dParams[WuiMain.ksParamGraphWizImpl] == 'charts':
64 from testmanager.webui.wuihlpgraphgooglechart import WuiHlpLineGraphErrorbarY as MyGraph;
65 self.oGraphClass = MyGraph;
66 elif dParams[WuiMain.ksParamGraphWizImpl] == 'matplotlib':
67 from testmanager.webui.wuihlpgraphmatplotlib import WuiHlpLineGraphErrorbarY as MyGraph;
68 self.oGraphClass = MyGraph;
69 else:
70 self.oGraphClass = WuiHlpLineGraphErrorbarY;
71
72
73 #
74 def _figureSeriesNameBits(self, aoSeries):
75 """ Figures out the method (bitmask) to use when naming series. """
76 if len(aoSeries) <= 1:
77 return WuiGraphWiz.kfSeriesName_TestBox;
78
79 # Start with all and drop unnecessary specs one-by-one.
80 fRet = WuiGraphWiz.kfSeriesName_All;
81
82 if [oSrs.idTestBox for oSrs in aoSeries].count(aoSeries[0].idTestBox) == len(aoSeries):
83 fRet &= ~WuiGraphWiz.kfSeriesName_TestBox;
84
85 if [oSrs.idBuildCategory for oSrs in aoSeries].count(aoSeries[0].idBuildCategory) == len(aoSeries):
86 fRet &= ~WuiGraphWiz.kfSeriesName_Product;
87 fRet &= ~WuiGraphWiz.kfSeriesName_Branch;
88 fRet &= ~WuiGraphWiz.kfSeriesName_BuildType;
89 fRet &= ~WuiGraphWiz.kfSeriesName_OsArchs;
90 else:
91 if [oSrs.oBuildCategory.sProduct for oSrs in aoSeries].count(aoSeries[0].oBuildCategory.sProduct) == len(aoSeries):
92 fRet &= ~WuiGraphWiz.kfSeriesName_Product;
93 if [oSrs.oBuildCategory.sBranch for oSrs in aoSeries].count(aoSeries[0].oBuildCategory.sBranch) == len(aoSeries):
94 fRet &= ~WuiGraphWiz.kfSeriesName_Branch;
95 if [oSrs.oBuildCategory.sType for oSrs in aoSeries].count(aoSeries[0].oBuildCategory.sType) == len(aoSeries):
96 fRet &= ~WuiGraphWiz.kfSeriesName_BuildType;
97
98 # Complicated.
99 fRet &= ~WuiGraphWiz.kfSeriesName_OsArchs;
100 daTestBoxes = {};
101 for oSeries in aoSeries:
102 if oSeries.idTestBox in daTestBoxes:
103 daTestBoxes[oSeries.idTestBox].append(oSeries);
104 else:
105 daTestBoxes[oSeries.idTestBox] = [oSeries,];
106 for aoSeriesPerTestBox in daTestBoxes.values():
107 if len(aoSeriesPerTestBox) >= 0:
108 asOsArches = aoSeriesPerTestBox[0].oBuildCategory.asOsArches;
109 for i in range(1, len(aoSeriesPerTestBox)):
110 if aoSeriesPerTestBox[i].oBuildCategory.asOsArches != asOsArches:
111 fRet |= WuiGraphWiz.kfSeriesName_OsArchs;
112 break;
113
114 if aoSeries[0].oTestCaseArgs is None:
115 fRet &= ~WuiGraphWiz.kfSeriesName_TestCaseArgs;
116 if [oSrs.idTestCase for oSrs in aoSeries].count(aoSeries[0].idTestCase) == len(aoSeries):
117 fRet &= ~WuiGraphWiz.kfSeriesName_TestCase;
118 else:
119 fRet &= ~WuiGraphWiz.kfSeriesName_TestCase;
120 if [oSrs.idTestCaseArgs for oSrs in aoSeries].count(aoSeries[0].idTestCaseArgs) == len(aoSeries):
121 fRet &= ~WuiGraphWiz.kfSeriesName_TestCaseArgs;
122
123 return fRet;
124
125 def _getSeriesNameFromBits(self, oSeries, fBits):
126 """ Creates a series name from bits (kfSeriesName_xxx). """
127 assert fBits != 0;
128 sName = '';
129
130 if fBits & WuiGraphWiz.kfSeriesName_Product:
131 if sName: sName += ' / ';
132 sName += oSeries.oBuildCategory.sProduct;
133
134 if fBits & WuiGraphWiz.kfSeriesName_Branch:
135 if sName: sName += ' / ';
136 sName += oSeries.oBuildCategory.sBranch;
137
138 if fBits & WuiGraphWiz.kfSeriesName_BuildType:
139 if sName: sName += ' / ';
140 sName += oSeries.oBuildCategory.sType;
141
142 if fBits & WuiGraphWiz.kfSeriesName_OsArchs:
143 if sName: sName += ' / ';
144 sName += ' & '.join(oSeries.oBuildCategory.asOsArches);
145
146 if fBits & WuiGraphWiz.kfSeriesName_TestCaseArgs:
147 if sName: sName += ' / ';
148 if oSeries.idTestCaseArgs is not None:
149 sName += oSeries.oTestCase.sName + ':#' + str(oSeries.idTestCaseArgs);
150 else:
151 sName += oSeries.oTestCase.sName;
152 elif fBits & WuiGraphWiz.kfSeriesName_TestCase:
153 if sName: sName += ' / ';
154 sName += oSeries.oTestCase.sName;
155
156 if fBits & WuiGraphWiz.kfSeriesName_TestBox:
157 if sName: sName += ' / ';
158 sName += oSeries.oTestBox.sName;
159
160 return sName;
161
162 def _calcGraphName(self, oSeries, fSeriesName, sSampleName):
163 """ Constructs a name for the graph. """
164 fGraphName = ~fSeriesName & ( WuiGraphWiz.kfSeriesName_TestBox
165 | WuiGraphWiz.kfSeriesName_Product
166 | WuiGraphWiz.kfSeriesName_Branch
167 | WuiGraphWiz.kfSeriesName_BuildType
168 );
169 sName = self._getSeriesNameFromBits(oSeries, fGraphName);
170 if sName: sName += ' - ';
171 sName += sSampleName;
172 return sName;
173
174 def _calcSampleName(self, oCollection):
175 """ Constructs a name for a sample source (collection). """
176 if oCollection.sValue is not None:
177 asSampleName = [oCollection.sValue, 'in',];
178 elif oCollection.sType == self._oModel.ksTypeElapsed:
179 asSampleName = ['Elapsed time', 'for', ];
180 elif oCollection.sType == self._oModel.ksTypeResult:
181 asSampleName = ['Error count', 'for',];
182 else:
183 return 'Invalid collection type: "%s"' % (oCollection.sType,);
184
185 sTestName = ', '.join(oCollection.asTests if oCollection.asTests[0] else oCollection.asTests[1:]);
186 if sTestName == '':
187 # Use the testcase name if there is only one for all series.
188 if not oCollection.aoSeries:
189 return asSampleName[0];
190 if len(oCollection.aoSeries) > 1:
191 idTestCase = oCollection.aoSeries[0].idTestCase;
192 for oSeries in oCollection.aoSeries:
193 if oSeries.idTestCase != idTestCase:
194 return asSampleName[0];
195 sTestName = oCollection.aoSeries[0].oTestCase.sName;
196 return ' '.join(asSampleName) + ' ' + sTestName;
197
198
199 def _splitSeries(self, aoSeries):
200 """
201 Splits the data series (ReportGraphModel.DataSeries) into one or more graphs.
202
203 Returns an array of data series arrays.
204 """
205 # Must be at least two series for something to be splittable.
206 if len(aoSeries) <= 1:
207 if len(aoSeries) < 1:
208 return [];
209 return [aoSeries,];
210
211 # Split on unit.
212 dUnitSeries = dict();
213 for oSeries in aoSeries:
214 if oSeries.iUnit not in dUnitSeries:
215 dUnitSeries[oSeries.iUnit] = [];
216 dUnitSeries[oSeries.iUnit].append(oSeries);
217
218 # Sort the per-unit series since the build category was only sorted by ID.
219 for iUnit in dUnitSeries:
220 def mycmp(oSelf, oOther):
221 """ __cmp__ like function. """
222 iCmp = utils.stricmp(oSelf.oBuildCategory.sProduct, oOther.oBuildCategory.sProduct);
223 if iCmp != 0:
224 return iCmp;
225 iCmp = utils.stricmp(oSelf.oBuildCategory.sBranch, oOther.oBuildCategory.sBranch);
226 if iCmp != 0:
227 return iCmp;
228 iCmp = utils.stricmp(oSelf.oBuildCategory.sType, oOther.oBuildCategory.sType);
229 if iCmp != 0:
230 return iCmp;
231 iCmp = utils.stricmp(oSelf.oTestBox.sName, oOther.oTestBox.sName);
232 if iCmp != 0:
233 return iCmp;
234 return 0;
235 dUnitSeries[iUnit] = sorted(dUnitSeries[iUnit], key = functools.cmp_to_key(mycmp));
236
237 # Split the per-unit series up if necessary.
238 cMaxPerGraph = self._dParams[WuiMain.ksParamGraphWizMaxPerGraph];
239 aaoRet = [];
240 for iUnit in dUnitSeries:
241 aoUnitSeries = dUnitSeries[iUnit];
242 while len(aoUnitSeries) > cMaxPerGraph:
243 aaoRet.append(aoUnitSeries[:cMaxPerGraph]);
244 aoUnitSeries = aoUnitSeries[cMaxPerGraph:];
245 if aoUnitSeries:
246 aaoRet.append(aoUnitSeries);
247
248 return aaoRet;
249
250 def _configureGraph(self, oGraph):
251 """
252 Configures oGraph according to user parameters and other config settings.
253
254 Returns oGraph.
255 """
256 oGraph.setWidth(self._dParams[WuiMain.ksParamGraphWizWidth])
257 oGraph.setHeight(self._dParams[WuiMain.ksParamGraphWizHeight])
258 oGraph.setDpi(self._dParams[WuiMain.ksParamGraphWizDpi])
259 oGraph.setErrorBarY(self._dParams[WuiMain.ksParamGraphWizErrorBarY]);
260 oGraph.setFontSize(self._dParams[WuiMain.ksParamGraphWizFontSize]);
261 if hasattr(oGraph, 'setXkcdStyle'):
262 oGraph.setXkcdStyle(self._dParams[WuiMain.ksParamGraphWizXkcdStyle]);
263
264 return oGraph;
265
266 def _generateInteractiveForm(self):
267 """
268 Generates the HTML for the interactive form.
269 Returns (sTopOfForm, sEndOfForm)
270 """
271
272 #
273 # The top of the form.
274 #
275 sTop = '<form action="#" method="get" id="graphwiz-form">\n' \
276 ' <input type="hidden" name="%s" value="%s"/>\n' \
277 ' <input type="hidden" name="%s" value="%u"/>\n' \
278 % ( WuiMain.ksParamAction, WuiMain.ksActionGraphWiz,
279 WuiMain.ksParamGraphWizSrcTestSetId, self._dParams[WuiMain.ksParamGraphWizSrcTestSetId],
280 );
281
282 sTop += ' <div id="graphwiz-nav">\n';
283 sTop += ' <script type="text/javascript">\n' \
284 ' window.onresize = function(){ return graphwizOnResizeRecalcWidth("graphwiz-nav", "%s"); }\n' \
285 ' window.onload = function(){ return graphwizOnLoadRememberWidth("graphwiz-nav"); }\n' \
286 ' </script>\n' \
287 % ( WuiMain.ksParamGraphWizWidth, );
288
289 #
290 # Top: First row.
291 #
292 sTop += ' <div id="graphwiz-top-1">\n';
293
294 # time.
295 sNow = self._dParams[WuiMain.ksParamEffectiveDate];
296 if sNow is None: sNow = '';
297 sTop += ' <div id="graphwiz-time">\n';
298 sTop += ' <label for="%s">Starting:</label>\n' \
299 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-time-input"/>\n' \
300 % ( WuiMain.ksParamEffectiveDate,
301 WuiMain.ksParamEffectiveDate, WuiMain.ksParamEffectiveDate, sNow, );
302
303 sTop += ' <input type="hidden" name="%s" value="%u"/>\n' % ( WuiMain.ksParamReportPeriods, 1, );
304 sTop += ' <label for="%s"> Going back:\n' \
305 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-period-input"/>\n' \
306 % ( WuiMain.ksParamReportPeriodInHours,
307 WuiMain.ksParamReportPeriodInHours, WuiMain.ksParamReportPeriodInHours,
308 utils.formatIntervalHours(self._dParams[WuiMain.ksParamReportPeriodInHours]) );
309 sTop += ' </div>\n';
310
311 # Graph options top row.
312 sTop += ' <div id="graphwiz-top-options-1">\n';
313
314 # graph type.
315 sTop += ' <label for="%s">Graph:</label>\n' \
316 ' <select name="%s" id="%s">\n' \
317 % ( WuiMain.ksParamGraphWizImpl, WuiMain.ksParamGraphWizImpl, WuiMain.ksParamGraphWizImpl, );
318 for (sImpl, sDesc) in WuiMain.kaasGraphWizImplCombo:
319 sTop += ' <option value="%s"%s>%s</option>\n' \
320 % (sImpl, ' selected' if sImpl == self._dParams[WuiMain.ksParamGraphWizImpl] else '', sDesc);
321 sTop += ' </select>\n';
322
323 # graph size.
324 sTop += ' <label for="%s">Graph size:</label>\n' \
325 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-pixel-input"> x\n' \
326 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-pixel-input">\n' \
327 ' <label for="%s">Dpi:</label>'\
328 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-dpi-input">\n' \
329 ' <button type="button" onclick="%s">Defaults</button>\n' \
330 % ( WuiMain.ksParamGraphWizWidth,
331 WuiMain.ksParamGraphWizWidth, WuiMain.ksParamGraphWizWidth, self._dParams[WuiMain.ksParamGraphWizWidth],
332 WuiMain.ksParamGraphWizHeight, WuiMain.ksParamGraphWizHeight, self._dParams[WuiMain.ksParamGraphWizHeight],
333 WuiMain.ksParamGraphWizDpi,
334 WuiMain.ksParamGraphWizDpi, WuiMain.ksParamGraphWizDpi, self._dParams[WuiMain.ksParamGraphWizDpi],
335 webutils.escapeAttr('return graphwizSetDefaultSizeValues("graphwiz-nav", "%s", "%s", "%s");'
336 % ( WuiMain.ksParamGraphWizWidth, WuiMain.ksParamGraphWizHeight,
337 WuiMain.ksParamGraphWizDpi )),
338 );
339
340 sTop += ' </div>\n'; # (options row 1)
341
342 sTop += ' </div>\n'; # (end of row 1)
343
344 #
345 # Top: Second row.
346 #
347 sTop += ' <div id="graphwiz-top-2">\n';
348
349 # Submit
350 sFormButton = '<button type="submit">Refresh</button>\n';
351 sTop += ' <div id="graphwiz-top-submit">' + sFormButton + '</div>\n';
352
353
354 # Options.
355 sTop += ' <div id="graphwiz-top-options-2">\n';
356
357 sTop += ' <input type="checkbox" name="%s" id="%s" value="1"%s/>\n' \
358 ' <label for="%s">Tabular data</label>\n' \
359 % ( WuiMain.ksParamGraphWizTabular, WuiMain.ksParamGraphWizTabular,
360 ' checked' if self._dParams[WuiMain.ksParamGraphWizTabular] else '',
361 WuiMain.ksParamGraphWizTabular);
362
363 if hasattr(self.oGraphClass, 'setXkcdStyle'):
364 sTop += ' <input type="checkbox" name="%s" id="%s" value="1"%s/>\n' \
365 ' <label for="%s">xkcd-style</label>\n' \
366 % ( WuiMain.ksParamGraphWizXkcdStyle, WuiMain.ksParamGraphWizXkcdStyle,
367 ' checked' if self._dParams[WuiMain.ksParamGraphWizXkcdStyle] else '',
368 WuiMain.ksParamGraphWizXkcdStyle);
369 elif self._dParams[WuiMain.ksParamGraphWizXkcdStyle]:
370 sTop += ' <input type="hidden" name="%s" id="%s" value="1"/>\n' \
371 % ( WuiMain.ksParamGraphWizXkcdStyle, WuiMain.ksParamGraphWizXkcdStyle, );
372
373 if not hasattr(self.oGraphClass, 'kfNoErrorBarsSupport'):
374 sTop += ' <input type="checkbox" name="%s" id="%s" value="1"%s title="%s"/>\n' \
375 ' <label for="%s">Error bars,</label>\n' \
376 ' <label for="%s">max: </label>\n' \
377 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-maxerrorbar-input" title="%s"/>\n' \
378 % ( WuiMain.ksParamGraphWizErrorBarY, WuiMain.ksParamGraphWizErrorBarY,
379 ' checked' if self._dParams[WuiMain.ksParamGraphWizErrorBarY] else '',
380 'Error bars shows some of the max and min results on the Y-axis.',
381 WuiMain.ksParamGraphWizErrorBarY,
382 WuiMain.ksParamGraphWizMaxErrorBarY,
383 WuiMain.ksParamGraphWizMaxErrorBarY, WuiMain.ksParamGraphWizMaxErrorBarY,
384 self._dParams[WuiMain.ksParamGraphWizMaxErrorBarY],
385 'Maximum number of Y-axis error bar per graph. (Too many makes it unreadable.)'
386 );
387 else:
388 if self._dParams[WuiMain.ksParamGraphWizErrorBarY]:
389 sTop += '<input type="hidden" name="%s" id="%s" value="1">\n' \
390 % ( WuiMain.ksParamGraphWizErrorBarY, WuiMain.ksParamGraphWizErrorBarY, );
391 sTop += '<input type="hidden" name="%s" id="%s" value="%u">\n' \
392 % ( WuiMain.ksParamGraphWizMaxErrorBarY, WuiMain.ksParamGraphWizMaxErrorBarY,
393 self._dParams[WuiMain.ksParamGraphWizMaxErrorBarY], );
394
395 sTop += ' <label for="%s">Font size: </label>\n' \
396 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-fontsize-input"/>\n' \
397 % ( WuiMain.ksParamGraphWizFontSize,
398 WuiMain.ksParamGraphWizFontSize, WuiMain.ksParamGraphWizFontSize,
399 self._dParams[WuiMain.ksParamGraphWizFontSize], );
400
401 sTop += ' <label for="%s">Data series: </label>\n' \
402 ' <input type="text" name="%s" id="%s" value="%s" class="graphwiz-maxpergraph-input" title="%s"/>\n' \
403 % ( WuiMain.ksParamGraphWizMaxPerGraph,
404 WuiMain.ksParamGraphWizMaxPerGraph, WuiMain.ksParamGraphWizMaxPerGraph,
405 self._dParams[WuiMain.ksParamGraphWizMaxPerGraph],
406 'Max data series per graph.' );
407
408 sTop += ' </div>\n'; # (options row 2)
409
410 sTop += ' </div>\n'; # (end of row 2)
411
412 sTop += ' </div>\n'; # end of top.
413
414 #
415 # The end of the page selection.
416 #
417 sEnd = ' <div id="graphwiz-end-selection">\n';
418
419 #
420 # Testbox selection
421 #
422 aidTestBoxes = list(self._dParams[WuiMain.ksParamGraphWizTestBoxIds]);
423 sEnd += ' <div id="graphwiz-testboxes" class="graphwiz-end-selection-group">\n' \
424 ' <h3>TestBox Selection:</h3>\n' \
425 ' <ol class="tmgraph-testboxes">\n';
426
427 # Get a list of eligible testboxes from the DB.
428 for oTestBox in self._oModel.getEligibleTestBoxes():
429 try: aidTestBoxes.remove(oTestBox.idTestBox);
430 except: sChecked = '';
431 else: sChecked = ' checked';
432 sEnd += ' <li><input type="checkbox" name="%s" value="%s" id="gw-tb-%u"%s/>' \
433 '<label for="gw-tb-%u">%s</label></li>\n' \
434 % ( WuiMain.ksParamGraphWizTestBoxIds, oTestBox.idTestBox, oTestBox.idTestBox, sChecked,
435 oTestBox.idTestBox, oTestBox.sName);
436
437 # List testboxes that have been checked in a different period or something.
438 for idTestBox in aidTestBoxes:
439 oTestBox = self._oModel.oCache.getTestBox(idTestBox);
440 sEnd += ' <li><input type="checkbox" name="%s" value="%s" id="gw-tb-%u" checked/>' \
441 '<label for="gw-tb-%u">%s</label></li>\n' \
442 % ( WuiMain.ksParamGraphWizTestBoxIds, oTestBox.idTestBox, oTestBox.idTestBox,
443 oTestBox.idTestBox, oTestBox.sName);
444
445 sEnd += ' </ol>\n' \
446 ' </div>\n';
447
448 #
449 # Build category selection.
450 #
451 aidBuildCategories = list(self._dParams[WuiMain.ksParamGraphWizBuildCatIds]);
452 sEnd += ' <div id="graphwiz-buildcategories" class="graphwiz-end-selection-group">\n' \
453 ' <h3>Build Category Selection:</h3>\n' \
454 ' <ol class="tmgraph-buildcategories">\n';
455 for oBuildCat in self._oModel.getEligibleBuildCategories():
456 try: aidBuildCategories.remove(oBuildCat.idBuildCategory);
457 except: sChecked = '';
458 else: sChecked = ' checked';
459 sEnd += ' <li><input type="checkbox" name="%s" value="%s" id="gw-bc-%u" %s/>' \
460 '<label for="gw-bc-%u">%s / %s / %s / %s</label></li>\n' \
461 % ( WuiMain.ksParamGraphWizBuildCatIds, oBuildCat.idBuildCategory, oBuildCat.idBuildCategory, sChecked,
462 oBuildCat.idBuildCategory,
463 oBuildCat.sProduct, oBuildCat.sBranch, oBuildCat.sType, ' & '.join(oBuildCat.asOsArches) );
464 assert not aidBuildCategories; # SQL should return all currently selected.
465
466 sEnd += ' </ol>\n' \
467 ' </div>\n';
468
469 #
470 # Testcase variations.
471 #
472 sEnd += ' <div id="graphwiz-testcase-variations" class="graphwiz-end-selection-group">\n' \
473 ' <h3>Miscellaneous:</h3>\n' \
474 ' <ol>';
475
476 sEnd += ' <li>\n' \
477 ' <input type="checkbox" id="%s" name="%s" value="1"%s/>\n' \
478 ' <label for="%s">Separate by testcase variation.</label>\n' \
479 ' </li>\n' \
480 % ( WuiMain.ksParamGraphWizSepTestVars, WuiMain.ksParamGraphWizSepTestVars,
481 ' checked' if self._dParams[WuiMain.ksParamGraphWizSepTestVars] else '',
482 WuiMain.ksParamGraphWizSepTestVars );
483
484
485 sEnd += ' <li>\n' \
486 ' <lable for="%s">Test case ID:</label>\n' \
487 ' <input type="text" id="%s" name="%s" value="%s" readonly/>\n' \
488 ' </li>\n' \
489 % ( WuiMain.ksParamGraphWizTestCaseIds,
490 WuiMain.ksParamGraphWizTestCaseIds, WuiMain.ksParamGraphWizTestCaseIds,
491 ','.join([str(i) for i in self._dParams[WuiMain.ksParamGraphWizTestCaseIds]]), );
492
493 sEnd += ' </ol>\n' \
494 ' </div>\n';
495
496 #sEnd += ' <h3>&nbsp;</h3>\n';
497
498 #
499 # Finish up the form.
500 #
501 sEnd += ' <div id="graphwiz-end-submit"><p>' + sFormButton + '</p></div>\n';
502 sEnd += ' </div>\n' \
503 '</form>\n';
504
505 return (sTop, sEnd);
506
507 def generateReportBody(self):
508 fInteractive = not self._fSubReport;
509
510 # Quick mockup.
511 self._sTitle = 'Graph Wizzard';
512
513 sHtml = '';
514 sHtml += '<h2>Incomplete code - no complaints yet, thank you!!</h2>\n';
515
516 #
517 # Create a form for altering the data we're working with.
518 #
519 if fInteractive:
520 (sTopOfForm, sEndOfForm) = self._generateInteractiveForm();
521 sHtml += sTopOfForm;
522 del sTopOfForm;
523
524 #
525 # Emit the graphs. At least one per sample source.
526 #
527 sHtml += ' <div id="graphwiz-graphs">\n';
528 iGraph = 0;
529 aoCollections = self._oModel.fetchGraphData();
530 for iCollection, oCollection in enumerate(aoCollections):
531 # Name the graph and add a checkbox for removing it.
532 sSampleName = self._calcSampleName(oCollection);
533 sHtml += ' <div class="graphwiz-collection" id="graphwiz-source-%u">\n' % (iCollection,);
534 if fInteractive:
535 sHtml += ' <div class="graphwiz-src-select">\n' \
536 ' <input type="checkbox" name="%s" id="%s" value="%s:%s%s" checked class="graphwiz-src-input">\n' \
537 ' <label for="%s">%s</label>\n' \
538 ' </div>\n' \
539 % ( WuiMain.ksParamReportSubjectIds, WuiMain.ksParamReportSubjectIds, oCollection.sType,
540 ':'.join([str(idStr) for idStr in oCollection.aidStrTests]),
541 ':%u' % oCollection.idStrValue if oCollection.idStrValue else '',
542 WuiMain.ksParamReportSubjectIds, sSampleName );
543
544 if oCollection.aoSeries:
545 #
546 # Split the series into sub-graphs as needed and produce SVGs.
547 #
548 aaoSeries = self._splitSeries(oCollection.aoSeries);
549 for aoSeries in aaoSeries:
550 # Gather the data for this graph. (Most big stuff is passed by
551 # reference, so there shouldn't be any large memory penalty for
552 # repacking the data here.)
553 sYUnit = None;
554 if aoSeries[0].iUnit < len(constants.valueunit.g_asNames) and aoSeries[0].iUnit > 0:
555 sYUnit = constants.valueunit.g_asNames[aoSeries[0].iUnit];
556 oData = WuiHlpGraphDataTableEx(sXUnit = 'Build revision', sYUnit = sYUnit);
557
558 fSeriesName = self._figureSeriesNameBits(aoSeries);
559 for oSeries in aoSeries:
560 sSeriesName = self._getSeriesNameFromBits(oSeries, fSeriesName);
561 asHtmlTooltips = None;
562 if len(oSeries.aoRevInfo) == len(oSeries.aiRevisions):
563 asHtmlTooltips = [];
564 for i in range(len(oSeries.aoRevInfo)):
565 sPlusMinus = '';
566 if oSeries.acSamples[i] > 1:
567 sPlusMinus = ' (+%s/-%s; %u samples)' \
568 % ( utils.formatNumber(oSeries.aiErrorBarAbove[i]),
569 utils.formatNumber(oSeries.aiErrorBarBelow[i]),
570 oSeries.acSamples[i])
571 sTooltip = '<table class=\'graphwiz-tt\'><tr><td>%s:</td><td>%s %s %s</td></tr>'\
572 '<tr><td>Rev:</td><td>r%s</td></tr>' \
573 % ( sSeriesName,
574 utils.formatNumber(oSeries.aiValues[i]),
575 sYUnit, sPlusMinus,
576 oSeries.aiRevisions[i],
577 );
578 oRevInfo = oSeries.aoRevInfo[i];
579 if oRevInfo.sAuthor is not None:
580 sMsg = oRevInfo.sMessage[:80].strip();
581 #if sMsg.find('\n') >= 0:
582 # sMsg = sMsg[:sMsg.find('\n')].strip();
583 sTooltip += '<tr><td>Author:</td><td>%s</td></tr>' \
584 '<tr><td>Date:</td><td>%s</td><tr>' \
585 '<tr><td>Message:</td><td>%s%s</td></tr>' \
586 % ( oRevInfo.sAuthor,
587 self.formatTsShort(oRevInfo.tsCreated),
588 sMsg, '...' if len(oRevInfo.sMessage) > len(sMsg) else '');
589 sTooltip += '</table>';
590 asHtmlTooltips.append(sTooltip);
591 oData.addDataSeries(sSeriesName, oSeries.aiRevisions, oSeries.aiValues, asHtmlTooltips,
592 oSeries.aiErrorBarBelow, oSeries.aiErrorBarAbove);
593 # Render the data into a graph.
594 oGraph = self.oGraphClass('tmgraph-%u' % (iGraph,), oData, self._oDisp);
595 self._configureGraph(oGraph);
596
597 oGraph.setTitle(self._calcGraphName(aoSeries[0], fSeriesName, sSampleName));
598 sHtml += ' <div class="graphwiz-graph" id="graphwiz-graph-%u">\n' % (iGraph,);
599 sHtml += oGraph.renderGraph();
600 sHtml += '\n </div>\n';
601 iGraph += 1;
602
603 #
604 # Emit raw tabular data if requested.
605 #
606 if self._dParams[WuiMain.ksParamGraphWizTabular]:
607 sHtml += ' <div class="graphwiz-tab-div" id="graphwiz-tab-%u">\n' \
608 ' <table class="tmtable graphwiz-tab">\n' \
609 % (iCollection, );
610 for aoSeries in aaoSeries:
611 if aoSeries[0].iUnit < len(constants.valueunit.g_asNames) and aoSeries[0].iUnit > 0:
612 sUnit = constants.valueunit.g_asNames[aoSeries[0].iUnit];
613 else:
614 sUnit = str(aoSeries[0].iUnit);
615
616 for iSeries, oSeries in enumerate(aoSeries):
617 sColor = self.oGraphClass.calcSeriesColor(iSeries);
618
619 sHtml += '<thead class="tmheader">\n' \
620 ' <tr class="graphwiz-tab graphwiz-tab-new-series-row">\n' \
621 ' <th colspan="5"><span style="background-color:%s;">&nbsp;&nbsp;</span> %s</th>\n' \
622 ' </tr>\n' \
623 ' <tr class="graphwiz-tab graphwiz-tab-col-hdr-row">\n' \
624 ' <th>Revision</th><th>Value (%s)</th><th>&Delta;max</th><th>&Delta;min</th>' \
625 '<th>Samples</th>\n' \
626 ' </tr>\n' \
627 '</thead>\n' \
628 % ( sColor,
629 self._getSeriesNameFromBits(oSeries, self.kfSeriesName_All & ~self.kfSeriesName_OsArchs),
630 sUnit );
631
632 for i in range(len(oSeries.aiRevisions)):
633 sHtml += ' <tr class="%s"><td>r%s</td><td>%s</td><td>+%s</td><td>-%s</td><td>%s</td></tr>\n' \
634 % ( 'tmodd' if i & 1 else 'tmeven',
635 oSeries.aiRevisions[i], oSeries.aiValues[i],
636 oSeries.aiErrorBarAbove[i], oSeries.aiErrorBarBelow[i],
637 oSeries.acSamples[i]);
638 sHtml += ' </table>\n' \
639 ' </div>\n';
640 else:
641 sHtml += '<i>No results.</i>\n';
642 sHtml += ' </div>\n'
643 sHtml += ' </div>\n';
644
645 #
646 # Finish the form.
647 #
648 if fInteractive:
649 sHtml += sEndOfForm;
650
651 return sHtml;
652
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