From 51785fe80cef21f74a3c42eb726488c6422a9cfb Mon Sep 17 00:00:00 2001 From: Jason Tibbitts Date: Fri, 14 Aug 2015 10:34:14 -0500 Subject: [PATCH 1/5] Disable merge review report. --- scripts/review-stats/review-stats.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/review-stats/review-stats.py b/scripts/review-stats/review-stats.py index f1aa556..6bab4cd 100755 --- a/scripts/review-stats/review-stats.py +++ b/scripts/review-stats/review-stats.py @@ -640,7 +640,6 @@ if __name__ == '__main__': subs['new'] = report_new(**args) subs['epel'] = report_epel(**args) subs['hidden'] = report_hidden(**args) - subs['merge'] = report_merge(**args) subs['needsponsor'] = report_needsponsor(usermap=usermap, **args) subs['review'] = report_review(**args) subs['trivial'] = report_trivial(**args) From 85455fb6fd4960a578c1939fc7ee44812749a05d Mon Sep 17 00:00:00 2001 From: Jason Tibbitts Date: Fri, 14 Aug 2015 10:35:52 -0500 Subject: [PATCH 2/5] Add local .gitignore. --- scripts/review-stats/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 scripts/review-stats/.gitignore diff --git a/scripts/review-stats/.gitignore b/scripts/review-stats/.gitignore new file mode 100644 index 0000000..a0ea384 --- /dev/null +++ b/scripts/review-stats/.gitignore @@ -0,0 +1 @@ +local.cfg From c957c2f8dcd2454408304b7f1e9aad7b84d69994 Mon Sep 17 00:00:00 2001 From: Jason Tibbitts Date: Fri, 14 Aug 2015 10:41:01 -0500 Subject: [PATCH 3/5] Bump version. --- scripts/review-stats/review-stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/review-stats/review-stats.py b/scripts/review-stats/review-stats.py index 6bab4cd..838cd09 100755 --- a/scripts/review-stats/review-stats.py +++ b/scripts/review-stats/review-stats.py @@ -1,5 +1,5 @@ #!/usr/bin/python -t -VERSION = "4.0" +VERSION = "4.1" # $Id: review-stats.py,v 1.12 2010/01/15 05:14:10 tibbs Exp $ # Note: This script presently lives in internal git and external cvs. External From 8a7d6414ae203fb1490e7ae4cc0d67026937f7ee Mon Sep 17 00:00:00 2001 From: Jason Tibbitts Date: Fri, 14 Aug 2015 11:28:05 -0500 Subject: [PATCH 4/5] Clean up to make flake8 happy. --- scripts/review-stats/review-stats.py | 145 +++++++++++++++------------ 1 file changed, 79 insertions(+), 66 deletions(-) diff --git a/scripts/review-stats/review-stats.py b/scripts/review-stats/review-stats.py index 838cd09..359fa54 100755 --- a/scripts/review-stats/review-stats.py +++ b/scripts/review-stats/review-stats.py @@ -1,17 +1,4 @@ #!/usr/bin/python -t -VERSION = "4.1" - -# $Id: review-stats.py,v 1.12 2010/01/15 05:14:10 tibbs Exp $ -# Note: This script presently lives in internal git and external cvs. External -# cvs is: -# http://cvs.fedoraproject.org/viewvc/status-report-scripts/review-stats.py?root=fedora -# or check it out with -# CVSROOT=:pserver:anonymous@cvs.fedoraproject.org:/cvs/fedora cvs co status-report-scripts -# -# Internal is in the puppet configs repository on puppet1. It needs to be -# there so that puppet can distribute to the servers. I recommend doing the -# work in the public cvs first, then copying to puppet's git after. - import bugzilla import datetime import glob @@ -28,18 +15,20 @@ from genshi.template import TemplateLoader from optparse import OptionParser from validate import Validator +VERSION = "4.1" + # Red Hat's bugzilla url = 'https://bugzilla.redhat.com/xmlrpc.cgi' # Some magic bug numbers -ACCEPT = 163779 -BUNDLED = 658489 -FEATURE = 654686 -GUIDELINES = 197974 -LEGAL = 182235 +ACCEPT = 163779 +BUNDLED = 658489 +FEATURE = 654686 +GUIDELINES = 197974 +LEGAL = 182235 NEEDSPONSOR = 177841 -SCITECH = 505154 -SECLAB = 563471 +SCITECH = 505154 +SECLAB = 563471 # These will show up in a query but aren't actual review tickets trackers = set([ACCEPT, BUNDLED, FEATURE, NEEDSPONSOR, GUIDELINES, SCITECH, SECLAB]) @@ -49,7 +38,7 @@ maxpackages = 5 # So the bugzilla module has some way to complain logging.basicConfig() -#logging.basicConfig(level=logging.DEBUG) + def parse_commandline(): usage = "usage: %prog [options] -c -d -t " @@ -66,7 +55,6 @@ def parse_commandline(): help="run verbosely") (options, args) = parser.parse_args() - tst = str(options.dirname) if str(options.dirname) == 'None': parser.error("Please specify destination directory") if not os.path.isdir(options.dirname): @@ -79,6 +67,7 @@ def parse_commandline(): return options + def parse_config(file): v = Validator() @@ -95,14 +84,14 @@ def parse_config(file): for entry in flatten_errors(cfg, res): section_list, key, error = entry section_list.append(key) - section_string = ','.join(section_list) - if error == False: + if not error: error = 'Missing value or section.' print(','.join(section_list), '=', error) sys.exit(1) return cfg['global'] + def nobody(str): '''Shorten the long "nobody's working on it" string.''' if (str == "Nobody's working on this, feel free to take it" @@ -110,20 +99,24 @@ def nobody(str): return "(Nobody)" return str + def nosec(str): '''Remove the seconds from an hh:mm:ss format string.''' return str[0:str.rfind(':')] + def human_date(t): '''Turn an ISO date into something more human-friendly.''' 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:] + def to_unicode(object, encoding='utf8', errors='replace'): if isinstance(object, basestring): if isinstance(object, str): @@ -132,6 +125,7 @@ def to_unicode(object, encoding='utf8', errors='replace'): return object return u'' + def reporter(bug): '''Extract the reporter from a bug, replacing an empty value with "(none)". Yes, bugzilla will return a blank reporter for some reason.''' @@ -139,28 +133,29 @@ def reporter(bug): return "(none)" return bug.reporter + def yrmonth(d): '''Turn a bugzilla date into Month YYYY string.''' m = ['January', 'February', 'March', 'April', 'May', 'June', 'July', - 'August', 'September', 'October', 'November', 'December'] + 'August', 'September', 'October', 'November', 'December'] - #year = str.split('-')[0] - #month = int(str.split('-')[1])-1 str = d.value year = str[0:4] - month = int(str[4:6])-1 + month = int(str[4:6]) - 1 return m[month] + ' ' + year + def dbprint(str): '''Print string if verbosity is turned on.''' if verbose: print(str) + def seq_max_split(seq, max_entries): """ Given a seq, split into a list of lists of length max_entries each. """ ret = [] num = len(seq) - seq = list(seq) # Trying to use a set/etc. here is bad + seq = list(seq) # Trying to use a set/etc. here is bad beg = 0 while num > max_entries: end = beg + max_entries @@ -170,20 +165,18 @@ def seq_max_split(seq, max_entries): ret.append(seq[beg:]) return ret + def run_query(bz): querydata = {} bugdata = {} - interesting = {} alldeps = set([]) closeddeps = set([]) - needinfo = set([]) usermap = {} querydata['include_fields'] = ['id', 'creation_time', 'last_change_time', 'bug_severity', 'alias', 'assigned_to', 'product', 'creator', 'creator_id', 'status', 'resolution', 'component', 'blocks', 'depends_on', 'summary', 'whiteboard', 'flags'] - #querydata['extra_values'] = [] querydata['bug_status'] = ['NEW', 'ASSIGNED', 'MODIFIED'] querydata['product'] = ['Fedora', 'Fedora EPEL'] querydata['component'] = ['Package Review'] @@ -197,7 +190,7 @@ def run_query(bz): dbprint("Running main query.") t = time.time() bugs = filter(lambda b: b.id not in trackers, bz.query(querydata)) - dbprint("Done, took {0:.2f}.".format(time.time()-t)) + dbprint("Done, took {0:.2f}.".format(time.time() - t)) for bug in bugs: bugdata[bug.id] = {} @@ -219,20 +212,21 @@ def run_query(bz): # Find which of the dependencies are closed dbprint("Looking up {0} bug deps.".format(len(alldeps))) - t=time.time() + t = time.time() for bug in filter(None, bz.query(bz.build_query(bug_id=list(alldeps), status=["CLOSED"]))): closeddeps.add(bug.id) - dbprint("Done; took {0:.2f}.".format(time.time()-t)) + dbprint("Done; took {0:.2f}.".format(time.time() - t)) # Hide tickets blocked by other bugs or those 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: wb = string.lower(bug.whiteboard) if bug.bug_status != 'CLOSED': if wb.find('notready') >= 0: bugdata[bug.id]['hidden'].append('notready') - if wb.find('buildfails') >= 0: + if wb.find('buildfails') >= 0: bugdata[bug.id]['hidden'].append('buildfails') if wb.find('stalledsubmitter') >= 0: bugdata[bug.id]['hidden'].append('stalled') @@ -265,13 +259,13 @@ def run_query(bz): # 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] = '' + usermap[i.reporter] = '' dbprint("Looking up {0} user names.".format(len(usermap))) - t=time.time() + t = time.time() for i in bz._proxy.User.get({'names': usermap.keys()})['users']: usermap[i['name']] = i['real_name'] - dbprint("Done; took {0:.2f}.".format(time.time()-t)) + dbprint("Done; took {0:.2f}.".format(time.time() - t)) # Now process the other three flags; not much special processing for them querydata['o1'] = 'equals' @@ -280,9 +274,9 @@ def run_query(bz): querydata['v1'] = 'fedora-review' + i dbprint("Looking up tickets with flag {0}.".format(i)) - t=time.time() + t = time.time() b1 = bz.query(querydata) - dbprint("Done; took {0:.2f}.".format(time.time()-t)) + dbprint("Done; took {0:.2f}.".format(time.time() - t)) for bug in b1: bugdata[bug.id] = {} @@ -307,6 +301,7 @@ def run_query(bz): # Tickets awaiting review but which were hidden for some reason # That should be all tickets in the Package Review component + def write_html(loader, template, data, dir, fname): '''Load and render the given template with the given data to the given filename in the specified directory.''' @@ -327,12 +322,14 @@ def write_html(loader, template, data, dir, fname): print(e.encoding, e.reason, e.object) f.close() + # Selection functions (should all be predicates) def select_hidden(bug, bugd): if len(bugd['hidden']) > 0: return 1 return 0 + def select_merge(bug, bugd): if (bugd['reviewflag'] == ' ' and bug.bug_status != 'CLOSED' @@ -340,6 +337,7 @@ def select_merge(bug, bugd): return 1 return 0 + def select_needsponsor(bug, bugd): wb = string.lower(bug.whiteboard) if (bugd['reviewflag'] == ' ' @@ -355,11 +353,13 @@ def select_needsponsor(bug, bugd): return 1 return 0 + def select_review(bug, bugd): if bugd['reviewflag'] == '?': return 1 return 0 + def select_trivial(bug, bugd): if (bugd['reviewflag'] == ' ' and bug.bug_status != 'CLOSED' @@ -367,6 +367,7 @@ def select_trivial(bug, bugd): return 1 return 0 + def select_epel(bug, bugd): '''If someone assigns themself to a ticket, it's theirs regardless of whether they set the flag properly or not.''' @@ -379,6 +380,7 @@ def select_epel(bug, bugd): return 1 return 0 + def select_new(bug, bugd): '''If someone assigns themself to a ticket, it's theirs regardless of whether they set the flag properly or not.''' @@ -391,10 +393,12 @@ def select_new(bug, bugd): return 1 return 0 + def rowclass_plain(count): - rowclass = 'bz_row_even' if count % 2 == 1: - rowclass = 'bz_row_odd' + return 'bz_row_odd' + return 'bz_row_even' + # Yes, the even/odd classes look backwards, but it looks better this way def rowclass_with_sponsor(bug, count): @@ -407,6 +411,7 @@ def rowclass_with_sponsor(bug, count): rowclass = 'bz_row_even' return rowclass + # The data from a standard row in a bug list def std_row(bug, rowclass): alias = '' @@ -422,6 +427,7 @@ def std_row(bug, rowclass): 'summary': to_unicode(bug.short_desc), } + def hidden_reason(reasons): r = '' if 'buildfails' in reasons: @@ -441,24 +447,25 @@ def hidden_reason(reasons): return r + # Report generators def report_hidden(bugs, bugdata, loader, tmpdir, subs): data = deepcopy(subs) data['description'] = 'This page lists all review tickets are hidden from the main review queues' data['title'] = 'Hidden reviews' - curmonth = '' for i in bugs: if select_hidden(i, bugdata[i.id]): rowclass = rowclass_with_sponsor(bugdata[i.id], data['count']) data['bugs'].append(std_row(i, rowclass)) data['bugs'][-1]['reason'] = hidden_reason(bugdata[i.id]['hidden']) - data['count'] +=1 + data['count'] += 1 write_html(loader, 'hidden.html', data, tmpdir, 'HIDDEN.html') return data['count'] + def report_review(bugs, bugdata, loader, tmpdir, subs): data = deepcopy(subs) data['description'] = 'This page lists tickets currently under review' @@ -468,12 +475,13 @@ def report_review(bugs, bugdata, loader, tmpdir, subs): if select_review(i, bugdata[i.id]): rowclass = rowclass_plain(data['count']) data['bugs'].append(std_row(i, rowclass)) - data['count'] +=1 + data['count'] += 1 write_html(loader, 'plain.html', data, tmpdir, 'REVIEW.html') return data['count'] + def report_trivial(bugs, bugdata, loader, tmpdir, subs): data = deepcopy(subs) data['description'] = 'This page lists review tickets marked as trivial' @@ -483,12 +491,13 @@ def report_trivial(bugs, bugdata, loader, tmpdir, subs): if select_trivial(i, bugdata[i.id]): rowclass = rowclass_plain(data['count']) data['bugs'].append(std_row(i, rowclass)) - data['count'] +=1 + data['count'] += 1 write_html(loader, 'plain.html', data, tmpdir, 'TRIVIAL.html') return data['count'] + def report_merge(bugs, bugdata, loader, tmpdir, subs): data = deepcopy(subs) data['description'] = 'This page lists all merge review tickets which need reviewers' @@ -498,12 +507,13 @@ def report_merge(bugs, bugdata, loader, tmpdir, subs): if select_merge(i, bugdata[i.id]): rowclass = rowclass_plain(data['count']) data['bugs'].append(std_row(i, rowclass)) - data['count'] +=1 + data['count'] += 1 write_html(loader, 'plain.html', data, tmpdir, 'MERGE.html') return data['count'] + def report_needsponsor(bugs, bugdata, loader, usermap, tmpdir, subs): data = deepcopy(subs) data['description'] = 'This page lists all new NEEDSPONSOR tickets (those without the fedora-revlew flag set).' @@ -529,7 +539,7 @@ def report_needsponsor(bugs, bugdata, loader, usermap, tmpdir, subs): for i in selected: rowclass = rowclass_plain(data['count']) - r = i.reporter; + r = i.reporter if curreporter != r: if (r in usermap and len(usermap[r])): @@ -541,16 +551,18 @@ def report_needsponsor(bugs, bugdata, loader, usermap, tmpdir, subs): curcount = 0 data['packagers'][-1]['bugs'].append(std_row(i, rowclass)) - data['count'] +=1 - curcount +=1 + data['count'] += 1 + curcount += 1 write_html(loader, 'needsponsor.html', data, tmpdir, 'NEEDSPONSOR.html') return data['count'] + def report_epel(bugs, bugdata, loader, tmpdir, subs): data = deepcopy(subs) - data['description'] = 'This page lists new, reviewable EPEL package review tickets. Tickets colored green require a sponsor.' + data['description'] = ('This page lists new, reviewable EPEL package review tickets.' + ' Tickets colored green require a sponsor.') data['title'] = 'New EPEL package review tickets' curmonth = '' @@ -567,8 +579,8 @@ def report_epel(bugs, bugdata, loader, tmpdir, subs): rowclass = rowclass_with_sponsor(bugdata[i.id], curcount) data['months'][-1]['bugs'].append(std_row(i, rowclass)) - data['count'] +=1 - curcount +=1 + data['count'] += 1 + curcount += 1 if curcount > 0: data['months'][-1]['month'] += (" (%d)" % curcount) @@ -577,9 +589,11 @@ def report_epel(bugs, bugdata, loader, tmpdir, subs): return data['count'] + def report_new(bugs, bugdata, loader, tmpdir, subs): data = deepcopy(subs) - data['description'] = 'This page lists new, reviewable Fedora package review tickets (excluding merge reviews). Tickets colored green require a sponsor.' + data['description'] = ('This page lists new, reviewable Fedora package review tickets (excluding merge reviews).' + ' Tickets colored green require a sponsor.') data['title'] = 'New package review tickets' curmonth = '' @@ -596,8 +610,8 @@ def report_new(bugs, bugdata, loader, tmpdir, subs): rowclass = rowclass_with_sponsor(bugdata[i.id], curcount) data['months'][-1]['bugs'].append(std_row(i, rowclass)) - data['count'] +=1 - curcount +=1 + data['count'] += 1 + curcount += 1 if curcount > 0: data['months'][-1]['month'] += (" (%d)" % curcount) @@ -631,18 +645,17 @@ if __name__ == '__main__': 'count': 0, 'months': [], 'packagers': [], - 'bugs': [], - } - args = {'bugs':bugs, 'bugdata':bugdata, 'loader':loader, 'tmpdir':tmpdir, 'subs':subs} + 'bugs': []} + args = {'bugs': bugs, 'bugdata': bugdata, 'loader': loader, 'tmpdir': tmpdir, 'subs': subs} t = time.time() - subs['new'] = report_new(**args) - subs['epel'] = report_epel(**args) - subs['hidden'] = report_hidden(**args) + subs['new'] = report_new(**args) + subs['epel'] = report_epel(**args) + subs['hidden'] = report_hidden(**args) subs['needsponsor'] = report_needsponsor(usermap=usermap, **args) - subs['review'] = report_review(**args) - subs['trivial'] = report_trivial(**args) + subs['review'] = report_review(**args) + subs['trivial'] = report_trivial(**args) # data['accepted_closed'] = report_accepted_closed(bugs, bugdata, loader, tmpdir) # data['accepted_open'] = report_accepted_open(bugs, bugdata, loader, tmpdir) # data['rejected_closed'] = report_rejected_closed(bugs, bugdata, loader, tmpdir) From 1bf10773f14b722c8f77ee9811aa71b48a1a7167 Mon Sep 17 00:00:00 2001 From: Jason Tibbitts Date: Fri, 14 Aug 2015 12:33:45 -0500 Subject: [PATCH 5/5] Remove merge review items from the index template. --- scripts/review-stats/review-templates/index.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/review-stats/review-templates/index.html b/scripts/review-stats/review-templates/index.html index 340cfd1..85d944d 100644 --- a/scripts/review-stats/review-templates/index.html +++ b/scripts/review-stats/review-templates/index.html @@ -38,13 +38,11 @@ available: Trivial tickets ($trivial) All review tickets marked as trivial. New reviewers should look here for simple packages to review. New tickets ($new) - All non-merge review tickets without an assigned reviewer, sorted by submission date. Tickets colored green require a sponsor. + All review tickets without an assigned reviewer, sorted by submission date. Tickets colored green require a sponsor. New EPEL tickets ($epel) All EPEL review tickets without an assigned reviewer, sorted by submission date. Tickets colored green require a sponsor. Needsponsor tickets ($needsponsor) All review tickets where a sponsor is required, sorted by reporter. Please see this page for more information on sponsorship. -Merge review tickets ($merge) - All merge review tickets. Please see this page for more information on merge reviews. Hidden tickets ($hidden) Tickets which have been hidden for some reason. These tickets either depend on other review tickets which have not yet been closed, or are unreviewable for some reason. See this page for more information on the various states a review ticket can have. Tickets under review ($review)