From 6c5d2da1a4c30129b257642f57f7b8add11e3163 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 1/20] repo request page in web ui


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index c35e256..18c6000 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -2680,6 +2680,27 @@ def repoinfo(environ, repoID):
     return _genHTML(environ, 'repoinfo.chtml')
 
 
+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 rows:
+        req = rows[0]
+    else:
+        req = None
+    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 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..3848343
--- /dev/null
+++ b/www/kojiweb/reporequest.chtml
@@ -0,0 +1,34 @@
+#import koji
+#from kojiweb import util
+
+#include "includes/header.chtml"
+
+<h4>Information for repo request $req_id</h4>
+
+#if not $req
+Repo request $req_id not found.
+#else
+<table>
+  <tr><th>ID</th><td>$req.id</td><th></tr>
+  <tr><th>Active</th><td>$req.active</td><th></tr>
+  <tr><th>Priority</th><td>$req.priority</td><th></tr>
+  <tr><th>Tag</th><td><a href="taginfo?tagID=$req.tag_id">$req.tag_name</a></td></tr>
+  #if $req.at_event
+  <tr><th>At specific event</th><td>$at_event.id ($util.formatTimeLong($at_event.ts))</td><th></tr>
+  #elif $req.min_event
+  <tr><th>Minimum event</th><td>$min_event.id ($util.formatTimeLong($min_event.ts))</td><th></tr>
+  #else
+  <tr><th>Invalid event</th><td>Unable to determine event for request</td><th></tr>
+  #end if
+  #if $req.task_id
+  <tr><th>Task ID</th><td><a href="taskinfo?taskID=$req.task_id">$req.task_id</a> ($util.taskState($req.task_state))</td></tr>
+  <tr><th>Tries</th><td>$req.tries</td></tr>
+  #end if
+  <tr><th>Owner</th><td><a href="userinfo?userID=$req.owner">$req.owner_name</a></td></tr>
+  <tr><th>Created</th><td>$util.formatTimeLong($req.create_ts)</td></tr>
+  <tr><th>Updated</th><td>$util.formatTimeLong($req.update_ts)</td></tr>
+</table>
+#end if
+
+
+#include "includes/footer.chtml"

From de23c8e8377f31fe62a2e50e72e911304ae807eb Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 2/20] link requests from repoinfo page


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index 18c6000..6aac80d 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -2675,8 +2675,9 @@ 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'])
     return _genHTML(environ, 'repoinfo.chtml')
 
 
diff --git a/www/kojiweb/repoinfo.chtml b/www/kojiweb/repoinfo.chtml
index 0f07b94..1fab544 100644
--- a/www/kojiweb/repoinfo.chtml
+++ b/www/kojiweb/repoinfo.chtml
@@ -21,6 +21,13 @@
   #end if
   <tr><th>Dist repo?</th><td class="$str($repo.dist).lower()">#if $repo.dist then 'yes' else 'no'#</td></tr>
   <tr><th>Number of buildroots: </th><td><a href="buildroots?repoID=$repo.id">$numBuildroots</a></td></tr>
+  #if $requests
+  <tr><th>Fulfills requests:</th><td>
+    #for req in $requests
+    <a href="reporequest?reqID=$req.id">$req.id</a>
+    #end for
+  </td/></tr>
+  #end if
 </table>
 #else
 Repo $repo_id not found.
diff --git a/www/kojiweb/reporequest.chtml b/www/kojiweb/reporequest.chtml
index 3848343..be8323a 100644
--- a/www/kojiweb/reporequest.chtml
+++ b/www/kojiweb/reporequest.chtml
@@ -20,6 +20,9 @@ Repo request $req_id not found.
   #else
   <tr><th>Invalid event</th><td>Unable to determine event for request</td><th></tr>
   #end if
+  #if $req.repo_id
+  <tr><th>Fulfilled by repo</th><td><a href="repoinfo?repoID=$req.repo_id">$req.repo_id</a</td></tr>
+  #end if
   #if $req.task_id
   <tr><th>Task ID</th><td><a href="taskinfo?taskID=$req.task_id">$req.task_id</a> ($util.taskState($req.task_state))</td></tr>
   <tr><th>Tries</th><td>$req.tries</td></tr>

From 71f63fd977e9b7d6046ff6237f2fbd18f64573a1 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 3/20] show begin/end events for repos


