Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0bddc7b

Browse files
QuLogicksunden
authored andcommitted
Clean up GitHub statistics script
This clears up the flake8 exception, and corrects the title and trailing whitespace that I always need to clean up when committing the file.
1 parent 0c8598c commit 0bddc7b

File tree

2 files changed

+137
-105
lines changed

2 files changed

+137
-105
lines changed

.flake8

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ exclude =
3636
doc/tutorials
3737
# External files.
3838
tools/gh_api.py
39-
tools/github_stats.py
4039
.tox
4140
.eggs
4241

tools/github_stats.py

Lines changed: 137 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env python
22
"""Simple tools to query github.com and gather stats about issues.
33
4-
To generate a report for IPython 2.0, run:
4+
To generate a report for Matplotlib 3.0.0, run:
55
6-
python github_stats.py --milestone 2.0 --since-tag rel-1.0.0
6+
python github_stats.py --milestone 3.0.0 --since-tag v2.0.0
77
"""
8-
#-----------------------------------------------------------------------------
8+
# -----------------------------------------------------------------------------
99
# Imports
10-
#-----------------------------------------------------------------------------
10+
# -----------------------------------------------------------------------------
1111

1212
import sys
1313

@@ -19,33 +19,72 @@
1919
get_paged_request, make_auth_header, get_pull_request, is_pull_request,
2020
get_milestone_id, get_issues_list, get_authors,
2121
)
22-
#-----------------------------------------------------------------------------
22+
# -----------------------------------------------------------------------------
2323
# Globals
24-
#-----------------------------------------------------------------------------
24+
# -----------------------------------------------------------------------------
2525

2626
ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
2727
PER_PAGE = 100
2828

29-
#-----------------------------------------------------------------------------
29+
REPORT_TEMPLATE = """\
30+
.. _github-stats:
31+
32+
{title}
33+
{title_underline}
34+
35+
GitHub statistics for {since_day} (tag: {tag}) - {today}
36+
37+
These lists are automatically generated, and may be incomplete or contain duplicates.
38+
39+
We closed {n_issues} issues and merged {n_pulls} pull requests.
40+
{milestone}
41+
The following {nauthors} authors contributed {ncommits} commits.
42+
43+
{unique_authors}
44+
{links}
45+
46+
Previous GitHub statistics
47+
--------------------------
48+
49+
.. toctree::
50+
:maxdepth: 1
51+
:glob:
52+
:reversed:
53+
54+
prev_whats_new/github_stats_*"""
55+
MILESTONE_TEMPLATE = (
56+
'The full list can be seen `on GitHub '
57+
'<https://github.com/{project}/milestone/{milestone_id}?closed=1>`__\n')
58+
LINKS_TEMPLATE = """
59+
GitHub issues and pull requests:
60+
61+
Pull Requests ({n_pulls}):
62+
63+
{pull_request_report}
64+
65+
Issues ({n_issues}):
66+
67+
{issue_report}
68+
"""
69+
70+
# -----------------------------------------------------------------------------
3071
# Functions
31-
#-----------------------------------------------------------------------------
72+
# -----------------------------------------------------------------------------
73+
3274

3375
def round_hour(dt):
34-
return dt.replace(minute=0,second=0,microsecond=0)
76+
return dt.replace(minute=0, second=0, microsecond=0)
77+
3578

3679
def _parse_datetime(s):
3780
"""Parse dates in the format returned by the GitHub API."""
38-
if s:
39-
return datetime.strptime(s, ISO8601)
40-
else:
41-
return datetime.fromtimestamp(0)
81+
return datetime.strptime(s, ISO8601) if s else datetime.fromtimestamp(0)
82+
4283

4384
def issues2dict(issues):
4485
"""Convert a list of issues to a dict, keyed by issue number."""
45-
idict = {}
46-
for i in issues:
47-
idict[i['number']] = i
48-
return idict
86+
return {i['number']: i for i in issues}
87+
4988

