From 75dd0ad497174bf4b2a0ddda5bbc1102fe346901 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 07 2025 16:25:52 +0000 Subject: [PATCH 1/13] repo requests web ui * new page for request details * new page for querying requests * add links in taginfo and taskinfo pages * show more data in repoinfo page --- diff --git a/tests/test_www/test_repoinfo.py b/tests/test_www/test_repoinfo.py index 81aafed..1994b66 100644 --- a/tests/test_www/test_repoinfo.py +++ b/tests/test_www/test_repoinfo.py @@ -33,7 +33,7 @@ class TestRepoInfo(unittest.TestCase): webidx.repoinfo(self.environ, self.repo_id) self.server.repoInfo.assert_called_once_with(int(self.repo_id), strict=False) - self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id)) + self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id), queryOpts={'countOnly': True}) def test_repoinfo_not_dist(self): """Test repoinfo function - not dist repo""" @@ -46,4 +46,7 @@ class TestRepoInfo(unittest.TestCase): webidx.repoinfo(self.environ, self.repo_id) self.server.repoInfo.assert_called_once_with(int(self.repo_id), strict=False) - self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id)) + self.server.listBuildroots.assert_called_once_with(repoID=int(self.repo_id), queryOpts={'countOnly': True}) + + +# the end diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index c237d4f..4a86592 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -797,6 +797,10 @@ def taskinfo(environ, taskID): else: values['perms'] = [] + values['requests'] = [] + if task['method'] == 'newRepo': + values['requests'] = server.repo.queryQueue([['task_id', '=', task['id']]], ['id']) + values['koji'] = koji values['S'] = SafeValue @@ -1051,8 +1055,9 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N values['srcTargets'] = srcTargets values['destTargets'] = destTargets values['all'] = all - values['repo'] = server.getRepo(tag['id'], state=koji.REPO_READY) + values['repo'] = server.repo.get(tag['id']) values['external_repos'] = server.getExternalRepoList(tag['id']) + values['request_count'] = server.repo.queryQueue([['tag_id', '=', tag['id']]], opts={'countOnly': True}) child = None if childID is not None: @@ -2696,13 +2701,72 @@ def repoinfo(environ, repoID): else: values['repo_json'] = os.path.join( pathinfo.repo(repo_info['id'], repo_info['tag_name']), 'repo.json') - num_buildroots = len(server.listBuildroots(repoID=repoID)) or 0 - values['numBuildroots'] = num_buildroots + num_buildroots = server.listBuildroots(repoID=repoID, queryOpts={'countOnly': True}) + values['numBuildroots'] = num_buildroots + values['requests'] = server.repo.queryQueue([['repo_id', '=', repoID]], ['id']) + values['state_name'] = kojiweb.util.repoState(repo_info['state']) values['create_time'] = kojiweb.util.formatTimeLong(repo_info['create_ts']) return _genHTML(environ, 'repoinfo.html.j2') +def reporequest(environ, reqID): + values = _initValues(environ, 'Repo Request', 'tags') + server = _getServer(environ) + + req_id = int(reqID) + values['req_id'] = req_id + rows = server.repo.queryQueue([['id', '=', req_id]], '**') + if not rows: + raise koji.GenericError("No such repo request: %s" % req_id) + req = rows[0] + if req['at_event']: + values['at_event'] = server.getEvent(req['at_event']) + elif req['min_event']: + values['min_event'] = server.getEvent(req['min_event']) + else: + # invalid, but technically not blocked in db + values['min_event'] = None + values['req'] = req + return _genHTML(environ, 'reporequest.chtml') + + +def reporequests(environ, active="true", tag=None, start=None, order=None): + values = _initValues(environ, 'Repo Requests', 'tags') + server = _getServer(environ) + + clauses = [] + desc_parts = [] + if active.lower() == 'all': + desc_parts.append('Recent repo requests') + elif active.lower() in ('false', 'no', '0'): + desc_parts.append('Inactive repo requests') + clauses.append(["active", False]) + active = 'false' + else: + desc_parts.append('Active repo requests') + clauses.append(["active", True]) + active = 'true' + if tag: + taginfo = server.getTag(_convert_if_int(tag), strict=True, event='auto') + clauses.append(["tag_id", taginfo['id']]) + desc_parts.append('for tag %(name)s' % taginfo) + tag = taginfo['name'] + else: + tag = None + if order is None: + order = '-id' + values['desc'] = ' '.join(desc_parts) + values['order'] = order + values['active'] = active + values['tag'] = tag + kojiweb.util.paginateMethod(server, values, 'repo.queryQueue', + args=(clauses, '**'), + start=start, dataName='reqs', prefix='req', order=order, + optsarg='opts') + return _genHTML(environ, 'reporequests.chtml') + + def activesession(environ, start=None, order=None): values = _initValues(environ, 'Active sessions', 'activesession') server = _getServer(environ) diff --git a/www/kojiweb/reporequest.chtml b/www/kojiweb/reporequest.chtml new file mode 100644 index 0000000..e67fe65 --- /dev/null +++ b/www/kojiweb/reporequest.chtml @@ -0,0 +1,41 @@ +#import json +#import koji +#from kojiweb import util + +#include "includes/header.chtml" + +

Information for repo request $req_id