---

diff --git a/www/kojiweb/repoinfo.chtml b/www/kojiweb/repoinfo.chtml
index 1fab544..2e857e6 100644
--- a/www/kojiweb/repoinfo.chtml
+++ b/www/kojiweb/repoinfo.chtml
@@ -14,7 +14,14 @@
   #end if
   #set $state = $util.repoState($repo.state)
   <tr><th>State</th><td class="repo$state">$state</td></tr>
-  <tr><th>Event</th><td>$repo.create_event ($util.formatTimeLong($repo.create_ts))</td></tr>
+  <tr><th>Created from Event</th><td>$repo.create_event ($util.formatTimeLong($repo.create_ts))</td></tr>
+  #if $repo.begin_event
+  #if $repo.end_event
+  <tr><th>Event range</th><td>$repo.begin_event ... $repo.end_event</td></tr>
+  #else
+  <tr><th>Event range</th><td>$repo.begin_event ... </td></tr>
+  #end if
+  #end if
   #if $repo.state != koji.REPO_STATES['DELETED']
   <tr><th>URL</th><td><a href="$url">repodata</a></td></tr>
   <tr><th>Repo json</th><td><a href="$repo_json">repo.json</a></td></tr>

From 14ab5fb5dc0f824275b77b201d346baa4cd331ae Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 4/20] show opts info


---

diff --git a/www/kojiweb/repoinfo.chtml b/www/kojiweb/repoinfo.chtml
index 2e857e6..38279fc 100644
--- a/www/kojiweb/repoinfo.chtml
+++ b/www/kojiweb/repoinfo.chtml
@@ -1,3 +1,4 @@
+#import json
 #import koji
 #from kojiweb import util
 
@@ -10,7 +11,7 @@
   <tr><th>ID</th><td>$repo.id</td><th></tr>
   <tr><th>Tag</th><td><a href="taginfo?tagID=$repo.tag_id">$repo.tag_name</a></td></tr>
   #if $repo.task_id
-  <tr><th>Task ID</th><td><a href="taskinfo?taskID=$repo.task_id">$repo.task_id</a></td></tr>
+  <tr><th>Task ID</th><td><a href="taskinfo?taskID=$repo.task_id">$repo.task_id</a> ($util.taskState($repo.task_state))</td></tr>
   #end if
   #set $state = $util.repoState($repo.state)
   <tr><th>State</th><td class="repo$state">$state</td></tr>
@@ -26,6 +27,9 @@
   <tr><th>URL</th><td><a href="$url">repodata</a></td></tr>
   <tr><th>Repo json</th><td><a href="$repo_json">repo.json</a></td></tr>
   #end if
+  #if $repo.custom_opts
+  <th>Custom Opts</th><td class="usertext">$json.dumps($repo.custom_opts, indent=4)</td>
+  #end if
   <tr><th>Dist repo?</th><td class="$str($repo.dist).lower()">#if $repo.dist then 'yes' else 'no'#</td></tr>
   <tr><th>Number of buildroots: </th><td><a href="buildroots?repoID=$repo.id">$numBuildroots</a></td></tr>
   #if $requests
diff --git a/www/kojiweb/reporequest.chtml b/www/kojiweb/reporequest.chtml
index be8323a..e67fe65 100644
--- a/www/kojiweb/reporequest.chtml
+++ b/www/kojiweb/reporequest.chtml
@@ -1,3 +1,4 @@
+#import json
 #import koji
 #from kojiweb import util
 
@@ -20,6 +21,9 @@ Repo request $req_id not found.
   #else
   <tr><th>Invalid event</th><td>Unable to determine event for request</td><th></tr>
   #end if
+  #if $req.opts
+  <th>Options</th><td class="usertext">$json.dumps($req.opts, indent=4)</td>
+  #end if
   #if $req.repo_id
   <tr><th>Fulfilled by repo</th><td><a href="repoinfo?repoID=$req.repo_id">$req.repo_id</a</td></tr>
   #end if

From 73f7777ebb84a82c4788c708c1d086041908c8c9 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 5/20] repo requests page


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index 6aac80d..a2d91af 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -2688,10 +2688,9 @@ def reporequest(environ, reqID):
     req_id = int(reqID)
     values['req_id'] = req_id
     rows = server.repo.queryQueue([['id', '=', req_id]], '**')
