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 fetchForListingEx(self, iStart, cMaxRows, tsNow, cDaysBack, aiSortColumns=None): """ Fetches SystemLog entries. Returns an array (list) of SystemLogData items, empty list if none. Raises exception on error. """ _ = aiSortColumns # # Construct the query. # oUserAccountLogic = UserAccountLogic(self._oDb) oVSheriff = oUserAccountLogic.tryFetchAccountByLoginName( self.ksVSheriffLoginName) uidVSheriff = oVSheriff.uid if oVSheriff is not None else -1 if tsNow is None: sWhereTime = self._oDb.formatBindArgs( ' WHERE tsEffective >= CURRENT_TIMESTAMP - \'%s days\'::interval\n', (cDaysBack, )) else: sWhereTime = self._oDb.formatBindArgs( ' WHERE tsEffective >= (%s::timestamptz - \'%s days\'::interval)\n' ' AND tsEffective <= %s\n', (tsNow, cDaysBack, tsNow)) # Special entry for the system log. sQuery = '(\n' sQuery += ' SELECT NULL AS uidAuthor,\n' sQuery += ' tsCreated AS tsEffective,\n' sQuery += ' sEvent AS sEvent,\n' sQuery += ' NULL AS idWhat,\n' sQuery += ' sLogText AS sDesc\n' sQuery += ' FROM SystemLog\n' sQuery += sWhereTime.replace('tsEffective', 'tsCreated') sQuery += ' ORDER BY tsCreated DESC\n' sQuery += ')' for asEntry in self.kaasChangelogTables: sQuery += ' UNION (\n' sQuery += ' SELECT uidAuthor, tsEffective, \'' + asEntry[ 0] + '\', ' + asEntry[2] + ', \'\'\n' sQuery += ' FROM ' + asEntry[1] + '\n' sQuery += sWhereTime if asEntry[4] == self.ksTweak_NotNullAuthor or asEntry[ 4] == self.ksTweak_NotNullAuthorOrVSheriff: sQuery += ' AND uidAuthor IS NOT NULL\n' if asEntry[4] == self.ksTweak_NotNullAuthorOrVSheriff: sQuery += ' AND uidAuthor <> %u\n' % (uidVSheriff, ) sQuery += ' ORDER BY tsEffective DESC\n' sQuery += ')' sQuery += ' ORDER BY 2 DESC\n' sQuery += ' LIMIT %u OFFSET %u\n' % ( cMaxRows, iStart, ) # # Execute the query and construct the return data. # self._oDb.execute(sQuery) aoRows = [] for aoRow in self._oDb.fetchAll(): aoRows.append( SystemChangelogEntry(aoRow[1], oUserAccountLogic.cachedLookup(aoRow[0]), aoRow[2], aoRow[3], aoRow[4])) return aoRows
def fetchForListingEx(self, iStart, cMaxRows, tsNow, cDaysBack, aiSortColumns = None): """ Fetches SystemLog entries. Returns an array (list) of SystemLogData items, empty list if none. Raises exception on error. """ _ = aiSortColumns; # # Construct the query. # oUserAccountLogic = UserAccountLogic(self._oDb); oVSheriff = oUserAccountLogic.tryFetchAccountByLoginName(self.ksVSheriffLoginName); uidVSheriff = oVSheriff.uid if oVSheriff is not None else -1; if tsNow is None: sWhereTime = self._oDb.formatBindArgs(' WHERE tsEffective >= CURRENT_TIMESTAMP - \'%s days\'::interval\n', (cDaysBack,)); else: sWhereTime = self._oDb.formatBindArgs(' WHERE tsEffective >= (%s::timestamptz - \'%s days\'::interval)\n' ' AND tsEffective <= %s\n', (tsNow, cDaysBack, tsNow)); # Special entry for the system log. sQuery = '(\n' sQuery += ' SELECT NULL AS uidAuthor,\n'; sQuery += ' tsCreated AS tsEffective,\n'; sQuery += ' sEvent AS sEvent,\n'; sQuery += ' NULL AS idWhat,\n'; sQuery += ' sLogText AS sDesc\n'; sQuery += ' FROM SystemLog\n'; sQuery += sWhereTime.replace('tsEffective', 'tsCreated'); sQuery += ' ORDER BY tsCreated DESC\n' sQuery += ')' for asEntry in self.kaasChangelogTables: sQuery += ' UNION (\n' sQuery += ' SELECT uidAuthor, tsEffective, \'' + asEntry[0] + '\', ' + asEntry[2] + ', \'\'\n'; sQuery += ' FROM ' + asEntry[1] + '\n' sQuery += sWhereTime; if asEntry[4] == self.ksTweak_NotNullAuthor or asEntry[4] == self.ksTweak_NotNullAuthorOrVSheriff: sQuery += ' AND uidAuthor IS NOT NULL\n'; if asEntry[4] == self.ksTweak_NotNullAuthorOrVSheriff: sQuery += ' AND uidAuthor <> %u\n' % (uidVSheriff,); sQuery += ' ORDER BY tsEffective DESC\n' sQuery += ')'; sQuery += ' ORDER BY 2 DESC\n'; sQuery += ' LIMIT %u OFFSET %u\n' % (cMaxRows, iStart, ); # # Execute the query and construct the return data. # self._oDb.execute(sQuery); aoRows = []; for aoRow in self._oDb.fetchAll(): aoRows.append(SystemChangelogEntry(aoRow[1], oUserAccountLogic.cachedLookup(aoRow[0]), aoRow[2], aoRow[3], aoRow[4])); return aoRows;