+ +#if not $req +Repo request $req_id not found. +#else + + + + + + #if $req.at_event + + #elif $req.min_event + + #else + + #end if + #if $req.opts + + #end if + #if $req.repo_id + + #end if + #if $req.task_id + + + #end if + + + +
ID$req.id
Active$req.active
Priority$req.priority
Tag$req.tag_name
At specific event$at_event.id ($util.formatTimeLong($at_event.ts))
Minimum event$min_event.id ($util.formatTimeLong($min_event.ts))
Invalid eventUnable to determine event for request
Options$json.dumps($req.opts, indent=4)
Fulfilled by repo$req.repo_id
Task ID$req.task_id ($util.taskState($req.task_state))
Tries$req.tries
Owner$req.owner_name
Created$util.formatTimeLong($req.create_ts)
Updated$util.formatTimeLong($req.update_ts)
+#end if + + +#include "includes/footer.chtml" diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml new file mode 100644 index 0000000..7f85281 --- /dev/null +++ b/www/kojiweb/reporequests.chtml @@ -0,0 +1,129 @@ +#encoding UTF-8 + +#from kojiweb import util +#from kojiweb.util import passthrough as P + +#include "includes/header.chtml" + + +#set $Pvars = ('active', 'tag', 'order') + +

$desc

+ + + + + + + + + + + + + + + #if $len($reqs) > 0 + #for $req in $reqs + + + + + #if $req.task_id + + #else + + #end if + #if $req.repo_id + + #else + + #end if + + + #end for + #else + + + + #end if + + + +
+
+ + + + +
+ Active: + + + + Tag: + + +
+
+
+ #if $len($reqPages) > 1 +
+ Page: + +
+ #end if + #if $reqStart > 0 + <<< + #end if + #if $totalReqs != 0 + Requests #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs + #end if + #if $reqStart + $reqCount < $totalReqs + >>> + #end if +
ID $util.sortImage($self, 'id')Priority $util.sortImage($self, 'priority')Tag $util.sortImage($self, 'tag_name')TaskRepoStatus
$req.id$req.priority + $req.tag_name + #if not $tag + + #end if + $req.task_id...$req.repo_id... + ## simulate a more helpful status + #if $req.active + $util.imageTag('waiting') + #elif $req.repo_id + $util.imageTag('yes') + #else + $util.imageTag('no') + #end if +
No repo requests
+ #if $len($reqPages) > 1 +
+ Page: + +
+ #end if + #if $reqStart > 0 + <<< + #end if + #if $totalReqs != 0 + Reqs #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs + #end if + #if $reqStart + $reqCount < $totalReqs + >>> + #end if +
+ +#include "includes/footer.chtml" diff --git a/www/kojiweb/templates/repoinfo.html.j2 b/www/kojiweb/templates/repoinfo.html.j2 index cfcd284..0d856b6 100644 --- a/www/kojiweb/templates/repoinfo.html.j2 +++ b/www/kojiweb/templates/repoinfo.html.j2 @@ -7,16 +7,35 @@ ID{{ repo.id }} Tag{{ repo.tag_name }} {% if repo.task_id %} - Task ID{{ repo.task_id }} + Task ID{{ repo.task_id }} ({{ util.taskState(repo.task_state) }}) {% endif %} State{{ state_name }} - Event{{ repo.create_event }} ({{ create_time }}) + Created{{ util.formatTimeLong(repo.creation_ts) }} + State changed{{ util.formatTimeLong(repo.state_ts) }} + Created from Event{{ repo.create_event }} ({{ util.formatTimeLong(repo.create_ts) }}) + {%- if repo.begin_event %} + {%- if repo.end_event %} + Event range{{ repo.begin_event }} ... {{ repo.end_event }} + {%- else %} + Event range{{ repo.begin_event }} ... + {%- endif %} + {%- endif %} {%- if state_name != 'deleted' %} URLrepodata Repo jsonrepo.json {%- endif %} + #if repo.custom_opts + Custom Opts{{ json.dumps(repo.custom_opts, indent=4) }} + #endif Dist repo?{{ 'yes' if repo.dist else 'no' }} Number of buildroots: {{ numBuildroots }} + #if requests + Fulfills requests: + #for req in requests + {{ req.id }} + #endfor + + #endif {% else %} Repo {{ repo_id }} not found. diff --git a/www/kojiweb/templates/taginfo.html.j2 b/www/kojiweb/templates/taginfo.html.j2 index 3b4bc3e..5202596 100644 --- a/www/kojiweb/templates/taginfo.html.j2 +++ b/www/kojiweb/templates/taginfo.html.j2 @@ -118,6 +118,10 @@ + Repo requests + {{ request_count }} + + Packages {{ numPackages }} diff --git a/www/kojiweb/templates/taskinfo.html.j2 b/www/kojiweb/templates/taskinfo.html.j2 index 453d37d..f2ff5a8 100644 --- a/www/kojiweb/templates/taskinfo.html.j2 +++ b/www/kojiweb/templates/taskinfo.html.j2 @@ -140,6 +140,14 @@ None #endfor #endif + #if requests + For request: + ## we only expect one, but if we get more print them all + #for req in requests + {{ req.id }} + #endfor + + #endif Created{{ util.formatTimeLong(task.create_ts) }} diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index c30a689..ad840b4 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -353,17 +353,17 @@ def _sortImage(orderVal, sortKey, orderVar): @safe_return @pass_context -def passthrough(context, *varnames, prefix='&', invert=False): +def passthrough(context, *varnames, prefix='&', invert=False, toggleOrder=None): if invert: _PASSTHROUGH = context.get('_PASSTHROUGH', None) if _PASSTHROUGH is None: raise Exception('template does not define _PASSTHROUGH') varnames = {n for n in _PASSTHROUGH if n not in varnames} data = {n: context.get(n, default=None) for n in varnames} - return _passthrough(data, prefix) + return _passthrough(data, prefixi, toggleOrder) -def _passthrough(data, prefix='&'): +def _passthrough(data, prefix='&', toggleOrder=None): """ Construct a url parameter string from template vars @@ -380,6 +380,11 @@ def _passthrough(data, prefix='&'): result = [] for var in sorted(data): value = data[var] + if var == 'order' and toggleOrder is not None: + if value == toggleOrder: + value = '-' + value + else: + value = toggleOrder if value is not None: if isinstance(value, str): if value.isdigit(): @@ -396,7 +401,7 @@ def _passthrough(data, prefix='&'): @pass_context -def passthrough_except(context, *exclude, prefix='&'): +def passthrough_except(context, *exclude, prefix='&', toggleOrder=None): """ Construct a string suitable for use as URL parameters. The template calling this method must have @@ -408,7 +413,7 @@ def passthrough_except(context, *exclude, prefix='&'): """ # note that we have to pass context ourselves here # the decorator only works when called directly from the template - return passthrough(context, *exclude, prefix=prefix, invert=True) + return passthrough(context, *exclude, prefix=prefix, invert=True, toggleOrder=toggleOrder) def sortByKeyFuncNoneGreatest(key): @@ -459,7 +464,7 @@ def paginateList(values, data, start, dataName, prefix=None, order=None, noneGre def paginateMethod(server, values, methodName, args=None, kw=None, start=None, dataName=None, prefix=None, order=None, pageSize=50, - first_page_count=True): + first_page_count=True, optsarg='queryOpts'): """Paginate the results of the method with the given name when called with the given args and kws. The method must support the queryOpts keyword parameter, and pagination is done in the database. @@ -483,10 +488,10 @@ def paginateMethod(server, values, methodName, args=None, kw=None, if start == 0 and not first_page_count: totalRows = None else: - kw['queryOpts'] = {'countOnly': True} + kw[optsarg] = {'countOnly': True} totalRows = getattr(server, methodName)(*args, **kw) - kw['queryOpts'] = {'order': order, + kw[optsarg] = {'order': order, 'offset': start, 'limit': pageSize} data = getattr(server, methodName)(*args, **kw) diff --git a/www/static/images/funnel.svg b/www/static/images/funnel.svg new file mode 100644 index 0000000..2e1b054 --- /dev/null +++ b/www/static/images/funnel.svg @@ -0,0 +1,12 @@ + + + + + + + + + From 0a6eb52c0a635bc34484356974e9de7a6c75fd4f Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 07 2025 16:27:02 +0000 Subject: [PATCH 2/13] typo --- diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index ad840b4..6deacc7 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -360,7 +360,7 @@ def passthrough(context, *varnames, prefix='&', invert=False, toggleOrder=None): raise Exception('template does not define _PASSTHROUGH') varnames = {n for n in _PASSTHROUGH if n not in varnames} data = {n: context.get(n, default=None) for n in varnames} - return _passthrough(data, prefixi, toggleOrder) + return _passthrough(data, prefix, toggleOrder) def _passthrough(data, prefix='&', toggleOrder=None): From ba6352ba812f18b35edf719c4651c3edec55aff9 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 07 2025 16:40:40 +0000 Subject: [PATCH 3/13] port new templates to jinja --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index 4a86592..fa2c6d5 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -2728,7 +2728,7 @@ def reporequest(environ, reqID): # invalid, but technically not blocked in db values['min_event'] = None values['req'] = req - return _genHTML(environ, 'reporequest.chtml') + return _genHTML(environ, 'reporequest.html.j2') def reporequests(environ, active="true", tag=None, start=None, order=None): @@ -2764,7 +2764,7 @@ def reporequests(environ, active="true", tag=None, start=None, order=None): args=(clauses, '**'), start=start, dataName='reqs', prefix='req', order=order, optsarg='opts') - return _genHTML(environ, 'reporequests.chtml') + return _genHTML(environ, 'reporequests.html.j2') def activesession(environ, start=None, order=None): diff --git a/www/kojiweb/reporequest.chtml b/www/kojiweb/reporequest.chtml deleted file mode 100644 index e67fe65..0000000 --- a/www/kojiweb/reporequest.chtml +++ /dev/null @@ -1,41 +0,0 @@ -#import json -#import koji -#from kojiweb import util - -#include "includes/header.chtml" - -