5089
def split_pulls(all_issues, project="matplotlib/matplotlib"):
5190
"""Split a list of closed issues into non-PR Issues and Pull Requests."""
@@ -60,9 +99,12 @@ def split_pulls(all_issues, project="matplotlib/matplotlib"):
6099
return issues, pulls
61100

62101

63-
def issues_closed_since(period=timedelta(days=365), project="matplotlib/matplotlib", pulls=False):
64-
"""Get all issues closed since a particular point in time. period
65-
can either be a datetime object, or a timedelta object. In the
102+
def issues_closed_since(period=timedelta(days=365),
103+
project='matplotlib/matplotlib', pulls=False):
104+
"""
105+
Get all issues closed since a particular point in time.
106+
107+
*period* can either be a datetime object, or a timedelta object. In the
66108
latter case, it is used as a time before the present.
67109
"""
68110

@@ -72,60 +114,73 @@ def issues_closed_since(period=timedelta(days=365), project="matplotlib/matplotl
72114
since = round_hour(datetime.utcnow() - period)
73115
else:
74116
since = period
75-
url = "https://api.github.com/repos/%s/%s?state=closed&sort=updated&since=%s&per_page=%i" % (project, which, since.strftime(ISO8601), PER_PAGE)
117+
url = (
118+
f'https://api.github.com/repos/{project}/{which}'
119+
f'?state=closed'
120+
f'&sort=updated'
121+
f'&since={since.strftime(ISO8601)}'
122+
f'&per_page={PER_PAGE}')
76123
allclosed = get_paged_request(url, headers=make_auth_header())
77124

78-
filtered = [ i for i in allclosed if _parse_datetime(i['closed_at']) > since ]
125+
filtered = (i for i in allclosed
126+
if _parse_datetime(i['closed_at']) > since)
79127
if pulls:
80-
filtered = [ i for i in filtered if _parse_datetime(i['merged_at']) > since ]
128+
filtered = (i for i in filtered
129+
if _parse_datetime(i['merged_at']) > since)
81130
# filter out PRs not against main (backports)
82-
filtered = [ i for i in filtered if i['base']['ref'] == 'main' ]
131+
filtered = (i for i in filtered if i['base']['ref'] == 'main')
83132
else:
84-
filtered = [ i for i in filtered if not is_pull_request(i) ]
133+
filtered = (i for i in filtered if not is_pull_request(i))
85134

86-
return filtered
135+
return list(filtered)
87136

88137

89138
def sorted_by_field(issues, field='closed_at', reverse=False):
90139
"""Return a list of issues sorted by closing date date."""
91-
return sorted(issues, key = lambda i:i[field], reverse=reverse)
140+
return sorted(issues, key=lambda i: i[field], reverse=reverse)
92141

93142

94143
def report(issues, show_urls=False):
95144
"""Summary report about a list of issues, printing number and title."""
145+
lines = []
96146
if show_urls:
97147
for i in issues:
98148
role = 'ghpull' if 'merged_at' in i else 'ghissue'
99-
print('* :%s:`%d`: %s' % (role, i['number'],
100-
i['title'].replace('`', '``')))
149+
number = i['number']
150+
title = i['title'].replace('`', '``').strip()
151+
lines.append(f'* :{role}:`{number}`: {title}')
101152
else:
102153
for i in issues:
103-
print('* %d: %s' % (i['number'], i['title'].replace('`', '``')))
154+
number = i['number']
155+
title = i['title'].replace('`', '``').strip()
156+
lines.append('* {number}: {title}')
157+
return '\n'.join(lines)
104158

105-
#-----------------------------------------------------------------------------
159+
# -----------------------------------------------------------------------------
106160
# Main script
107-
#-----------------------------------------------------------------------------
161+
# -----------------------------------------------------------------------------
108162

109163
if __name__ == "__main__":
110164
# Whether to add reST urls for all issues in printout.
111165
show_urls = True
112166

113167
parser = ArgumentParser()
114-
parser.add_argument('--since-tag', type=str,
115-
help="The git tag to use for the starting point (typically the last major release)."
116-
)
117-
parser.add_argument('--milestone', type=str,
118-
help="The GitHub milestone to use for filtering issues [optional]."
119-
)
120-
parser.add_argument('--days', type=int,
121-
help="The number of days of data to summarize (use this or --since-tag)."
122-
)
123-
parser.add_argument('--project', type=str, default="matplotlib/matplotlib",
124-
help="The project to summarize."
125-
)
126-
parser.add_argument('--links', action='store_true', default=False,
127-
help="Include links to all closed Issues and PRs in the output."
128-
)
168+
parser.add_argument(
169+
'--since-tag', type=str,
170+
help='The git tag to use for the starting point '
171+
'(typically the last major release).')
172+
parser.add_argument(
173+
'--milestone', type=str,
174+
help='The GitHub milestone to use for filtering issues [optional].')
175+
parser.add_argument(
176+
'--days', type=int,
177+
help='The number of days of data to summarize (use this or --since-tag).')
178+
parser.add_argument(
179+
'--project', type=str, default='matplotlib/matplotlib',
180+
help='The project to summarize.')
181+
parser.add_argument(
182+
'--links', action='store_true', default=False,
183+
help='Include links to all closed Issues and PRs in the output.')
129184

130185
opts = parser.parse_args()
131186
tag = opts.since_tag
@@ -135,9 +190,10 @@ def report(issues, show_urls=False):
135190
since = datetime.utcnow() - timedelta(days=opts.days)
136191
else:
137192
if not tag:
138-
tag = check_output(['git', 'describe', '--abbrev=0']).strip().decode('utf8')
193+
tag = check_output(['git', 'describe', '--abbrev=0'],
194+
encoding='utf8').strip()
139195
cmd = ['git', 'log', '-1', '--format=%ai', tag]
140-
tagday, tz = check_output(cmd).strip().decode('utf8').rsplit(' ', 1)
196+
tagday, tz = check_output(cmd, encoding='utf8').strip().rsplit(' ', 1)
141197
since = datetime.strptime(tagday, "%Y-%m-%d %H:%M:%S")
142198
h = int(tz[1:3])
143199
m = int(tz[3:])
@@ -152,21 +208,19 @@ def report(issues, show_urls=False):
152208
milestone = opts.milestone
153209
project = opts.project
154210

155-
print("fetching GitHub stats since %s (tag: %s, milestone: %s)" % (since, tag, milestone), file=sys.stderr)
211+
print(f'fetching GitHub stats since {since} (tag: {tag}, milestone: {milestone})',
212+
file=sys.stderr)
156213
if milestone:
157214
milestone_id = get_milestone_id(project=project, milestone=milestone,
158-
auth=True)
159-
issues_and_pulls = get_issues_list(project=project,
160-
milestone=milestone_id,
161-
state='closed',
162-
auth=True,
163-
)
215+
auth=True)
216+
issues_and_pulls = get_issues_list(project=project, milestone=milestone_id,
217+
state='closed', auth=True)
164218
issues, pulls = split_pulls(issues_and_pulls, project=project)
165219
else:
166220
issues = issues_closed_since(since, project=project, pulls=False)
167221
pulls = issues_closed_since(since, project=project, pulls=True)
168222

169-
# For regular reports, it's nice to show them in reverse chronological order
223+
# For regular reports, it's nice to show them in reverse chronological order.
170224
issues = sorted_by_field(issues, reverse=True)
171225
pulls = sorted_by_field(pulls, reverse=True)
172226

@@ -175,71 +229,50 @@ def report(issues, show_urls=False):
175229
since_day = since.strftime("%Y/%m/%d")
176230
today = datetime.today()
177231

178-
# Print summary report we can directly include into release notes.
179-
print('.. _github-stats:')
180-
print()
181-
title = 'GitHub statistics ' + today.strftime('(%b %d, %Y)')
182-
print(title)
183-
print('=' * len(title))
184-
185-
print()
186-
print("GitHub statistics for %s (tag: %s) - %s" % (since_day, tag, today.strftime("%Y/%m/%d"), ))
187-
print()
188-
print("These lists are automatically generated, and may be incomplete or contain duplicates.")
189-
print()
232+
title = (f'GitHub statistics for {milestone.lstrip("v")} '
233+
f'{today.strftime("(%b %d, %Y)")}')
190234

191235
ncommits = 0
192236
all_authors = []
193237
if tag:
194238
# print git info, in addition to GitHub info:
195-
since_tag = tag+'..'
239+
since_tag = f'{tag}..'
196240
cmd = ['git', 'log', '--oneline', since_tag]
197241
ncommits += len(check_output(cmd).splitlines())
198242

199-
author_cmd = ['git', 'log', '--use-mailmap', "--format=* %aN", since_tag]
200-
all_authors.extend(check_output(author_cmd).decode('utf-8', 'replace').splitlines())
243+
author_cmd = ['git', 'log', '--use-mailmap', '--format=* %aN', since_tag]
244+
all_authors.extend(
245+
check_output(author_cmd, encoding='utf-8', errors='replace').splitlines())
201246

202247
pr_authors = []
203248
for pr in pulls:
204249
pr_authors.extend(get_authors(pr))
205250
ncommits = len(pr_authors) + ncommits - len(pulls)
206251
author_cmd = ['git', 'check-mailmap'] + pr_authors
207-
with_email = check_output(author_cmd).decode('utf-8', 'replace').splitlines()
252+
with_email = check_output(author_cmd,
253+
encoding='utf-8', errors='replace').splitlines()
208254
all_authors.extend(['* ' + a.split(' <')[0] for a in with_email])
209255
unique_authors = sorted(set(all_authors), key=lambda s: s.lower())
210256

211-
print("We closed %d issues and merged %d pull requests." % (n_issues, n_pulls))
212257
if milestone:
213-
print("The full list can be seen `on GitHub <https://github.com/%s/milestone/%s?closed=1>`__"
214-
% (project, milestone_id)
215-
)
216-
217-
print()
218-
print("The following %i authors contributed %i commits." % (len(unique_authors), ncommits))
219-
print()
220-
print('\n'.join(unique_authors))
258+
milestone_str = MILESTONE_TEMPLATE.format(project=project,
259+
milestone_id=milestone_id)
260+
else:
261+
milestone_str = ''
221262

222263
if opts.links:
223-
print()
224-
print("GitHub issues and pull requests:")
225-
print()
226-
print('Pull Requests (%d):\n' % n_pulls)
227-
report(pulls, show_urls)
228-
print()
229-
print('Issues (%d):\n' % n_issues)
230-
report(issues, show_urls)
231-
print()
232-
print()
233-
print("""\
234-
Previous GitHub statistics
235-
--------------------------
236-
237-
238-
.. toctree::
239-
:maxdepth: 1
240-
:glob:
241-
:reversed:
242-
243-
prev_whats_new/github_stats_*
264+
links = LINKS_TEMPLATE.format(n_pulls=n_pulls,
265+
pull_request_report=report(pulls, show_urls),
266+
n_issues=n_issues,
267+
issue_report=report(issues, show_urls))
268+
else:
269+
links = ''
244270

245-
""")
271+
# Print summary report we can directly include into release notes.
272+
print(REPORT_TEMPLATE.format(title=title, title_underline='=' * len(title),
273+
since_day=since_day, tag=tag,
274+
today=today.strftime('%Y/%m/%d'),
275+
n_issues=n_issues, n_pulls=n_pulls,
276+
milestone=milestone_str,
277+
nauthors=len(unique_authors), ncommits=ncommits,
278+
unique_authors='\n'.join(unique_authors), links=links))

0 commit comments

Comments
 (0)