| 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. |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 issues) | 339 issues) |
| 340 issues = sorted(issues, key=lambda i: i['modified'], reverse=True) | 340 issues = sorted(issues, key=lambda i: i['modified'], reverse=True) |
| 341 | 341 |
| 342 return issues | 342 return issues |
| 343 | 343 |
| 344 def process_rietveld_issue(self, instance, issue): | 344 def process_rietveld_issue(self, instance, issue): |
| 345 ret = {} | 345 ret = {} |
| 346 ret['owner'] = issue['owner_email'] | 346 ret['owner'] = issue['owner_email'] |
| 347 ret['author'] = ret['owner'] | 347 ret['author'] = ret['owner'] |
| 348 | 348 |
| 349 ret['reviewers'] = set(username(r) for r in issue['reviewers']) | 349 ret['reviewers'] = set(issue['reviewers']) |
| 350 | 350 |
| 351 shorturl = instance['url'] | 351 shorturl = instance['url'] |
| 352 if 'shorturl' in instance: | 352 if 'shorturl' in instance: |
| 353 shorturl = instance['shorturl'] | 353 shorturl = instance['shorturl'] |
| 354 | 354 |
| 355 ret['review_url'] = 'http://%s/%d' % (shorturl, issue['issue']) | 355 ret['review_url'] = 'http://%s/%d' % (shorturl, issue['issue']) |
| 356 ret['header'] = issue['description'].split('\n')[0] | 356 |
| 357 # Rietveld sometimes has '\r\n' instead of '\n'. |
| 358 ret['header'] = issue['description'].replace('\r', '').split('\n')[0] |
| 357 | 359 |
| 358 ret['modified'] = datetime_from_rietveld(issue['modified']) | 360 ret['modified'] = datetime_from_rietveld(issue['modified']) |
| 359 ret['created'] = datetime_from_rietveld(issue['created']) | 361 ret['created'] = datetime_from_rietveld(issue['created']) |
| 360 ret['replies'] = self.process_rietveld_replies(issue['messages']) | 362 ret['replies'] = self.process_rietveld_replies(issue['messages']) |
| 361 | 363 |
| 362 return ret | 364 return ret |
| 363 | 365 |
| 364 @staticmethod | 366 @staticmethod |
| 365 def process_rietveld_replies(replies): | 367 def process_rietveld_replies(replies): |
| 366 ret = [] | 368 ret = [] |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 403 ret['review_url'] = issue['url'] | 405 ret['review_url'] = issue['url'] |
| 404 ret['header'] = issue['subject'] | 406 ret['header'] = issue['subject'] |
| 405 ret['owner'] = issue['owner']['email'] | 407 ret['owner'] = issue['owner']['email'] |
| 406 ret['author'] = ret['owner'] | 408 ret['author'] = ret['owner'] |
| 407 ret['created'] = datetime.fromtimestamp(issue['createdOn']) | 409 ret['created'] = datetime.fromtimestamp(issue['createdOn']) |
| 408 ret['modified'] = datetime.fromtimestamp(issue['lastUpdated']) | 410 ret['modified'] = datetime.fromtimestamp(issue['lastUpdated']) |
| 409 if 'comments' in issue: | 411 if 'comments' in issue: |
| 410 ret['replies'] = self.process_gerrit_issue_replies(issue['comments']) | 412 ret['replies'] = self.process_gerrit_issue_replies(issue['comments']) |
| 411 else: | 413 else: |
| 412 ret['replies'] = [] | 414 ret['replies'] = [] |
| 415 ret['reviewers'] = set() |
| 416 for reply in ret['replies']: |
| 417 if reply['author'] != ret['author']: |
| 418 ret['reviewers'].add(reply['author']) |
| 413 return ret | 419 return ret |
| 414 | 420 |
| 415 @staticmethod | 421 @staticmethod |
| 416 def process_gerrit_issue_replies(replies): | 422 def process_gerrit_issue_replies(replies): |
| 417 ret = [] | 423 ret = [] |
| 418 replies = filter(lambda r: 'email' in r['reviewer'], replies) | 424 replies = filter(lambda r: 'email' in r['reviewer'], replies) |
| 419 for reply in replies: | 425 for reply in replies: |
| 420 r = {} | 426 r = {} |
| 421 r['author'] = reply['reviewer']['email'] | 427 r['author'] = reply['reviewer']['email'] |
| 422 r['created'] = datetime.fromtimestamp(reply['timestamp']) | 428 r['created'] = datetime.fromtimestamp(reply['timestamp']) |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 591 reviewers.append(match.group(1)) | 597 reviewers.append(match.group(1)) |
| 592 if instance['review_re']: | 598 if instance['review_re']: |
| 593 match = re.match(instance['review_re'], line) | 599 match = re.match(instance['review_re'], line) |
| 594 if match: | 600 if match: |
| 595 reviews.append(int(match.group(1))) | 601 reviews.append(int(match.group(1))) |
| 596 if instance['change_re']: | 602 if instance['change_re']: |
| 597 match = re.match(instance['change_re'], line) | 603 match = re.match(instance['change_re'], line) |
| 598 if match: | 604 if match: |
| 599 changes.append(int(match.group(1))) | 605 changes.append(int(match.group(1))) |
| 600 | 606 |
| 601 # TODO(enne): should convert full names to usernames via CommitterList. | 607 committer_list = webkitpy.common.config.committers.CommitterList() |
| 602 ret['reviewers'] = set(reviewers) | 608 ret['reviewers'] = set( |
| 609 (committer_list.contributor_by_name(r).emails[0] for r in reviewers)) |
| 603 | 610 |
| 604 # Reviews more useful than change link itself, but tricky if multiple | 611 # Reviews more useful than change link itself, but tricky if multiple |
| 605 # Reviews == bugs for WebKit changes | 612 # Reviews == bugs for WebKit changes |
| 606 if len(reviews) == 1: | 613 if len(reviews) == 1: |
| 607 url = 'http://%s/%d' % (instance['review_url'], reviews[0]) | 614 url = 'http://%s/%d' % (instance['review_url'], reviews[0]) |
| 608 if instance['review_prop']: | 615 if instance['review_prop']: |
| 609 ret[instance['review_prop']] = reviews[0] | 616 ret[instance['review_prop']] = reviews[0] |
| 610 else: | 617 else: |
| 611 url = 'http://%s/%d' % (instance['change_url'], changes[0]) | 618 url = 'http://%s/%d' % (instance['change_url'], changes[0]) |
| 612 ret['review_url'] = url | 619 ret['review_url'] = url |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 703 print "Failed to import WebKit committer list, skipping WebKit checks." | 710 print "Failed to import WebKit committer list, skipping WebKit checks." |
| 704 self.webkit_repo = None | 711 self.webkit_repo = None |
| 705 return | 712 return |
| 706 | 713 |
| 707 if not webkit_account(self.user): | 714 if not webkit_account(self.user): |
| 708 email = self.user + "@chromium.org" | 715 email = self.user + "@chromium.org" |
| 709 print "No %s in committers.py, skipping WebKit checks." % email | 716 print "No %s in committers.py, skipping WebKit checks." % email |
| 710 self.webkit_repo = None | 717 self.webkit_repo = None |
| 711 | 718 |
| 712 def print_change(self, change): | 719 def print_change(self, change): |
| 720 optional_values = { |
| 721 'reviewers': ', '.join(change['reviewers']) |
| 722 } |
| 713 self.print_generic(self.options.output_format, | 723 self.print_generic(self.options.output_format, |
| 714 self.options.output_format_changes, | 724 self.options.output_format_changes, |
| 715 change['header'], | 725 change['header'], |
| 716 change['review_url'], | 726 change['review_url'], |
| 717 change['author']) | 727 change['author'], |
| 728 optional_values) |
| 718 | 729 |
| 719 def print_issue(self, issue): | 730 def print_issue(self, issue): |
| 720 optional_values = { | 731 optional_values = { |
| 721 'owner': issue['owner'], | 732 'owner': issue['owner'], |
| 722 } | 733 } |
| 723 self.print_generic(self.options.output_format, | 734 self.print_generic(self.options.output_format, |
| 724 self.options.output_format_issues, | 735 self.options.output_format_issues, |
| 725 issue['header'], | 736 issue['header'], |
| 726 issue['url'], | 737 issue['url'], |
| 727 issue['author'], | 738 issue['author'], |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 921 'all activity types are url, title and author. ' | 932 'all activity types are url, title and author. ' |
| 922 'Format options for specific activity types will ' | 933 'Format options for specific activity types will ' |
| 923 'override the generic format.') | 934 'override the generic format.') |
| 924 output_format_group.add_option( | 935 output_format_group.add_option( |
| 925 '-f', '--output-format', metavar='<format>', | 936 '-f', '--output-format', metavar='<format>', |
| 926 default=u'{url} {title}', | 937 default=u'{url} {title}', |
| 927 help='Specifies the format to use when printing all your activity.') | 938 help='Specifies the format to use when printing all your activity.') |
| 928 output_format_group.add_option( | 939 output_format_group.add_option( |
| 929 '--output-format-changes', metavar='<format>', | 940 '--output-format-changes', metavar='<format>', |
| 930 default=None, | 941 default=None, |
| 931 help='Specifies the format to use when printing changes.') | 942 help='Specifies the format to use when printing changes. Supports the ' |
| 943 'additional variable {reviewers}') |
| 932 output_format_group.add_option( | 944 output_format_group.add_option( |
| 933 '--output-format-issues', metavar='<format>', | 945 '--output-format-issues', metavar='<format>', |
| 934 default=None, | 946 default=None, |
| 935 help='Specifies the format to use when printing issues. Has support ' | 947 help='Specifies the format to use when printing issues. Supports the ' |
| 936 'for the additional variable owner.') | 948 'additional variable {owner}.') |
| 937 output_format_group.add_option( | 949 output_format_group.add_option( |
| 938 '--output-format-reviews', metavar='<format>', | 950 '--output-format-reviews', metavar='<format>', |
| 939 default=None, | 951 default=None, |
| 940 help='Specifies the format to use when printing reviews.') | 952 help='Specifies the format to use when printing reviews.') |
| 941 parser.add_option_group(output_format_group) | 953 parser.add_option_group(output_format_group) |
| 942 | 954 |
| 943 # Remove description formatting | 955 # Remove description formatting |
| 944 parser.format_description = ( | 956 parser.format_description = ( |
| 945 lambda _: parser.description) # pylint: disable=E1101 | 957 lambda _: parser.description) # pylint: disable=E1101 |
| 946 | 958 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1003 print '\n\n\n' | 1015 print '\n\n\n' |
| 1004 | 1016 |
| 1005 my_activity.print_changes() | 1017 my_activity.print_changes() |
| 1006 my_activity.print_reviews() | 1018 my_activity.print_reviews() |
| 1007 my_activity.print_issues() | 1019 my_activity.print_issues() |
| 1008 return 0 | 1020 return 0 |
| 1009 | 1021 |
| 1010 | 1022 |
| 1011 if __name__ == '__main__': | 1023 if __name__ == '__main__': |
| 1012 sys.exit(main()) | 1024 sys.exit(main()) |
| OLD | NEW |