Information for repo request $req_id

- -#if not $req -Repo request $req_id not found. -#else - - - - - - #if $req.at_event - - #elif $req.min_event - - #else - - #end if - #if $req.opts - - #end if - #if $req.repo_id - - #end if - #if $req.task_id - - - #end if - - - -
ID$req.id
Active$req.active
Priority$req.priority
Tag$req.tag_name
At specific event$at_event.id ($util.formatTimeLong($at_event.ts))
Minimum event$min_event.id ($util.formatTimeLong($min_event.ts))
Invalid eventUnable to determine event for request
Options$json.dumps($req.opts, indent=4)
Fulfilled by repo$req.repo_id
Task ID$req.task_id ($util.taskState($req.task_state))
Tries$req.tries
Owner$req.owner_name
Created$util.formatTimeLong($req.create_ts)
Updated$util.formatTimeLong($req.update_ts)
-#end if - - -#include "includes/footer.chtml" diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml deleted file mode 100644 index 7f85281..0000000 --- a/www/kojiweb/reporequests.chtml +++ /dev/null @@ -1,129 +0,0 @@ -#encoding UTF-8 - -#from kojiweb import util -#from kojiweb.util import passthrough as P - -#include "includes/header.chtml" - - -#set $Pvars = ('active', 'tag', 'order') - -

