| 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 | |
| 25 import datetime | 24 import datetime |
| 26 from datetime import datetime | 25 from datetime import datetime |
| 27 from datetime import timedelta | 26 from datetime import timedelta |
| 28 from functools import partial | 27 from functools import partial |
| 29 import json | 28 import json |
| 30 import optparse | 29 import optparse |
| 31 import os | 30 import os |
| 32 import re | |
| 33 import subprocess | 31 import subprocess |
| 34 import sys | 32 import sys |
| 35 import urllib | 33 import urllib |
| 36 import urllib2 | 34 import urllib2 |
| 37 | 35 |
| 38 import gerrit_util | 36 import gerrit_util |
| 39 import rietveld | 37 import rietveld |
| 40 from third_party import upload | 38 from third_party import upload |
| 41 | 39 |
| 42 # Imported later, once options are set. | |
| 43 webkitpy = None | |
| 44 | |
| 45 try: | 40 try: |
| 46 from dateutil.relativedelta import relativedelta # pylint: disable=F0401 | 41 from dateutil.relativedelta import relativedelta # pylint: disable=F0401 |
| 47 except ImportError: | 42 except ImportError: |
| 48 print 'python-dateutil package required' | 43 print 'python-dateutil package required' |
| 49 exit(1) | 44 exit(1) |
| 50 | 45 |
| 51 # python-keyring provides easy access to the system keyring. | 46 # python-keyring provides easy access to the system keyring. |
| 52 try: | 47 try: |
| 53 import keyring # pylint: disable=W0611,F0401 | 48 import keyring # pylint: disable=W0611,F0401 |
| 54 except ImportError: | 49 except ImportError: |
| 55 print 'Consider installing python-keyring' | 50 print 'Consider installing python-keyring' |
| 56 | 51 |
| 57 def webkit_account(user): | |
| 58 if not webkitpy: | |
| 59 return None | |
| 60 committer_list = webkitpy.common.config.committers.CommitterList() | |
| 61 email = user + "@chromium.org" | |
| 62 return committer_list.account_by_email(email) | |
| 63 | |
| 64 def user_to_webkit_email(user): | |
| 65 account = webkit_account(user) | |
| 66 if not account: | |
| 67 return None | |
| 68 return account.emails[0] | |
| 69 | |
| 70 def user_to_webkit_owner_search(user): | |
| 71 account = webkit_account(user) | |
| 72 if not account: | |
| 73 return ['--author=%s@chromium.org' % user] | |
| 74 search = [] | |
| 75 for email in account.emails: | |
| 76 search.append('--author=' + email) | |
| 77 # commit-bot is author for contributors who are not committers. | |
| 78 search.append('--grep=Patch by ' + account.full_name) | |
| 79 return search | |
| 80 | |
| 81 def user_to_webkit_reviewer_search(user): | |
| 82 committer_list = webkitpy.common.config.committers.CommitterList() | |
| 83 email = user + "@chromium.org" | |
| 84 account = committer_list.reviewer_by_email(email) | |
| 85 if not account: | |
| 86 return [] | |
| 87 return ['--grep=Reviewed by ' + account.full_name] | |
| 88 | |
| 89 rietveld_instances = [ | 52 rietveld_instances = [ |
| 90 { | 53 { |
| 91 'url': 'codereview.chromium.org', | 54 'url': 'codereview.chromium.org', |
| 92 'shorturl': 'crrev.com', | 55 'shorturl': 'crrev.com', |
| 93 'supports_owner_modified_query': True, | 56 'supports_owner_modified_query': True, |
| 94 'requires_auth': False, | 57 'requires_auth': False, |
| 95 'email_domain': 'chromium.org', | 58 'email_domain': 'chromium.org', |
| 96 }, | 59 }, |
| 97 { | 60 { |
| 98 'url': 'chromereviews.googleplex.com', | 61 'url': 'chromereviews.googleplex.com', |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 'name': 'google-breakpad', | 113 'name': 'google-breakpad', |
| 151 }, | 114 }, |
| 152 { | 115 { |
| 153 'name': 'gyp', | 116 'name': 'gyp', |
| 154 }, | 117 }, |
| 155 { | 118 { |
| 156 'name': 'skia', | 119 'name': 'skia', |
| 157 }, | 120 }, |
| 158 ] | 121 ] |
| 159 | 122 |
| 160 bugzilla_instances = [ | |
| 161 { | |
| 162 'search_url': 'http://bugs.webkit.org/buglist.cgi', | |
| 163 'url': 'wkb.ug', | |
| 164 'user_func': user_to_webkit_email, | |
| 165 }, | |
| 166 ] | |
| 167 | |
| 168 git_instances = [ | |
| 169 { | |
| 170 'option': 'webkit_repo', | |
| 171 'change_re': | |
| 172 r'git-svn-id: http://svn\.webkit\.org/repository/webkit/trunk@(\d*)', | |
| 173 'change_url': 'trac.webkit.org/changeset', | |
| 174 'review_re': r'https://bugs\.webkit\.org/show_bug\.cgi\?id\=(\d*)', | |
| 175 'review_url': 'wkb.ug', | |
| 176 'review_prop': 'webkit_bug_id', | |
| 177 | |
| 178 'owner_search_func': user_to_webkit_owner_search, | |
| 179 'reviewer_search_func': user_to_webkit_reviewer_search, | |
| 180 }, | |
| 181 ] | |
| 182 | |
| 183 # Uses ClientLogin to authenticate the user for Google Code issue trackers. | 123 # Uses ClientLogin to authenticate the user for Google Code issue trackers. |
| 184 def get_auth_token(email): | 124 def get_auth_token(email): |
| 185 # KeyringCreds will use the system keyring on the first try, and prompt for | 125 # KeyringCreds will use the system keyring on the first try, and prompt for |
| 186 # a password on the next ones. | 126 # a password on the next ones. |
| 187 creds = upload.KeyringCreds('code.google.com', 'code.google.com', email) | 127 creds = upload.KeyringCreds('code.google.com', 'code.google.com', email) |
| 188 for _ in xrange(3): | 128 for _ in xrange(3): |
| 189 email, password = creds.GetUserCredentials() | 129 email, password = creds.GetUserCredentials() |
| 190 url = 'https://www.google.com/accounts/ClientLogin' | 130 url = 'https://www.google.com/accounts/ClientLogin' |
| 191 data = urllib.urlencode({ | 131 data = urllib.urlencode({ |
| 192 'Email': email, | 132 'Email': email, |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 def __init__(self, options): | 202 def __init__(self, options): |
| 263 self.options = options | 203 self.options = options |
| 264 self.modified_after = options.begin | 204 self.modified_after = options.begin |
| 265 self.modified_before = options.end | 205 self.modified_before = options.end |
| 266 self.user = options.user | 206 self.user = options.user |
| 267 self.changes = [] | 207 self.changes = [] |
| 268 self.reviews = [] | 208 self.reviews = [] |
| 269 self.issues = [] | 209 self.issues = [] |
| 270 self.check_cookies() | 210 self.check_cookies() |
| 271 self.google_code_auth_token = None | 211 self.google_code_auth_token = None |
| 272 self.webkit_repo = options.webkit_repo | |
| 273 if self.webkit_repo: | |
| 274 self.setup_webkit_info() | |
| 275 | 212 |
| 276 # Check the codereview cookie jar to determine which Rietveld instances to | 213 # Check the codereview cookie jar to determine which Rietveld instances to |
| 277 # authenticate to. | 214 # authenticate to. |
| 278 def check_cookies(self): | 215 def check_cookies(self): |
| 279 cookie_file = os.path.expanduser('~/.codereview_upload_cookies') | 216 cookie_file = os.path.expanduser('~/.codereview_upload_cookies') |
| 280 if not os.path.exists(cookie_file): | 217 if not os.path.exists(cookie_file): |
| 281 print 'No Rietveld cookie file found.' | 218 print 'No Rietveld cookie file found.' |
| 282 cookie_jar = [] | 219 cookie_jar = [] |
| 283 else: | 220 else: |
| 284 cookie_jar = cookielib.MozillaCookieJar(cookie_file) | 221 cookie_jar = cookielib.MozillaCookieJar(cookie_file) |
| (...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 601 | 538 |
| 602 ret = [] | 539 ret = [] |
| 603 for entry in replies['feed']['entry']: | 540 for entry in replies['feed']['entry']: |
| 604 e = {} | 541 e = {} |
| 605 e['created'] = datetime_from_google_code(entry['published']['$t']) | 542 e['created'] = datetime_from_google_code(entry['published']['$t']) |
| 606 e['content'] = entry['content']['$t'] | 543 e['content'] = entry['content']['$t'] |
| 607 e['author'] = entry['author'][0]['name']['$t'] | 544 e['author'] = entry['author'][0]['name']['$t'] |
| 608 ret.append(e) | 545 ret.append(e) |
| 609 return ret | 546 return ret |
| 610 | 547 |
| 611 @staticmethod | |
| 612 def git_cmd(repo, *args): | |
| 613 cmd = ['git', '--git-dir=%s/.git' % repo] | |
| 614 cmd.extend(args) | |
| 615 [stdout, _] = subprocess.Popen(cmd, stdout=subprocess.PIPE, | |
| 616 stderr=subprocess.PIPE).communicate() | |
| 617 lines = str(stdout).split('\n')[:-1] | |
| 618 return lines | |
| 619 | |
| 620 def git_search(self, instance, owner=None, reviewer=None): | |
| 621 repo = getattr(self, instance['option']) | |
| 622 if not repo: | |
| 623 return [] | |
| 624 | |
| 625 search = [] | |
| 626 if owner: | |
| 627 search.extend(instance['owner_search_func'](owner)) | |
| 628 if reviewer: | |
| 629 search.extend(instance['reviewer_search_func'](reviewer)) | |
| 630 if not len(search): | |
| 631 return [] | |
| 632 | |
| 633 self.git_cmd(repo, 'fetch', 'origin') | |
| 634 | |
| 635 time_format = '%Y-%m-%d %H:%M:%S' | |
| 636 log_args = [ | |
| 637 '--after=' + self.modified_after.strftime(time_format), | |
| 638 '--before=' + self.modified_before.strftime(time_format), | |
| 639 '--format=%H' | |
| 640 ] | |
| 641 commits = set() | |
| 642 for query in search: | |
| 643 query_args = [query] | |
| 644 query_args.extend(log_args) | |
| 645 commits |= set(self.git_cmd(repo, 'log', 'origin/master', *query_args)) | |
| 646 | |
| 647 ret = [] | |
| 648 for commit in commits: | |
| 649 output = self.git_cmd(repo, 'log', commit + "^!", "--format=%cn%n%cd%n%B") | |
| 650 author = output[0] | |
| 651 date = datetime.strptime(output[1], "%a %b %d %H:%M:%S %Y +0000") | |
| 652 processed = self.process_git_commit(instance, author, date, output[2:]) | |
| 653 if processed: | |
| 654 ret.append(processed) | |
| 655 | |
| 656 ret = sorted(ret, key=lambda i: i['modified'], reverse=True) | |
| 657 return ret | |
| 658 | |
| 659 @staticmethod | |
| 660 def process_git_commit(instance, author, date, log): | |
| 661 ret = {} | |
| 662 ret['owner'] = author | |
| 663 ret['author'] = author | |
| 664 ret['modified'] = date | |
| 665 ret['created'] = date | |
| 666 ret['header'] = log[0] | |
| 667 | |
| 668 reviews = [] | |
| 669 reviewers = [] | |
| 670 changes = [] | |
| 671 | |
| 672 for line in log: | |
| 673 match = re.match(r'Reviewed by ([^.]*)', line) | |
| 674 if match: | |
| 675 reviewers.append(match.group(1)) | |
| 676 if instance['review_re']: | |
| 677 match = re.match(instance['review_re'], line) | |
| 678 if match: | |
| 679 reviews.append(int(match.group(1))) | |
| 680 if instance['change_re']: | |
| 681 match = re.match(instance['change_re'], line) | |
| 682 if match: | |
| 683 changes.append(int(match.group(1))) | |
| 684 | |
| 685 committer_list = webkitpy.common.config.committers.CommitterList() | |
| 686 ret['reviewers'] = set( | |
| 687 (committer_list.contributor_by_name(r).emails[0] for r in reviewers)) | |
| 688 | |
| 689 # Reviews more useful than change link itself, but tricky if multiple | |
| 690 # Reviews == bugs for WebKit changes | |
| 691 if len(reviews) == 1: | |
| 692 url = 'http://%s/%d' % (instance['review_url'], reviews[0]) | |
| 693 if instance['review_prop']: | |
| 694 ret[instance['review_prop']] = reviews[0] | |
| 695 elif len(changes) == 1: | |
| 696 url = 'http://%s/%d' % (instance['change_url'], changes[0]) | |
| 697 else: | |
| 698 # Couldn't find anything. | |
| 699 return None | |
| 700 ret['review_url'] = url | |
| 701 | |
| 702 return ret | |
| 703 | |
| 704 def bugzilla_issues(self, instance, user): | |
| 705 if instance['user_func']: | |
| 706 user = instance['user_func'](user) | |
| 707 if not user: | |
| 708 return [] | |
| 709 | |
| 710 # This search is a little iffy, as it returns any bug that has been | |
| 711 # modified over a time period in any way and that a user has ever commented | |
| 712 # on, but that's the best that Bugzilla can get us. Oops. | |
| 713 commented = { 'emaillongdesc1': 1 } | |
| 714 issues = self.bugzilla_search(instance, user, commented) | |
| 715 issues = filter(lambda issue: issue['owner'] != user, issues) | |
| 716 | |
| 717 reported = { 'emailreporter1': 1, 'chfield': '[Bug creation]' } | |
| 718 issues.extend(self.bugzilla_search(instance, user, reported)) | |
| 719 | |
| 720 # Remove duplicates by bug id | |
| 721 seen = {} | |
| 722 pruned = [] | |
| 723 for issue in issues: | |
| 724 bug_id = issue['webkit_bug_id'] | |
| 725 if bug_id in seen: | |
| 726 continue | |
| 727 seen[bug_id] = True | |
| 728 pruned.append(issue) | |
| 729 | |
| 730 # Bugzilla has no modified time, so sort by id? | |
| 731 pruned = sorted(pruned, key=lambda i: i['webkit_bug_id']) | |
| 732 return issues | |
| 733 | |
| 734 def bugzilla_search(self, instance, user, params): | |
| 735 time_format = '%Y-%m-%d' | |
| 736 values = { | |
| 737 'chfieldfrom': self.modified_after.strftime(time_format), | |
| 738 'chfieldto': self.modified_before.strftime(time_format), | |
| 739 'ctype': 'csv', | |
| 740 'emailtype1': 'substring', | |
| 741 'email1': '%s' % user, | |
| 742 } | |
| 743 values.update(params) | |
| 744 | |
| 745 # Must be GET not POST | |
| 746 data = urllib.urlencode(values) | |
| 747 req = urllib2.Request("%s?%s" % (instance['search_url'], data)) | |
| 748 response = urllib2.urlopen(req) | |
| 749 reader = csv.reader(response) | |
| 750 reader.next() # skip the header line | |
| 751 | |
| 752 issues = map(partial(self.process_bugzilla_issue, instance), reader) | |
| 753 return issues | |
| 754 | |
| 755 @staticmethod | |
| 756 def process_bugzilla_issue(instance, issue): | |
| 757 bug_id, owner, desc = int(issue[0]), issue[4], issue[7] | |
| 758 | |
| 759 ret = {} | |
| 760 ret['owner'] = owner | |
| 761 ret['author'] = owner | |
| 762 ret['review_url'] = 'http://%s/%d' % (instance['url'], bug_id) | |
| 763 ret['url'] = ret['review_url'] | |
| 764 ret['header'] = desc | |
| 765 ret['webkit_bug_id'] = bug_id | |
| 766 return ret | |
| 767 | |
| 768 def setup_webkit_info(self): | |
| 769 assert(self.webkit_repo) | |
| 770 git_dir = os.path.normpath(self.webkit_repo + "/.git") | |
| 771 if not os.path.exists(git_dir): | |
| 772 print "%s doesn't exist, skipping WebKit checks." % git_dir | |
| 773 self.webkit_repo = None | |
| 774 return | |
| 775 | |
| 776 try: | |
| 777 self.git_cmd(self.webkit_repo, "fetch", "origin") | |
| 778 except subprocess.CalledProcessError: | |
| 779 print "Failed to update WebKit repo, skipping WebKit checks." | |
| 780 self.webkit_repo = None | |
| 781 return | |
| 782 | |
| 783 path = "Tools/Scripts" | |
| 784 full_path = os.path.normpath("%s/%s" % (self.options.webkit_repo, path)) | |
| 785 sys.path.append(full_path) | |
| 786 | |
| 787 try: | |
| 788 global webkitpy | |
| 789 webkitpy = __import__('webkitpy.common.config.committers') | |
| 790 except ImportError: | |
| 791 print "Failed to import WebKit committer list, skipping WebKit checks." | |
| 792 self.webkit_repo = None | |
| 793 return | |
| 794 | |
| 795 if not webkit_account(self.user): | |
| 796 email = self.user + "@chromium.org" | |
| 797 print "No %s in committers.py, skipping WebKit checks." % email | |
| 798 self.webkit_repo = None | |
| 799 | |
| 800 def print_heading(self, heading): | 548 def print_heading(self, heading): |
| 801 print | 549 print |
| 802 print self.options.output_format_heading.format(heading=heading) | 550 print self.options.output_format_heading.format(heading=heading) |
| 803 | 551 |
| 804 def print_change(self, change): | 552 def print_change(self, change): |
| 805 optional_values = { | 553 optional_values = { |
| 806 'reviewers': ', '.join(change['reviewers']) | 554 'reviewers': ', '.join(change['reviewers']) |
| 807 } | 555 } |
| 808 self.print_generic(self.options.output_format, | 556 self.print_generic(self.options.output_format, |
| 809 self.options.output_format_changes, | 557 self.options.output_format_changes, |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 886 self.google_code_auth_token = ( | 634 self.google_code_auth_token = ( |
| 887 get_auth_token(self.options.local_user + '@chromium.org')) | 635 get_auth_token(self.options.local_user + '@chromium.org')) |
| 888 | 636 |
| 889 def get_changes(self): | 637 def get_changes(self): |
| 890 for instance in rietveld_instances: | 638 for instance in rietveld_instances: |
| 891 self.changes += self.rietveld_search(instance, owner=self.user) | 639 self.changes += self.rietveld_search(instance, owner=self.user) |
| 892 | 640 |
| 893 for instance in gerrit_instances: | 641 for instance in gerrit_instances: |
| 894 self.changes += self.gerrit_search(instance, owner=self.user) | 642 self.changes += self.gerrit_search(instance, owner=self.user) |
| 895 | 643 |
| 896 for instance in git_instances: | |
| 897 self.changes += self.git_search(instance, owner=self.user) | |
| 898 | |
| 899 def print_changes(self): | 644 def print_changes(self): |
| 900 if self.changes: | 645 if self.changes: |
| 901 self.print_heading('Changes') | 646 self.print_heading('Changes') |
| 902 for change in self.changes: | 647 for change in self.changes: |
| 903 self.print_change(change) | 648 self.print_change(change) |
| 904 | 649 |
| 905 def get_reviews(self): | 650 def get_reviews(self): |
| 906 for instance in rietveld_instances: | 651 for instance in rietveld_instances: |
| 907 self.reviews += self.rietveld_search(instance, reviewer=self.user) | 652 self.reviews += self.rietveld_search(instance, reviewer=self.user) |
| 908 | 653 |
| 909 for instance in gerrit_instances: | 654 for instance in gerrit_instances: |
| 910 reviews = self.gerrit_search(instance, reviewer=self.user) | 655 reviews = self.gerrit_search(instance, reviewer=self.user) |
| 911 reviews = filter(lambda r: not username(r['owner']) == self.user, reviews) | 656 reviews = filter(lambda r: not username(r['owner']) == self.user, reviews) |
| 912 self.reviews += reviews | 657 self.reviews += reviews |
| 913 | 658 |
| 914 for instance in git_instances: | |
| 915 self.reviews += self.git_search(instance, reviewer=self.user) | |
| 916 | |
| 917 def print_reviews(self): | 659 def print_reviews(self): |
| 918 if self.reviews: | 660 if self.reviews: |
| 919 self.print_heading('Reviews') | 661 self.print_heading('Reviews') |
| 920 for review in self.reviews: | 662 for review in self.reviews: |
| 921 self.print_review(review) | 663 self.print_review(review) |
| 922 | 664 |
| 923 def get_issues(self): | 665 def get_issues(self): |
| 924 for project in google_code_projects: | 666 for project in google_code_projects: |
| 925 self.issues += self.google_code_issue_search(project) | 667 self.issues += self.google_code_issue_search(project) |
| 926 | 668 |
| 927 for instance in bugzilla_instances: | |
| 928 self.issues += self.bugzilla_issues(instance, self.user) | |
| 929 | |
| 930 def print_issues(self): | 669 def print_issues(self): |
| 931 if self.issues: | 670 if self.issues: |
| 932 self.print_heading('Issues') | 671 self.print_heading('Issues') |
| 933 for issue in self.issues: | 672 for issue in self.issues: |
| 934 self.print_issue(issue) | 673 self.print_issue(issue) |
| 935 | 674 |
| 936 def process_activities(self): | |
| 937 # If a webkit bug was a review, don't list it as an issue. | |
| 938 ids = {} | |
| 939 for review in self.reviews + self.changes: | |
| 940 if 'webkit_bug_id' in review: | |
| 941 ids[review['webkit_bug_id']] = True | |
| 942 | |
| 943 def duplicate_issue(issue): | |
| 944 if 'webkit_bug_id' not in issue: | |
| 945 return False | |
| 946 return issue['webkit_bug_id'] in ids | |
| 947 | |
| 948 self.issues = filter(lambda issue: not duplicate_issue(issue), self.issues) | |
| 949 | |
| 950 def print_activity(self): | 675 def print_activity(self): |
| 951 self.print_changes() | 676 self.print_changes() |
| 952 self.print_reviews() | 677 self.print_reviews() |
| 953 self.print_issues() | 678 self.print_issues() |
| 954 | 679 |
| 955 | 680 |
| 956 def main(): | 681 def main(): |
| 957 # Silence upload.py. | 682 # Silence upload.py. |
| 958 rietveld.upload.verbosity = 0 | 683 rietveld.upload.verbosity = 0 |
| 959 | 684 |
| 960 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) | 685 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
| 961 parser.add_option( | 686 parser.add_option( |
| 962 '-u', '--user', metavar='<email>', | 687 '-u', '--user', metavar='<email>', |
| 963 default=os.environ.get('USER'), | 688 default=os.environ.get('USER'), |
| 964 help='Filter on user, default=%default') | 689 help='Filter on user, default=%default') |
| 965 parser.add_option( | 690 parser.add_option( |
| 966 '--webkit_repo', metavar='<dir>', | |
| 967 default='%s' % os.environ.get('WEBKIT_DIR'), | |
| 968 help='Local path to WebKit repository, default=%default') | |
| 969 parser.add_option( | |
| 970 '-b', '--begin', metavar='<date>', | 691 '-b', '--begin', metavar='<date>', |
| 971 help='Filter issues created after the date') | 692 help='Filter issues created after the date') |
| 972 parser.add_option( | 693 parser.add_option( |
| 973 '-e', '--end', metavar='<date>', | 694 '-e', '--end', metavar='<date>', |
| 974 help='Filter issues created before the date') | 695 help='Filter issues created before the date') |
| 975 quarter_begin, quarter_end = get_quarter_of(datetime.today() - | 696 quarter_begin, quarter_end = get_quarter_of(datetime.today() - |
| 976 relativedelta(months=2)) | 697 relativedelta(months=2)) |
| 977 parser.add_option( | 698 parser.add_option( |
| 978 '-Q', '--last_quarter', action='store_true', | 699 '-Q', '--last_quarter', action='store_true', |
| 979 help='Use last quarter\'s dates, e.g. %s to %s' % ( | 700 help='Use last quarter\'s dates, e.g. %s to %s' % ( |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1100 | 821 |
| 1101 print 'Looking up activity.....' | 822 print 'Looking up activity.....' |
| 1102 | 823 |
| 1103 if options.changes: | 824 if options.changes: |
| 1104 my_activity.get_changes() | 825 my_activity.get_changes() |
| 1105 if options.reviews: | 826 if options.reviews: |
| 1106 my_activity.get_reviews() | 827 my_activity.get_reviews() |
| 1107 if options.issues: | 828 if options.issues: |
| 1108 my_activity.get_issues() | 829 my_activity.get_issues() |
| 1109 | 830 |
| 1110 my_activity.process_activities() | |
| 1111 | |
| 1112 print '\n\n\n' | 831 print '\n\n\n' |
| 1113 | 832 |
| 1114 my_activity.print_changes() | 833 my_activity.print_changes() |
| 1115 my_activity.print_reviews() | 834 my_activity.print_reviews() |
| 1116 my_activity.print_issues() | 835 my_activity.print_issues() |
| 1117 return 0 | 836 return 0 |
| 1118 | 837 |
| 1119 | 838 |
| 1120 if __name__ == '__main__': | 839 if __name__ == '__main__': |
| 1121 sys.exit(main()) | 840 sys.exit(main()) |
| OLD | NEW |