blob: 18c3c8052a451b94c9eb89ff5dc9f7f555db2980 [file] [log] [blame]
#!/usr/bin/env python
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Utility to show outstanding reviews for the project
Usage: list-missing-shipits [--server=RB_SERVER_URL]
"""
from __future__ import print_function
import datetime
import getpass
import json
import os
import subprocess
import sys
from optparse import OptionParser
class Review(object):
def __init__(self, raw_review):
self._review = raw_review
@property
def shipit(self):
return self._review['ship_it']
@property
def timestamp(self):
return self._review['timestamp']
@property
def reviewer(self):
return self._review['links']['user']['title']
class Diff(object):
def __init__(self, raw_diff):
self._diff = raw_diff
@property
def timestamp(self):
return self._diff['timestamp']
class ReviewRequest(object):
SUMMARY_TRUNCATE = 20
def __init__(self, raw_request):
self._request = raw_request
@property
def submitter(self):
return self._request['links']['submitter']['title']
@property
def ships_required(self):
return [p['title'] for p in self._request['target_people']]
@property
def id(self):
return self._request['id']
@property
def summary(self):
full = self._request['summary']
return ((full[:(self.SUMMARY_TRUNCATE - 3)] + '...')
if len(full) > self.SUMMARY_TRUNCATE else full)
@property
def reviews_href(self):
return self._request['links']['reviews']['href']
@property
def diffs_href(self):
return self._request['links']['diffs']['href']
class Reviewboard(object):
def __init__(self, url=None):
self._path = os.path.realpath(os.path.join(os.path.dirname(sys.argv[0]), '..', '..', 'rbt'))
self._url = url
def _get(self, resource, params={}):
base_cmd = [self._path, 'api-get']
if self._url is not None:
base_cmd += ['--server=%s' % self._url]
return json.loads(subprocess.Popen(
base_cmd + [resource] + ['--%s=%s' % (k, v) for k, v in params.items()],
stdout=subprocess.PIPE).communicate()[0])
def get_server_url(self):
return self._get('info')['info']['site']['url']
def get_review_requests(self, params):
return [ReviewRequest(r) for r in self._get('review-requests', params)['review_requests']]
def get_reviews(self, review_request):
return [Review(r) for r in self._get(review_request.reviews_href)['reviews']]
def get_diffs(self, review_request):
return [Diff(d) for d in self._get(review_request.diffs_href)['diffs']]
def main():
parser = OptionParser()
parser.add_option('--server',
dest='server',
help='ReviewBoard server',
default=None)
(options, args) = parser.parse_args()
api = Reviewboard(options.server)
server_url = api.get_server_url()
def request_url(id):
return '%sr/%s' % (server_url, id)
pending_requests = api.get_review_requests({
'to-groups': 'Aurora',
'status': 'pending',
})
def to_row(request):
reviews = api.get_reviews(request)
diffs = api.get_diffs(request)
ships_required = set(request.ships_required)
for event in sorted(reviews + diffs, key=lambda x: x.timestamp, reverse=True):
if isinstance(event, Review):
if event.shipit:
# A 'ship-it' review leaves remaining reviewers on the hook.
ships_required.discard(event.reviewer)
continue
elif event.reviewer in ships_required:
waiting_for = 'awaiting updated diff'
break
else:
ships = [r.reviewer for r in reviews if r.shipit]
needed = set(request.ships_required) - set(ships)
if not request.ships_required:
waiting_for = 'no reviewers specified'
elif not needed:
waiting_for = 'ready to submit'
else:
waiting_for = 'need review from %s' % ', '.join(needed)
break
return (str(event.timestamp),
request_url(request.id),
request.submitter.ljust(10),
request.summary.ljust(ReviewRequest.SUMMARY_TRUNCATE),
waiting_for)
table = [to_row(r) for r in pending_requests]
sorted_table = sorted(table, key=lambda row: row[0])
print('\n'.join('\t'.join(row) for row in sorted_table))
print()
recently_submitted_days = 1
updated_from = ((datetime.datetime.utcnow() - datetime.timedelta(days=1))
.replace(microsecond=0).isoformat())
recently_submitted = api.get_review_requests({
'to-groups': 'Aurora',
'status': 'submitted',
'last-updated-from': updated_from
})
print('Recently submitted: %s' % len(recently_submitted))
for request in recently_submitted:
print('\t'.join((request_url(request.id), request.submitter, request.summary)))
if __name__ == '__main__':
main()