$desc

- - - - - - - - - - - - - - - #if $len($reqs) > 0 - #for $req in $reqs - - - - - #if $req.task_id - - #else - - #end if - #if $req.repo_id - - #else - - #end if - - - #end for - #else - - - - #end if - - - -
-
- - - - -
- Active: - - - - Tag: - - -
-
-
- #if $len($reqPages) > 1 -
- Page: - -
- #end if - #if $reqStart > 0 - <<< - #end if - #if $totalReqs != 0 - Requests #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs - #end if - #if $reqStart + $reqCount < $totalReqs - >>> - #end if -
ID $util.sortImage($self, 'id')Priority $util.sortImage($self, 'priority')Tag $util.sortImage($self, 'tag_name')TaskRepoStatus
$req.id$req.priority - $req.tag_name - #if not $tag - - #end if - $req.task_id...$req.repo_id... - ## simulate a more helpful status - #if $req.active - $util.imageTag('waiting') - #elif $req.repo_id - $util.imageTag('yes') - #else - $util.imageTag('no') - #end if -
No repo requests
- #if $len($reqPages) > 1 -
- Page: - -
- #end if - #if $reqStart > 0 - <<< - #end if - #if $totalReqs != 0 - Reqs #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs - #end if - #if $reqStart + $reqCount < $totalReqs - >>> - #end if -
- -#include "includes/footer.chtml" diff --git a/www/kojiweb/templates/reporequest.html.j2 b/www/kojiweb/templates/reporequest.html.j2 new file mode 100644 index 0000000..dc90c08 --- /dev/null +++ b/www/kojiweb/templates/reporequest.html.j2 @@ -0,0 +1,38 @@ + +#include "header.html.j2" + +

Information for repo request {{ req_id }}

+ +#if not req +Repo request {{ req_id }} not found. +#else + + + + + + #if req.at_event + + #elif req.min_event + + #else + + #endif + #if req.opts + + #endif + #if req.repo_id + + #endif + #if req.task_id + + + #endif + + + +
ID{{ req.id }}
Active{{ req.active }}
Priority{{ req.priority }}
Tag{{ req.tag_name }}
At specific event{{ at_event.id }} ({{ util.formatTimeLong(at_event.ts) }})
Minimum event{{ min_event.id }} ({{ util.formatTimeLong(min_event.ts) }})
Invalid eventUnable to determine event for request
Options{{ json.dumps(req.opts, indent=4) }}
Fulfilled by repo{{ req.repo_id }}
Task ID{{ req.task_id }} ({{ util.taskState(req.task_state) }})
Tries{{ req.tries }}
Owner{{ req.owner_name }}
Created{{ util.formatTimeLong(req.create_ts) }}
Updated{{ util.formatTimeLong(req.update_ts) }}
+#endif + + +#include "footer.html.j2" diff --git a/www/kojiweb/templates/reporequests.html.j2 b/www/kojiweb/templates/reporequests.html.j2 new file mode 100644 index 0000000..a06c691 --- /dev/null +++ b/www/kojiweb/templates/reporequests.html.j2 @@ -0,0 +1,126 @@ +#include "header.html.j2" + +# from "macros.html.j2" import rowToggle + +#set Pvars = ('active', 'tag', 'order') +#set P = util.passthrough + +

{{ desc }}

