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