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 |