+ + + + + + + + + + + + + + + #if (reqs |length) > 0 + #for req in reqs + + + + + #if req.task_id + + #else + + #endif + #if req.repo_id + + #else + + #endif + + + #endfor + #else + + + + #endif + + + +
+
+ + + + +
+ Active: + + + + Tag: + + +
+
+
+ #if (reqPages |length) > 1 +
+ Page: + +
+ #endif + #if reqStart > 0 + <<< + #endif + #if totalReqs != 0 + Requests {{ reqStart + 1 }} through {{ reqStart + reqCount }} of {{ totalReqs }} + #endif + #if reqStart + reqCount < totalReqs + >>> + #endif +
ID {{ util.sortImage('id') }}Priority {{ util.sortImage('priority') }}Tag {{ util.sortImage('tag_name') }}TaskRepoStatus
{{ req.id }}{{ req.priority }} + {{ req.tag_name }} + #if not tag + + #endif + {{ req.task_id }}...{{ req.repo_id }}... + ## simulate a more helpful status + #if req.active + {{ util.imageTag('waiting') }} + #elif req.repo_id + {{ util.imageTag('yes') }} + #else + {{ util.imageTag('no') }} + #endif +
No repo requests
+ #if (reqPages |length) > 1 +
+ Page: + +
+ #endif + #if reqStart > 0 + <<< + #endif + #if totalReqs != 0 + Reqs {{ reqStart + 1 }} through {{ reqStart + reqCount }} of {{ totalReqs }} + #endif + #if reqStart + reqCount < totalReqs + >>> + #endif +
+ +#include "footer.html.j2" From 4944e3960bc1bc8d26ac913f3578d66558ae13ee Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 07 2025 18:31:55 +0000 Subject: [PATCH 4/13] update call data for unit test --- diff --git a/tests/test_cli/fakeclient.py b/tests/test_cli/fakeclient.py index f074dce..0190974 100644 --- a/tests/test_cli/fakeclient.py +++ b/tests/test_cli/fakeclient.py @@ -44,6 +44,8 @@ class FakeClientSession(BaseFakeClientSession): super(FakeClientSession, self).__init__(*a, **kw) self._calldata = {} self._offsets = {} + self._missing_rsession = None + # caller can set _missing_rsession to a Recording session to handle missing calls def load_calls(self, data): """Load call data @@ -75,6 +77,9 @@ class FakeClientSession(BaseFakeClientSession): # we may have a series of calls for each key calls = self._calldata.get(key) ofs = self._offsets.get(key, 0) + if calls is None: + # we don't have it + return self._handle_missing(name, args, kwargs) call = calls[ofs] ofs += 1 if ofs < len(calls): @@ -90,6 +95,15 @@ class FakeClientSession(BaseFakeClientSession): else: return mock.MagicMock() + def _handle_missing(self, name, args, kwargs): + print('Missing call data for: %s %r %r' % (name, args, kwargs)) + rsession = self._missing_rsession + if rsession is None: + return mock.MagicMock() + + # otherwise use the recording session + return rsession._callMethod(name, args, kwargs) + def _munge(self, data): def callback(value): if isinstance(value, list): diff --git a/tests/test_www/data/pages_calls.json b/tests/test_www/data/pages_calls.json index 5c45cc1..0a53d33 100644 --- a/tests/test_www/data/pages_calls.json +++ b/tests/test_www/data/pages_calls.json @@ -56142,5 +56142,155 @@ "task_id": null } ] + }, + { + "args": [], + "kwargs": { + "queryOpts": { + "countOnly": true + }, + "repoID": 2580 + }, + "method": "listBuildroots", + "result": 2 + }, + { + "args": [ + [ + [ + "repo_id", + "=", + 2580 + ] + ], + [ + "id" + ] + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [] + }, + { + "args": [ + 798 + ], + "kwargs": {}, + "method": "repo.get", + "result": null + }, + { + "args": [ + [ + [ + "tag_id", + "=", + 798 + ] + ] + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 0 + }, + { + "args": [], + "kwargs": { + "queryOpts": { + "countOnly": true + }, + "repoID": 88 + }, + "method": "listBuildroots", + "result": 0 + }, + { + "args": [ + [ + [ + "repo_id", + "=", + 88 + ] + ], + [ + "id" + ] + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [] + }, + { + "args": [ + 2 + ], + "kwargs": {}, + "method": "repo.get", + "result": { + "begin_event": 497768, + "begin_ts": 1732049290.238136, + "create_event": 497784, + "create_ts": 1732569672.685269, + "creation_time": "2024-11-25 21:21:12.682068+00:00", + "creation_ts": 1732569672.682068, + "custom_opts": {}, + "dist": false, + "end_event": null, + "end_ts": null, + "id": 2599, + "opts": { + "debuginfo": false, + "maven": false, + "separate_src": false, + "src": false + }, + "state": 1, + "state_time": "2024-11-25 21:21:19.711390+00:00", + "state_ts": 1732569679.71139, + "tag_id": 2, + "tag_name": "f24-build", + "task_id": 14431, + "task_state": 2 + } + }, + { + "args": [ + [ + [ + "tag_id", + "=", + 2 + ] + ] + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 0 + }, + { + "args": [ + [ + [ + "task_id", + "=", + 1 + ] + ], + [ + "id" + ] + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [] } -] \ No newline at end of file +] diff --git a/tests/test_www/test_pages.py b/tests/test_www/test_pages.py index f4be222..9e739f9 100644 --- a/tests/test_www/test_pages.py +++ b/tests/test_www/test_pages.py @@ -27,7 +27,9 @@ class TestPages(unittest.TestCase): def setUpClass(cls): # recording session used across tests in recording mode cls.cfile = os.path.dirname(__file__) + f'/data/pages_calls.json' + cls.cfile2 = os.path.dirname(__file__) + f'/data/pages_calls_updates.json' cls.recording = False + cls.updating = False cls.rsession = RecordingClientSession('http://localhost/kojihub', {}) @classmethod @@ -35,6 +37,8 @@ class TestPages(unittest.TestCase): if cls.recording: # save recorded calls cls.rsession.dump(cls.cfile) + elif cls.updating: + cls.rsession.dump(cls.cfile2) def setUp(self): self.environ = { @@ -77,6 +81,8 @@ class TestPages(unittest.TestCase): else: self.server = FakeClientSession('SERVER', {}) self.server.load(self.cfile) + if self.updating: + self.server._missing_rsession = self.rsession return self.server def tearDown(self): From bf8dd0a1a89c1eb12e8932c420e59bea012b66cd Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 07 2025 18:35:49 +0000 Subject: [PATCH 5/13] fix None display in query form --- diff --git a/www/kojiweb/templates/reporequests.html.j2 b/www/kojiweb/templates/reporequests.html.j2 index a06c691..1019766 100644 --- a/www/kojiweb/templates/reporequests.html.j2 +++ b/www/kojiweb/templates/reporequests.html.j2 @@ -22,7 +22,7 @@ Tag: - + From 6ef45649897d057cb8f7456d004ff1eca53e846f Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 07 2025 18:50:09 +0000 Subject: [PATCH 6/13] flake8 --- diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py index fa2c6d5..c9ae5ff 100644 --- a/www/kojiweb/index.py +++ b/www/kojiweb/index.py @@ -1057,7 +1057,8 @@ def taginfo(environ, tagID, all='0', packageOrder='package_name', packageStart=N values['all'] = all values['repo'] = server.repo.get(tag['id']) values['external_repos'] = server.getExternalRepoList(tag['id']) - values['request_count'] = server.repo.queryQueue([['tag_id', '=', tag['id']]], opts={'countOnly': True}) + values['request_count'] = server.repo.queryQueue([['tag_id', '=', tag['id']]], + opts={'countOnly': True}) child = None if childID is not None: diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py index 6deacc7..2519f9d 100644 --- a/www/lib/kojiweb/util.py +++ b/www/lib/kojiweb/util.py @@ -492,8 +492,8 @@ def paginateMethod(server, values, methodName, args=None, kw=None, totalRows = getattr(server, methodName)(*args, **kw) kw[optsarg] = {'order': order, - 'offset': start, - 'limit': pageSize} + 'offset': start, + 'limit': pageSize} data = getattr(server, methodName)(*args, **kw) count = len(data) From eb795960dd90a64d01b72962693d1359de593a6c Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 08 2025 11:28:59 +0000 Subject: [PATCH 7/13] install svg file --- diff --git a/www/static/images/Makefile b/www/static/images/Makefile index fd7f2ca..3a7891c 100644 --- a/www/static/images/Makefile +++ b/www/static/images/Makefile @@ -1,5 +1,5 @@ SERVERDIR = /images -FILES = $(wildcard *.gif *.png *.ico) +FILES = $(wildcard *.gif *.png *.ico *.svg) _default: @echo "nothing to make. try make install" From 5e622be80ce2bf6037a34bad001dc6312bde5a31 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 08 2025 12:17:38 +0000 Subject: [PATCH 8/13] fix json handling in template --- diff --git a/www/kojiweb/templates/reporequest.html.j2 b/www/kojiweb/templates/reporequest.html.j2 index dc90c08..9430c70 100644 --- a/www/kojiweb/templates/reporequest.html.j2 +++ b/www/kojiweb/templates/reporequest.html.j2 @@ -19,7 +19,7 @@ Repo request {{ req_id }} not found. Invalid eventUnable to determine event for request #endif #if req.opts - Options{{ json.dumps(req.opts, indent=4) }} + Options{{ req.opts | tojson(indent=4) }} #endif #if req.repo_id Fulfilled by repo{{ req.repo_id }} From 1a2e96b1c502b8bc72a305e83e27d1859d3026c5 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 08 2025 12:17:38 +0000 Subject: [PATCH 9/13] update test_pages to cover new code --- diff --git a/tests/test_www/data/pages_calls.json b/tests/test_www/data/pages_calls.json index 0a53d33..911e8e0 100644 --- a/tests/test_www/data/pages_calls.json +++ b/tests/test_www/data/pages_calls.json @@ -56292,5 +56292,931 @@ "kwargs": {}, "method": "repo.queryQueue", "result": [] + }, + { + "args": [ + [ + [ + "active", + true + ] + ], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 7 + }, + { + "args": [ + [ + [ + "active", + true + ] + ], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "-id" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + }, + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + }, + { + "active": true, + "at_event": 497087, + "create_time": "2025-05-08 11:42:18.526583+00:00", + "create_ts": 1746704538.526583, + "id": 131, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:18.526583+00:00", + "update_ts": 1746704538.526583 + }, + { + "active": true, + "at_event": 497088, + "create_time": "2025-05-08 11:42:15.500883+00:00", + "create_ts": 1746704535.500883, + "id": 130, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:15.500883+00:00", + "update_ts": 1746704535.500883 + }, + { + "active": true, + "at_event": 497089, + "create_time": "2025-05-08 11:42:03.370177+00:00", + "create_ts": 1746704523.370177, + "id": 129, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:03.370177+00:00", + "update_ts": 1746704523.370177 + }, + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + } + ] + }, + { + "args": [ + [], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 9 + }, + { + "args": [ + [], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "tag_name" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + }, + { + "active": true, + "at_event": 497089, + "create_time": "2025-05-08 11:42:03.370177+00:00", + "create_ts": 1746704523.370177, + "id": 129, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:03.370177+00:00", + "update_ts": 1746704523.370177 + }, + { + "active": true, + "at_event": 497088, + "create_time": "2025-05-08 11:42:15.500883+00:00", + "create_ts": 1746704535.500883, + "id": 130, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:15.500883+00:00", + "update_ts": 1746704535.500883 + }, + { + "active": true, + "at_event": 497087, + "create_time": "2025-05-08 11:42:18.526583+00:00", + "create_ts": 1746704538.526583, + "id": 131, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:18.526583+00:00", + "update_ts": 1746704538.526583 + }, + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + }, + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:04:43.253576+00:00", + "create_ts": 1737151483.253576, + "id": 126, + "min_event": 497788, + "opts": { + "debuginfo": true + }, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14444, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + }, + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:21:15.725044+00:00", + "create_ts": 1737152475.725044, + "id": 127, + "min_event": 497788, + "opts": {}, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14445, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + } + ] + }, + { + "args": [ + [ + [ + "active", + false + ] + ], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 2 + }, + { + "args": [ + [ + [ + "active", + false + ] + ], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "-id" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:21:15.725044+00:00", + "create_ts": 1737152475.725044, + "id": 127, + "min_event": 497788, + "opts": {}, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14445, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + }, + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:04:43.253576+00:00", + "create_ts": 1737151483.253576, + "id": 126, + "min_event": 497788, + "opts": { + "debuginfo": true + }, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14444, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + } + ] + }, + { + "args": [ + 1 + ], + "kwargs": { + "event": "auto", + "strict": true + }, + "method": "getTag", + "result": { + "arches": "x86_64", + "extra": { + "bar": 101, + "foo": 1, + "fuzz": 3 + }, + "id": 1, + "locked": false, + "maven_include_all": false, + "maven_support": false, + "name": "f24", + "perm": null, + "perm_id": null + } + }, + { + "args": [ + [ + [ + "tag_id", + 1 + ] + ], + "**" + ], + "kwargs": { + "opts": { + "countOnly": true + } + }, + "method": "repo.queryQueue", + "result": 7 + }, + { + "args": [ + [ + [ + "tag_id", + 1 + ] + ], + "**" + ], + "kwargs": { + "opts": { + "limit": 50, + "offset": 0, + "order": "-id" + } + }, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + }, + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + }, + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + }, + { + "active": true, + "at_event": 497087, + "create_time": "2025-05-08 11:42:18.526583+00:00", + "create_ts": 1746704538.526583, + "id": 131, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:18.526583+00:00", + "update_ts": 1746704538.526583 + }, + { + "active": true, + "at_event": 497088, + "create_time": "2025-05-08 11:42:15.500883+00:00", + "create_ts": 1746704535.500883, + "id": 130, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:15.500883+00:00", + "update_ts": 1746704535.500883 + }, + { + "active": true, + "at_event": 497089, + "create_time": "2025-05-08 11:42:03.370177+00:00", + "create_ts": 1746704523.370177, + "id": 129, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:03.370177+00:00", + "update_ts": 1746704523.370177 + }, + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + } + ] + }, + { + "args": [ + [ + [ + "id", + "=", + 127 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": false, + "at_event": null, + "create_time": "2025-01-17 22:21:15.725044+00:00", + "create_ts": 1737152475.725044, + "id": 127, + "min_event": 497788, + "opts": {}, + "owner": 4, + "owner_name": "builder-01", + "priority": 20, + "repo_id": 2602, + "tag_id": 2099, + "tag_name": "kpatch-kernel-fake-1.1-5-build", + "task_id": 14445, + "task_state": 2, + "tries": 1, + "update_time": "2025-01-17 22:21:48.169150+00:00", + "update_ts": 1737152508.16915 + } + ] + }, + { + "args": [ + 497788 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497788, + "ts": 1737145925.263978 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 128 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": null, + "create_time": "2025-05-08 11:32:15.973760+00:00", + "create_ts": 1746703935.97376, + "id": 128, + "min_event": 497768, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:32:15.973760+00:00", + "update_ts": 1746703935.97376 + } + ] + }, + { + "args": [ + 497768 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497768, + "ts": 1732049290.238136 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 132 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497086, + "create_time": "2025-05-08 11:42:21.704141+00:00", + "create_ts": 1746704541.704141, + "id": 132, + "min_event": null, + "opts": {}, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:42:21.704141+00:00", + "update_ts": 1746704541.704141 + } + ] + }, + { + "args": [ + 497086 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497086, + "ts": 1706727187.174471 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 133 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:36.529538+00:00", + "create_ts": 1746704616.529538, + "id": 133, + "min_event": null, + "opts": { + "debuginfo": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:36.529538+00:00", + "update_ts": 1746704616.529538 + } + ] + }, + { + "args": [ + 497085 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497085, + "ts": 1706727156.407864 + } + }, + { + "args": [ + [ + [ + "id", + "=", + 134 + ] + ], + "**" + ], + "kwargs": {}, + "method": "repo.queryQueue", + "result": [ + { + "active": true, + "at_event": 497085, + "create_time": "2025-05-08 11:43:40.126768+00:00", + "create_ts": 1746704620.126768, + "id": 134, + "min_event": null, + "opts": { + "src": true + }, + "owner": 1, + "owner_name": "mikem", + "priority": 20, + "repo_id": null, + "tag_id": 1, + "tag_name": "f24", + "task_id": null, + "task_state": null, + "tries": 0, + "update_time": "2025-05-08 11:43:40.126768+00:00", + "update_ts": 1746704620.126768 + } + ] + }, + { + "args": [ + 497085 + ], + "kwargs": {}, + "method": "getEvent", + "result": { + "id": 497085, + "ts": 1706727156.407864 + } } ] diff --git a/tests/test_www/test_pages.py b/tests/test_www/test_pages.py index 9e739f9..3ff9f1d 100644 --- a/tests/test_www/test_pages.py +++ b/tests/test_www/test_pages.py @@ -172,6 +172,15 @@ class TestPages(unittest.TestCase): ['buildroots', ''], ['buildroots', 'start=50&order=id'], #['builds', 'start=50&order=id'], + ['reporequests', ''], + ['reporequests', 'active=all&order=tag_name'], + ['reporequests', 'active=false&order=-id'], + ['reporequests', 'tag=1&active=all'], + ['reporequest', 'reqID=127'], + ['reporequest', 'reqID=128'], + ['reporequest', 'reqID=132'], + ['reporequest', 'reqID=133'], + ['reporequest', 'reqID=134'], ] def prep_handler(self, method, query): From ba29013abf0fc7dc0242063ba4832a0217ab2aab Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 08 2025 15:12:30 +0000 Subject: [PATCH 10/13] test_pages updates This fixes issues with test_pages that I found while checking coverage. The environ dict was recycled across each call in the loop, but those calls can modify it, leaking data from one pass to another. Also, the mocking for _getServer was not placing the session in the environ, which was causing the tests to miss some cases. --- diff --git a/tests/test_www/data/pages_calls.json b/tests/test_www/data/pages_calls.json index 911e8e0..49268e1 100644 --- a/tests/test_www/data/pages_calls.json +++ b/tests/test_www/data/pages_calls.json @@ -51058,6 +51058,12 @@ "result": true }, { + "args": [], + "kwargs": {}, + "method": "winEnabled", + "result": true + }, + { "args": [ 2 ], diff --git a/tests/test_www/test_pages.py b/tests/test_www/test_pages.py index 3ff9f1d..612c8ba 100644 --- a/tests/test_www/test_pages.py +++ b/tests/test_www/test_pages.py @@ -70,6 +70,8 @@ class TestPages(unittest.TestCase): self.time.return_value = 1735707600.0 def __get_server(env): + # this is replacing the call to _getServer + env['koji.session'] = self.server return self.server self.get_server.side_effect = __get_server @@ -186,12 +188,13 @@ class TestPages(unittest.TestCase): def prep_handler(self, method, query): """Takes method name and query string, returns handler and data""" # based loosely on publisher prep_handler - self.environ['QUERY_STRING'] = query - self.environ['koji.method'] = method - self.environ['SCRIPT_NAME'] = method + environ = self.environ.copy() + environ['QUERY_STRING'] = query + environ['koji.method'] = method + environ['SCRIPT_NAME'] = method handler = getattr(webidx, method) - fs = FieldStorageCompat(self.environ) - self.environ['koji.form'] = fs + fs = FieldStorageCompat(environ) + environ['koji.form'] = fs # even though we have curated urls, we need to filter args for some cases, e.g. search args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \ inspect.getfullargspec(handler) @@ -199,14 +202,14 @@ class TestPages(unittest.TestCase): data = dslice(fs.data, args, strict=False) else: data = fs.data.copy() - return handler, data + return handler, data, environ def test_web_handlers(self): """Test a bunch of web handlers""" for method, query in self.CALLS: - handler, data = self.prep_handler(method, query) + handler, data, environ = self.prep_handler(method, query) - result = handler(self.environ, **data) + result = handler(environ, **data) # result should be a string containing the rendered template self.assertIsInstance(result, str) From 9c5c6be678d51adf4ac41516801a3cda3cf31643 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 29 2025 14:15:44 +0000 Subject: [PATCH 11/13] fix missing tr element --- diff --git a/www/kojiweb/templates/reporequests.html.j2 b/www/kojiweb/templates/reporequests.html.j2 index 1019766..decf27f 100644 --- a/www/kojiweb/templates/reporequests.html.j2 +++ b/www/kojiweb/templates/reporequests.html.j2 @@ -7,6 +7,7 @@