-    if rows:
-        req = rows[0]
-    else:
-        req = None
+    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']:
@@ -2702,6 +2701,43 @@ def reporequest(environ, reqID):
     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('All 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(tag, strict=True)
+        clauses.append(["tag_id", taginfo['id']])
+        desc_parts.append('for tag %(name)s' % tag)
+        tag = taginfo['id']
+    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/reporequests.chtml b/www/kojiweb/reporequests.chtml
new file mode 100644
index 0000000..2eb4378
--- /dev/null
+++ b/www/kojiweb/reporequests.chtml
@@ -0,0 +1,87 @@
+#from kojiweb import util
+
+#include "includes/header.chtml"
+
+  <h4>$desc</h4>
+  <table class="data-list">
+    <tr>
+      <td class="paginate" colspan="5">
+        #if $len($reqPages) > 1
+        <form class="pageJump" action="">
+          Page:
+          <select onchange="javascript: window.location = 'reporequests?start=' + this.value * $reqRange + '$util.passthrough($self, 'active', 'tag','order')';">
+            #for $pageNum in $reqPages
+            <option value="$pageNum"#if $pageNum == $reqCurrentPage then ' selected' else ''#>#echo $pageNum + 1#</option>
+            #end for
+          </select>
+        </form>
+        #end if
+        #if $reqStart > 0
+        <a href="reporequests?start=#echo $reqStart - $reqRange #$util.passthrough($self, 'active', 'tag', 'order')">&lt;&lt;&lt;</a>
+        #end if
+        #if $totalReqs != 0
+        <strong>Reqs #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs</strong>
+        #end if
+        #if $reqStart + $reqCount < $totalReqs
+        <a href="reporequests?start=#echo $reqStart + $reqRange#$util.passthrough($self, 'active', 'tag', 'order')">&gt;&gt;&gt;</a>
+        #end if
+      </td>
+    </tr>
+    <tr class="list-header">
+      <th><a href="reporequests?$util.passthrough($self, 'active', 'tag', 'order', toggleOrder='id', prefix='')">ID</a> $util.sortImage($self, 'id')</th>
+      <th><a href="reporequests?$util.passthrough($self, 'active', 'tag', 'order', toggleOrder='priority', prefix='')">Priority</a> $util.sortImage($self, 'priority')</th>
+      <th><a href="reporequests?$util.passthrough($self, 'active', 'tag', 'order', toggleOrder='tag_name', prefix='')">Tag</a> $util.sortImage($self, 'tag_name')</th>
+      <th>Active</th>
+      <th>Task</th>
+      <th>Repo</th>
+    </tr>
+    #if $len($reqs) > 0
+      #for $req in $reqs
+        <tr class="$util.rowToggle($self)">
+          <td><a href="reporequest?reqID=$req.id">$req.id</a></td>
+          <td>$req.priority</td>
+          <td><a href="taginfo?tagID=$req.tag_id">$req.tag_name</a></td>
+          <td>$req.active</td>
+          #if $req.task_id
+          <td><a href="taskinfo?taskID=$req.task_id">$req.task_id</a></td>
+          #else
+          <td>...</td>
+          #end if
+          #if $req.repo_id
+          <td><a href="repoinfo?repoID=$req.repo_id">$req.repo_id</a></td>
+          #else
+          <td>...</td>
+          #end if
+        </tr>
+      #end for
+    #else
+      <tr class="row-odd">
+        <td colspan="2">No repo requests</td>
+      </tr>
+    #end if
+    <tr>
+      <td class="paginate" colspan="2">
+        #if $len($reqPages) > 1
+        <form class="pageJump" action="">
+          Page:
+          <select onchange="javascript: window.location = 'reporequests?start=' + this.value * $reqRange + '$util.passthrough($self, 'userID', 'tagID', 'order', 'childID')';">
+            #for $pageNum in $reqPages
+            <option value="$pageNum"#if $pageNum == $reqCurrentPage then ' selected' else ''#>#echo $pageNum + 1#</option>
+            #end for
+          </select>
+        </form>
+        #end if
+        #if $reqStart > 0
+        <a href="reporequests?start=#echo $reqStart - $reqRange #$util.passthrough($self, 'userID', 'tagID', 'order', 'childID')">&lt;&lt;&lt;</a>
+        #end if
+        #if $totalReqs != 0
+        <strong>Reqs #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs</strong>
+        #end if
+        #if $reqStart + $reqCount < $totalReqs
+        <a href="reporequests?start=#echo $reqStart + $reqRange#$util.passthrough($self, 'userID', 'tagID', 'order', 'childID')">&gt;&gt;&gt;</a>
+        #end if
+      </td>
+    </tr>
+  </table>
+
+#include "includes/footer.chtml"
diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py
index 7aa0366..ab74e54 100644
--- a/www/lib/kojiweb/util.py
+++ b/www/lib/kojiweb/util.py
@@ -302,7 +302,7 @@ def sortImage(template, sortKey, orderVar='order'):
 
 
 @safe_return
-def passthrough(template, *vars, prefix='&'):
+def passthrough(template, *vars, prefix='&', toggleOrder=None):
     """
     Construct a url parameter string from template vars
 
@@ -319,6 +319,11 @@ def passthrough(template, *vars, prefix='&'):
     result = []
     for var in vars:
         value = template.getVar(var, default=None)
+        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():
@@ -334,7 +339,7 @@ def passthrough(template, *vars, prefix='&'):
         return ''
 
 
-def passthrough_except(template, *exclude, prefix='&'):
+def passthrough_except(template, *exclude, prefix='&', toggleOrder=None):
     """
     Construct a string suitable for use as URL
     parameters.  The template calling this method must have
@@ -348,7 +353,7 @@ def passthrough_except(template, *exclude, prefix='&'):
     for var in template._PASSTHROUGH:
         if var not in exclude:
             passvars.append(var)
-    return passthrough(template, *passvars, prefix=prefix)
+    return passthrough(template, *passvars, prefix=prefix, toggleOrder=toggleOrder)
 
 
 def sortByKeyFuncNoneGreatest(key):
@@ -399,7 +404,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.
@@ -423,10 +428,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)

