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 csv | |
cjhopman
2012/11/04 16:32:03
Nit: csv after cookielib
| |
23 import cookielib | 24 import cookielib |
24 import datetime | 25 import datetime |
25 from datetime import datetime | 26 from datetime import datetime |
26 from datetime import timedelta | 27 from datetime import timedelta |
27 from functools import partial | 28 from functools import partial |
28 import json | 29 import json |
29 import optparse | 30 import optparse |
30 import os | 31 import os |
32 import re | |
31 import subprocess | 33 import subprocess |
32 import sys | 34 import sys |
33 import urllib | 35 import urllib |
34 import urllib2 | 36 import urllib2 |
35 | 37 |
36 import rietveld | 38 import rietveld |
37 from third_party import upload | 39 from third_party import upload |
38 | 40 |
41 # Imported later, once options are set | |
cjhopman
2012/11/04 16:32:03
Nit: full stop
| |
42 webkitpy = None | |
43 | |
39 try: | 44 try: |
40 from dateutil.relativedelta import relativedelta # pylint: disable=F0401 | 45 from dateutil.relativedelta import relativedelta # pylint: disable=F0401 |
41 except ImportError: | 46 except ImportError: |
42 print 'python-dateutil package required' | 47 print 'python-dateutil package required' |
43 exit(1) | 48 exit(1) |
44 | 49 |
45 # python-keyring provides easy access to the system keyring. | 50 # python-keyring provides easy access to the system keyring. |
46 try: | 51 try: |
47 import keyring # pylint: disable=W0611,F0401 | 52 import keyring # pylint: disable=W0611,F0401 |
48 except ImportError: | 53 except ImportError: |
49 print 'Consider installing python-keyring' | 54 print 'Consider installing python-keyring' |
50 | 55 |
56 def webkit_account(user): | |
57 committer_list = webkitpy.common.config.committers.CommitterList() | |
58 email = user + "@chromium.org" | |
59 return committer_list.account_by_email(email) | |
60 | |
61 def user_to_webkit_email(user): | |
62 account = webkit_account(user) | |
63 if not account: | |
64 return None | |
65 return account.emails[0] | |
66 | |
67 def user_to_webkit_owner_search(user): | |
68 account = webkit_account(user) | |
69 if not account: | |
70 return ['--author=%s@chromium.org' % user] | |
71 search = [] | |
72 for email in account.emails: | |
73 search.append('--author=' + email) | |
74 # commit-bot is author for contributors who are not committers | |
cjhopman
2012/11/04 16:32:03
Nit: full stop
| |
75 search.append('--grep=Patch by ' + account.full_name) | |
76 return search | |
77 | |
78 def user_to_webkit_reviewer_search(user): | |
79 committer_list = webkitpy.common.config.committers.CommitterList() | |
80 email = user + "@chromium.org" | |
81 account = committer_list.reviewer_by_email(email) | |
82 if not account: | |
83 return [] | |
84 return ['--grep=Reviewed by ' + account.full_name] | |
51 | 85 |
52 rietveld_instances = [ | 86 rietveld_instances = [ |
53 { | 87 { |
54 'url': 'codereview.chromium.org', | 88 'url': 'codereview.chromium.org', |
55 'shorturl': 'crrev.com', | 89 'shorturl': 'crrev.com', |
56 'supports_owner_modified_query': True, | 90 'supports_owner_modified_query': True, |
57 'requires_auth': False, | 91 'requires_auth': False, |
58 'email_domain': 'chromium.org', | 92 'email_domain': 'chromium.org', |
59 }, | 93 }, |
60 { | 94 { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
101 'name': 'chrome-os-partner', | 135 'name': 'chrome-os-partner', |
102 }, | 136 }, |
103 { | 137 { |
104 'name': 'google-breakpad', | 138 'name': 'google-breakpad', |
105 }, | 139 }, |
106 { | 140 { |
107 'name': 'gyp', | 141 'name': 'gyp', |
108 } | 142 } |
109 ] | 143 ] |
110 | 144 |
145 bugzilla_instances = [ | |
146 { | |
147 'search_url': 'http://bugs.webkit.org/buglist.cgi', | |
148 'url': 'wkb.ug', | |
149 'user_func': user_to_webkit_email, | |
150 }, | |
151 ] | |
152 | |
153 git_instances = [ | |
154 { | |
155 'option': 'webkit_repo', | |
156 'change_re': | |
157 r'git-svn-id: http://svn\.webkit\.org/repository/webkit/trunk@(\d*)', | |
158 'change_url': 'trac.webkit.org/changeset', | |
159 'review_re': r'https://bugs\.webkit\.org/show_bug\.cgi\?id\=(\d*)', | |
160 'review_url': 'wkb.ug', | |
161 'review_prop': 'webkit_bug_id', | |
162 | |
163 'owner_search_func': user_to_webkit_owner_search, | |
164 'reviewer_search_func': user_to_webkit_reviewer_search, | |
165 }, | |
166 ] | |
111 | 167 |
112 # Uses ClientLogin to authenticate the user for Google Code issue trackers. | 168 # Uses ClientLogin to authenticate the user for Google Code issue trackers. |
113 def get_auth_token(email): | 169 def get_auth_token(email): |
114 # KeyringCreds will use the system keyring on the first try, and prompt for | 170 # KeyringCreds will use the system keyring on the first try, and prompt for |
115 # a password on the next ones. | 171 # a password on the next ones. |
116 creds = upload.KeyringCreds('code.google.com', 'code.google.com', email) | 172 creds = upload.KeyringCreds('code.google.com', 'code.google.com', email) |
117 for _ in xrange(3): | 173 for _ in xrange(3): |
118 email, password = creds.GetUserCredentials() | 174 email, password = creds.GetUserCredentials() |
119 url = 'https://www.google.com/accounts/ClientLogin' | 175 url = 'https://www.google.com/accounts/ClientLogin' |
120 data = urllib.urlencode({ | 176 data = urllib.urlencode({ |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
187 def __init__(self, options): | 243 def __init__(self, options): |
188 self.options = options | 244 self.options = options |
189 self.modified_after = options.begin | 245 self.modified_after = options.begin |
190 self.modified_before = options.end | 246 self.modified_before = options.end |
191 self.user = options.user | 247 self.user = options.user |
192 self.changes = [] | 248 self.changes = [] |
193 self.reviews = [] | 249 self.reviews = [] |
194 self.issues = [] | 250 self.issues = [] |
195 self.check_cookies() | 251 self.check_cookies() |
196 self.google_code_auth_token = None | 252 self.google_code_auth_token = None |
253 self.webkit_repo = options.webkit_repo | |
254 if self.webkit_repo: | |
255 self.setup_webkit_info() | |
197 | 256 |
198 # Check the codereview cookie jar to determine which Rietveld instances to | 257 # Check the codereview cookie jar to determine which Rietveld instances to |
199 # authenticate to. | 258 # authenticate to. |
200 def check_cookies(self): | 259 def check_cookies(self): |
201 cookie_file = os.path.expanduser('~/.codereview_upload_cookies') | 260 cookie_file = os.path.expanduser('~/.codereview_upload_cookies') |
202 cookie_jar = cookielib.MozillaCookieJar(cookie_file) | 261 cookie_jar = cookielib.MozillaCookieJar(cookie_file) |
203 if not os.path.exists(cookie_file): | 262 if not os.path.exists(cookie_file): |
204 exit(1) | 263 exit(1) |
205 | 264 |
206 try: | 265 try: |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
455 ret = [] | 514 ret = [] |
456 for entry in replies['feed']['entry']: | 515 for entry in replies['feed']['entry']: |
457 e = {} | 516 e = {} |
458 e['created'] = datetime_from_google_code(entry['published']['$t']) | 517 e['created'] = datetime_from_google_code(entry['published']['$t']) |
459 e['content'] = entry['content']['$t'] | 518 e['content'] = entry['content']['$t'] |
460 e['author'] = entry['author'][0]['name']['$t'] | 519 e['author'] = entry['author'][0]['name']['$t'] |
461 ret.append(e) | 520 ret.append(e) |
462 return ret | 521 return ret |
463 | 522 |
464 @staticmethod | 523 @staticmethod |
524 def git_cmd(repo, *args): | |
525 cmd = ['git', '--git-dir=%s/.git' % repo] | |
526 cmd.extend(args) | |
527 [stdout, _] = subprocess.Popen(cmd, stdout=subprocess.PIPE, | |
528 stderr=subprocess.PIPE).communicate() | |
529 lines = str(stdout).split('\n')[:-1] | |
530 return lines | |
531 | |
532 def git_search(self, instance, owner=None, reviewer=None): | |
533 search = [] | |
534 if owner: | |
535 search.extend(instance['owner_search_func'](owner)) | |
536 if reviewer: | |
537 search.extend(instance['reviewer_search_func'](reviewer)) | |
538 if not len(search): | |
539 return [] | |
540 | |
541 repo = getattr(self, instance['option']) | |
542 self.git_cmd(repo, 'fetch', 'origin') | |
543 | |
544 time_format = '%Y-%m-%d %H:%M:%S' | |
545 log_args = [ | |
546 '--after=' + self.modified_after.strftime(time_format), | |
547 '--before=' + self.modified_before.strftime(time_format), | |
548 '--format=%H' | |
549 ] | |
550 commits = set() | |
551 for query in search: | |
552 query_args = [query] | |
553 query_args.extend(log_args) | |
554 commits |= set(self.git_cmd(repo, 'log', 'origin/master', *query_args)) | |
555 | |
556 ret = [] | |
557 for commit in commits: | |
558 output = self.git_cmd(repo, 'log', commit + "^!", "--format=%cn%n%cd%n%B") | |
559 author = output[0] | |
560 date = datetime.strptime(output[1], "%a %b %d %H:%M:%S %Y +0000") | |
561 ret.append(self.process_git_commit(instance, author, date, output[2:])) | |
562 | |
563 ret = sorted(ret, key=lambda i: i['modified'], reverse=True) | |
564 return ret | |
565 | |
566 @staticmethod | |
567 def process_git_commit(instance, author, date, log): | |
568 ret = {} | |
569 ret['owner'] = author | |
570 ret['author'] = author | |
571 ret['modified'] = date | |
572 ret['created'] = date | |
573 ret['header'] = log[0] | |
574 | |
575 reviews = [] | |
576 reviewers = [] | |
577 changes = [] | |
578 | |
579 for line in log: | |
580 match = re.match(r'Reviewed by ([^.]*)', line) | |
581 if match: | |
582 reviewers.append(match.group(1)) | |
583 if instance['review_re']: | |
584 match = re.match(instance['review_re'], line) | |
585 if match: | |
586 reviews.append(int(match.group(1))) | |
587 if instance['change_re']: | |
588 match = re.match(instance['change_re'], line) | |
589 if match: | |
590 changes.append(int(match.group(1))) | |
591 | |
592 # TODO(enne): should convert full names to usernames via CommitterList | |
cjhopman
2012/11/04 16:32:03
Nit: full stop
| |
593 ret['reviewers'] = set(reviewers) | |
594 | |
595 # Reviews more useful than change link itself, but tricky if multiple | |
596 # Reviews == bugs for WebKit changes | |
597 if len(reviews) == 1: | |
598 url = 'http://%s/%d' % (instance['review_url'], reviews[0]) | |
599 if instance['review_prop']: | |
600 ret[instance['review_prop']] = reviews[0] | |
601 else: | |
602 url = 'http://%s/%d' % (instance['change_url'], changes[0]) | |
603 ret['review_url'] = url | |
604 | |
605 return ret | |
606 | |
607 def bugzilla_issues(self, instance, user): | |
608 if instance['user_func']: | |
609 user = instance['user_func'](user) | |
610 if not user: | |
611 return [] | |
612 | |
613 # This search is a little iffy, as it returns any bug that has been | |
614 # modified over a time period in any way and that a user has ever commented | |
615 # on, but that's the best that Bugzilla can get us. Oops. | |
616 commented = { 'emaillongdesc1': 1 } | |
617 issues = self.bugzilla_search(instance, user, commented) | |
618 issues = filter(lambda issue: issue['owner'] != user, issues) | |
619 | |
620 reported = { 'emailreporter1': 1, 'chfield': '[Bug creation]' } | |
621 issues.extend(self.bugzilla_search(instance, user, reported)) | |
622 | |
623 # Remove duplicates by bug id | |
624 seen = {} | |
625 pruned = [] | |
626 for issue in issues: | |
627 bug_id = issue['webkit_bug_id'] | |
628 if bug_id in seen: | |
629 continue | |
630 seen[bug_id] = True | |
631 pruned.append(issue) | |
632 | |
633 # Bugzilla has no modified time, so sort by id? | |
634 pruned = sorted(pruned, key=lambda i: i['webkit_bug_id']) | |
635 return issues | |
636 | |
637 def bugzilla_search(self, instance, user, params): | |
638 time_format = '%Y-%m-%d' | |
639 values = { | |
640 'chfieldfrom': self.modified_after.strftime(time_format), | |
641 'chfieldto': self.modified_before.strftime(time_format), | |
642 'ctype': 'csv', | |
643 'emailtype1': 'substring', | |
644 'email1': '%s' % user, | |
645 } | |
646 values.update(params) | |
647 | |
648 # Must be GET not POST | |
649 data = urllib.urlencode(values) | |
650 req = urllib2.Request("%s?%s" % (instance['search_url'], data)) | |
651 response = urllib2.urlopen(req) | |
652 reader = csv.reader(response) | |
653 reader.next() # skip the header line | |
654 | |
655 issues = map(partial(self.process_bugzilla_issue, instance), reader) | |
656 return issues | |
657 | |
658 @staticmethod | |
659 def process_bugzilla_issue(instance, issue): | |
660 bug_id, owner, desc = int(issue[0]), issue[4], issue[7] | |
661 | |
662 ret = {} | |
663 ret['owner'] = owner | |
664 ret['author'] = owner | |
665 ret['review_url'] = 'http://%s/%d' % (instance['url'], bug_id) | |
666 ret['url'] = ret['review_url'] | |
667 ret['header'] = desc | |
668 ret['webkit_bug_id'] = bug_id | |
669 return ret | |
670 | |
671 def setup_webkit_info(self): | |
672 assert(self.webkit_repo) | |
673 git_dir = os.path.normpath(self.webkit_repo + "/.git") | |
674 if not os.path.exists(git_dir): | |
675 print "%s doesn't exists, turning off WebKit checks." % git_dir | |
676 self.webkit_repo = None | |
cjhopman
2012/11/04 16:32:03
I've missed how this disables the webkit checks. I
enne (OOO)
2012/11/04 23:35:47
You're quite right. This used to work, but then a
| |
677 return | |
678 | |
679 try: | |
680 self.git_cmd(self.webkit_repo, "fetch", "origin") | |
681 except subprocess.CalledProcessError: | |
682 print "Failed to update WebKit repo, turning off WebKit checks." | |
683 self.webkit_repo = None | |
684 return | |
685 | |
686 path = "Tools/Scripts" | |
687 full_path = os.path.normpath("%s/%s" % (self.options.webkit_repo, path)) | |
688 sys.path.append(full_path) | |
689 | |
690 try: | |
691 global webkitpy | |
692 webkitpy = __import__('webkitpy.common.config.committers') | |
693 except ImportError: | |
694 print "Failed to import WebKit committer list, turning off WebKit checks." | |
695 self.webkit_repo = None | |
696 return | |
697 | |
698 if not webkit_account(self.user): | |
699 email = self.user + "@chromium.org" | |
700 print "No %s in committers.py, turning off WebKit checks." % email | |
701 self.webkit_repo = None | |
702 | |
703 @staticmethod | |
465 def print_change(change): | 704 def print_change(change): |
466 print '%s %s' % ( | 705 print '%s %s' % ( |
467 change['review_url'], | 706 change['review_url'], |
468 change['header'], | 707 change['header'], |
469 ) | 708 ) |
470 | 709 |
471 @staticmethod | 710 @staticmethod |
472 def print_issue(issue): | 711 def print_issue(issue): |
473 print '%s %s' % ( | 712 print '%s %s' % ( |
474 issue['url'], | 713 issue['url'], |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
512 self.google_code_auth_token = ( | 751 self.google_code_auth_token = ( |
513 get_auth_token(self.options.local_user + '@chromium.org')) | 752 get_auth_token(self.options.local_user + '@chromium.org')) |
514 | 753 |
515 def get_changes(self): | 754 def get_changes(self): |
516 for instance in rietveld_instances: | 755 for instance in rietveld_instances: |
517 self.changes += self.rietveld_search(instance, owner=self.user) | 756 self.changes += self.rietveld_search(instance, owner=self.user) |
518 | 757 |
519 for instance in gerrit_instances: | 758 for instance in gerrit_instances: |
520 self.changes += self.gerrit_search(instance, owner=self.user) | 759 self.changes += self.gerrit_search(instance, owner=self.user) |
521 | 760 |
761 for instance in git_instances: | |
762 self.changes += self.git_search(instance, owner=self.user) | |
763 | |
522 def print_changes(self): | 764 def print_changes(self): |
523 if self.changes: | 765 if self.changes: |
524 print '\nChanges:' | 766 print '\nChanges:' |
525 for change in self.changes: | 767 for change in self.changes: |
526 self.print_change(change) | 768 self.print_change(change) |
527 | 769 |
528 def get_reviews(self): | 770 def get_reviews(self): |
529 for instance in rietveld_instances: | 771 for instance in rietveld_instances: |
530 self.reviews += self.rietveld_search(instance, reviewer=self.user) | 772 self.reviews += self.rietveld_search(instance, reviewer=self.user) |
531 | 773 |
532 for instance in gerrit_instances: | 774 for instance in gerrit_instances: |
533 reviews = self.gerrit_search(instance, reviewer=self.user) | 775 reviews = self.gerrit_search(instance, reviewer=self.user) |
534 reviews = filter(lambda r: not username(r['owner']) == self.user, reviews) | 776 reviews = filter(lambda r: not username(r['owner']) == self.user, reviews) |
535 self.reviews += reviews | 777 self.reviews += reviews |
536 | 778 |
779 for instance in git_instances: | |
780 self.reviews += self.git_search(instance, reviewer=self.user) | |
781 | |
537 def print_reviews(self): | 782 def print_reviews(self): |
538 if self.reviews: | 783 if self.reviews: |
539 print '\nReviews:' | 784 print '\nReviews:' |
540 for review in self.reviews: | 785 for review in self.reviews: |
541 self.print_change(review) | 786 self.print_change(review) |
542 | 787 |
543 def get_issues(self): | 788 def get_issues(self): |
544 for project in google_code_projects: | 789 for project in google_code_projects: |
545 self.issues += self.google_code_issue_search(project) | 790 self.issues += self.google_code_issue_search(project) |
546 | 791 |
792 for instance in bugzilla_instances: | |
793 self.issues += self.bugzilla_issues(instance, self.user) | |
794 | |
795 def process_activities(self): | |
796 # If a webkit bug was a review, don't list it as an issue | |
cjhopman
2012/11/04 16:32:03
Nit: full stop
| |
797 ids = {} | |
798 for review in self.reviews + self.changes: | |
799 if 'webkit_bug_id' in review: | |
800 ids[review['webkit_bug_id']] = True | |
801 | |
802 def duplicate_issue(issue): | |
803 if 'webkit_bug_id' not in issue: | |
804 return False | |
805 return issue['webkit_bug_id'] in ids | |
806 | |
807 self.issues = filter(lambda issue: not duplicate_issue(issue), self.issues) | |
808 | |
547 def print_issues(self): | 809 def print_issues(self): |
548 if self.issues: | 810 if self.issues: |
549 print '\nIssues:' | 811 print '\nIssues:' |
550 for c in self.issues: | 812 for c in self.issues: |
551 self.print_issue(c) | 813 self.print_issue(c) |
552 | 814 |
553 def print_activity(self): | 815 def print_activity(self): |
554 self.print_changes() | 816 self.print_changes() |
555 self.print_reviews() | 817 self.print_reviews() |
556 self.print_issues() | 818 self.print_issues() |
557 | 819 |
558 | 820 |
559 def main(): | 821 def main(): |
560 # Silence upload.py. | 822 # Silence upload.py. |
561 rietveld.upload.verbosity = 0 | 823 rietveld.upload.verbosity = 0 |
562 | 824 |
563 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) | 825 parser = optparse.OptionParser(description=sys.modules[__name__].__doc__) |
564 parser.add_option( | 826 parser.add_option( |
565 '-u', '--user', metavar='<email>', | 827 '-u', '--user', metavar='<email>', |
566 default=os.environ.get('USER'), | 828 default=os.environ.get('USER'), |
567 help='Filter on user, default=%default') | 829 help='Filter on user, default=%default') |
568 parser.add_option( | 830 parser.add_option( |
831 '--webkit_repo', metavar='<dir>', | |
832 default='%s' % os.environ.get('WEBKIT_DIR'), | |
833 help='WebKit repository, default=%default') | |
cjhopman
2012/11/04 16:32:03
Maybe note here that it should be a local path?
| |
834 parser.add_option( | |
569 '-b', '--begin', metavar='<date>', | 835 '-b', '--begin', metavar='<date>', |
570 help='Filter issues created after the date') | 836 help='Filter issues created after the date') |
571 parser.add_option( | 837 parser.add_option( |
572 '-e', '--end', metavar='<date>', | 838 '-e', '--end', metavar='<date>', |
573 help='Filter issues created before the date') | 839 help='Filter issues created before the date') |
574 quarter_begin, quarter_end = get_quarter_of(datetime.today() - | 840 quarter_begin, quarter_end = get_quarter_of(datetime.today() - |
575 relativedelta(months=2)) | 841 relativedelta(months=2)) |
576 parser.add_option( | 842 parser.add_option( |
577 '-Q', '--last_quarter', action='store_true', | 843 '-Q', '--last_quarter', action='store_true', |
578 help='Use last quarter\'s dates, e.g. %s to %s' % ( | 844 help='Use last quarter\'s dates, e.g. %s to %s' % ( |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
657 | 923 |
658 print 'Looking up activity.....' | 924 print 'Looking up activity.....' |
659 | 925 |
660 if options.changes: | 926 if options.changes: |
661 my_activity.get_changes() | 927 my_activity.get_changes() |
662 if options.reviews: | 928 if options.reviews: |
663 my_activity.get_reviews() | 929 my_activity.get_reviews() |
664 if options.issues: | 930 if options.issues: |
665 my_activity.get_issues() | 931 my_activity.get_issues() |
666 | 932 |
933 my_activity.process_activities() | |
934 | |
667 print '\n\n\n' | 935 print '\n\n\n' |
668 | 936 |
669 my_activity.print_changes() | 937 my_activity.print_changes() |
670 my_activity.print_reviews() | 938 my_activity.print_reviews() |
671 my_activity.print_issues() | 939 my_activity.print_issues() |
672 return 0 | 940 return 0 |
673 | 941 |
674 | 942 |
675 if __name__ == '__main__': | 943 if __name__ == '__main__': |
676 sys.exit(main()) | 944 sys.exit(main()) |
OLD | NEW |