##// END OF EJS Templates
teach github_stats about milestones...
MinRK -
Show More
@@ -1,5 +1,9 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Simple tools to query github.com and gather stats about issues.
2 """Simple tools to query github.com and gather stats about issues.
3
4 To generate a report for IPython 2.0, run:
5
6 python github_stats.py --milestone 2.0 --since-tag rel-1.0.0
3 """
7 """
4 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
5 # Imports
9 # Imports
@@ -7,14 +11,18 b''
7
11
8 from __future__ import print_function
12 from __future__ import print_function
9
13
14 import codecs
10 import json
15 import json
11 import re
16 import re
12 import sys
17 import sys
13
18
19 from argparse import ArgumentParser
14 from datetime import datetime, timedelta
20 from datetime import datetime, timedelta
15 from subprocess import check_output
21 from subprocess import check_output
16 from gh_api import get_paged_request, make_auth_header, get_pull_request, is_pull_request
22 from gh_api import (
17
23 get_paged_request, make_auth_header, get_pull_request, is_pull_request,
24 get_milestone_id, get_issues_list,
25 )
18 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
19 # Globals
27 # Globals
20 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
@@ -26,12 +34,6 b' PER_PAGE = 100'
26 # Functions
34 # Functions
27 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
28
36
29 def get_issues(project="ipython/ipython", state="closed", pulls=False):
30 """Get a list of the issues from the Github API."""
31 which = 'pulls' if pulls else 'issues'
32 url = "https://api.github.com/repos/%s/%s?state=%s&per_page=%i" % (project, which, state, PER_PAGE)
33 return get_paged_request(url, headers=make_auth_header())
34
35 def round_hour(dt):
37 def round_hour(dt):
36 return dt.replace(minute=0,second=0,microsecond=0)
38 return dt.replace(minute=0,second=0,microsecond=0)
37
39
@@ -42,7 +44,6 b' def _parse_datetime(s):'
42 else:
44 else:
43 return datetime.fromtimestamp(0)
45 return datetime.fromtimestamp(0)
44
46
45
46 def issues2dict(issues):
47 def issues2dict(issues):
47 """Convert a list of issues to a dict, keyed by issue number."""
48 """Convert a list of issues to a dict, keyed by issue number."""
48 idict = {}
49 idict = {}
@@ -63,7 +64,6 b' def split_pulls(all_issues, project="ipython/ipython"):'
63 return issues, pulls
64 return issues, pulls
64
65
65
66
66
67 def issues_closed_since(period=timedelta(days=365), project="ipython/ipython", pulls=False):
67 def issues_closed_since(period=timedelta(days=365), project="ipython/ipython", pulls=False):
68 """Get all issues closed since a particular point in time. period
68 """Get all issues closed since a particular point in time. period
69 can either be a datetime object, or a timedelta object. In the
69 can either be a datetime object, or a timedelta object. In the
@@ -114,23 +114,31 b' def report(issues, show_urls=False):'
114
114
115 if __name__ == "__main__":
115 if __name__ == "__main__":
116 # deal with unicode
116 # deal with unicode
117 import codecs
118 sys.stdout = codecs.getwriter('utf8')(sys.stdout)
117 sys.stdout = codecs.getwriter('utf8')(sys.stdout)
119
118
120 # Whether to add reST urls for all issues in printout.
119 # Whether to add reST urls for all issues in printout.
121 show_urls = True
120 show_urls = True
122
123 # By default, search one month back
124 tag = None
125 if len(sys.argv) > 1:
126 try:
127 days = int(sys.argv[1])
128 except:
129 tag = sys.argv[1]
130 else:
131 tag = check_output(['git', 'describe', '--abbrev=0']).strip()
132
121
133 if tag:
122 parser = ArgumentParser()
123 parser.add_argument('--since-tag', type=str,
124 help="The git tag to use for the starting point (typically the last major release)."
125 )
126 parser.add_argument('--milestone', type=str,
127 help="The GitHub milestone to use for filtering issues [optional]."
128 )
129 parser.add_argument('--days', type=int,
130 help="The number of days of data to summarize (use this or --since-tag)."
131 )
132
133 opts = parser.parse_args()
134 tag = opts.since_tag
135
136 # set `since` from days or git tag
137 if opts.days:
138 since = datetime.utcnow() - timedelta(days=opts.days)
139 else:
140 if not tag:
141 tag = check_output(['git', 'describe', '--abbrev=0']).strip()
134 cmd = ['git', 'log', '-1', '--format=%ai', tag]
142 cmd = ['git', 'log', '-1', '--format=%ai', tag]
135 tagday, tz = check_output(cmd).strip().rsplit(' ', 1)
143 tagday, tz = check_output(cmd).strip().rsplit(' ', 1)
136 since = datetime.strptime(tagday, "%Y-%m-%d %H:%M:%S")
144 since = datetime.strptime(tagday, "%Y-%m-%d %H:%M:%S")
@@ -141,16 +149,23 b' if __name__ == "__main__":'
141 since += td
149 since += td
142 else:
150 else:
143 since -= td
151 since -= td
144 else:
145 since = datetime.utcnow() - timedelta(days=days)
146
152
147 since = round_hour(since)
153 since = round_hour(since)
148
154
149 print("fetching GitHub stats since %s (tag: %s)" % (since, tag), file=sys.stderr)
155 milestone = opts.milestone
150 # turn off to play interactively without redownloading, use %run -i
156
151 if 1:
157 print("fetching GitHub stats since %s (tag: %s, milestone: %s)" % (since, tag, milestone), file=sys.stderr)
158 if milestone:
159 milestone_id = get_milestone_id("ipython/ipython", milestone,
160 auth=True)
161 issues = get_issues_list("ipython/ipython",
162 milestone=milestone_id,
163 state='closed',
164 auth=True,
165 )
166 else:
152 issues = issues_closed_since(since, pulls=False)
167 issues = issues_closed_since(since, pulls=False)
153 pulls = issues_closed_since(since, pulls=True)
168 pulls = issues_closed_since(since, pulls=True)
154
169
155 # For regular reports, it's nice to show them in reverse chronological order
170 # For regular reports, it's nice to show them in reverse chronological order
156 issues = sorted_by_field(issues, reverse=True)
171 issues = sorted_by_field(issues, reverse=True)
General Comments 0
You need to be logged in to leave comments. Login now