From 16fdcae811dc968676a78680f07c1e852c4ec499 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 6/20] typo


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index a2d91af..45da5a9 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -2721,7 +2721,7 @@ def reporequests(environ, active="true", tag=None, start=None, order=None):
     if tag:
         taginfo = server.getTag(tag, strict=True)
         clauses.append(["tag_id", taginfo['id']])
-        desc_parts.append('for tag %(name)s' % tag)
+        desc_parts.append('for tag %(name)s' % taginfo)
         tag = taginfo['id']
     else:
         tag = None

From 0b3d9e2df5e63ebc776fde82b2a8e47528100ddd Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 7/20] cleanup


---

diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index 2eb4378..ccab570 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -1,7 +1,11 @@
 #from kojiweb import util
+#from kojiweb.util import passthrough as P
 
 #include "includes/header.chtml"
 
+
+#set $Pvars = ('active', 'tag', 'order')
+
   <h4>$desc</h4>
   <table class="data-list">
     <tr>
@@ -9,7 +13,7 @@
         #if $len($reqPages) > 1
         <form class="pageJump" action="">
           Page:
-          <select onchange="javascript: window.location = 'reporequests?start=' + this.value * $reqRange + '$util.passthrough($self, 'active', 'tag','order')';">
+          <select onchange="javascript: window.location = 'reporequests?start=' + this.value * $reqRange + '$P($self, *Pvars)';">
             #for $pageNum in $reqPages
             <option value="$pageNum"#if $pageNum == $reqCurrentPage then ' selected' else ''#>#echo $pageNum + 1#</option>
             #end for
@@ -17,20 +21,20 @@
         </form>
         #end if
         #if $reqStart > 0
-        <a href="reporequests?start=#echo $reqStart - $reqRange #$util.passthrough($self, 'active', 'tag', 'order')">&lt;&lt;&lt;</a>
+        <a href="reporequests?start=#echo $reqStart - $reqRange #$P($self, *Pvars)">&lt;&lt;&lt;</a>
         #end if
         #if $totalReqs != 0
         <strong>Reqs #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs</strong>
         #end if
         #if $reqStart + $reqCount < $totalReqs
