Support JSON output in my_activity.py

That JSON output format shows some additional fields such as bug number
for CLs, label and component for issues.

Also does minor changes like replacing the old code.google.com
references and using short urls when possible.

BUG=None

Change-Id: I988d292dc57b72a2f2c6f12096266df8a09a4dd8
Reviewed-on: https://chromium-review.googlesource.com/422203
Reviewed-by: Aaron Gable <agable@chromium.org>
Commit-Queue: Nicolas Dossou-Gbété <dgn@chromium.org>
changes/03/422203/4
Nicolas Dossou-gbete 9 years ago committed by Commit Bot
parent 370ca1bdce
commit e5deedfce6

@ -11,6 +11,7 @@ Example:
- my_activity.py -Y for stats for this year. - my_activity.py -Y for stats for this year.
- my_activity.py -b 4/5/12 for stats since 4/5/12. - my_activity.py -b 4/5/12 for stats since 4/5/12.
- my_activity.py -b 4/5/12 -e 6/7/12 for stats between 4/5/12 and 6/7/12. - my_activity.py -b 4/5/12 -e 6/7/12 for stats between 4/5/12 and 6/7/12.
- my_activity.py -jd to output stats for the week to json with deltas data.
""" """
# TODO(vadimsh): This script knows too much about ClientLogin and cookies. It # TODO(vadimsh): This script knows too much about ClientLogin and cookies. It
@ -34,6 +35,7 @@ import subprocess
from string import Formatter from string import Formatter
import sys import sys
import urllib import urllib
import re
import auth import auth
import fix_encoding import fix_encoding
@ -250,6 +252,26 @@ class MyActivity(object):
return issues return issues
def extract_bug_number_from_description(self, issue):
description = None
if 'description' in issue:
# Getting the description for Rietveld
description = issue['description']
elif 'revisions' in issue:
# Getting the description for REST Gerrit
revision = issue['revisions'][issue['current_revision']]
description = revision['commit']['message']
bugs = []
if description:
matches = re.findall('BUG=(((\d+)(,\s?)?)+)', description)
if matches:
for match in matches:
bugs.extend(match[0].replace(' ', '').split(','))
return bugs
def process_rietveld_issue(self, remote, instance, issue): def process_rietveld_issue(self, remote, instance, issue):
ret = {} ret = {}
if self.options.deltas: if self.options.deltas:
@ -287,6 +309,9 @@ class MyActivity(object):
ret['created'] = datetime_from_rietveld(issue['created']) ret['created'] = datetime_from_rietveld(issue['created'])
ret['replies'] = self.process_rietveld_replies(issue['messages']) ret['replies'] = self.process_rietveld_replies(issue['messages'])
ret['bug'] = self.extract_bug_number_from_description(issue)
ret['landed_days_ago'] = issue['landed_days_ago']
return ret return ret
@staticmethod @staticmethod
@ -323,7 +348,8 @@ class MyActivity(object):
# Instantiate the generator to force all the requests now and catch the # Instantiate the generator to force all the requests now and catch the
# errors here. # errors here.
return list(gerrit_util.GenerateAllChanges(instance['url'], req, return list(gerrit_util.GenerateAllChanges(instance['url'], req,
o_params=['MESSAGES', 'LABELS', 'DETAILED_ACCOUNTS'])) o_params=['MESSAGES', 'LABELS', 'DETAILED_ACCOUNTS',
'CURRENT_REVISION', 'CURRENT_COMMIT']))
except gerrit_util.GerritError, e: except gerrit_util.GerritError, e:
logging.error('Looking up %r: %s', instance['url'], e) logging.error('Looking up %r: %s', instance['url'], e)
return [] return []
@ -374,6 +400,7 @@ class MyActivity(object):
ret['replies'] = [] ret['replies'] = []
ret['reviewers'] = set(r['author'] for r in ret['replies']) ret['reviewers'] = set(r['author'] for r in ret['replies'])
ret['reviewers'].discard(ret['author']) ret['reviewers'].discard(ret['author'])
ret['bug'] = self.extract_bug_number_from_description(issue)
return ret return ret
@staticmethod @staticmethod
@ -412,6 +439,7 @@ class MyActivity(object):
ret['replies'] = [] ret['replies'] = []
ret['reviewers'] = set(r['author'] for r in ret['replies']) ret['reviewers'] = set(r['author'] for r in ret['replies'])
ret['reviewers'].discard(ret['author']) ret['reviewers'].discard(ret['author'])
ret['bug'] = self.extract_bug_number_from_description(issue)
return ret return ret
@staticmethod @staticmethod
@ -455,15 +483,21 @@ class MyActivity(object):
if 'items' in content: if 'items' in content:
items = content['items'] items = content['items']
for item in items: for item in items:
if instance.get('shorturl'):
item_url = 'https://%s/%d' % (instance['shorturl'], item['id'])
else:
item_url = 'https://bugs.chromium.org/p/%s/issues/detail?id=%d' % (
instance['name'], item['id'])
issue = { issue = {
'header': item['title'], 'header': item['title'],
'created': dateutil.parser.parse(item['published']), 'created': dateutil.parser.parse(item['published']),
'modified': dateutil.parser.parse(item['updated']), 'modified': dateutil.parser.parse(item['updated']),
'author': item['author']['name'], 'author': item['author']['name'],
'url': 'https://code.google.com/p/%s/issues/detail?id=%s' % ( 'url': item_url,
instance['name'], item['id']),
'comments': [], 'comments': [],
'status': item['status'], 'status': item['status'],
'labels': [],
'components': []
} }
if 'shorturl' in instance: if 'shorturl' in instance:
issue['url'] = 'http://%s/%d' % (instance['shorturl'], item['id']) issue['url'] = 'http://%s/%d' % (instance['shorturl'], item['id'])
@ -474,6 +508,10 @@ class MyActivity(object):
issue['owner'] = 'None' issue['owner'] = 'None'
if issue['owner'] == user_str or issue['author'] == user_str: if issue['owner'] == user_str or issue['author'] == user_str:
issues.append(issue) issues.append(issue)
if 'labels' in item:
issue['labels'] = item['labels']
if 'components' in item:
issue['components'] = item['components']
return issues return issues
@ -634,6 +672,35 @@ class MyActivity(object):
self.print_reviews() self.print_reviews()
self.print_issues() self.print_issues()
def dump_json(self, ignore_keys=None):
if ignore_keys is None:
ignore_keys = ['replies']
def format_for_json_dump(in_array):
output = {}
for item in in_array:
url = item.get('url') or item.get('review_url')
if not url:
raise Exception('Dumped item %s does not specify url' % item)
output[url] = dict(
(k, v) for k,v in item.iteritems() if k not in ignore_keys)
return output
class PythonObjectEncoder(json.JSONEncoder):
def default(self, obj): # pylint: disable=method-hidden
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, set):
return list(obj)
return json.JSONEncoder.default(self, obj)
output = {
'reviews': format_for_json_dump(self.reviews),
'changes': format_for_json_dump(self.changes),
'issues': format_for_json_dump(self.issues)
}
print json.dumps(output, indent=2, cls=PythonObjectEncoder)
def main(): def main():
# Silence upload.py. # Silence upload.py.
@ -728,6 +795,9 @@ def main():
'-m', '--markdown', action='store_true', '-m', '--markdown', action='store_true',
help='Use markdown-friendly output (overrides --output-format ' help='Use markdown-friendly output (overrides --output-format '
'and --output-format-heading)') 'and --output-format-heading)')
output_format_group.add_option(
'-j', '--json', action='store_true',
help='Output json data (overrides other format options)')
parser.add_option_group(output_format_group) parser.add_option_group(output_format_group)
auth.add_auth_options(parser) auth.add_auth_options(parser)
@ -746,6 +816,9 @@ def main():
const=logging.ERROR, const=logging.ERROR,
help='Suppress non-error messages.' help='Suppress non-error messages.'
) )
parser.add_option(
'-o', '--output', metavar='<file>',
help='Where to output the results. By default prints to stdout.')
# Remove description formatting # Remove description formatting
parser.format_description = ( parser.format_description = (
@ -819,9 +892,27 @@ def main():
except auth.AuthenticationError as e: except auth.AuthenticationError as e:
logging.error('auth.AuthenticationError: %s', e) logging.error('auth.AuthenticationError: %s', e)
my_activity.print_changes() output_file = None
my_activity.print_reviews() try:
my_activity.print_issues() if options.output:
output_file = open(options.output, 'w')
logging.info('Printing output to "%s"', options.output)
sys.stdout = output_file
except (IOError, OSError) as e:
logging.error('Unable to write output: %s', e)
else:
if options.json:
my_activity.dump_json()
else:
my_activity.print_changes()
my_activity.print_reviews()
my_activity.print_issues()
finally:
if output_file:
logging.info('Done printing to file.')
sys.stdout = sys.__stdout__
output_file.close()
return 0 return 0

Loading…
Cancel
Save