Revamp needsponsor report.

Now includes names or addresses if names not set.
Includes submission date of oldest ticket.
Names are clickable to find all review commentary.
This commit is contained in:
Jason Tibbitts 2012-08-20 11:42:14 -05:00
parent 8789303caf
commit 66eaae6702
2 changed files with 165 additions and 24 deletions

View file

@ -99,7 +99,8 @@ def parse_config(file):
def nobody(str): def nobody(str):
'''Shorten the long "nobody's working on it" string.''' '''Shorten the long "nobody's working on it" string.'''
if str == "Nobody's working on this, feel free to take it": if (str == "Nobody's working on this, feel free to take it"
or str == "nobody@fedoraproject.org"):
return "(Nobody)" return "(Nobody)"
return str return str
@ -107,9 +108,14 @@ def nosec(str):
'''Remove the seconds from an hh:mm:ss format string.''' '''Remove the seconds from an hh:mm:ss format string.'''
return str[0:str.rfind(':')] return str[0:str.rfind(':')]
def human_time(t): def human_date(t):
'''Turn an ISO date into something more human-friendly.''' '''Turn an ISO date into something more human-friendly.'''
t = str(t) t = str(t)
return t[0:4] + '-' + t[4:6] + '-' + t[6:8]
def human_time(t):
'''Turn an ISO date into something more human-friendly, with time.'''
t = str(t)
return t[0:4] + '-' + t[4:6] + '-' + t[6:8] + ' ' + t[9:] return t[0:4] + '-' + t[4:6] + '-' + t[6:8] + ' ' + t[9:]
def to_unicode(object, encoding='utf8', errors='replace'): def to_unicode(object, encoding='utf8', errors='replace'):
@ -156,20 +162,23 @@ def seq_max_split(seq, max_entries):
def run_query(bz): def run_query(bz):
querydata = {} querydata = {}
bugdata = {} bugdata = {}
interesting = {}
alldeps = set([]) alldeps = set([])
closeddeps = set([]) closeddeps = set([])
needinfo = set([])
usermap = {}
querydata['include_fields'] = ['id', 'creation_time', 'last_change_time', 'bug_severity', querydata['include_fields'] = ['id', 'creation_time', 'last_change_time', 'bug_severity',
'alias', 'assigned_to', 'product', 'creator', 'creator_id', 'status', 'resolution', 'alias', 'assigned_to', 'product', 'creator', 'creator_id', 'status', 'resolution',
'component', 'blocks', 'depends_on', 'summary', 'component', 'blocks', 'depends_on', 'summary',
'whiteboard', 'flags'] 'whiteboard', 'flags']
querydata['extra_values'] = [] #querydata['extra_values'] = []
querydata['bug_status'] = ['NEW', 'ASSIGNED', 'MODIFIED'] querydata['bug_status'] = ['NEW', 'ASSIGNED', 'MODIFIED']
querydata['product'] = ['Fedora', 'Fedora EPEL'] querydata['product'] = ['Fedora', 'Fedora EPEL']
querydata['component'] = ['Package Review'] querydata['component'] = ['Package Review']
querydata['query_format'] = 'advanced' querydata['query_format'] = 'advanced'
# Look up tickets with no flag set # Look up tickets with no fedora-review flag set
querydata['f1'] = 'flagtypes.name' querydata['f1'] = 'flagtypes.name'
querydata['o1'] = 'notregexp' querydata['o1'] = 'notregexp'
querydata['v1'] = 'fedora-review[-+?]' querydata['v1'] = 'fedora-review[-+?]'
@ -178,21 +187,40 @@ def run_query(bz):
for bug in bugs: for bug in bugs:
bugdata[bug.id] = {} bugdata[bug.id] = {}
bugdata[bug.id]['hidden'] = 0 bugdata[bug.id]['hidden'] = 0
bugdata[bug.id]['needinfo'] = 0
bugdata[bug.id]['blocks'] = bug.blocks bugdata[bug.id]['blocks'] = bug.blocks
bugdata[bug.id]['depends'] = bug.depends_on bugdata[bug.id]['depends'] = bug.depends_on
bugdata[bug.id]['reviewflag'] = ' ' bugdata[bug.id]['reviewflag'] = ' '
# Keep track of dependencies in unflagged tickets # Keep track of "interesting" bugs for which we'll need to do complete
# lookups. We want anything with
if bug.depends_on: if bug.depends_on:
alldeps.update(bug.depends_on) alldeps.update(bug.depends_on)
# Get the status of each dependency if bug.flags.find('needinfo?') >= 0:
for i in seq_max_split(alldeps, 500): needinfo.add(bug.id)
for bug in bz.getbugssimple(i):
if bug.status == 'CLOSED':
closeddeps.add(bug.id)
# Some special processing for those unflagged tickets # Get the status of each "interesting" bug
for i in seq_max_split(alldeps.union(needinfo), 500):
for bug in filter(None, bz._proxy.Bug.get_bugs({'ids':i, 'permissive': 1, 'extra_fields': ['flags']})['bugs']):
interesting[bug['id']] = bug
# Note the dependencies which are closed
for i in alldeps:
if interesting[i]['status'] == 'CLOSED':
closeddeps.add(i)
# Note the ones flagged needinfo->reporter
for i in needinfo:
for j in interesting[i]['flags']:
if (j['name'] == 'needinfo'
and j['status'] == '?'
and j['requestee'] == interesting[i]['creator']):
bugdata[i]['needinfo'] = 1
bugdata[i]['hidden'] = 1
# Hide tickets blocked by other bugs or whose with various blockers and
# statuses.
def opendep(id): return id not in closeddeps def opendep(id): return id not in closeddeps
for bug in bugs: for bug in bugs:
wb = string.lower(bug.whiteboard) wb = string.lower(bug.whiteboard)
@ -206,6 +234,14 @@ def run_query(bz):
or filter(opendep, bugdata[bug.id]['depends']))): or filter(opendep, bugdata[bug.id]['depends']))):
bugdata[bug.id]['hidden'] = 1 bugdata[bug.id]['hidden'] = 1
# Now we need to look up the names of the users
for i in bugs:
if select_needsponsor(i, bugdata[i.id]):
usermap[i.reporter] = ''
for i in bz._proxy.User.get({'names': usermap.keys()})['users']:
usermap[i['name']] = i['real_name']
# Now process the other three flags; not much special processing for them # Now process the other three flags; not much special processing for them
querydata['o1'] = 'equals' querydata['o1'] = 'equals'
# for i in ['-', '+', '?']: # for i in ['-', '+', '?']:
@ -222,7 +258,7 @@ def run_query(bz):
bugs.sort(key=operator.attrgetter('id')) bugs.sort(key=operator.attrgetter('id'))
return [bugs, bugdata] return [bugs, bugdata, usermap]
# Need to generate reports: # Need to generate reports:
# "Accepted" and closed # "Accepted" and closed
@ -267,6 +303,7 @@ def select_merge(bug, bugd):
def select_needsponsor(bug, bugd): def select_needsponsor(bug, bugd):
wb = string.lower(bug.whiteboard) wb = string.lower(bug.whiteboard)
if (bugd['reviewflag'] == ' ' if (bugd['reviewflag'] == ' '
and bugd['needinfo'] == 0
and NEEDSPONSOR in bugd['blocks'] and NEEDSPONSOR in bugd['blocks']
and LEGAL not in bugd['blocks'] and LEGAL not in bugd['blocks']
and bug.bug_status != 'CLOSED' and bug.bug_status != 'CLOSED'
@ -403,13 +440,13 @@ def report_merge(bugs, bugdata, loader, tmpdir, subs):
return data['count'] return data['count']
def report_needsponsor(bugs, bugdata, loader, tmpdir, subs): def report_needsponsor(bugs, bugdata, loader, usermap, tmpdir, subs):
# Note that this abuses the "month" view to group by reporter instead of month.
data = deepcopy(subs) data = deepcopy(subs)
data['description'] = 'This page lists all new NEEDSPONSOR tickets (those without the fedora-revlew flag set)' data['description'] = 'This page lists all new NEEDSPONSOR tickets (those without the fedora-revlew flag set).'
data['title'] = 'NEEDSPONSOR tickets' data['title'] = 'NEEDSPONSOR tickets'
curreporter = '' curreporter = ''
curcount = 0 curcount = 0
oldest = {}
selected = [] selected = []
for i in bugs: for i in bugs:
@ -417,19 +454,31 @@ def report_needsponsor(bugs, bugdata, loader, tmpdir, subs):
selected.append(i) selected.append(i)
selected.sort(key=reporter) selected.sort(key=reporter)
# Determine the oldest reported bug
for i in selected:
if i.reporter not in oldest:
oldest[i.reporter] = i.creation_time
elif i.creation_time < oldest[i.reporter]:
oldest[i.reporter] = i.creation_time
for i in selected: for i in selected:
rowclass = rowclass_plain(data['count']) rowclass = rowclass_plain(data['count'])
r = i.reporter;
if curreporter != reporter(i): if curreporter != r:
data['months'].append({'month': reporter(i), 'bugs': []}) if (r in usermap and len(usermap[r])):
curreporter = reporter(i) name = usermap[r]
else:
name = r
data['packagers'].append({'email': r, 'name': name, 'oldest': human_date(oldest[r]), 'bugs': []})
curreporter = r
curcount = 0 curcount = 0
data['months'][-1]['bugs'].append(std_row(i, rowclass)) data['packagers'][-1]['bugs'].append(std_row(i, rowclass))
data['count'] +=1 data['count'] +=1
curcount +=1 curcount +=1
write_html(loader, 'bymonth.html', data, tmpdir, 'NEEDSPONSOR.html') write_html(loader, 'needsponsor.html', data, tmpdir, 'NEEDSPONSOR.html')
return data['count'] return data['count']
@ -494,10 +543,10 @@ def report_new(bugs, bugdata, loader, tmpdir, subs):
if __name__ == '__main__': if __name__ == '__main__':
options = parse_commandline() options = parse_commandline()
config = parse_config(options.configfile) config = parse_config(options.configfile)
#bz = bugzilla.Bugzilla(url=config['url'], cookiefile=None, user=config['username'], password=config['password']) bz = bugzilla.RHBugzilla(url=config['url'], cookiefile=None, user=config['username'], password=config['password'])
bz = bugzilla.Bugzilla(url=config['url'], cookiefile=None) #bz = bugzilla.RHBugzilla(url=config['url'], cookiefile=None)
t = time.time() t = time.time()
(bugs, bugdata) = run_query(bz) (bugs, bugdata, usermap) = run_query(bz)
querytime = time.time() - t querytime = time.time() - t
# Don't bother running this stuff until the query completes, since it fails # Don't bother running this stuff until the query completes, since it fails
@ -512,6 +561,7 @@ if __name__ == '__main__':
'version': VERSION, 'version': VERSION,
'count': 0, 'count': 0,
'months': [], 'months': [],
'packagers': [],
'bugs': [], 'bugs': [],
} }
args = {'bugs':bugs, 'bugdata':bugdata, 'loader':loader, 'tmpdir':tmpdir, 'subs':subs} args = {'bugs':bugs, 'bugdata':bugdata, 'loader':loader, 'tmpdir':tmpdir, 'subs':subs}
@ -522,7 +572,7 @@ if __name__ == '__main__':
subs['epel'] = report_epel(**args) subs['epel'] = report_epel(**args)
subs['hidden'] = report_hidden(**args) subs['hidden'] = report_hidden(**args)
subs['merge'] = report_merge(**args) subs['merge'] = report_merge(**args)
subs['needsponsor'] = report_needsponsor(**args) subs['needsponsor'] = report_needsponsor(usermap=usermap, **args)
subs['review'] = report_review(**args) subs['review'] = report_review(**args)
subs['trivial'] = report_trivial(**args) subs['trivial'] = report_trivial(**args)
# data['accepted_closed'] = report_accepted_closed(bugs, bugdata, loader, tmpdir) # data['accepted_closed'] = report_accepted_closed(bugs, bugdata, loader, tmpdir)