-        <a href="reporequests?start=#echo $reqStart + $reqRange#$util.passthrough($self, 'active', 'tag', 'order')">&gt;&gt;&gt;</a>
+        <a href="reporequests?start=#echo $reqStart + $reqRange#$P($self, *Pvars)">&gt;&gt;&gt;</a>
         #end if
       </td>
     </tr>
     <tr class="list-header">
-      <th><a href="reporequests?$util.passthrough($self, 'active', 'tag', 'order', toggleOrder='id', prefix='')">ID</a> $util.sortImage($self, 'id')</th>
-      <th><a href="reporequests?$util.passthrough($self, 'active', 'tag', 'order', toggleOrder='priority', prefix='')">Priority</a> $util.sortImage($self, 'priority')</th>
-      <th><a href="reporequests?$util.passthrough($self, 'active', 'tag', 'order', toggleOrder='tag_name', prefix='')">Tag</a> $util.sortImage($self, 'tag_name')</th>
+      <th><a href="reporequests?$P($self, *Pvars, toggleOrder='id', prefix='')">ID</a> $util.sortImage($self, 'id')</th>
+      <th><a href="reporequests?$P($self, *Pvars, toggleOrder='priority', prefix='')">Priority</a> $util.sortImage($self, 'priority')</th>
+      <th><a href="reporequests?$P($self, *Pvars, toggleOrder='tag_name', prefix='')">Tag</a> $util.sortImage($self, 'tag_name')</th>
       <th>Active</th>
       <th>Task</th>
       <th>Repo</th>

From ee25b4138f80cb3b7ec8a46cded3c1f40a371cf1 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 8/20] use newer repo.get in web ui


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index 45da5a9..b41fbc3 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -1043,7 +1043,7 @@ 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'])
 
     child = None

From 11eeedc0fea2686e3f6fa092ef3602742e63bcc8 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 9/20] repo request query form


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index b41fbc3..4e013ae 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -2709,7 +2709,7 @@ def reporequests(environ, active="true", tag=None, start=None, order=None):
     clauses = []
     desc_parts = []
     if active.lower() == 'all':
-        desc_parts.append('All repo requests')
+        desc_parts.append('Recent repo requests')
     elif active.lower() in ('false', 'no', '0'):
         desc_parts.append('Inactive repo requests')
         clauses.append(["active", False])
@@ -2719,10 +2719,10 @@ def reporequests(environ, active="true", tag=None, start=None, order=None):
         clauses.append(["active", True])
         active = 'true'
     if tag:
-        taginfo = server.getTag(tag, strict=True)
+        taginfo = server.getTag(_convert_if_int(tag), strict=True)
         clauses.append(["tag_id", taginfo['id']])
         desc_parts.append('for tag %(name)s' % taginfo)
-        tag = taginfo['id']
+        tag = taginfo['name']
     else:
         tag = None
     if order is None:
diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index ccab570..cd63c94 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -8,8 +8,30 @@
 
   <h4>$desc</h4>
   <table class="data-list">
+      <td colspan="6">
+        <form action="">
+        <table class="nested">
+          <tr><td>
+              <strong>Active</strong>:
+          </td><td>
+              <select name="active" class="filterlist" onchange="javascript: window.location = 'reporequests?active=' + this.value + '$P($self, 'tag', 'order')';">
+                <option value="true" #if $active == 'true' then 'selected' else ''#>true</option>
+                <option value="false" #if $active == 'false' then 'selected' else ''#>false</option>
+                <option value="all" #if $active == 'all' then 'selected' else ''#>all</option>
+              </select>
+          </td>
+          <td>
+              <strong>Tag</strong>:
+          </td><td>
+              <input type="text" name="tag" value="$tag"/>
+          </td>
+          </tr>
+        </table>
+        </form>
+      </td>
+    </tr>
     <tr>
-      <td class="paginate" colspan="5">
+      <td class="paginate" colspan="6">
         #if $len($reqPages) > 1
         <form class="pageJump" action="">
           Page:

From 4bc134d2528f61ec57965faf57858a83e1f4faba Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 10/20] link requests from taskinfo


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index 4e013ae..2e6eb48 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -1045,6 +1045,7 @@ 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})
 
     child = None
     if childID is not None:
diff --git a/www/kojiweb/taginfo.chtml b/www/kojiweb/taginfo.chtml
index 7568cb6..44e42f8 100644
--- a/www/kojiweb/taginfo.chtml
+++ b/www/kojiweb/taginfo.chtml
@@ -121,6 +121,10 @@
       </td>
     </tr>
     <tr>
