Index: my_activity.py |
diff --git a/my_activity.py b/my_activity.py |
index 16d3f7dba4770c620b381233818d5651d2bc88e8..3d73c8b1c3908853d714b14754f412203eb43ddd 100755 |
--- a/my_activity.py |
+++ b/my_activity.py |
@@ -116,14 +116,22 @@ rietveld_instances = [ |
gerrit_instances = [ |
{ |
- 'url': 'gerrit.chromium.org', |
- 'port': 29418, |
+ 'url': 'chromium-review.googlesource.com', |
'shorturl': 'crosreview.com', |
}, |
+ # TODO(deymo): chrome-internal-review requires login credentials. Enable once |
+ # login support is added to this client. See crbug.com/281695. |
+ #{ |
+ # 'url': 'chrome-internal-review.googlesource.com', |
+ # 'shorturl': 'crosreview.com/i', |
+ #}, |
+ { |
+ 'host': 'gerrit.chromium.org', |
+ 'port': 29418, |
+ }, |
{ |
- 'url': 'gerrit-int.chromium.org', |
+ 'host': 'gerrit-int.chromium.org', |
'port': 29419, |
- 'shorturl': 'crosreview.com/i', |
}, |
] |
@@ -239,6 +247,10 @@ def get_yes_or_no(msg): |
return False |
+def datetime_from_gerrit(date_string): |
+ return datetime.strptime(date_string, '%Y-%m-%d %H:%M:%S.%f000') |
+ |
+ |
def datetime_from_rietveld(date_string): |
return datetime.strptime(date_string, '%Y-%m-%d %H:%M:%S.%f') |
@@ -376,33 +388,70 @@ class MyActivity(object): |
ret.append(r) |
return ret |
- def gerrit_search(self, instance, owner=None, reviewer=None): |
- max_age = datetime.today() - self.modified_after |
- max_age = max_age.days * 24 * 3600 + max_age.seconds |
- |
+ @staticmethod |
+ def gerrit_changes_over_ssh(instance, filters): |
# See https://review.openstack.org/Documentation/cmd-query.html |
# Gerrit doesn't allow filtering by created time, only modified time. |
- user_filter = 'owner:%s' % owner if owner else 'reviewer:%s' % reviewer |
- gquery_cmd = ['ssh', '-p', str(instance['port']), instance['url'], |
+ gquery_cmd = ['ssh', '-p', str(instance['port']), instance['host'], |
'gerrit', 'query', |
'--format', 'JSON', |
'--comments', |
- '--', |
- '-age:%ss' % str(max_age), |
- user_filter] |
- [stdout, _] = subprocess.Popen(gquery_cmd, stdout=subprocess.PIPE, |
+ '--'] + filters |
+ (stdout, _) = subprocess.Popen(gquery_cmd, stdout=subprocess.PIPE, |
stderr=subprocess.PIPE).communicate() |
- issues = str(stdout).split('\n')[:-2] |
- issues = map(json.loads, issues) |
+ # Drop the last line of the output with the stats. |
+ issues = stdout.splitlines()[:-1] |
+ return map(json.loads, issues) |
+ |
+ @staticmethod |
+ def gerrit_changes_over_rest(instance, filters): |
+ # See https://gerrit-review.googlesource.com/Documentation/rest-api.html |
+ # Gerrit doesn't allow filtering by created time, only modified time. |
+ args = urllib.urlencode([ |
+ ('q', ' '.join(filters)), |
+ ('o', 'MESSAGES'), |
+ ('o', 'LABELS')]) |
+ rest_url = 'https://%s/changes/?%s' % (instance['url'], args) |
+ |
+ req = urllib2.Request(rest_url, headers={'Accept': 'text/plain'}) |
+ try: |
+ response = urllib2.urlopen(req) |
+ stdout = response.read() |
+ except urllib2.HTTPError, e: |
+ print 'ERROR: Looking up %r: %s' % (rest_url, e) |
+ return [] |
+ |
+ # Check that the returned JSON starts with the right marker. |
+ if stdout[:5] != ")]}'\n": |
+ print 'ERROR: Marker not found on REST API response: %r' % stdout[:5] |
+ return [] |
+ return json.loads(stdout[5:]) |
+ |
+ def gerrit_search(self, instance, owner=None, reviewer=None): |
+ max_age = datetime.today() - self.modified_after |
+ max_age = max_age.days * 24 * 3600 + max_age.seconds |
+ user_filter = 'owner:%s' % owner if owner else 'reviewer:%s' % reviewer |
+ filters = ['-age:%ss' % max_age, user_filter] |
+ |
+ # Determine the gerrit interface to use: SSH or REST API: |
+ if 'host' in instance: |
+ issues = self.gerrit_changes_over_ssh(instance, filters) |
+ issues = [self.process_gerrit_ssh_issue(instance, issue) |
+ for issue in issues] |
+ elif 'url' in instance: |
+ issues = self.gerrit_changes_over_rest(instance, filters) |
+ issues = [self.process_gerrit_rest_issue(instance, issue) |
+ for issue in issues] |
+ else: |
+ raise Exception('Invalid gerrit_instances configuration.') |
# TODO(cjhopman): should we filter abandoned changes? |
- issues = [self.process_gerrit_issue(instance, issue) for issue in issues] |
issues = filter(self.filter_issue, issues) |
issues = sorted(issues, key=lambda i: i['modified'], reverse=True) |
return issues |
- def process_gerrit_issue(self, instance, issue): |
+ def process_gerrit_ssh_issue(self, instance, issue): |
ret = {} |
ret['review_url'] = issue['url'] |
if 'shorturl' in instance: |
@@ -414,25 +463,56 @@ class MyActivity(object): |
ret['created'] = datetime.fromtimestamp(issue['createdOn']) |
ret['modified'] = datetime.fromtimestamp(issue['lastUpdated']) |
if 'comments' in issue: |
- ret['replies'] = self.process_gerrit_issue_replies(issue['comments']) |
+ ret['replies'] = self.process_gerrit_ssh_issue_replies(issue['comments']) |
else: |
ret['replies'] = [] |
- ret['reviewers'] = set() |
- for reply in ret['replies']: |
- if reply['author'] != ret['author']: |
- ret['reviewers'].add(reply['author']) |
+ ret['reviewers'] = set(r['author'] for r in ret['replies']) |
+ ret['reviewers'].discard(ret['author']) |
return ret |
@staticmethod |
- def process_gerrit_issue_replies(replies): |
+ def process_gerrit_ssh_issue_replies(replies): |
ret = [] |
replies = filter(lambda r: 'email' in r['reviewer'], replies) |
for reply in replies: |
- r = {} |
- r['author'] = reply['reviewer']['email'] |
- r['created'] = datetime.fromtimestamp(reply['timestamp']) |
- r['content'] = '' |
- ret.append(r) |
+ ret.append({ |
+ 'author': reply['reviewer']['email'], |
+ 'created': datetime.fromtimestamp(reply['timestamp']), |
+ 'content': '', |
+ }) |
+ return ret |
+ |
+ def process_gerrit_rest_issue(self, instance, issue): |
+ ret = {} |
+ ret['review_url'] = 'https://%s/%s' % (instance['url'], issue['_number']) |
+ if 'shorturl' in instance: |
+ # TODO(deymo): Move this short link to https once crosreview.com supports |
+ # it. |
+ ret['review_url'] = 'http://%s/%s' % (instance['shorturl'], |
+ issue['_number']) |
+ ret['header'] = issue['subject'] |
+ ret['owner'] = issue['owner']['email'] |
+ ret['author'] = ret['owner'] |
+ ret['created'] = datetime_from_gerrit(issue['created']) |
+ ret['modified'] = datetime_from_gerrit(issue['updated']) |
+ if 'messages' in issue: |
+ ret['replies'] = self.process_gerrit_rest_issue_replies(issue['messages']) |
+ else: |
+ ret['replies'] = [] |
+ ret['reviewers'] = set(r['author'] for r in ret['replies']) |
+ ret['reviewers'].discard(ret['author']) |
+ return ret |
+ |
+ @staticmethod |
+ def process_gerrit_rest_issue_replies(replies): |
+ ret = [] |
+ replies = filter(lambda r: 'email' in r['author'], replies) |
+ for reply in replies: |
+ ret.append({ |
+ 'author': reply['author']['email'], |
+ 'created': datetime_from_gerrit(reply['date']), |
+ 'content': reply['message'], |
+ }) |
return ret |
def google_code_issue_search(self, instance): |