Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Get stats about your activity. | 6 """Get stats about your activity. |
| 7 | 7 |
| 8 Example: | 8 Example: |
| 9 - my_activity.py for stats for the current week (last week on mondays). | 9 - my_activity.py for stats for the current week (last week on mondays). |
| 10 - my_activity.py -Q for stats for last quarter. | 10 - my_activity.py -Q for stats for last quarter. |
| 11 - my_activity.py -Y for stats for this year. | 11 - my_activity.py -Y for stats for this year. |
| 12 - my_activity.py -b 4/5/12 for stats since 4/5/12. | 12 - my_activity.py -b 4/5/12 for stats since 4/5/12. |
| 13 - my_activity.py -b 4/5/12 -e 6/7/12 for stats between 4/5/12 and 6/7/12. | 13 - my_activity.py -b 4/5/12 -e 6/7/12 for stats between 4/5/12 and 6/7/12. |
| 14 """ | 14 """ |
| 15 | 15 |
| 16 # These services typically only provide a created time and a last modified time | 16 # These services typically only provide a created time and a last modified time |
| 17 # for each item for general queries. This is not enough to determine if there | 17 # for each item for general queries. This is not enough to determine if there |
| 18 # was activity in a given time period. So, we first query for all things created | 18 # was activity in a given time period. So, we first query for all things created |
| 19 # before end and modified after begin. Then, we get the details of each item and | 19 # before end and modified after begin. Then, we get the details of each item and |
| 20 # check those details to determine if there was activity in the given period. | 20 # check those details to determine if there was activity in the given period. |
| 21 # This means that query time scales mostly with (today() - begin). | 21 # This means that query time scales mostly with (today() - begin). |
| 22 | 22 |
| 23 import cookielib | 23 import cookielib |
| 24 import csv | 24 import csv |
| 25 import datetime | 25 import datetime |
| 26 from datetime import datetime | 26 from datetime import datetime |
| 27 from datetime import timedelta | 27 from datetime import timedelta |
| 28 from functools import partial | 28 from functools import partial |
| 29 import gerrit_util | |
| 29 import json | 30 import json |
| 30 import optparse | 31 import optparse |
| 31 import os | 32 import os |
| 32 import re | 33 import re |
| 33 import subprocess | 34 import subprocess |
| 34 import sys | 35 import sys |
| 35 import urllib | 36 import urllib |
| 36 import urllib2 | 37 import urllib2 |
| 37 | 38 |
| 38 import rietveld | 39 import rietveld |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 112 'requires_auth': False, | 113 'requires_auth': False, |
| 113 'email_domain': 'chromium.org', | 114 'email_domain': 'chromium.org', |
| 114 }, | 115 }, |
| 115 ] | 116 ] |
| 116 | 117 |
| 117 gerrit_instances = [ | 118 gerrit_instances = [ |
| 118 { | 119 { |
| 119 'url': 'chromium-review.googlesource.com', | 120 'url': 'chromium-review.googlesource.com', |
| 120 'shorturl': 'crosreview.com', | 121 'shorturl': 'crosreview.com', |
| 121 }, | 122 }, |
| 122 # TODO(deymo): chrome-internal-review requires login credentials. Enable once | 123 { |
| 123 # login support is added to this client. See crbug.com/281695. | 124 'url': 'chrome-internal-review.googlesource.com', |
| 124 #{ | 125 'shorturl': 'crosreview.com/i', |
| 125 # 'url': 'chrome-internal-review.googlesource.com', | 126 }, |
| 126 # 'shorturl': 'crosreview.com/i', | |
| 127 #}, | |
| 128 { | 127 { |
| 129 'host': 'gerrit.chromium.org', | 128 'host': 'gerrit.chromium.org', |
| 130 'port': 29418, | 129 'port': 29418, |
| 131 }, | 130 }, |
| 132 { | 131 { |
| 133 'host': 'gerrit-int.chromium.org', | 132 'host': 'gerrit-int.chromium.org', |
| 134 'port': 29419, | 133 'port': 29419, |
| 135 }, | 134 }, |
| 136 ] | 135 ] |
| 137 | 136 |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 398 '--comments', | 397 '--comments', |
| 399 '--'] + filters | 398 '--'] + filters |
| 400 (stdout, _) = subprocess.Popen(gquery_cmd, stdout=subprocess.PIPE, | 399 (stdout, _) = subprocess.Popen(gquery_cmd, stdout=subprocess.PIPE, |
| 401 stderr=subprocess.PIPE).communicate() | 400 stderr=subprocess.PIPE).communicate() |
| 402 # Drop the last line of the output with the stats. | 401 # Drop the last line of the output with the stats. |
| 403 issues = stdout.splitlines()[:-1] | 402 issues = stdout.splitlines()[:-1] |
| 404 return map(json.loads, issues) | 403 return map(json.loads, issues) |
| 405 | 404 |
| 406 @staticmethod | 405 @staticmethod |
| 407 def gerrit_changes_over_rest(instance, filters): | 406 def gerrit_changes_over_rest(instance, filters): |
| 408 # See https://gerrit-review.googlesource.com/Documentation/rest-api.html | 407 # Convert the "key:value" filter to a dictionary. |
| 409 # Gerrit doesn't allow filtering by created time, only modified time. | 408 req = dict(f.split(':', 1) for f in filters) |
| 410 args = urllib.urlencode([ | 409 return gerrit_util.QueryAllChanges(instance['url'], req, |
|
Ryan Tseng
2013/10/30 22:20:45
Try/Catch gerrit_util.GerritError here, print the
deymo
2013/10/30 22:32:31
Agreed.
| |
| 411 ('q', ' '.join(filters)), | 410 o_params=['MESSAGES', 'LABELS', 'DETAILED_ACCOUNTS']) |
| 412 ('o', 'MESSAGES'), | |
| 413 ('o', 'LABELS')]) | |
| 414 rest_url = 'https://%s/changes/?%s' % (instance['url'], args) | |
| 415 | |
| 416 req = urllib2.Request(rest_url, headers={'Accept': 'text/plain'}) | |
| 417 try: | |
| 418 response = urllib2.urlopen(req) | |
| 419 stdout = response.read() | |
| 420 except urllib2.HTTPError, e: | |
| 421 print 'ERROR: Looking up %r: %s' % (rest_url, e) | |
| 422 return [] | |
| 423 | |
| 424 # Check that the returned JSON starts with the right marker. | |
| 425 if stdout[:5] != ")]}'\n": | |
| 426 print 'ERROR: Marker not found on REST API response: %r' % stdout[:5] | |
| 427 return [] | |
| 428 return json.loads(stdout[5:]) | |
| 429 | 411 |
| 430 def gerrit_search(self, instance, owner=None, reviewer=None): | 412 def gerrit_search(self, instance, owner=None, reviewer=None): |
| 431 max_age = datetime.today() - self.modified_after | 413 max_age = datetime.today() - self.modified_after |
| 432 max_age = max_age.days * 24 * 3600 + max_age.seconds | 414 max_age = max_age.days * 24 * 3600 + max_age.seconds |
| 433 user_filter = 'owner:%s' % owner if owner else 'reviewer:%s' % reviewer | 415 user_filter = 'owner:%s' % owner if owner else 'reviewer:%s' % reviewer |
| 434 filters = ['-age:%ss' % max_age, user_filter] | 416 filters = ['-age:%ss' % max_age, user_filter] |
| 435 | 417 |
| 436 # Determine the gerrit interface to use: SSH or REST API: | 418 # Determine the gerrit interface to use: SSH or REST API: |
| 437 if 'host' in instance: | 419 if 'host' in instance: |
| 438 issues = self.gerrit_changes_over_ssh(instance, filters) | 420 issues = self.gerrit_changes_over_ssh(instance, filters) |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 499 ret['replies'] = self.process_gerrit_rest_issue_replies(issue['messages']) | 481 ret['replies'] = self.process_gerrit_rest_issue_replies(issue['messages']) |
| 500 else: | 482 else: |
| 501 ret['replies'] = [] | 483 ret['replies'] = [] |
| 502 ret['reviewers'] = set(r['author'] for r in ret['replies']) | 484 ret['reviewers'] = set(r['author'] for r in ret['replies']) |
| 503 ret['reviewers'].discard(ret['author']) | 485 ret['reviewers'].discard(ret['author']) |
| 504 return ret | 486 return ret |
| 505 | 487 |
| 506 @staticmethod | 488 @staticmethod |
| 507 def process_gerrit_rest_issue_replies(replies): | 489 def process_gerrit_rest_issue_replies(replies): |
| 508 ret = [] | 490 ret = [] |
| 509 replies = filter(lambda r: 'email' in r['author'], replies) | 491 replies = filter(lambda r: 'author' in r and 'email' in r['author'], |
| 492 replies) | |
| 510 for reply in replies: | 493 for reply in replies: |
| 511 ret.append({ | 494 ret.append({ |
| 512 'author': reply['author']['email'], | 495 'author': reply['author']['email'], |
| 513 'created': datetime_from_gerrit(reply['date']), | 496 'created': datetime_from_gerrit(reply['date']), |
| 514 'content': reply['message'], | 497 'content': reply['message'], |
| 515 }) | 498 }) |
| 516 return ret | 499 return ret |
| 517 | 500 |
| 518 def google_code_issue_search(self, instance): | 501 def google_code_issue_search(self, instance): |
| 519 time_format = '%Y-%m-%dT%T' | 502 time_format = '%Y-%m-%dT%T' |
| (...skipping 585 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1105 print '\n\n\n' | 1088 print '\n\n\n' |
| 1106 | 1089 |
| 1107 my_activity.print_changes() | 1090 my_activity.print_changes() |
| 1108 my_activity.print_reviews() | 1091 my_activity.print_reviews() |
| 1109 my_activity.print_issues() | 1092 my_activity.print_issues() |
| 1110 return 0 | 1093 return 0 |
| 1111 | 1094 |
| 1112 | 1095 |
| 1113 if __name__ == '__main__': | 1096 if __name__ == '__main__': |
| 1114 sys.exit(main()) | 1097 sys.exit(main()) |
| OLD | NEW |