+      <th>Repo&nbsp;requests</th>
+      <td><a href="reporequests?active=all&tag=$tag.id">$request_count</a></td>
+    </tr>
+    <tr>
       <th>Packages</th>
       <td><a href="packages?blocked=0&tagID=$tag.id">$numPackages</a></td>
     </tr>

From 7e46bb12de3c63b39f2812c9e36135ed160dbae3 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 11/20] show requests on taskinfo page


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index 2e6eb48..27ca6bc 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -792,6 +792,10 @@ def taskinfo(environ, taskID):
     else:
         values['perms'] = []
 
+    values['requests'] = []
+    if task['method'] == 'newRepo':
+        values['requests'] = server.repo.queryQueue([['task_id', '=', task['id']]], ['id'])
+
     return _genHTML(environ, 'taskinfo.chtml')
 
 
diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml
index 2ccee38..5c695b5 100644
--- a/www/kojiweb/taskinfo.chtml
+++ b/www/kojiweb/taskinfo.chtml
@@ -139,6 +139,13 @@ $value
     </tr>
     #end for
     #end if
+    #if $requests
+    <tr><th>For requests:</th><td>
+      #for req in $requests
+      <a href="reporequest?reqID=$req.id">$req.id</a>
+      #end for
+    </td/></tr>
+    #end if
     <tr>
       <th>Created</th><td>$util.formatTimeLong($task.create_ts)</td>
     </tr>

From 9dce8db904fd3f2d1e7bc8359766b71bf4c789c9 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 12/20] quick links for tag filtering, plus fixes


---

diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index cd63c94..50760cb 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -66,7 +66,12 @@
         <tr class="$util.rowToggle($self)">
           <td><a href="reporequest?reqID=$req.id">$req.id</a></td>
           <td>$req.priority</td>
-          <td><a href="taginfo?tagID=$req.tag_id">$req.tag_name</a></td>
+          <td>
+            <a href="taginfo?tagID=$req.tag_id">$req.tag_name</a>
+            #if not $tag
+            <a href="reporequests?tag=$req.tag_name$P($self, 'active', 'order')">↻</a>
+            #end if
+          </td>
           <td>$req.active</td>
           #if $req.task_id
           <td><a href="taskinfo?taskID=$req.task_id">$req.task_id</a></td>
@@ -90,7 +95,7 @@
         #if $len($reqPages) > 1
         <form class="pageJump" action="">
           Page:
-          <select onchange="javascript: window.location = 'reporequests?start=' + this.value * $reqRange + '$util.passthrough($self, 'userID', 'tagID', 'order', 'childID')';">
+          <select onchange="javascript: window.location = 'reporequests?start=' + this.value * $reqRange + '$P($self, *Pvars)';">
             #for $pageNum in $reqPages
             <option value="$pageNum"#if $pageNum == $reqCurrentPage then ' selected' else ''#>#echo $pageNum + 1#</option>
             #end for
@@ -98,13 +103,13 @@
         </form>
         #end if
         #if $reqStart > 0
-        <a href="reporequests?start=#echo $reqStart - $reqRange #$util.passthrough($self, 'userID', 'tagID', 'order', 'childID')">&lt;&lt;&lt;</a>
+        <a href="reporequests?start=#echo $reqStart - $reqRange #$P($self, *Pvars)">&lt;&lt;&lt;</a>
         #end if
         #if $totalReqs != 0
         <strong>Reqs #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs</strong>
         #end if
         #if $reqStart + $reqCount < $totalReqs
-        <a href="reporequests?start=#echo $reqStart + $reqRange#$util.passthrough($self, 'userID', 'tagID', 'order', 'childID')">&gt;&gt;&gt;</a>
+        <a href="reporequests?start=#echo $reqStart + $reqRange#$P($self, *Pvars)">&gt;&gt;&gt;</a>
         #end if
       </td>
     </tr>

From 178b9e8ebc5c76d32960359590f010e92e142e12 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 13/20] fixes


---

diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index 27ca6bc..4cd401b 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -2724,7 +2724,7 @@ def reporequests(environ, active="true", tag=None, start=None, order=None):
         clauses.append(["active", True])
         active = 'true'
     if tag:
