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
This commit is contained in:
parent
573bd41654
commit
357587a57c
9 changed files with 300 additions and 15 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
41
www/kojiweb/reporequest.chtml
Normal file
41
www/kojiweb/reporequest.chtml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#import json
|
||||
#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.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
|
||||
#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"
|
||||
129
www/kojiweb/reporequests.chtml
Normal file
129
www/kojiweb/reporequests.chtml
Normal file
|
|
@ -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')
|
||||
|
||||
<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="6">
|
||||
#if $len($reqPages) > 1
|
||||
<form class="pageJump" action="">
|
||||
Page:
|
||||
<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
|
||||
</select>
|
||||
</form>
|
||||
#end if
|
||||
#if $reqStart > 0
|
||||
<a href="reporequests?start=#echo $reqStart - $reqRange #$P($self, *Pvars)"><<<</a>
|
||||
#end if
|
||||
#if $totalReqs != 0
|
||||
<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)">>>></a>
|
||||
#end if
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="list-header">
|
||||
<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>Task</th>
|
||||
<th>Repo</th>
|
||||
<th>Status</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>
|
||||
#if not $tag
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<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 + '$P($self, *Pvars)';">
|
||||
#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 #$P($self, *Pvars)"><<<</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#$P($self, *Pvars)">>>></a>
|
||||
#end if
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
#include "includes/footer.chtml"
|
||||
|
|
@ -7,16 +7,35 @@
|
|||
<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>
|
||||
{% endif %}
|
||||
<tr><th>State</th><td class="repo{{ state_name }}">{{ state_name }}</td></tr>
|
||||
<tr><th>Event</th><td>{{ repo.create_event }} ({{ create_time }})</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 %}
|
||||
<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>
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
{%- if state_name != '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>
|
||||
{%- endif %}
|
||||
#if repo.custom_opts
|
||||
<th>Custom Opts</th><td class="usertext">{{ json.dumps(repo.custom_opts, indent=4) }}</td>
|
||||
#endif
|
||||
<tr><th>Dist repo?</th><td class="{{ repo.dist | lower }}">{{ 'yes' if repo.dist 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>
|
||||
#endfor
|
||||
</td/></tr>
|
||||
#endif
|
||||
</table>
|
||||
{% else %}
|
||||
Repo {{ repo_id }} not found.
|
||||
|
|
|
|||
|
|
@ -117,6 +117,10 @@
|
|||
#endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Repo 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>
|
||||
|
|
|
|||
|
|
@ -140,6 +140,14 @@ None
|
|||
</tr>
|
||||
#endfor
|
||||
#endif
|
||||
#if requests
|
||||
<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>
|
||||
#endfor
|
||||
</td/></tr>
|
||||
#endif
|
||||
<tr>
|
||||
<th>Created</th><td>{{ util.formatTimeLong(task.create_ts) }}</td>
|
||||
</tr>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
12
www/static/images/funnel.svg
Normal file
12
www/static/images/funnel.svg
Normal file
|
|
@ -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="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>
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 716 B |
Loading…
Add table
Add a link
Reference in a new issue