View file

@ -0,0 +1,91 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<base href="https://bugzilla.redhat.com/bugzilla/"/>
<title>$title</title>
<style type="text/css" media="screen">
.bz_state_NEEDSPONSOR {background-color: #AAFFAA}
.bz_state_FEATURE {background-color: #FFAAAA}
.bz_row_even {background-color: #FFFFFF}
.bz_row_odd {background-color: #EEEEEE}
#content
{
margin-left: 0ex!important;
}
td, th
{
border: none!important;
padding: 0.5ex 2ex!important;
}
</style>
<link rel="stylesheet" type="text/css" media="all"
href="http://fedoraproject.org/static/css/fedora.css" />
</head>
<body>
<div id="wrapper">
<div id="head">
<h1><a href="http://fedoraproject.org/index.html">Fedora</a></h1>
</div>
<div id="content">
<h2>$description</h2>
Click the name to find all comments made in any review tickets.<br/>
In parentheses is the date their oldest submission was made.<br/>
Last Update: $update (v$version)<br/>
There are $count tickets in this category<br/>
<table class="buglist" cellspacing="0" cellpadding="4" width="100%">
<thead>
<tr align="left">
<th>ID</th>
<th>Alias</th>
<th>Last Change</th>
<th>Summary</th>
</tr>
</thead>
<py:for each="packager in packagers">
<tr><td colspan="5"><b>
<a href="buglist.cgi?emaillongdesc1=1&amp;emailtype1=substring&amp;component=Package%20Review&amp;query_format=advanced&amp;email1=${packager['email']}">${packager['name']}</a> (${packager['oldest']})
</b></td></tr>
<py:for each="bug in packager['bugs']">
<tr class="${bug['class']}">
<td>
<a href="show_bug.cgi?id=${bug['id']}">${bug['id']}</a>
</td>
<td>${bug['alias']} </td>
<td>${bug['lastchange']}</td>
<td>${bug['summary']}</td>
</tr>
</py:for>
</py:for>
</table>
</div>
</div>
<div id="bottom">
<div id="footer">
<p class="copy">
&copy; 2012 Red Hat, Inc. and others.
Currently maintained by Jason Tibbitts; please send comments or questions to him or file tickets on <a href="https://fedorahosted.org/fedora-infrastructure/">the infrastructure trac</a>.
</p>
<p class="disclaimer">
The Fedora Project is maintained and driven by the community and sponsored by Red Hat. This is a community maintained site. Red Hat is not responsible for content.
</p>
<ul>
<li class="first"><a href="http://fedoraproject.org/wiki/Legal:Main">Legal</a></li>
<li><a href="http://fedoraproject.org/wiki/Legal:Trademark_guidelines">Trademark Guidelines</a></li>
</ul>
</div>
</div>
</body>
</html>