diff --git a/tools/github_stats.py b/tools/github_stats.py old mode 100644 new mode 100755 index 9854c95..dd4a54b --- a/tools/github_stats.py +++ b/tools/github_stats.py @@ -1,17 +1,40 @@ +#!/usr/bin/env python +"""Simple tools to query github.com and gather stats about issues. +""" +from __future__ import print_function + import json -from datetime import datetime, timedelta import sys + +from datetime import datetime, timedelta from urllib import urlopen + def get_issues(project="ipython/ipython/", state="open"): """Get a list of the issues from the Github API.""" - f = urlopen("http://github.com/api/v2/json/issues/list/%s%s" % (project, state)) + f = urlopen("http://github.com/api/v2/json/issues/list/%s%s" % (project, + state)) return json.load(f)['issues'] + def _parse_datetime(s): """Parse dates in the format returned by the Github API.""" return datetime.strptime(s.rpartition(" ")[0], "%Y/%m/%d %H:%M:%S") + +def issues2dict(issues): + """Convert a list of issues to a dict, keyed by issue number.""" + idict = {} + for i in issues: + idict[i['number']] = i + return idict + + +def is_pull_request(issue): + """Return True if the given issue is a pull request.""" + return 'pull_request_url' in issue + + def issues_closed_since(period=timedelta(days=365), project="ipython/ipython/"): """Get all issues closed since a particular point in time. period can either be a datetime object, or a timedelta object. In the @@ -21,11 +44,47 @@ latter case, it is used as a time before the present.""" period = datetime.now() - period return [i for i in allclosed if _parse_datetime(i['closed_at']) > period] + +def sorted_by_field(issues, field='closed_at', reverse=False): + """Return a list of issues sorted by closing date date.""" + return sorted(issues, key = lambda i:i[field], reverse=reverse) + + +def report(issues, show_urls=False): + """Summary report about a list of issues, printing number and title. + """ + # titles may have unicode in them, so we must encode everything below + if show_urls: + for i in issues: + print('* `%d <%s>`_: %s' % (i['number'], + i['html_url'].encode('utf-8'), + i['title'].encode('utf-8'))) + else: + for i in issues: + print('* %d: %s' % (i['number'], i['title'].encode('utf-8'))) + + if __name__ == "__main__": - # Demo + # Demo, search one year back + show_urls = True if len(sys.argv) > 1: days = int(sys.argv[1]) else: days = 365 - n = len(issues_closed_since(timedelta(days=days))) - print "%d issues closed in the last %d days." % (n, days) + + if 1: + issues = sorted_by_field(issues_closed_since(timedelta(days=days)), + reverse=True) + + pulls = filter(is_pull_request, issues) + regular = filter(lambda i: not is_pull_request(i), issues) + n = len(issues) + + print("%d total issues closed in the last %d days." % (n, days)) + print("%d pull requests and %d regular issues." % (len(pulls), len(regular))) + print() + print('Pull requests (%d):\n' % len(pulls)) + report(pulls, show_urls) + print() + print('Regular issues (%d):\n' % len(regular)) + report(regular, show_urls)