VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/webui/wuiadminsystemchangelog.py

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.4 KB
Line 
1# -*- coding: utf-8 -*-
2# $Id: wuiadminsystemchangelog.py 106061 2024-09-16 14:03:52Z vboxsync $
3
4"""
5Test Manager WUI - Admin - System changelog.
6"""
7
8__copyright__ = \
9"""
10Copyright (C) 2012-2024 Oracle and/or its affiliates.
11
12This file is part of VirtualBox base platform packages, as
13available from https://www.virtualbox.org.
14
15This program is free software; you can redistribute it and/or
16modify it under the terms of the GNU General Public License
17as published by the Free Software Foundation, in version 3 of the
18License.
19
20This program is distributed in the hope that it will be useful, but
21WITHOUT ANY WARRANTY; without even the implied warranty of
22MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23General Public License for more details.
24
25You should have received a copy of the GNU General Public License
26along with this program; if not, see <https://www.gnu.org/licenses>.
27
28The contents of this file may alternatively be used under the terms
29of the Common Development and Distribution License Version 1.0
30(CDDL), a copy of it is provided in the "COPYING.CDDL" file included
31in the VirtualBox distribution, in which case the provisions of the
32CDDL are applicable instead of those of the GPL.
33
34You may elect to license modified versions of this file under the
35terms and conditions of either the GPL or the CDDL or both.
36
37SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
38"""
39__version__ = "$Revision: 106061 $"
40
41
42from common import webutils;
43
44# Validation Kit imports.
45from testmanager.webui.wuicontentbase import WuiListContentBase, WuiHtmlKeeper, WuiAdminLink, \
46 WuiMainLink, WuiElementText, WuiHtmlBase;
47
48from testmanager.core.base import AttributeChangeEntryPre;
49from testmanager.core.buildblacklist import BuildBlacklistLogic, BuildBlacklistData;
50from testmanager.core.build import BuildLogic, BuildData;
51from testmanager.core.buildsource import BuildSourceLogic, BuildSourceData;
52from testmanager.core.globalresource import GlobalResourceLogic, GlobalResourceData;
53from testmanager.core.failurecategory import FailureCategoryLogic, FailureCategoryData;
54from testmanager.core.failurereason import FailureReasonLogic, FailureReasonData;
55from testmanager.core.systemlog import SystemLogData;
56from testmanager.core.systemchangelog import SystemChangelogLogic;
57from testmanager.core.schedgroup import SchedGroupLogic, SchedGroupData;
58from testmanager.core.testbox import TestBoxLogic, TestBoxData;
59from testmanager.core.testcase import TestCaseLogic, TestCaseData;
60from testmanager.core.testgroup import TestGroupLogic, TestGroupData;
61from testmanager.core.testset import TestSetData;
62from testmanager.core.useraccount import UserAccountLogic, UserAccountData;
63
64
65class WuiAdminSystemChangelogList(WuiListContentBase):
66 """
67 WUI System Changelog Content Generator.
68 """
69
70 def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns = None):
71 WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog',
72 fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns);
73 self._asColumnHeaders = [ 'When', 'User', 'Event', 'Details' ];
74 self._asColumnAttribs = [ 'align="center"', 'align="center"', '', '' ];
75 self._oBuildBlacklistLogic = BuildBlacklistLogic(oDisp.getDb());
76 self._oBuildLogic = BuildLogic(oDisp.getDb());
77 self._oBuildSourceLogic = BuildSourceLogic(oDisp.getDb());
78 self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb());
79 self._oFailureReasonLogic = FailureReasonLogic(oDisp.getDb());
80 self._oGlobalResourceLogic = GlobalResourceLogic(oDisp.getDb());
81 self._oSchedGroupLogic = SchedGroupLogic(oDisp.getDb());
82 self._oTestBoxLogic = TestBoxLogic(oDisp.getDb());
83 self._oTestCaseLogic = TestCaseLogic(oDisp.getDb());
84 self._oTestGroupLogic = TestGroupLogic(oDisp.getDb());
85 self._oUserAccountLogic = UserAccountLogic(oDisp.getDb());
86 self._sPrevDate = '';
87 _ = cDaysBack;
88
89 # oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);
90 def _createBlacklistingDetailsLink(self, idBlacklisting, tsEffective):
91 """ Creates a link to the build source details. """
92 oBlacklisting = self._oBuildBlacklistLogic.cachedLookup(idBlacklisting);
93 if oBlacklisting is not None:
94 from testmanager.webui.wuiadmin import WuiAdmin;
95 return WuiAdminLink('Blacklisting #%u' % (oBlacklisting.idBlacklisting,),
96 WuiAdmin.ksActionBuildBlacklistDetails, tsEffective,
97 { BuildBlacklistData.ksParam_idBlacklisting: oBlacklisting.idBlacklisting },
98 fBracketed = False);
99 return WuiElementText('[blacklisting #%u not found]' % (idBlacklisting,));
100
101 def _createBuildDetailsLink(self, idBuild, tsEffective):
102 """ Creates a link to the build details. """
103 oBuild = self._oBuildLogic.cachedLookup(idBuild);
104 if oBuild is not None:
105 from testmanager.webui.wuiadmin import WuiAdmin;
106 return WuiAdminLink('%s %sr%u' % ( oBuild.oCat.sProduct, oBuild.sVersion, oBuild.iRevision),
107 WuiAdmin.ksActionBuildDetails, tsEffective,
108 { BuildData.ksParam_idBuild: oBuild.idBuild },
109 fBracketed = False,
110 sTitle = 'build #%u for %s, type %s'
111 % (oBuild.idBuild, ' & '.join(oBuild.oCat.asOsArches), oBuild.oCat.sType));
112 return WuiElementText('[build #%u not found]' % (idBuild,));
113
114 def _createBuildSourceDetailsLink(self, idBuildSrc, tsEffective):
115 """ Creates a link to the build source details. """
116 oBuildSource = self._oBuildSourceLogic.cachedLookup(idBuildSrc);
117 if oBuildSource is not None:
118 from testmanager.webui.wuiadmin import WuiAdmin;
119 return WuiAdminLink(oBuildSource.sName, WuiAdmin.ksActionBuildSrcDetails, tsEffective,
120 { BuildSourceData.ksParam_idBuildSrc: oBuildSource.idBuildSrc },
121 fBracketed = False,
122 sTitle = 'Build source #%u' % (oBuildSource.idBuildSrc,));
123 return WuiElementText('[build source #%u not found]' % (idBuildSrc,));
124
125 def _createFailureCategoryDetailsLink(self, idFailureCategory, tsEffective):
126 """ Creates a link to the failure category details. """
127 oFailureCategory = self._oFailureCategoryLogic.cachedLookup(idFailureCategory);
128 if oFailureCategory is not None:
129 from testmanager.webui.wuiadmin import WuiAdmin;
130 return WuiAdminLink(oFailureCategory.sShort, WuiAdmin.ksActionFailureCategoryDetails, tsEffective,
131 { FailureCategoryData.ksParam_idFailureCategory: oFailureCategory.idFailureCategory },
132 fBracketed = False,
133 sTitle = 'Failure category #%u' % (oFailureCategory.idFailureCategory,));
134 return WuiElementText('[failure category #%u not found]' % (idFailureCategory,));
135
136 def _createFailureReasonDetailsLink(self, idFailureReason, tsEffective):
137 """ Creates a link to the failure reason details. """
138 oFailureReason = self._oFailureReasonLogic.cachedLookup(idFailureReason);
139 if oFailureReason is not None:
140 from testmanager.webui.wuiadmin import WuiAdmin;
141 return WuiAdminLink(oFailureReason.sShort, WuiAdmin.ksActionFailureReasonDetails, tsEffective,
142 { FailureReasonData.ksParam_idFailureReason: oFailureReason.idFailureReason },
143 fBracketed = False,
144 sTitle = 'Failure reason #%u, category %s'
145 % (oFailureReason.idFailureReason, oFailureReason.oCategory.sShort));
146 return WuiElementText('[failure reason #%u not found]' % (idFailureReason,));
147
148 def _createGlobalResourceDetailsLink(self, idGlobalRsrc, tsEffective):
149 """ Creates a link to the global resource details. """
150 oGlobalResource = self._oGlobalResourceLogic.cachedLookup(idGlobalRsrc);
151 if oGlobalResource is not None:
152 return WuiAdminLink(oGlobalResource.sName, '@todo', tsEffective,
153 { GlobalResourceData.ksParam_idGlobalRsrc: oGlobalResource.idGlobalRsrc },
154 fBracketed = False,
155 sTitle = 'Global resource #%u' % (oGlobalResource.idGlobalRsrc,));
156 return WuiElementText('[global resource #%u not found]' % (idGlobalRsrc,));
157
158 def _createSchedGroupDetailsLink(self, idSchedGroup, tsEffective):
159 """ Creates a link to the scheduling group details. """
160 oSchedGroup = self._oSchedGroupLogic.cachedLookup(idSchedGroup);
161 if oSchedGroup is not None:
162 from testmanager.webui.wuiadmin import WuiAdmin;
163 return WuiAdminLink(oSchedGroup.sName, WuiAdmin.ksActionSchedGroupDetails, tsEffective,
164 { SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup },
165 fBracketed = False,
166 sTitle = 'Scheduling group #%u' % (oSchedGroup.idSchedGroup,));
167 return WuiElementText('[scheduling group #%u not found]' % (idSchedGroup,));
168
169 def _createTestBoxDetailsLink(self, idTestBox, tsEffective):
170 """ Creates a link to the testbox details. """
171 oTestBox = self._oTestBoxLogic.cachedLookup(idTestBox);
172 if oTestBox is not None:
173 from testmanager.webui.wuiadmin import WuiAdmin;
174 return WuiAdminLink(oTestBox.sName, WuiAdmin.ksActionTestBoxDetails, tsEffective,
175 { TestBoxData.ksParam_idTestBox: oTestBox.idTestBox },
176 fBracketed = False, sTitle = 'Testbox #%u' % (oTestBox.idTestBox,));
177 return WuiElementText('[testbox #%u not found]' % (idTestBox,));
178
179 def _createTestCaseDetailsLink(self, idTestCase, tsEffective):
180 """ Creates a link to the test case details. """
181 oTestCase = self._oTestCaseLogic.cachedLookup(idTestCase);
182 if oTestCase is not None:
183 from testmanager.webui.wuiadmin import WuiAdmin;
184 return WuiAdminLink(oTestCase.sName, WuiAdmin.ksActionTestCaseDetails, tsEffective,
185 { TestCaseData.ksParam_idTestCase: oTestCase.idTestCase },
186 fBracketed = False, sTitle = 'Test case #%u' % (oTestCase.idTestCase,));
187 return WuiElementText('[test case #%u not found]' % (idTestCase,));
188
189 def _createTestGroupDetailsLink(self, idTestGroup, tsEffective):
190 """ Creates a link to the test group details. """
191 oTestGroup = self._oTestGroupLogic.cachedLookup(idTestGroup);
192 if oTestGroup is not None:
193 from testmanager.webui.wuiadmin import WuiAdmin;
194 return WuiAdminLink(oTestGroup.sName, WuiAdmin.ksActionTestGroupDetails, tsEffective,
195 { TestGroupData.ksParam_idTestGroup: oTestGroup.idTestGroup },
196 fBracketed = False, sTitle = 'Test group #%u' % (oTestGroup.idTestGroup,));
197 return WuiElementText('[test group #%u not found]' % (idTestGroup,));
198
199 def _createTestSetResultsDetailsLink(self, idTestSet, tsEffective):
200 """ Creates a link to the test set results. """
201 _ = tsEffective;
202 from testmanager.webui.wuimain import WuiMain;
203 return WuiMainLink('test set #%u' % idTestSet, WuiMain.ksActionTestSetDetails,
204 { TestSetData.ksParam_idTestSet: idTestSet }, fBracketed = False);
205
206 def _createTestSetDetailsLinkByResult(self, idTestResult, tsEffective):
207 """ Creates a link to the test set results. """
208 _ = tsEffective;
209 from testmanager.webui.wuimain import WuiMain;
210 return WuiMainLink('test result #%u' % idTestResult, WuiMain.ksActionTestSetDetailsFromResult,
211 { TestSetData.ksParam_idTestResult: idTestResult }, fBracketed = False);
212
213 def _createUserAccountDetailsLink(self, uid, tsEffective):
214 """ Creates a link to the user account details. """
215 oUser = self._oUserAccountLogic.cachedLookup(uid);
216 if oUser is not None:
217 return WuiAdminLink(oUser.sUsername, '@todo', tsEffective, { UserAccountData.ksParam_uid: oUser.uid },
218 fBracketed = False, sTitle = '%s (#%u)' % (oUser.sFullName, oUser.uid));
219 return WuiElementText('[user #%u not found]' % (uid,));
220
221 def _formatDescGeneric(self, sDesc, oEntry):
222 """
223 Generically format system log the description.
224 """
225 oRet = WuiHtmlKeeper();
226 asWords = sDesc.split();
227 for sWord in asWords:
228 offEqual = sWord.find('=');
229 if offEqual > 0:
230 sKey = sWord[:offEqual];
231 try: idValue = int(sWord[offEqual+1:].rstrip('.,'));
232 except: pass;
233 else:
234 if sKey == 'idTestSet':
235 oRet.append(self._createTestSetResultsDetailsLink(idValue, oEntry.tsEffective));
236 continue;
237 if sKey == 'idTestBox':
238 oRet.append(self._createTestBoxDetailsLink(idValue, oEntry.tsEffective));
239 continue;
240 if sKey == 'idSchedGroup':
241 oRet.append(self._createSchedGroupDetailsLink(idValue, oEntry.tsEffective));
242 continue;
243
244 oRet.append(WuiElementText(sWord));
245 return oRet;
246
247 def _formatListEntryHtml(self, iEntry): # pylint: disable=too-many-statements
248 """
249 Overridden parent method.
250 """
251 oEntry = self._aoEntries[iEntry];
252 sRowClass = 'tmodd' if (iEntry + 1) & 1 else 'tmeven';
253 sHtml = u'';
254
255 #
256 # Format the timestamp.
257 #
258 sDate = self.formatTsShort(oEntry.tsEffective);
259 if sDate[:10] != self._sPrevDate:
260 self._sPrevDate = sDate[:10];
261 sHtml += ' <tr class="%s tmdaterow" align="left"><td colspan="7">%s</td></tr>\n' % (sRowClass, sDate[:10],);
262 sDate = sDate[11:]
263
264 #
265 # System log events.
266 # pylint: disable=redefined-variable-type
267 #
268 aoChanges = None;
269 if oEntry.sEvent == SystemLogData.ksEvent_CmdNacked:
270 sEvent = 'Command not acknowleged';
271 oDetails = oEntry.sDesc;
272
273 elif oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown:
274 sEvent = 'Unknown testbox';
275 oDetails = oEntry.sDesc;
276
277 elif oEntry.sEvent == SystemLogData.ksEvent_TestSetAbandoned:
278 sEvent = 'Abandoned ' if oEntry.sDesc.startswith('idTestSet') else 'Abandoned test set';
279 oDetails = self._formatDescGeneric(oEntry.sDesc, oEntry);
280
281 elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown:
282 sEvent = 'Unknown user account';
283 oDetails = oEntry.sDesc;
284
285 elif oEntry.sEvent == SystemLogData.ksEvent_XmlResultMalformed:
286 sEvent = 'Malformed XML result';
287 oDetails = oEntry.sDesc;
288
289 elif oEntry.sEvent == SystemLogData.ksEvent_SchedQueueRecreate:
290 sEvent = 'Recreating scheduling queue';
291 asWords = oEntry.sDesc.split();
292 if len(asWords) > 3 and asWords[0] == 'User' and asWords[1][0] == '#':
293 try: idAuthor = int(asWords[1][1:]);
294 except: pass;
295 else:
296 oEntry.oAuthor = self._oUserAccountLogic.cachedLookup(idAuthor);
297 if oEntry.oAuthor is not None:
298 i = 2;
299 if asWords[i] == 'recreated': i += 1;
300 oEntry.sDesc = ' '.join(asWords[i:]);
301 oDetails = self._formatDescGeneric(oEntry.sDesc.replace('sched queue #', 'for scheduling group idSchedGroup='),
302 oEntry);
303 #
304 # System changelog events.
305 #
306 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Blacklisting:
307 sEvent = 'Modified blacklisting';
308 oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective);
309
310 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Build:
311 sEvent = 'Modified build';
312 oDetails = self._createBuildDetailsLink(oEntry.idWhat, oEntry.tsEffective);
313
314 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_BuildSource:
315 sEvent = 'Modified build source';
316 oDetails = self._createBuildSourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);
317
318 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_GlobalRsrc:
319 sEvent = 'Modified global resource';
320 oDetails = self._createGlobalResourceDetailsLink(oEntry.idWhat, oEntry.tsEffective);
321
322 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureCategory:
323 sEvent = 'Modified failure category';
324 oDetails = self._createFailureCategoryDetailsLink(oEntry.idWhat, oEntry.tsEffective);
325 (aoChanges, _) = self._oFailureCategoryLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
326
327 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureReason:
328 sEvent = 'Modified failure reason';
329 oDetails = self._createFailureReasonDetailsLink(oEntry.idWhat, oEntry.tsEffective);
330 (aoChanges, _) = self._oFailureReasonLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
331
332 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_SchedGroup:
333 sEvent = 'Modified scheduling group';
334 oDetails = self._createSchedGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);
335
336 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestBox:
337 sEvent = 'Modified testbox';
338 oDetails = self._createTestBoxDetailsLink(oEntry.idWhat, oEntry.tsEffective);
339 (aoChanges, _) = self._oTestBoxLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
340
341 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestCase:
342 sEvent = 'Modified test case';
343 oDetails = self._createTestCaseDetailsLink(oEntry.idWhat, oEntry.tsEffective);
344 (aoChanges, _) = self._oTestCaseLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective);
345
346 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestGroup:
347 sEvent = 'Modified test group';
348 oDetails = self._createTestGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective);
349
350 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestResult:
351 sEvent = 'Modified test failure reason';
352 oDetails = self._createTestSetDetailsLinkByResult(oEntry.idWhat, oEntry.tsEffective);
353
354 elif oEntry.sEvent == SystemChangelogLogic.ksWhat_User:
355 sEvent = 'Modified user account';
356 oDetails = self._createUserAccountDetailsLink(oEntry.idWhat, oEntry.tsEffective);
357
358 else:
359 sEvent = '%s(%s)' % (oEntry.sEvent, oEntry.idWhat,);
360 oDetails = '!Unknown event!' + (oEntry.sDesc if oEntry.sDesc else '');
361
362 #
363 # Do the formatting.
364 #
365
366 if aoChanges:
367 oChangeEntry = aoChanges[0];
368 cAttribsChanged = len(oChangeEntry.aoChanges) + 1;
369 if oChangeEntry.oOldRaw is None and sEvent.startswith('Modified '):
370 sEvent = 'Created ' + sEvent[9:];
371
372 else:
373 oChangeEntry = None;
374 cAttribsChanged = -1;
375
376 sHtml += u' <tr class="%s">\n' \
377 u' <td rowspan="%d" align="center" >%s</td>\n' \
378 u' <td rowspan="%d" align="center" >%s</td>\n' \
379 u' <td colspan="5" class="%s%s">%s %s</td>\n' \
380 u' </tr>\n' \
381 % ( sRowClass,
382 1 + cAttribsChanged + 1, sDate,
383 1 + cAttribsChanged + 1, webutils.escapeElem(oEntry.oAuthor.sUsername if oEntry.oAuthor is not None else ''),
384 sRowClass, ' tmsyschlogevent' if oChangeEntry is not None else '', webutils.escapeElem(sEvent),
385 oDetails.toHtml() if isinstance(oDetails, WuiHtmlBase) else oDetails,
386 );
387
388 if oChangeEntry is not None:
389 sHtml += u' <tr class="%s tmsyschlogspacerrowabove">\n' \
390 u' <td xrowspan="%d" style="border-right: 0px; border-bottom: 0px;"></td>\n' \
391 u' <td colspan="3" style="border-right: 0px;"></td>\n' \
392 u' <td rowspan="%d" class="%s tmsyschlogspacer"></td>\n' \
393 u' </tr>\n' \
394 % (sRowClass, cAttribsChanged + 1, cAttribsChanged + 1, sRowClass);
395 for j, oChange in enumerate(oChangeEntry.aoChanges):
396 fLastRow = j + 1 == len(oChangeEntry.aoChanges);
397 sHtml += u' <tr class="%s%s tmsyschlogattr%s">\n' \
398 % ( sRowClass, 'odd' if j & 1 else 'even', ' tmsyschlogattrfinal' if fLastRow else '',);
399 if j == 0:
400 sHtml += u' <td class="%s tmsyschlogspacer" rowspan="%d"></td>\n' % (sRowClass, cAttribsChanged - 1,);
401
402 if isinstance(oChange, AttributeChangeEntryPre):
403 sHtml += u' <td class="%s%s">%s</td>\n' \
404 u' <td><div class="tdpre"><pre>%s</pre></div></td>\n' \
405 u' <td class="%s%s"><div class="tdpre"><pre>%s</pre></div></td>\n' \
406 % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
407 webutils.escapeElem(oChange.sAttr),
408 webutils.escapeElem(oChange.sOldText),
409 ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
410 webutils.escapeElem(oChange.sNewText), );
411 else:
412 sHtml += u' <td class="%s%s">%s</td>\n' \
413 u' <td>%s</td>\n' \
414 u' <td class="%s%s">%s</td>\n' \
415 % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '',
416 webutils.escapeElem(oChange.sAttr),
417 webutils.escapeElem(oChange.sOldText),
418 ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '',
419 webutils.escapeElem(oChange.sNewText), );
420 sHtml += u' </tr>\n';
421
422 if oChangeEntry is not None:
423 sHtml += u' <tr class="%s tmsyschlogspacerrowbelow "><td colspan="5"></td></tr>\n\n' % (sRowClass,);
424 return sHtml;
425
426
427 def _generateTableHeaders(self):
428 """
429 Overridden parent method.
430 """
431
432 sHtml = u'<thead class="tmheader">\n' \
433 u' <tr>\n' \
434 u' <th rowspan="2">When</th>\n' \
435 u' <th rowspan="2">Who</th>\n' \
436 u' <th colspan="5">Event</th>\n' \
437 u' </tr>\n' \
438 u' <tr>\n' \
439 u' <th style="border-right: 0px;"></th>\n' \
440 u' <th>Attribute</th>\n' \
441 u' <th>Old</th>\n' \
442 u' <th style="border-right: 0px;">New</th>\n' \
443 u' <th></th>\n' \
444 u' </tr>\n' \
445 u'</thead>\n';
446 return sHtml;
447
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