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 |