def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns=None): WuiListContentBase.__init__( self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog', fnDPrint=fnDPrint, oDisp=oDisp, aiSelectedSortColumns=aiSelectedSortColumns) self._asColumnHeaders = ['When', 'User', 'Event', 'Details'] self._asColumnAttribs = ['align="center"', 'align="center"', '', ''] self._oBuildBlacklistLogic = BuildBlacklistLogic(oDisp.getDb()) self._oBuildLogic = BuildLogic(oDisp.getDb()) self._oBuildSourceLogic = BuildSourceLogic(oDisp.getDb()) self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb()) self._oFailureReasonLogic = FailureReasonLogic(oDisp.getDb()) self._oGlobalResourceLogic = GlobalResourceLogic(oDisp.getDb()) self._oSchedGroupLogic = SchedGroupLogic(oDisp.getDb()) self._oTestBoxLogic = TestBoxLogic(oDisp.getDb()) self._oTestCaseLogic = TestCaseLogic(oDisp.getDb()) self._oTestGroupLogic = TestGroupLogic(oDisp.getDb()) self._oUserAccountLogic = UserAccountLogic(oDisp.getDb()) self._sPrevDate = '' _ = cDaysBack
def _populateForm(self, oForm, oData): """ Construct an HTML form """ aoFailureCategories = FailureCategoryLogic(TMDatabaseConnection()).getFailureCategoriesForCombo() if len(aoFailureCategories) == 0: from testmanager.webui.wuiadmin import WuiAdmin sExceptionMsg = 'Please <a href="%s?%s=%s">add</a> Failure Category first.' % \ (WuiAdmin.ksScriptName, WuiAdmin.ksParamAction, WuiAdmin.ksActionFailureCategoryShowAdd) raise WuiException(sExceptionMsg) oForm.addIntRO (FailureReasonData.ksParam_idFailureReason, oData.idFailureReason, 'Failure Reason ID') oForm.addTimestampRO (FailureReasonData.ksParam_tsEffective, oData.tsEffective, 'Last changed') oForm.addTimestampRO (FailureReasonData.ksParam_tsExpire, oData.tsExpire, 'Expires (excl)') oForm.addIntRO (FailureReasonData.ksParam_uidAuthor, oData.uidAuthor, 'Changed by UID') oForm.addComboBox (FailureReasonData.ksParam_idFailureCategory, oData.idFailureCategory, 'Failure Category', aoFailureCategories) oForm.addText (FailureReasonData.ksParam_sShort, oData.sShort, 'Short Description') oForm.addText (FailureReasonData.ksParam_sFull, oData.sFull, 'Full Description') oForm.addInt (FailureReasonData.ksParam_iTicket, oData.iTicket, 'Ticket Number') oForm.addMultilineText(FailureReasonData.ksParam_asUrls, oData.asUrls, 'Other URLs to reports ' 'or discussions of the ' 'observed symptoms') oForm.addSubmit() return True
def _ensureCachesPresent(self): """ Ensures we've got the cache references resolved. """ if self.oCategoryLogic is None: from testmanager.core.failurecategory import FailureCategoryLogic self.oCategoryLogic = FailureCategoryLogic(self._oDb) if self.oUserAccountLogic is None: self.oUserAccountLogic = UserAccountLogic(self._oDb) return True
def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns = None): WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog', fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns); self._asColumnHeaders = [ 'When', 'User', 'Event', 'Details' ]; self._asColumnAttribs = [ 'align="center"', 'align="center"', '', '' ]; self._oBuildBlacklistLogic = BuildBlacklistLogic(oDisp.getDb()); self._oBuildLogic = BuildLogic(oDisp.getDb()); self._oBuildSourceLogic = BuildSourceLogic(oDisp.getDb()); self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb()); self._oFailureReasonLogic = FailureReasonLogic(oDisp.getDb()); self._oGlobalResourceLogic = GlobalResourceLogic(oDisp.getDb()); self._oSchedGroupLogic = SchedGroupLogic(oDisp.getDb()); self._oTestBoxLogic = TestBoxLogic(oDisp.getDb()); self._oTestCaseLogic = TestCaseLogic(oDisp.getDb()); self._oTestGroupLogic = TestGroupLogic(oDisp.getDb()); self._oUserAccountLogic = UserAccountLogic(oDisp.getDb()); self._sPrevDate = ''; _ = cDaysBack;
def fetchForChangeLog(self, idTestResult, iStart, cMaxRows, tsNow): # pylint: disable=R0914 """ Fetches change log entries for a failure reason. Returns an array of ChangeLogEntry instance and an indicator whether there are more entries. Raises exception on error. """ if tsNow is None: tsNow = self._oDb.getCurrentTimestamp() # 1. Get a list of the changes from both TestResultFailures and assoicated # FailureReasons. The latter is useful since the failure reason # description may evolve along side the invidiual failure analysis. self._oDb.execute( "( SELECT trf.tsEffective AS tsEffectiveChangeLog,\n" " trf.uidAuthor AS uidAuthorChangeLog,\n" " trf.*,\n" " fr.*\n" " FROM TestResultFailures trf,\n" " FailureReasons fr\n" " WHERE trf.idTestResult = %s\n" " AND trf.tsEffective <= %s\n" " AND trf.idFailureReason = fr.idFailureReason\n" " AND fr.tsEffective <= trf.tsEffective\n" " AND fr.tsExpire > trf.tsEffective\n" ")\n" "UNION\n" "( SELECT fr.tsEffective AS tsEffectiveChangeLog,\n" " fr.uidAuthor AS uidAuthorChangeLog,\n" " trf.*,\n" " fr.*\n" " FROM TestResultFailures trf,\n" " FailureReasons fr\n" " WHERE trf.idTestResult = %s\n" " AND trf.tsEffective <= %s\n" " AND trf.idFailureReason = fr.idFailureReason\n" " AND fr.tsEffective > trf.tsEffective\n" " AND fr.tsEffective < trf.tsExpire\n" ")\n" "ORDER BY tsEffectiveChangeLog DESC\n" "LIMIT %s OFFSET %s\n", (idTestResult, tsNow, idTestResult, tsNow, cMaxRows + 1, iStart), ) aaoRows = [] for aoChange in self._oDb.fetchAll(): oTrf = TestResultFailureDataEx().initFromDbRow(aoChange[2:]) oFr = FailureReasonData().initFromDbRow(aoChange[(2 + TestResultFailureData.kcDbColumns) :]) oTrf.oFailureReason = oFr aaoRows.append([aoChange[0], aoChange[1], oTrf, oFr]) # 2. Calculate the changes. oFailureCategoryLogic = None aoEntries = [] for i in xrange(0, len(aaoRows) - 1): aoNew = aaoRows[i] aoOld = aaoRows[i + 1] aoChanges = [] oNew = aoNew[2] oOld = aoOld[2] for sAttr in oNew.getDataAttributes(): if sAttr not in ["tsEffective", "tsExpire", "uidAuthor", "oFailureReason", "oAuthor"]: oOldAttr = getattr(oOld, sAttr) oNewAttr = getattr(oNew, sAttr) if oOldAttr != oNewAttr: if sAttr == "idFailureReason": oNewAttr = "%s (%s)" % (oNewAttr, oNew.oFailureReason.sShort) oOldAttr = "%s (%s)" % (oOldAttr, oOld.oFailureReason.sShort) aoChanges.append(AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr))) if oOld.idFailureReason == oNew.idFailureReason: oNew = aoNew[3] oOld = aoOld[3] for sAttr in oNew.getDataAttributes(): if sAttr not in ["tsEffective", "tsExpire", "uidAuthor"]: oOldAttr = getattr(oOld, sAttr) oNewAttr = getattr(oNew, sAttr) if oOldAttr != oNewAttr: if sAttr == "idFailureCategory": if oFailureCategoryLogic is None: from testmanager.core.failurecategory import FailureCategoryLogic oFailureCategoryLogic = FailureCategoryLogic(self._oDb) oCat = oFailureCategoryLogic.cachedLookup(oNewAttr) if oCat is not None: oNewAttr = "%s (%s)" % (oNewAttr, oCat.sShort) oCat = oFailureCategoryLogic.cachedLookup(oOldAttr) if oCat is not None: oOldAttr = "%s (%s)" % (oOldAttr, oCat.sShort) aoChanges.append( AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr)) ) tsExpire = aaoRows[i - 1][0] if i > 0 else aoNew[2].tsExpire aoEntries.append(ChangeLogEntry(aoNew[1], None, aoNew[0], tsExpire, aoNew[2], aoOld[2], aoChanges)) # If we're at the end of the log, add the initial entry. if len(aaoRows) <= cMaxRows and len(aaoRows) > 0: aoNew = aaoRows[-1] tsExpire = aaoRows[-1 - 1][0] if len(aaoRows) > 1 else aoNew[2].tsExpire aoEntries.append(ChangeLogEntry(aoNew[1], None, aoNew[0], tsExpire, aoNew[2], None, [])) return (UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries), len(aaoRows) > cMaxRows)
class WuiAdminSystemChangelogList(WuiListContentBase): """ WUI System Changelog Content Generator. """ def __init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, fnDPrint, oDisp, cDaysBack, aiSelectedSortColumns = None): WuiListContentBase.__init__(self, aoEntries, iPage, cItemsPerPage, tsEffective, 'System Changelog', fnDPrint = fnDPrint, oDisp = oDisp, aiSelectedSortColumns = aiSelectedSortColumns); self._asColumnHeaders = [ 'When', 'User', 'Event', 'Details' ]; self._asColumnAttribs = [ 'align="center"', 'align="center"', '', '' ]; self._oBuildBlacklistLogic = BuildBlacklistLogic(oDisp.getDb()); self._oBuildLogic = BuildLogic(oDisp.getDb()); self._oBuildSourceLogic = BuildSourceLogic(oDisp.getDb()); self._oFailureCategoryLogic = FailureCategoryLogic(oDisp.getDb()); self._oFailureReasonLogic = FailureReasonLogic(oDisp.getDb()); self._oGlobalResourceLogic = GlobalResourceLogic(oDisp.getDb()); self._oSchedGroupLogic = SchedGroupLogic(oDisp.getDb()); self._oTestBoxLogic = TestBoxLogic(oDisp.getDb()); self._oTestCaseLogic = TestCaseLogic(oDisp.getDb()); self._oTestGroupLogic = TestGroupLogic(oDisp.getDb()); self._oUserAccountLogic = UserAccountLogic(oDisp.getDb()); self._sPrevDate = ''; _ = cDaysBack; # oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective); def _createBlacklistingDetailsLink(self, idBlacklisting, tsEffective): """ Creates a link to the build source details. """ oBlacklisting = self._oBuildBlacklistLogic.cachedLookup(idBlacklisting); if oBlacklisting is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink('Blacklisting #%u' % (oBlacklisting.idBlacklisting,), WuiAdmin.ksActionBuildBlacklistDetails, tsEffective, { BuildBlacklistData.ksParam_idBlacklisting: oBlacklisting.idBlacklisting }, fBracketed = False); return WuiElementText('[blacklisting #%u not found]' % (idBlacklisting,)); def _createBuildDetailsLink(self, idBuild, tsEffective): """ Creates a link to the build details. """ oBuild = self._oBuildLogic.cachedLookup(idBuild); if oBuild is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink('%s %sr%u' % ( oBuild.oCat.sProduct, oBuild.sVersion, oBuild.iRevision), WuiAdmin.ksActionBuildDetails, tsEffective, { BuildData.ksParam_idBuild: oBuild.idBuild }, fBracketed = False, sTitle = 'build #%u for %s, type %s' % (oBuild.idBuild, ' & '.join(oBuild.oCat.asOsArches), oBuild.oCat.sType)); return WuiElementText('[build #%u not found]' % (idBuild,)); def _createBuildSourceDetailsLink(self, idBuildSrc, tsEffective): """ Creates a link to the build source details. """ oBuildSource = self._oBuildSourceLogic.cachedLookup(idBuildSrc); if oBuildSource is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink(oBuildSource.sName, WuiAdmin.ksActionBuildSrcDetails, tsEffective, { BuildSourceData.ksParam_idBuildSrc: oBuildSource.idBuildSrc }, fBracketed = False, sTitle = 'Build source #%u' % (oBuildSource.idBuildSrc,)); return WuiElementText('[build source #%u not found]' % (idBuildSrc,)); def _createFailureCategoryDetailsLink(self, idFailureCategory, tsEffective): """ Creates a link to the failure category details. """ oFailureCategory = self._oFailureCategoryLogic.cachedLookup(idFailureCategory); if oFailureCategory is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink(oFailureCategory.sShort, WuiAdmin.ksActionFailureCategoryDetails, tsEffective, { FailureCategoryData.ksParam_idFailureCategory: oFailureCategory.idFailureCategory }, fBracketed = False, sTitle = 'Failure category #%u' % (oFailureCategory.idFailureCategory,)); return WuiElementText('[failure category #%u not found]' % (idFailureCategory,)); def _createFailureReasonDetailsLink(self, idFailureReason, tsEffective): """ Creates a link to the failure reason details. """ oFailureReason = self._oFailureReasonLogic.cachedLookup(idFailureReason); if oFailureReason is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink(oFailureReason.sShort, WuiAdmin.ksActionFailureReasonDetails, tsEffective, { FailureReasonData.ksParam_idFailureReason: oFailureReason.idFailureReason }, fBracketed = False, sTitle = 'Failure reason #%u, category %s' % (oFailureReason.idFailureReason, oFailureReason.oCategory.sShort)); return WuiElementText('[failure reason #%u not found]' % (idFailureReason,)); def _createGlobalResourceDetailsLink(self, idGlobalRsrc, tsEffective): """ Creates a link to the global resource details. """ oGlobalResource = self._oGlobalResourceLogic.cachedLookup(idGlobalRsrc); if oGlobalResource is not None: return WuiAdminLink(oGlobalResource.sName, '@todo', tsEffective, { GlobalResourceData.ksParam_idGlobalRsrc: oGlobalResource.idGlobalRsrc }, fBracketed = False, sTitle = 'Global resource #%u' % (oGlobalResource.idGlobalRsrc,)); return WuiElementText('[global resource #%u not found]' % (idGlobalRsrc,)); def _createSchedGroupDetailsLink(self, idSchedGroup, tsEffective): """ Creates a link to the scheduling group details. """ oSchedGroup = self._oSchedGroupLogic.cachedLookup(idSchedGroup); if oSchedGroup is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink(oSchedGroup.sName, WuiAdmin.ksActionSchedGroupDetails, tsEffective, { SchedGroupData.ksParam_idSchedGroup: oSchedGroup.idSchedGroup }, fBracketed = False, sTitle = 'Scheduling group #%u' % (oSchedGroup.idSchedGroup,)); return WuiElementText('[scheduling group #%u not found]' % (idSchedGroup,)); def _createTestBoxDetailsLink(self, idTestBox, tsEffective): """ Creates a link to the testbox details. """ oTestBox = self._oTestBoxLogic.cachedLookup(idTestBox); if oTestBox is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink(oTestBox.sName, WuiAdmin.ksActionTestBoxDetails, tsEffective, { TestBoxData.ksParam_idTestBox: oTestBox.idTestBox }, fBracketed = False, sTitle = 'Testbox #%u' % (oTestBox.idTestBox,)); return WuiElementText('[testbox #%u not found]' % (idTestBox,)); def _createTestCaseDetailsLink(self, idTestCase, tsEffective): """ Creates a link to the test case details. """ oTestCase = self._oTestCaseLogic.cachedLookup(idTestCase); if oTestCase is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink(oTestCase.sName, WuiAdmin.ksActionTestCaseDetails, tsEffective, { TestCaseData.ksParam_idTestCase: oTestCase.idTestCase }, fBracketed = False, sTitle = 'Test case #%u' % (oTestCase.idTestCase,)); return WuiElementText('[test case #%u not found]' % (idTestCase,)); def _createTestGroupDetailsLink(self, idTestGroup, tsEffective): """ Creates a link to the test group details. """ oTestGroup = self._oTestGroupLogic.cachedLookup(idTestGroup); if oTestGroup is not None: from testmanager.webui.wuiadmin import WuiAdmin; return WuiAdminLink(oTestGroup.sName, WuiAdmin.ksActionTestGroupDetails, tsEffective, { TestGroupData.ksParam_idTestGroup: oTestGroup.idTestGroup }, fBracketed = False, sTitle = 'Test group #%u' % (oTestGroup.idTestGroup,)); return WuiElementText('[test group #%u not found]' % (idTestGroup,)); def _createTestSetResultsDetailsLink(self, idTestSet, tsEffective): """ Creates a link to the test set results. """ _ = tsEffective; from testmanager.webui.wuimain import WuiMain; return WuiMainLink('test set #%u' % idTestSet, WuiMain.ksActionTestSetDetails, { TestSetData.ksParam_idTestSet: idTestSet }, fBracketed = False); def _createTestSetDetailsLinkByResult(self, idTestResult, tsEffective): """ Creates a link to the test set results. """ _ = tsEffective; from testmanager.webui.wuimain import WuiMain; return WuiMainLink('test result #%u' % idTestResult, WuiMain.ksActionTestSetDetailsFromResult, { TestSetData.ksParam_idTestResult: idTestResult }, fBracketed = False); def _createUserAccountDetailsLink(self, uid, tsEffective): """ Creates a link to the user account details. """ oUser = self._oUserAccountLogic.cachedLookup(uid); if oUser is not None: return WuiAdminLink(oUser.sUsername, '@todo', tsEffective, { UserAccountData.ksParam_uid: oUser.uid }, fBracketed = False, sTitle = '%s (#%u)' % (oUser.sFullName, oUser.uid)); return WuiElementText('[user #%u not found]' % (uid,)); def _formatDescGeneric(self, sDesc, oEntry): """ Generically format system log the description. """ oRet = WuiHtmlKeeper(); asWords = sDesc.split(); for sWord in asWords: offEqual = sWord.find('='); if offEqual > 0: sKey = sWord[:offEqual]; try: idValue = int(sWord[offEqual+1:].rstrip('.,')); except: pass; else: if sKey == 'idTestSet': oRet.append(self._createTestSetResultsDetailsLink(idValue, oEntry.tsEffective)); continue; if sKey == 'idTestBox': oRet.append(self._createTestBoxDetailsLink(idValue, oEntry.tsEffective)); continue; if sKey == 'idSchedGroup': oRet.append(self._createSchedGroupDetailsLink(idValue, oEntry.tsEffective)); continue; oRet.append(WuiElementText(sWord)); return oRet; def _formatListEntryHtml(self, iEntry): # pylint: disable=too-many-statements """ Overridden parent method. """ oEntry = self._aoEntries[iEntry]; sRowClass = 'tmodd' if (iEntry + 1) & 1 else 'tmeven'; sHtml = u''; # # Format the timestamp. # sDate = self.formatTsShort(oEntry.tsEffective); if sDate[:10] != self._sPrevDate: self._sPrevDate = sDate[:10]; sHtml += ' <tr class="%s tmdaterow" align="left"><td colspan="7">%s</td></tr>\n' % (sRowClass, sDate[:10],); sDate = sDate[11:] # # System log events. # pylint: disable=redefined-variable-type # aoChanges = None; if oEntry.sEvent == SystemLogData.ksEvent_CmdNacked: sEvent = 'Command not acknowleged'; oDetails = oEntry.sDesc; elif oEntry.sEvent == SystemLogData.ksEvent_TestBoxUnknown: sEvent = 'Unknown testbox'; oDetails = oEntry.sDesc; elif oEntry.sEvent == SystemLogData.ksEvent_TestSetAbandoned: sEvent = 'Abandoned ' if oEntry.sDesc.startswith('idTestSet') else 'Abandoned test set'; oDetails = self._formatDescGeneric(oEntry.sDesc, oEntry); elif oEntry.sEvent == SystemLogData.ksEvent_UserAccountUnknown: sEvent = 'Unknown user account'; oDetails = oEntry.sDesc; elif oEntry.sEvent == SystemLogData.ksEvent_XmlResultMalformed: sEvent = 'Malformed XML result'; oDetails = oEntry.sDesc; elif oEntry.sEvent == SystemLogData.ksEvent_SchedQueueRecreate: sEvent = 'Recreating scheduling queue'; asWords = oEntry.sDesc.split(); if len(asWords) > 3 and asWords[0] == 'User' and asWords[1][0] == '#': try: idAuthor = int(asWords[1][1:]); except: pass; else: oEntry.oAuthor = self._oUserAccountLogic.cachedLookup(idAuthor); if oEntry.oAuthor is not None: i = 2; if asWords[i] == 'recreated': i += 1; oEntry.sDesc = ' '.join(asWords[i:]); oDetails = self._formatDescGeneric(oEntry.sDesc.replace('sched queue #', 'for scheduling group idSchedGroup='), oEntry); # # System changelog events. # elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Blacklisting: sEvent = 'Modified blacklisting'; oDetails = self._createBlacklistingDetailsLink(oEntry.idWhat, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_Build: sEvent = 'Modified build'; oDetails = self._createBuildDetailsLink(oEntry.idWhat, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_BuildSource: sEvent = 'Modified build source'; oDetails = self._createBuildSourceDetailsLink(oEntry.idWhat, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_GlobalRsrc: sEvent = 'Modified global resource'; oDetails = self._createGlobalResourceDetailsLink(oEntry.idWhat, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureCategory: sEvent = 'Modified failure category'; oDetails = self._createFailureCategoryDetailsLink(oEntry.idWhat, oEntry.tsEffective); (aoChanges, _) = self._oFailureCategoryLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_FailureReason: sEvent = 'Modified failure reason'; oDetails = self._createFailureReasonDetailsLink(oEntry.idWhat, oEntry.tsEffective); (aoChanges, _) = self._oFailureReasonLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_SchedGroup: sEvent = 'Modified scheduling group'; oDetails = self._createSchedGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestBox: sEvent = 'Modified testbox'; oDetails = self._createTestBoxDetailsLink(oEntry.idWhat, oEntry.tsEffective); (aoChanges, _) = self._oTestBoxLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestCase: sEvent = 'Modified test case'; oDetails = self._createTestCaseDetailsLink(oEntry.idWhat, oEntry.tsEffective); (aoChanges, _) = self._oTestCaseLogic.fetchForChangeLog(oEntry.idWhat, 0, 1, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestGroup: sEvent = 'Modified test group'; oDetails = self._createTestGroupDetailsLink(oEntry.idWhat, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_TestResult: sEvent = 'Modified test failure reason'; oDetails = self._createTestSetDetailsLinkByResult(oEntry.idWhat, oEntry.tsEffective); elif oEntry.sEvent == SystemChangelogLogic.ksWhat_User: sEvent = 'Modified user account'; oDetails = self._createUserAccountDetailsLink(oEntry.idWhat, oEntry.tsEffective); else: sEvent = '%s(%s)' % (oEntry.sEvent, oEntry.idWhat,); oDetails = '!Unknown event!' + (oEntry.sDesc if oEntry.sDesc else ''); # # Do the formatting. # if aoChanges: oChangeEntry = aoChanges[0]; cAttribsChanged = len(oChangeEntry.aoChanges) + 1; if oChangeEntry.oOldRaw is None and sEvent.startswith('Modified '): sEvent = 'Created ' + sEvent[9:]; else: oChangeEntry = None; cAttribsChanged = -1; sHtml += u' <tr class="%s">\n' \ u' <td rowspan="%d" align="center" >%s</td>\n' \ u' <td rowspan="%d" align="center" >%s</td>\n' \ u' <td colspan="5" class="%s%s">%s %s</td>\n' \ u' </tr>\n' \ % ( sRowClass, 1 + cAttribsChanged + 1, sDate, 1 + cAttribsChanged + 1, webutils.escapeElem(oEntry.oAuthor.sUsername if oEntry.oAuthor is not None else ''), sRowClass, ' tmsyschlogevent' if oChangeEntry is not None else '', webutils.escapeElem(sEvent), oDetails.toHtml() if isinstance(oDetails, WuiHtmlBase) else oDetails, ); if oChangeEntry is not None: sHtml += u' <tr class="%s tmsyschlogspacerrowabove">\n' \ u' <td xrowspan="%d" style="border-right: 0px; border-bottom: 0px;"></td>\n' \ u' <td colspan="3" style="border-right: 0px;"></td>\n' \ u' <td rowspan="%d" class="%s tmsyschlogspacer"></td>\n' \ u' </tr>\n' \ % (sRowClass, cAttribsChanged + 1, cAttribsChanged + 1, sRowClass); for j, oChange in enumerate(oChangeEntry.aoChanges): fLastRow = j + 1 == len(oChangeEntry.aoChanges); sHtml += u' <tr class="%s%s tmsyschlogattr%s">\n' \ % ( sRowClass, 'odd' if j & 1 else 'even', ' tmsyschlogattrfinal' if fLastRow else '',); if j == 0: sHtml += u' <td class="%s tmsyschlogspacer" rowspan="%d"></td>\n' % (sRowClass, cAttribsChanged - 1,); if isinstance(oChange, AttributeChangeEntryPre): sHtml += u' <td class="%s%s">%s</td>\n' \ u' <td><div class="tdpre"><pre>%s</pre></div></td>\n' \ u' <td class="%s%s"><div class="tdpre"><pre>%s</pre></div></td>\n' \ % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '', webutils.escapeElem(oChange.sAttr), webutils.escapeElem(oChange.sOldText), ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '', webutils.escapeElem(oChange.sNewText), ); else: sHtml += u' <td class="%s%s">%s</td>\n' \ u' <td>%s</td>\n' \ u' <td class="%s%s">%s</td>\n' \ % ( ' tmtopleft' if j == 0 else '', ' tmbottomleft' if fLastRow else '', webutils.escapeElem(oChange.sAttr), webutils.escapeElem(oChange.sOldText), ' tmtopright' if j == 0 else '', ' tmbottomright' if fLastRow else '', webutils.escapeElem(oChange.sNewText), ); sHtml += u' </tr>\n'; if oChangeEntry is not None: sHtml += u' <tr class="%s tmsyschlogspacerrowbelow "><td colspan="5"></td></tr>\n\n' % (sRowClass,); return sHtml; def _generateTableHeaders(self): """ Overridden parent method. """ sHtml = u'<thead class="tmheader">\n' \ u' <tr>\n' \ u' <th rowspan="2">When</th>\n' \ u' <th rowspan="2">Who</th>\n' \ u' <th colspan="5">Event</th>\n' \ u' </tr>\n' \ u' <tr>\n' \ u' <th style="border-right: 0px;"></th>\n' \ u' <th>Attribute</th>\n' \ u' <th>Old</th>\n' \ u' <th style="border-right: 0px;">New</th>\n' \ u' <th></th>\n' \ u' </tr>\n' \ u'</thead>\n'; return sHtml;
def fetchForChangeLog(self, idTestResult, iStart, cMaxRows, tsNow): # pylint: disable=R0914 """ Fetches change log entries for a failure reason. Returns an array of ChangeLogEntry instance and an indicator whether there are more entries. Raises exception on error. """ if tsNow is None: tsNow = self._oDb.getCurrentTimestamp() # 1. Get a list of the changes from both TestResultFailures and assoicated # FailureReasons. The latter is useful since the failure reason # description may evolve along side the invidiual failure analysis. self._oDb.execute( '( SELECT trf.tsEffective AS tsEffectiveChangeLog,\n' ' trf.uidAuthor AS uidAuthorChangeLog,\n' ' trf.*,\n' ' fr.*\n' ' FROM TestResultFailures trf,\n' ' FailureReasons fr\n' ' WHERE trf.idTestResult = %s\n' ' AND trf.tsEffective <= %s\n' ' AND trf.idFailureReason = fr.idFailureReason\n' ' AND fr.tsEffective <= trf.tsEffective\n' ' AND fr.tsExpire > trf.tsEffective\n' ')\n' 'UNION\n' '( SELECT fr.tsEffective AS tsEffectiveChangeLog,\n' ' fr.uidAuthor AS uidAuthorChangeLog,\n' ' trf.*,\n' ' fr.*\n' ' FROM TestResultFailures trf,\n' ' FailureReasons fr\n' ' WHERE trf.idTestResult = %s\n' ' AND trf.tsEffective <= %s\n' ' AND trf.idFailureReason = fr.idFailureReason\n' ' AND fr.tsEffective > trf.tsEffective\n' ' AND fr.tsEffective < trf.tsExpire\n' ')\n' 'ORDER BY tsEffectiveChangeLog DESC\n' 'LIMIT %s OFFSET %s\n', ( idTestResult, tsNow, idTestResult, tsNow, cMaxRows + 1, iStart, )) aaoRows = [] for aoChange in self._oDb.fetchAll(): oTrf = TestResultFailureDataEx().initFromDbRow(aoChange[2:]) oFr = FailureReasonData().initFromDbRow( aoChange[(2 + TestResultFailureData.kcDbColumns):]) oTrf.oFailureReason = oFr aaoRows.append([aoChange[0], aoChange[1], oTrf, oFr]) # 2. Calculate the changes. oFailureCategoryLogic = None aoEntries = [] for i in xrange(0, len(aaoRows) - 1): aoNew = aaoRows[i] aoOld = aaoRows[i + 1] aoChanges = [] oNew = aoNew[2] oOld = aoOld[2] for sAttr in oNew.getDataAttributes(): if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', 'oFailureReason', 'oAuthor' ]: oOldAttr = getattr(oOld, sAttr) oNewAttr = getattr(oNew, sAttr) if oOldAttr != oNewAttr: if sAttr == 'idFailureReason': oNewAttr = '%s (%s)' % ( oNewAttr, oNew.oFailureReason.sShort, ) oOldAttr = '%s (%s)' % ( oOldAttr, oOld.oFailureReason.sShort, ) aoChanges.append( AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr))) if oOld.idFailureReason == oNew.idFailureReason: oNew = aoNew[3] oOld = aoOld[3] for sAttr in oNew.getDataAttributes(): if sAttr not in [ 'tsEffective', 'tsExpire', 'uidAuthor', ]: oOldAttr = getattr(oOld, sAttr) oNewAttr = getattr(oNew, sAttr) if oOldAttr != oNewAttr: if sAttr == 'idFailureCategory': if oFailureCategoryLogic is None: from testmanager.core.failurecategory import FailureCategoryLogic oFailureCategoryLogic = FailureCategoryLogic( self._oDb) oCat = oFailureCategoryLogic.cachedLookup( oNewAttr) if oCat is not None: oNewAttr = '%s (%s)' % ( oNewAttr, oCat.sShort, ) oCat = oFailureCategoryLogic.cachedLookup( oOldAttr) if oCat is not None: oOldAttr = '%s (%s)' % ( oOldAttr, oCat.sShort, ) aoChanges.append( AttributeChangeEntry(sAttr, oNewAttr, oOldAttr, str(oNewAttr), str(oOldAttr))) tsExpire = aaoRows[i - 1][0] if i > 0 else aoNew[2].tsExpire aoEntries.append( ChangeLogEntry(aoNew[1], None, aoNew[0], tsExpire, aoNew[2], aoOld[2], aoChanges)) # If we're at the end of the log, add the initial entry. if len(aaoRows) <= cMaxRows and len(aaoRows) > 0: aoNew = aaoRows[-1] tsExpire = aaoRows[-1 - 1][0] if len(aaoRows) > 1 else aoNew[2].tsExpire aoEntries.append( ChangeLogEntry(aoNew[1], None, aoNew[0], tsExpire, aoNew[2], None, [])) return (UserAccountLogic(self._oDb).resolveChangeLogAuthors(aoEntries), len(aaoRows) > cMaxRows)