{{ desc }}

+
From 8554932e3e87d8594051ee553cbcdc3987be71ef Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 29 2025 15:02:32 +0000 Subject: [PATCH 12/13] fix malformed html element --- diff --git a/www/kojiweb/templates/reporequest.html.j2 b/www/kojiweb/templates/reporequest.html.j2 index 9430c70..6eeaebc 100644 --- a/www/kojiweb/templates/reporequest.html.j2 +++ b/www/kojiweb/templates/reporequest.html.j2 @@ -22,7 +22,7 @@ Repo request {{ req_id }} not found. #endif #if req.repo_id - + #endif #if req.task_id From c5ed63e4e84fab74aa3c343bba4920586356c285 Mon Sep 17 00:00:00 2001 From: Mike McLean Date: May 30 2025 14:36:09 +0000 Subject: [PATCH 13/13] html fixes --- diff --git a/www/kojiweb/templates/reporequest.html.j2 b/www/kojiweb/templates/reporequest.html.j2 index 6eeaebc..19818b0 100644 --- a/www/kojiweb/templates/reporequest.html.j2 +++ b/www/kojiweb/templates/reporequest.html.j2 @@ -7,16 +7,16 @@ Repo request {{ req_id }} not found. #else
Options{{ req.opts | tojson(indent=4) }}
Fulfilled by repo{{ req.repo_id }}
Fulfilled by repo{{ req.repo_id }}
Task ID{{ req.task_id }} ({{ util.taskState(req.task_state) }})
- - - + + + #if req.at_event - + #elif req.min_event - + #else - + #endif #if req.opts diff --git a/www/kojiweb/templates/reporequests.html.j2 b/www/kojiweb/templates/reporequests.html.j2 index decf27f..c97f9f0 100644 --- a/www/kojiweb/templates/reporequests.html.j2 +++ b/www/kojiweb/templates/reporequests.html.j2 @@ -69,7 +69,7 @@ #if req.task_id
ID{{ req.id }}
Active{{ req.active }}
Priority{{ req.priority }}
ID{{ req.id }}
Active{{ req.active }}
Priority{{ req.priority }}
Tag{{ req.tag_name }}
At specific event{{ at_event.id }} ({{ util.formatTimeLong(at_event.ts) }})
At specific event{{ at_event.id }} ({{ util.formatTimeLong(at_event.ts) }})
Minimum event{{ min_event.id }} ({{ util.formatTimeLong(min_event.ts) }})
Minimum event{{ min_event.id }} ({{ util.formatTimeLong(min_event.ts) }})
Invalid eventUnable to determine event for request
Invalid eventUnable to determine event for request
Options{{ req.opts | tojson(indent=4) }} {{ req.tag_name }} #if not tag - + ^ #endif