-        taginfo = server.getTag(_convert_if_int(tag), strict=True)
+        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']
diff --git a/www/kojiweb/repoinfo.chtml b/www/kojiweb/repoinfo.chtml
index 38279fc..b108156 100644
--- a/www/kojiweb/repoinfo.chtml
+++ b/www/kojiweb/repoinfo.chtml
@@ -15,6 +15,8 @@
   #end if
   #set $state = $util.repoState($repo.state)
   <tr><th>State</th><td class="repo$state">$state</td></tr>
+  <tr><th>Created</th><td>$util.formatTimeLong($repo.creation_ts)</td></tr>
+  <tr><th>State changed</th><td>$util.formatTimeLong($repo.state_ts)</td></tr>
   <tr><th>Created from Event</th><td>$repo.create_event ($util.formatTimeLong($repo.create_ts))</td></tr>
   #if $repo.begin_event
   #if $repo.end_event
diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index 50760cb..9ba7cbe 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -46,7 +46,7 @@
         <a href="reporequests?start=#echo $reqStart - $reqRange #$P($self, *Pvars)">&lt;&lt;&lt;</a>
         #end if
         #if $totalReqs != 0
-        <strong>Reqs #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs</strong>
+        <strong>Requests #echo $reqStart + 1 # through #echo $reqStart + $reqCount # of $totalReqs</strong>
         #end if
         #if $reqStart + $reqCount < $totalReqs
         <a href="reporequests?start=#echo $reqStart + $reqRange#$P($self, *Pvars)">&gt;&gt;&gt;</a>
@@ -69,7 +69,7 @@
           <td>
             <a href="taginfo?tagID=$req.tag_id">$req.tag_name</a>
             #if not $tag
-            <a href="reporequests?tag=$req.tag_name$P($self, 'active', 'order')">↻</a>
+            <a href="reporequests?tag=$req.tag_id$P($self, 'active', 'order')">↻</a>
             #end if
           </td>
           <td>$req.active</td>

From 0ded9d090b17a8e5c7fdc47d6f86579d8c3f302c Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 14/20] prettier status


---

diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index 9ba7cbe..f176a77 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -57,9 +57,9 @@
       <th><a href="reporequests?$P($self, *Pvars, toggleOrder='id', prefix='')">ID</a> $util.sortImage($self, 'id')</th>
       <th><a href="reporequests?$P($self, *Pvars, toggleOrder='priority', prefix='')">Priority</a> $util.sortImage($self, 'priority')</th>
       <th><a href="reporequests?$P($self, *Pvars, toggleOrder='tag_name', prefix='')">Tag</a> $util.sortImage($self, 'tag_name')</th>
-      <th>Active</th>
       <th>Task</th>
       <th>Repo</th>
+      <th>Status</th>
     </tr>
     #if $len($reqs) > 0
       #for $req in $reqs
@@ -72,7 +72,6 @@
             <a href="reporequests?tag=$req.tag_id$P($self, 'active', 'order')">↻</a>
             #end if
           </td>
-          <td>$req.active</td>
           #if $req.task_id
           <td><a href="taskinfo?taskID=$req.task_id">$req.task_id</a></td>
           #else
@@ -83,6 +82,16 @@
           #else
           <td>...</td>
           #end if
+          <td>
+          ## simulate a more helpful status
+          #if $req.active
+          $util.imageTag('waiting')
+          #elif $req.repo_id
+          $util.imageTag('yes')
+          #else
+          $util.imageTag('no')
+          #end if
+          </td>
         </tr>
       #end for
     #else

From f408072f843e1c4d0b0038a84a55abd75b276c8a Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 15/20] tweak heading


---

diff --git a/www/kojiweb/taskinfo.chtml b/www/kojiweb/taskinfo.chtml
index 5c695b5..40d00ea 100644
--- a/www/kojiweb/taskinfo.chtml
+++ b/www/kojiweb/taskinfo.chtml
@@ -140,7 +140,8 @@ $value
     #end for
     #end if
     #if $requests
-    <tr><th>For requests:</th><td>
+    <tr><th>For request:</th><td>
+      ## we only expect one, but if we get more print them all
       #for req in $requests
       <a href="reporequest?reqID=$req.id">$req.id</a>
       #end for

