Pep8 compliant
Change-Id: I1638b5c161e92dc864bc4441a5c536f412aba4e1 Signed-off-by: David Caro <dcaroest@redhat.com>
This commit is contained in:
parent
6aa42ce112
commit
3f7dbba420
@ -34,12 +34,16 @@ def main(argv=None):
|
|||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
optparser = optparse.OptionParser()
|
optparser = optparse.OptionParser()
|
||||||
optparser.add_option('-p', '--project', default='projects/nova.json',
|
optparser.add_option(
|
||||||
help='JSON file describing the project to generate stats for')
|
'-p', '--project', default='projects/nova.json',
|
||||||
optparser.add_option('-a', '--all', action='store_true',
|
help='JSON file describing the project to generate stats for')
|
||||||
help='Generate stats across all known projects (*.json)')
|
optparser.add_option(
|
||||||
optparser.add_option('-u', '--user', default=getpass.getuser(), help='gerrit user')
|
'-a', '--all', action='store_true',
|
||||||
optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit')
|
help='Generate stats across all known projects (*.json)')
|
||||||
|
optparser.add_option(
|
||||||
|
'-u', '--user', default=getpass.getuser(), help='gerrit user')
|
||||||
|
optparser.add_option(
|
||||||
|
'-k', '--key', default=None, help='ssh key for gerrit')
|
||||||
optparser.add_option('-s', '--stable', action='store_true',
|
optparser.add_option('-s', '--stable', action='store_true',
|
||||||
help='Include stable branch commits')
|
help='Include stable branch commits')
|
||||||
options, args = optparser.parse_args()
|
options, args = optparser.parse_args()
|
||||||
@ -66,7 +70,8 @@ def main(argv=None):
|
|||||||
if approved(patch_set) and not approved(change['patchSets'][-1]):
|
if approved(patch_set) and not approved(change['patchSets'][-1]):
|
||||||
if has_negative_feedback(change['patchSets'][-1]):
|
if has_negative_feedback(change['patchSets'][-1]):
|
||||||
continue
|
continue
|
||||||
approved_and_rebased.add("%s %s" % (change['url'], change['subject']))
|
approved_and_rebased.add("%s %s" % (change['url'],
|
||||||
|
change['subject']))
|
||||||
|
|
||||||
for x in approved_and_rebased:
|
for x in approved_and_rebased:
|
||||||
print x
|
print x
|
||||||
@ -76,7 +81,8 @@ def main(argv=None):
|
|||||||
def has_negative_feedback(patch_set):
|
def has_negative_feedback(patch_set):
|
||||||
approvals = patch_set.get('approvals', [])
|
approvals = patch_set.get('approvals', [])
|
||||||
for review in approvals:
|
for review in approvals:
|
||||||
if review['type'] in ('CRVW','VRIF') and review['value'] in ('-1', '-2'):
|
if review['type'] in ('CRVW', 'VRIF') \
|
||||||
|
and review['value'] in ('-1', '-2'):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
101
openreviews.py
101
openreviews.py
@ -20,8 +20,6 @@ import calendar
|
|||||||
import datetime
|
import datetime
|
||||||
import getpass
|
import getpass
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import utils
|
import utils
|
||||||
@ -36,7 +34,7 @@ def sec_to_period_string(seconds):
|
|||||||
|
|
||||||
def get_age_of_patch(patch, now_ts):
|
def get_age_of_patch(patch, now_ts):
|
||||||
approvals = patch.get('approvals', [])
|
approvals = patch.get('approvals', [])
|
||||||
approvals.sort(key=lambda a:a['grantedOn'])
|
approvals.sort(key=lambda a: a['grantedOn'])
|
||||||
# The createdOn timestamp on the patch isn't what we want.
|
# The createdOn timestamp on the patch isn't what we want.
|
||||||
# It's when the patch was written, not submitted for review.
|
# It's when the patch was written, not submitted for review.
|
||||||
# The next best thing in the data we have is the time of the
|
# The next best thing in the data we have is the time of the
|
||||||
@ -94,37 +92,40 @@ def gen_stats(projects, waiting_on_reviewer, waiting_on_submitter, options):
|
|||||||
|
|
||||||
result = []
|
result = []
|
||||||
result.append(('Projects', '%s' % [project['name']
|
result.append(('Projects', '%s' % [project['name']
|
||||||
for project in projects]))
|
for project in projects]))
|
||||||
stats = []
|
stats = []
|
||||||
stats.append(('Total Open Reviews', '%d' % (
|
stats.append(('Total Open Reviews', '%d' % (
|
||||||
len(waiting_on_reviewer) + len(waiting_on_submitter))))
|
len(waiting_on_reviewer) + len(waiting_on_submitter))))
|
||||||
stats.append(('Waiting on Submitter', '%d' % len(waiting_on_submitter)))
|
stats.append(('Waiting on Submitter', '%d' % len(waiting_on_submitter)))
|
||||||
stats.append(('Waiting on Reviewer', '%d' % len(waiting_on_reviewer)))
|
stats.append(('Waiting on Reviewer', '%d' % len(waiting_on_reviewer)))
|
||||||
|
|
||||||
latest_rev_stats = []
|
latest_rev_stats = []
|
||||||
latest_rev_stats.append(('Average wait time', '%s' % (
|
latest_rev_stats.append(('Average wait time', '%s'
|
||||||
average_age(waiting_on_reviewer))))
|
% (average_age(waiting_on_reviewer))))
|
||||||
latest_rev_stats.append(('Median wait time', '%s' % (
|
latest_rev_stats.append(('Median wait time', '%s'
|
||||||
median_age(waiting_on_reviewer))))
|
% (median_age(waiting_on_reviewer))))
|
||||||
latest_rev_stats.append(('Number waiting more than %i days' %
|
latest_rev_stats.append((
|
||||||
options.waiting_more, '%i' % (number_waiting_more_than(
|
'Number waiting more than %i days' % options.waiting_more,
|
||||||
|
'%i' % (number_waiting_more_than(
|
||||||
age_sorted, 60 * 60 * 24 * options.waiting_more))))
|
age_sorted, 60 * 60 * 24 * options.waiting_more))))
|
||||||
stats.append(('Stats since the latest revision', latest_rev_stats))
|
stats.append(('Stats since the latest revision', latest_rev_stats))
|
||||||
|
|
||||||
first_rev_stats = []
|
first_rev_stats = []
|
||||||
first_rev_stats.append(('Average wait time', '%s' % (
|
first_rev_stats.append(('Average wait time', '%s'
|
||||||
average_age(waiting_on_reviewer, key='age2'))))
|
% (average_age(waiting_on_reviewer, key='age2'))))
|
||||||
first_rev_stats.append(('Median wait time', '%s' % (
|
first_rev_stats.append(('Median wait time', '%s'
|
||||||
median_age(waiting_on_reviewer, key='age2'))))
|
% (median_age(waiting_on_reviewer, key='age2'))))
|
||||||
stats.append(('Stats since the first revision', first_rev_stats))
|
stats.append(('Stats since the first revision', first_rev_stats))
|
||||||
|
|
||||||
last_without_nack_stats = []
|
last_without_nack_stats = []
|
||||||
last_without_nack_stats.append(('Average wait time', '%s' % (
|
last_without_nack_stats.append(('Average wait time', '%s'
|
||||||
average_age(waiting_on_reviewer, key='age3'))))
|
% (average_age(waiting_on_reviewer,
|
||||||
last_without_nack_stats.append(('Median wait time', '%s' % (
|
key='age3'))))
|
||||||
median_age(waiting_on_reviewer, key='age3'))))
|
last_without_nack_stats.append(('Median wait time', '%s'
|
||||||
stats.append(('Stats since the last revision without -1 or -2 (ignoring jenkins)',
|
% (median_age(waiting_on_reviewer,
|
||||||
last_without_nack_stats))
|
key='age3'))))
|
||||||
|
stats.append(('Stats since the last revision without -1 or -2 '
|
||||||
|
'(ignoring jenkins)', last_without_nack_stats))
|
||||||
|
|
||||||
changes = []
|
changes = []
|
||||||
for change in age_sorted[:options.longest_waiting]:
|
for change in age_sorted[:options.longest_waiting]:
|
||||||
@ -136,19 +137,19 @@ def gen_stats(projects, waiting_on_reviewer, waiting_on_submitter, options):
|
|||||||
|
|
||||||
changes = []
|
changes = []
|
||||||
for change in age2_sorted[:options.longest_waiting]:
|
for change in age2_sorted[:options.longest_waiting]:
|
||||||
changes.append('%s %s (%s)' % (sec_to_period_string(change['age2']),
|
changes.append('%s %s (%s)' % (sec_to_period_string(change['age2']),
|
||||||
format_url(change['url'], options),
|
format_url(change['url'], options),
|
||||||
change['subject']))
|
change['subject']))
|
||||||
stats.append(('Longest waiting reviews (based on first revision)',
|
stats.append(('Longest waiting reviews (based on first revision)',
|
||||||
changes))
|
changes))
|
||||||
|
|
||||||
changes = []
|
changes = []
|
||||||
for change in age3_sorted[:options.longest_waiting]:
|
for change in age3_sorted[:options.longest_waiting]:
|
||||||
changes.append('%s %s (%s)' % (sec_to_period_string(change['age3']),
|
changes.append('%s %s (%s)' % (sec_to_period_string(change['age3']),
|
||||||
format_url(change['url'], options),
|
format_url(change['url'], options),
|
||||||
change['subject']))
|
change['subject']))
|
||||||
stats.append(('Longest waiting reviews (based on oldest rev without nack, ignoring jenkins)',
|
stats.append(('Longest waiting reviews (based on oldest rev without nack, '
|
||||||
changes))
|
'ignoring jenkins)', changes))
|
||||||
|
|
||||||
result.append(stats)
|
result.append(stats)
|
||||||
|
|
||||||
@ -231,20 +232,28 @@ def main(argv=None):
|
|||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
optparser = optparse.OptionParser()
|
optparser = optparse.OptionParser()
|
||||||
optparser.add_option('-p', '--project', default='projects/nova.json',
|
optparser.add_option(
|
||||||
help='JSON file describing the project to generate stats for')
|
'-p', '--project', default='projects/nova.json',
|
||||||
optparser.add_option('-a', '--all', action='store_true',
|
help='JSON file describing the project to generate stats for')
|
||||||
help='Generate stats across all known projects (*.json)')
|
optparser.add_option(
|
||||||
optparser.add_option('-u', '--user', default=getpass.getuser(), help='gerrit user')
|
'-a', '--all', action='store_true',
|
||||||
optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit')
|
help='Generate stats across all known projects (*.json)')
|
||||||
optparser.add_option('-s', '--stable', action='store_true',
|
optparser.add_option(
|
||||||
help='Include stable branch commits')
|
'-u', '--user', default=getpass.getuser(), help='gerrit user')
|
||||||
optparser.add_option('-l', '--longest-waiting', type='int', default=5,
|
optparser.add_option(
|
||||||
help='Show n changesets that have waited the longest)')
|
'-k', '--key', default=None, help='ssh key for gerrit')
|
||||||
optparser.add_option('-m', '--waiting-more', type='int', default=7,
|
optparser.add_option(
|
||||||
help='Show number of changesets that have waited more than n days)')
|
'-s', '--stable', action='store_true',
|
||||||
optparser.add_option('-H', '--html', action='store_true',
|
help='Include stable branch commits')
|
||||||
help='Use HTML output instead of plain text')
|
optparser.add_option(
|
||||||
|
'-l', '--longest-waiting', type='int', default=5,
|
||||||
|
help='Show n changesets that have waited the longest)')
|
||||||
|
optparser.add_option(
|
||||||
|
'-m', '--waiting-more', type='int', default=7,
|
||||||
|
help='Show number of changesets that have waited more than n days)')
|
||||||
|
optparser.add_option(
|
||||||
|
'-H', '--html', action='store_true',
|
||||||
|
help='Use HTML output instead of plain text')
|
||||||
|
|
||||||
options, args = optparser.parse_args()
|
options, args = optparser.parse_args()
|
||||||
|
|
||||||
@ -255,7 +264,7 @@ def main(argv=None):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
changes = utils.get_changes(projects, options.user, options.key,
|
changes = utils.get_changes(projects, options.user, options.key,
|
||||||
only_open=True)
|
only_open=True)
|
||||||
|
|
||||||
waiting_on_submitter = []
|
waiting_on_submitter = []
|
||||||
waiting_on_reviewer = []
|
waiting_on_reviewer = []
|
||||||
@ -274,7 +283,7 @@ def main(argv=None):
|
|||||||
latest_patch = change['patchSets'][-1]
|
latest_patch = change['patchSets'][-1]
|
||||||
waiting_for_review = True
|
waiting_for_review = True
|
||||||
approvals = latest_patch.get('approvals', [])
|
approvals = latest_patch.get('approvals', [])
|
||||||
approvals.sort(key=lambda a:a['grantedOn'])
|
approvals.sort(key=lambda a: a['grantedOn'])
|
||||||
for review in approvals:
|
for review in approvals:
|
||||||
if review['type'] not in ('CRVW', 'VRIF'):
|
if review['type'] not in ('CRVW', 'VRIF'):
|
||||||
continue
|
continue
|
||||||
|
59
reviewers.py
59
reviewers.py
@ -16,16 +16,10 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import calendar
|
import calendar
|
||||||
import datetime
|
import datetime
|
||||||
import getpass
|
import getpass
|
||||||
import glob
|
|
||||||
import json
|
|
||||||
import optparse
|
import optparse
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
from pprint import pprint
|
|
||||||
import prettytable
|
import prettytable
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -42,8 +36,8 @@ def process_patchset(project, patchset, reviewers, ts):
|
|||||||
latest_core_pos_vote = 0
|
latest_core_pos_vote = 0
|
||||||
for review in patchset.get('approvals', []):
|
for review in patchset.get('approvals', []):
|
||||||
if review['type'] != 'CRVW':
|
if review['type'] != 'CRVW':
|
||||||
# Only count code reviews. Don't add another for Approved, which is
|
# Only count code reviews. Don't add another for Approved, which
|
||||||
# type 'APRV'
|
# is type 'APRV'
|
||||||
continue
|
continue
|
||||||
if review['by'].get('username', 'unknown') not in project['core-team']:
|
if review['by'].get('username', 'unknown') not in project['core-team']:
|
||||||
# Only checking for disagreements from core team members
|
# Only checking for disagreements from core team members
|
||||||
@ -60,13 +54,13 @@ def process_patchset(project, patchset, reviewers, ts):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if review['type'] != 'CRVW':
|
if review['type'] != 'CRVW':
|
||||||
# Only count code reviews. Don't add another for Approved, which is
|
# Only count code reviews. Don't add another for Approved, which
|
||||||
# type 'APRV'
|
# is type 'APRV'
|
||||||
continue
|
continue
|
||||||
|
|
||||||
reviewer = review['by'].get('username', 'unknown')
|
reviewer = review['by'].get('username', 'unknown')
|
||||||
reviewers.setdefault(reviewer,
|
reviewers.setdefault(reviewer,
|
||||||
{'votes': {'-2': 0, '-1': 0, '1': 0, '2': 0}})
|
{'votes': {'-2': 0, '-1': 0, '1': 0, '2': 0}})
|
||||||
reviewers[reviewer].setdefault('disagreements', 0)
|
reviewers[reviewer].setdefault('disagreements', 0)
|
||||||
reviewers[reviewer]['total'] = reviewers[reviewer].get('total', 0) + 1
|
reviewers[reviewer]['total'] = reviewers[reviewer].get('total', 0) + 1
|
||||||
cur = reviewers[reviewer]['votes'][review['value']]
|
cur = reviewers[reviewer]['votes'][review['value']]
|
||||||
@ -90,14 +84,19 @@ def main(argv=None):
|
|||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
optparser = optparse.OptionParser()
|
optparser = optparse.OptionParser()
|
||||||
optparser.add_option('-p', '--project', default='projects/nova.json',
|
optparser.add_option(
|
||||||
help='JSON file describing the project to generate stats for')
|
'-p', '--project', default='projects/nova.json',
|
||||||
optparser.add_option('-a', '--all', action='store_true',
|
help='JSON file describing the project to generate stats for')
|
||||||
help='Generate stats across all known projects (*.json)')
|
optparser.add_option(
|
||||||
optparser.add_option('-d', '--days', type='int', default=14,
|
'-a', '--all', action='store_true',
|
||||||
help='Number of days to consider')
|
help='Generate stats across all known projects (*.json)')
|
||||||
optparser.add_option('-u', '--user', default=getpass.getuser(), help='gerrit user')
|
optparser.add_option(
|
||||||
optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit')
|
'-d', '--days', type='int', default=14,
|
||||||
|
help='Number of days to consider')
|
||||||
|
optparser.add_option(
|
||||||
|
'-u', '--user', default=getpass.getuser(), help='gerrit user')
|
||||||
|
optparser.add_option(
|
||||||
|
'-k', '--key', default=None, help='ssh key for gerrit')
|
||||||
|
|
||||||
options, args = optparser.parse_args()
|
options, args = optparser.parse_args()
|
||||||
|
|
||||||
@ -120,21 +119,22 @@ def main(argv=None):
|
|||||||
|
|
||||||
reviewers = [(v, k) for k, v in reviewers.iteritems()
|
reviewers = [(v, k) for k, v in reviewers.iteritems()
|
||||||
if k.lower() not in ('jenkins', 'smokestack')]
|
if k.lower() not in ('jenkins', 'smokestack')]
|
||||||
reviewers.sort(reverse=True, key=lambda r:r[0]['total'])
|
reviewers.sort(reverse=True, key=lambda r: r[0]['total'])
|
||||||
|
|
||||||
if options.all:
|
if options.all:
|
||||||
print 'Reviews for the last %d days in projects: %s' % (options.days,
|
print 'Reviews for the last %d days in projects: %s' \
|
||||||
[project['name'] for project in projects])
|
% (options.days, [project['name'] for project in projects])
|
||||||
else:
|
else:
|
||||||
print 'Reviews for the last %d days in %s' % (options.days, projects[0]['name'])
|
print 'Reviews for the last %d days in %s' \
|
||||||
|
% (options.days, projects[0]['name'])
|
||||||
if options.all:
|
if options.all:
|
||||||
print '** -- Member of at least one core reviewer team'
|
print '** -- Member of at least one core reviewer team'
|
||||||
else:
|
else:
|
||||||
print '** -- %s-core team member' % projects[0]['name']
|
print '** -- %s-core team member' % projects[0]['name']
|
||||||
table = prettytable.PrettyTable(
|
table = prettytable.PrettyTable(
|
||||||
('Reviewer',
|
('Reviewer',
|
||||||
'Reviews -2 -1 +1 +2 +/- %',
|
'Reviews -2 -1 +1 +2 +/- %',
|
||||||
'Disagreements*'))
|
'Disagreements*'))
|
||||||
total = 0
|
total = 0
|
||||||
for k, v in reviewers:
|
for k, v in reviewers:
|
||||||
in_core_team = False
|
in_core_team = False
|
||||||
@ -146,9 +146,10 @@ def main(argv=None):
|
|||||||
plus = float(k['votes']['2'] + k['votes']['1'])
|
plus = float(k['votes']['2'] + k['votes']['1'])
|
||||||
minus = float(k['votes']['-2'] + k['votes']['-1'])
|
minus = float(k['votes']['-2'] + k['votes']['-1'])
|
||||||
ratio = (plus / (plus + minus)) * 100
|
ratio = (plus / (plus + minus)) * 100
|
||||||
r = '%7d %3d %3d %3d %3d %5.1f%%' % (k['total'],
|
r = '%7d %3d %3d %3d %3d %5.1f%%' % (
|
||||||
k['votes']['-2'], k['votes']['-1'],
|
k['total'], k['votes']['-2'],
|
||||||
k['votes']['1'], k['votes']['2'], ratio)
|
k['votes']['-1'], k['votes']['1'],
|
||||||
|
k['votes']['2'], ratio)
|
||||||
dratio = ((float(k['disagreements']) / plus) * 100) if plus else 0.0
|
dratio = ((float(k['disagreements']) / plus) * 100) if plus else 0.0
|
||||||
d = '%3d (%5.1f%%)' % (k['disagreements'], dratio)
|
d = '%3d (%5.1f%%)' % (k['disagreements'], dratio)
|
||||||
table.add_row((name, r, d))
|
table.add_row((name, r, d))
|
||||||
|
@ -26,13 +26,15 @@ import utils
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = ArgumentParser(
|
parser = ArgumentParser(
|
||||||
description="Get reviews for open bugs against a milestone")
|
description="Get reviews for open bugs against a milestone")
|
||||||
parser.add_argument('-p', '--project', default='projects/nova.json',
|
parser.add_argument(
|
||||||
help='JSON file describing the project to generate stats for')
|
'-p', '--project', default='projects/nova.json',
|
||||||
parser.add_argument('-m', '--milestone', default='',
|
help='JSON file describing the project to generate stats for')
|
||||||
help='Only show bugs targeted to a specified milestone')
|
parser.add_argument(
|
||||||
parser.add_argument('-u', '--user', default=getpass.getuser(),
|
'-m', '--milestone', default='',
|
||||||
help='gerrit user')
|
help='Only show bugs targeted to a specified milestone')
|
||||||
|
parser.add_argument(
|
||||||
|
'-u', '--user', default=getpass.getuser(), help='gerrit user')
|
||||||
parser.add_argument('-k', '--key', default=None, help='ssh key for gerrit')
|
parser.add_argument('-k', '--key', default=None, help='ssh key for gerrit')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -79,15 +81,15 @@ def main():
|
|||||||
milestones[milestone].append((change['url'], bugid))
|
milestones[milestone].append((change['url'], bugid))
|
||||||
|
|
||||||
print 'Reviews for bugs grouped by milestone for project: %s\n' % (
|
print 'Reviews for bugs grouped by milestone for project: %s\n' % (
|
||||||
project_name)
|
project_name)
|
||||||
|
|
||||||
for milestone, reviews in milestones.items():
|
for milestone, reviews in milestones.items():
|
||||||
if args.milestone and milestone != args.milestone:
|
if args.milestone and milestone != args.milestone:
|
||||||
continue
|
continue
|
||||||
print 'Milestone: %s' % milestone
|
print 'Milestone: %s' % milestone
|
||||||
for review, bugid in reviews:
|
for review, bugid in reviews:
|
||||||
print '--> %s -- https://bugs.launchpad.net/%s/+bug/%s' % (review,
|
print '--> %s -- https://bugs.launchpad.net/%s/+bug/%s' \
|
||||||
project_name, bugid)
|
% (review, project_name, bugid)
|
||||||
print
|
print
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Loading…
Reference in New Issue
Block a user