From fad71ebcd80f75cac857b581c0469b203a6e574e Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 16/20] fix unit test


---

diff --git a/tests/test_www/test_repoinfo.py b/tests/test_www/test_repoinfo.py
index 2ab51ec..22a80fa 100644
--- a/tests/test_www/test_repoinfo.py
+++ b/tests/test_www/test_repoinfo.py
@@ -31,7 +31,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"""
@@ -43,4 +43,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

From 3cfc102ca4a9113e57748fb6cd8114bb88698b6a Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 17/20] add explicit encoding


---

diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index f176a77..ad69c29 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -1,3 +1,5 @@
+#encoding UTF-8
+
 #from kojiweb import util
 #from kojiweb.util import passthrough as P
 

From 9b90f5c38ad4a1ddf5fba76ec273c9726712710a Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 18/20] add link title


---

diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index ad69c29..d8555f5 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -71,7 +71,7 @@
           <td>
             <a href="taginfo?tagID=$req.tag_id">$req.tag_name</a>
             #if not $tag
-            <a href="reporequests?tag=$req.tag_id$P($self, 'active', 'order')">↻</a>
+            <a href="reporequests?tag=$req.tag_id$P($self, 'active', 'order')" title="Filter by tag">↻</a>
             #end if
           </td>
           #if $req.task_id

From 829f822ca6542894164708b6cee308a5e25c65c9 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 19/20] Use funnel icon for links to add query filters


Icon from School Collection
CC0 licensed
https://www.svgrepo.com/svg/152218/funnel

---

diff --git a/www/kojiweb/reporequests.chtml b/www/kojiweb/reporequests.chtml
index d8555f5..7f85281 100644
--- a/www/kojiweb/reporequests.chtml
+++ b/www/kojiweb/reporequests.chtml
@@ -71,7 +71,7 @@
           <td>
             <a href="taginfo?tagID=$req.tag_id">$req.tag_name</a>
             #if not $tag
-            <a href="reporequests?tag=$req.tag_id$P($self, 'active', 'order')" title="Filter by tag">↻</a>
+            <a href="reporequests?tag=$req.tag_id$P($self, 'active', 'order')" title="Filter by tag"><img src="$util.themePath('images/funnel.svg')"></a>
             #end if
           </td>
           #if $req.task_id
diff --git a/www/static/images/funnel.svg b/www/static/images/funnel.svg
new file mode 100644
index 0000000..5a9530d
--- /dev/null
+++ b/www/static/images/funnel.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg fill="#000000" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
+	 viewBox="0 0 485.008 485.008" xml:space="preserve">
+<g>
+	<g>
+		<path d="M171.501,464.698v-237.9l-166.3-192.6c-8.9-10.9-7.9-33.3,15.1-33.3h443.6c21.6,0,26.6,19.8,15.1,33.3l-162.3,187.5v147.2
+			c0,6-2,11.1-7.1,15.1l-103.8,95.8C193.801,488.698,171.501,483.898,171.501,464.698z M64.701,41.298l142.2,164.3c3,4,5,8.1,5,13.1
+			v200.6l64.5-58.5v-146.1c0-5,2-9.1,5-13.1l138.1-160.3L64.701,41.298L64.701,41.298z"/>
+	</g>
+</g>
+</svg>
\ No newline at end of file

From d6c17b20cefb92f54c7f6192081d25f3ac7dac99 Mon Sep 17 00:00:00 2001
From: Mike McLean <mikem@redhat.com>
Date: Feb 24 2025 23:10:11 +0000
Subject: [PATCH 20/20] adjust icon size


---

diff --git a/www/static/images/funnel.svg b/www/static/images/funnel.svg
index 5a9530d..2e1b054 100644
--- a/www/static/images/funnel.svg
+++ b/www/static/images/funnel.svg
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="iso-8859-1"?>
 <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
-<svg fill="#000000" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
+<svg fill="#000000" height="16px" width="16px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
 	 viewBox="0 0 485.008 485.008" xml:space="preserve">
 <g>
 	<g>
@@ -9,4 +9,4 @@
 			v200.6l64.5-58.5v-146.1c0-5,2-9.1,5-13.1l138.1-160.3L64.701,41.298L64.701,41.298z"/>
 	</g>
 </g>
-</svg>
\ No newline at end of file
+</svg>

