##// END OF EJS Templates
fix backward n_pulls, n_issues in github stats
Min RK -
Show More
@@ -1,148 +1,149 b''
1 1 A. J. Holyoake <a.j.holyoake@gmail.com> ajholyoake <a.j.holyoake@gmail.com>
2 2 Aaron Culich <aculich@gmail.com> Aaron Culich <aculich@eecs.berkeley.edu>
3 3 Aron Ahmadia <aron@ahmadia.net> ahmadia <aron@ahmadia.net>
4 4 Benjamin Ragan-Kelley <benjaminrk@gmail.com> <minrk@Mercury.local>
5 5 Benjamin Ragan-Kelley <benjaminrk@gmail.com> Min RK
6 6 Benjamin Ragan-Kelley <benjaminrk@gmail.com> MinRK <benjaminrk@gmail.com>
7 7 Barry Wark <barrywark@gmail.com> Barry Wark <barrywarkatgmaildotcom>
8 8 Ben Edwards <bedwards@cs.unm.edu> Ben Edwards <bedwards@sausage.(none)>
9 9 Bradley M. Froehle <brad.froehle@gmail.com> Bradley M. Froehle <bfroehle@math.berkeley.edu>
10 10 Bradley M. Froehle <brad.froehle@gmail.com> Bradley Froehle <brad.froehle@gmail.com>
11 11 Brandon Parsons <brandon@parsonstx.com> Brandon Parsons <brandon.parsons@hp.com>
12 12 Brian E. Granger <ellisonbg@gmail.com> Brian Granger
13 13 Brian E. Granger <ellisonbg@gmail.com> Brian Granger <>
14 14 Brian E. Granger <ellisonbg@gmail.com> bgranger <>
15 15 Brian E. Granger <ellisonbg@gmail.com> bgranger <bgranger@red>
16 16 Christoph Gohlke <cgohlke@uci.edu> cgohlke <cgohlke@uci.edu>
17 17 Cyrille Rossant <cyrille.rossant@gmail.com> rossant <rossant@github>
18 18 DamiΓ‘n Avila <damianavila82@yahoo.com.ar> damianavila <damianavila82@yahoo.com.ar>
19 19 DamiΓ‘n Avila <damianavila82@yahoo.com.ar> damianavila <damianavila@gmail.com>
20 20 Damon Allen <damontallen@gmail.com> damontallen <damontallen@gmail.com>
21 21 Darren Dale <dsdale24@gmail.com> darren.dale <>
22 22 Darren Dale <dsdale24@gmail.com> Darren Dale <>
23 23 Dav Clark <davclark@berkeley.edu> Dav Clark <>
24 24 Dav Clark <davclark@berkeley.edu> Dav Clark <davclark@gmail.com>
25 25 David Hirschfeld <david.hirschfeld@gazprom-mt.com> dhirschfeld <david.hirschfeld@gazprom-mt.com>
26 26 David P. Sanders <dpsanders@gmail.com> David P. Sanders <dpsanders@ciencias.unam.mx>
27 27 David Warde-Farley <wardefar@iro.umontreal.ca> David Warde-Farley <>
28 28 Doug Blank <dblank@cs.brynmawr.edu> Doug Blank <doug.blank@gmail.com>
29 29 Eugene Van den Bulke <eugene.van-den-bulke@gmail.com> Eugene Van den Bulke <eugene.vandenbulke@gmail.com>
30 30 Evan Patterson <epatters@enthought.com> <epatters@EPattersons-MacBook-Pro.local>
31 31 Evan Patterson <epatters@enthought.com> <epatters@evan-laptop.localdomain>
32 32 Evan Patterson <epatters@enthought.com> <epatters@caltech.edu>
33 33 Evan Patterson <epatters@enthought.com> <ejpatters@gmail.com>
34 34 Evan Patterson <epatters@enthought.com> epatters <ejpatters@gmail.com>
35 35 Evan Patterson <epatters@enthought.com> epatters <epatters@enthought.com>
36 36 Ernie French <ernestfrench@gmail.com> Ernie French <ernie@gqpbj.com>
37 37 Ernie French <ernestfrench@gmail.com> ernie french <ernestfrench@gmail.com>
38 38 Ernie French <ernestfrench@gmail.com> ernop <ernestfrench@gmail.com>
39 39 Fernando Perez <Fernando.Perez@berkeley.edu> <fperez.net@gmail.com>
40 40 Fernando Perez <Fernando.Perez@berkeley.edu> Fernando Perez <fernando.perez@berkeley.edu>
41 41 Fernando Perez <Fernando.Perez@berkeley.edu> fperez <>
42 42 Fernando Perez <Fernando.Perez@berkeley.edu> fptest <>
43 43 Fernando Perez <Fernando.Perez@berkeley.edu> fptest1 <>
44 44 Fernando Perez <Fernando.Perez@berkeley.edu> Fernando Perez <fernando.perez@berkeley.edu>
45 45 Fernando Perez <fernando.perez@berkeley.edu> Fernando Perez <>
46 46 Fernando Perez <fernando.perez@berkeley.edu> Fernando Perez <fperez@maqroll>
47 47 Frank Murphy <fpmurphy@mtu.edu> Frank Murphy <fmurphy@arbor.net>
48 48 Gabriel Becker <gmbecker@ucdavis.edu> gmbecker <gmbecker@ucdavis.edu>
49 49 Gael Varoquaux <gael.varoquaux@normalesup.org> gael.varoquaux <>
50 50 Gael Varoquaux <gael.varoquaux@normalesup.org> gvaroquaux <gvaroquaux@gvaroquaux-desktop>
51 51 Gael Varoquaux <gael.varoquaux@normalesup.org> Gael Varoquaux <>
52 52 Ingolf Becker <ingolf.becker@googlemail.com> watercrossing <ingolf.becker@googlemail.com>
53 53 Jake Vanderplas <jakevdp@gmail.com> Jake Vanderplas <vanderplas@astro.washington.edu>
54 54 Jakob Gager <jakob.gager@gmail.com> jakobgager <jakob.gager@gmail.com>
55 55 Jakob Gager <jakob.gager@gmail.com> jakobgager <gager@ilsb.tuwien.ac.at>
56 56 Jakob Gager <jakob.gager@gmail.com> jakobgager <jakobgager@hotmail.com>
57 57 Jason Grout <jgrout6@bloomberg.net> <jason.grout@drake.edu>
58 58 Jason Grout <jgrout6@bloomberg.net> <jason-github@creativetrax.com>
59 59 Jason Gors <jason.gors.work@gmail.com> jason gors <jason.gors.work@gmail.com>
60 60 Jason Gors <jason.gors.work@gmail.com> jgors <jason.gors.work@gmail.com>
61 61 Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens Hedegaard Nielsen <jhn@jhn-Znote.(none)>
62 62 Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens H Nielsen <jenshnielsen@gmail.com>
63 63 Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens H. Nielsen <jenshnielsen@gmail.com>
64 64 Jez Ng <jezreel@gmail.com> Jez Ng <me@jezng.com>
65 65 Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <jonathan@LifebookMint.(none)>
66 66 Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <jon.freder@gmail.com>
67 67 Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <xh3xx.goose@gmail.com>
68 68 Jonathan Frederic <jdfreder@calpoly.edu> jon <jon.freder@gmail.com>
69 69 Jonathan Frederic <jdfreder@calpoly.edu> U-Jon-PC\Jon <Jon@Jon-PC.(none)>
70 70 Jonathan March <jmarch@enthought.com> Jonathan March <JDM@MarchRay.net>
71 71 Jonathan March <jmarch@enthought.com> jdmarch <JDM@marchRay.net>
72 72 JΓΆrgen Stenarson <jorgen.stenarson@kroywen.se> JΓΆrgen Stenarson <jorgen.stenarson@bostream.nu>
73 73 JΓΆrgen Stenarson <jorgen.stenarson@kroywen.se> Jorgen Stenarson <jorgen.stenarson@bostream.nu>
74 74 JΓΆrgen Stenarson <jorgen.stenarson@kroywen.se> Jorgen Stenarson <>
75 75 JΓΆrgen Stenarson <jorgen.stenarson@kroywen.se> jstenar <jorgen.stenarson@bostream.nu>
76 76 JΓΆrgen Stenarson <jorgen.stenarson@kroywen.se> jstenar <>
77 77 JΓΆrgen Stenarson <jorgen.stenarson@kroywen.se> Jârgen Stenarson <jorgen.stenarson@kroywen.se>
78 78 Juergen Hasch <python@elbonia.de> juhasch <python@elbonia.de>
79 79 Juergen Hasch <python@elbonia.de> juhasch <hasch@VMBOX.fritz.box>
80 80 Julia Evans <julia@jvns.ca> Julia Evans <julia@stripe.com>
81 81 Kester Tong <kestert@google.com> KesterTong <kestert@google.com>
82 82 Kyle Kelley <rgbkrk@gmail.com> Kyle Kelley <kyle.kelley@rackspace.com>
83 83 Kyle Kelley <rgbkrk@gmail.com> rgbkrk <rgbkrk@gmail.com>
84 84 Laurent DufrΓ©chou <laurent.dufrechou@gmail.com> <laurent.dufrechou@gmail.com>
85 85 Laurent DufrΓ©chou <laurent.dufrechou@gmail.com> <laurent@Pep>
86 86 Laurent DufrΓ©chou <laurent.dufrechou@gmail.com> laurent dufrechou <>
87 87 Laurent DufrΓ©chou <laurent.dufrechou@gmail.com> laurent.dufrechou <>
88 88 Laurent DufrΓ©chou <laurent.dufrechou@gmail.com> Laurent Dufrechou <>
89 89 Laurent DufrΓ©chou <laurent.dufrechou@gmail.com> laurent.dufrechou@gmail.com <>
90 90 Laurent DufrΓ©chou <laurent.dufrechou@gmail.com> ldufrechou <ldufrechou@PEP>
91 Lorena Pantano <lorena.pantano@gmail.com> Lorena <lorena.pantano@gmail.com>
91 92 Luis Pedro Coelho <luis@luispedro.org> Luis Pedro Coelho <lpc@cmu.edu>
92 93 Marc Molla <marcmolla@gmail.com> marcmolla <marcmolla@gmail.com>
93 94 MartΓ­n GaitΓ‘n <gaitan@gmail.com> MartΓ­n GaitΓ‘n <gaitan@phasety.com>
94 95 Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias BUSSONNIER <bussonniermatthias@gmail.com>
95 96 Matthias Bussonnier <bussonniermatthias@gmail.com> Bussonnier Matthias <bussonniermatthias@gmail.com>
96 97 Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias BUSSONNIER <bussonniermatthias@umr168-curn-1-24x-6561.curie.fr>
97 98 Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias Bussonnier <carreau@Aspire.(none)>
98 99 Michael Droettboom <mdboom@gmail.com> Michael Droettboom <mdroe@stsci.edu>
99 100 Nicholas Bollweg <nick.bollweg@gmail.com> Nicholas Bollweg (Nick) <nick.bollweg@gmail.com>
100 101 Nicolas Rougier <Nicolas.Rougier@inria.fr> <Nicolas.rougier@inria.fr>
101 102 Nikolay Koldunov <koldunovn@gmail.com> Nikolay Koldunov <nikolay.koldunov@zmaw.de>
102 103 Omar AndrΓ©s Zapata Mesa <andresete.chaos@gmail.com> Omar Andres Zapata Mesa <andresete.chaos@gmail.com>
103 104 Omar AndrΓ©s Zapata Mesa <andresete.chaos@gmail.com> Omar Andres Zapata Mesa <omazapa@tuxhome>
104 105 Pankaj Pandey <pankaj86@gmail.com> Pankaj Pandey <pankaj@enthought.com>
105 106 Pascal Schetelat <pascal.schetelat@gmail.com> pascal-schetelat <pascal.schetelat@gmail.com>
106 107 Paul Ivanov <pi@berkeley.edu> Paul Ivanov <pivanov314@gmail.com>
107 108 Pauli Virtanen <pauli.virtanen@iki.fi> Pauli Virtanen <>
108 109 Pauli Virtanen <pauli.virtanen@iki.fi> Pauli Virtanen <pav@iki.fi>
109 110 Pierre Gerold <pierre.gerold@laposte.net> Pierre Gerold <gerold@crans.org>
110 111 Pietro Berkes <pberkes@enthought.com> Pietro Berkes <pietro.berkes@googlemail.com>
111 112 Piti Ongmongkolkul <piti118@gmail.com> piti118 <piti118@gmail.com>
112 113 Prabhu Ramachandran <prabhu@enthought.com> Prabhu Ramachandran <>
113 114 Puneeth Chaganti <punchagan@gmail.com> Puneeth Chaganti <punchagan@muse-amuse.in>
114 115 Robert Kern <robert.kern@gmail.com> rkern <>
115 116 Robert Kern <robert.kern@gmail.com> Robert Kern <rkern@enthought.com>
116 117 Robert Kern <robert.kern@gmail.com> Robert Kern <rkern@Sacrilege.local>
117 118 Robert Kern <robert.kern@gmail.com> Robert Kern <>
118 119 Robert Marchman <bo.marchman@gmail.com> Robert Marchman <robert.l.marchman@dartmouth.edu>
119 120 Satrajit Ghosh <satra@mit.edu> Satrajit Ghosh <satra@ba5.mit.edu>
120 121 Satrajit Ghosh <satra@mit.edu> Satrajit Ghosh <satrajit.ghosh@gmail.com>
121 122 Scott Sanderson <scoutoss@gmail.com> Scott Sanderson <ssanderson@quantopian.com>
122 123 smithj1 <smithj1@LMC-022896.local> smithj1 <smithj1@LMC-022896.swisscom.com>
123 124 smithj1 <smithj1@LMC-022896.local> smithj1 <smithj1@lmc-022896.local>
124 125 Steven Johnson <steven.johnson@drake.edu> stevenJohnson <steven.johnson@drake.edu>
125 126 Steven Silvester <steven.silvester@ieee.org> blink1073 <steven.silvester@ieee.org>
126 127 S. Weber <s8weber@c4.usr.sh> s8weber <s8weber@c5.usr.sh>
127 128 Stefan van der Walt <stefan@sun.ac.za> Stefan van der Walt <bzr@mentat.za.net>
128 129 Silvia Vinyes <silvia.vinyes@gmail.com> Silvia <silvia@silvia-U44SG.(none)>
129 130 Silvia Vinyes <silvia.vinyes@gmail.com> silviav12 <silvia.vinyes@gmail.com>
130 131 Sylvain Corlay <scorlay@bloomberg.net> <sylvain.corlay@gmail.com>
131 132 Sylvain Corlay <scorlay@bloomberg.net> sylvain.corlay <sylvain.corlay@gmail.com>
132 133 Ted Drain <ted.drain@gmail.com> TD22057 <ted.drain@gmail.com>
133 134 ThΓ©ophile Studer <theo.studer@gmail.com> ThΓ©ophile Studer <studer@users.noreply.github.com>
134 135 Thomas Kluyver <takowl@gmail.com> Thomas <takowl@gmail.com>
135 136 Thomas Spura <tomspur@fedoraproject.org> Thomas Spura <thomas.spura@gmail.com>
136 137 Timo Paulssen <timonator@perpetuum-immobile.de> timo <timonator@perpetuum-immobile.de>
137 138 vds <vds@VIVIAN> vds2212 <vds2212@VIVIAN>
138 139 vds <vds@VIVIAN> vds <vds@vivian>
139 140 Ville M. Vainio <vivainio@gmail.com> <vivainio2@WN-W0941>
140 141 Ville M. Vainio <vivainio@gmail.com> ville <ville@VILLE-PC>
141 142 Ville M. Vainio <vivainio@gmail.com> ville <ville@ville-desktop>
142 143 Ville M. Vainio <vivainio@gmail.com> vivainio <>
143 144 Ville M. Vainio <vivainio@gmail.com> Ville M. Vainio <vivainio@villev>
144 145 Ville M. Vainio <vivainio@gmail.com> Ville M. Vainio <vivainio@ville_vmw>
145 146 Walter Doerwald <walter@livinglogic.de> walter.doerwald <>
146 147 Walter Doerwald <walter@livinglogic.de> Walter Doerwald <>
147 148 W. Trevor King <wking@tremily.us> W. Trevor King <wking@drexel.edu>
148 149 Yoval P. <yoval@gmx.com> y-p <yoval@gmx.com>
@@ -1,232 +1,232 b''
1 1 #!/usr/bin/env python
2 2 """Simple tools to query github.com and gather stats about issues.
3 3
4 4 To generate a report for IPython 2.0, run:
5 5
6 6 python github_stats.py --milestone 2.0 --since-tag rel-1.0.0
7 7 """
8 8 #-----------------------------------------------------------------------------
9 9 # Imports
10 10 #-----------------------------------------------------------------------------
11 11
12 12 from __future__ import print_function
13 13
14 14 import codecs
15 15 import sys
16 16
17 17 from argparse import ArgumentParser
18 18 from datetime import datetime, timedelta
19 19 from subprocess import check_output
20 20
21 21 from gh_api import (
22 22 get_paged_request, make_auth_header, get_pull_request, is_pull_request,
23 23 get_milestone_id, get_issues_list, get_authors,
24 24 )
25 25 #-----------------------------------------------------------------------------
26 26 # Globals
27 27 #-----------------------------------------------------------------------------
28 28
29 29 ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
30 30 PER_PAGE = 100
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Functions
34 34 #-----------------------------------------------------------------------------
35 35
36 36 def round_hour(dt):
37 37 return dt.replace(minute=0,second=0,microsecond=0)
38 38
39 39 def _parse_datetime(s):
40 40 """Parse dates in the format returned by the Github API."""
41 41 if s:
42 42 return datetime.strptime(s, ISO8601)
43 43 else:
44 44 return datetime.fromtimestamp(0)
45 45
46 46 def issues2dict(issues):
47 47 """Convert a list of issues to a dict, keyed by issue number."""
48 48 idict = {}
49 49 for i in issues:
50 50 idict[i['number']] = i
51 51 return idict
52 52
53 53 def split_pulls(all_issues, project="ipython/ipython"):
54 54 """split a list of closed issues into non-PR Issues and Pull Requests"""
55 55 pulls = []
56 56 issues = []
57 57 for i in all_issues:
58 58 if is_pull_request(i):
59 59 pull = get_pull_request(project, i['number'], auth=True)
60 60 pulls.append(pull)
61 61 else:
62 62 issues.append(i)
63 63 return issues, pulls
64 64
65 65
66 66 def issues_closed_since(period=timedelta(days=365), project="ipython/ipython", pulls=False):
67 67 """Get all issues closed since a particular point in time. period
68 68 can either be a datetime object, or a timedelta object. In the
69 69 latter case, it is used as a time before the present.
70 70 """
71 71
72 72 which = 'pulls' if pulls else 'issues'
73 73
74 74 if isinstance(period, timedelta):
75 75 since = round_hour(datetime.utcnow() - period)
76 76 else:
77 77 since = period
78 78 url = "https://api.github.com/repos/%s/%s?state=closed&sort=updated&since=%s&per_page=%i" % (project, which, since.strftime(ISO8601), PER_PAGE)
79 79 allclosed = get_paged_request(url, headers=make_auth_header())
80 80
81 81 filtered = [ i for i in allclosed if _parse_datetime(i['closed_at']) > since ]
82 82 if pulls:
83 83 filtered = [ i for i in filtered if _parse_datetime(i['merged_at']) > since ]
84 84 # filter out PRs not against master (backports)
85 85 filtered = [ i for i in filtered if i['base']['ref'] == 'master' ]
86 86 else:
87 87 filtered = [ i for i in filtered if not is_pull_request(i) ]
88 88
89 89 return filtered
90 90
91 91
92 92 def sorted_by_field(issues, field='closed_at', reverse=False):
93 93 """Return a list of issues sorted by closing date date."""
94 94 return sorted(issues, key = lambda i:i[field], reverse=reverse)
95 95
96 96
97 97 def report(issues, show_urls=False):
98 98 """Summary report about a list of issues, printing number and title."""
99 99 if show_urls:
100 100 for i in issues:
101 101 role = 'ghpull' if 'merged_at' in i else 'ghissue'
102 102 print(u'* :%s:`%d`: %s' % (role, i['number'],
103 103 i['title'].replace(u'`', u'``')))
104 104 else:
105 105 for i in issues:
106 106 print(u'* %d: %s' % (i['number'], i['title'].replace(u'`', u'``')))
107 107
108 108 #-----------------------------------------------------------------------------
109 109 # Main script
110 110 #-----------------------------------------------------------------------------
111 111
112 112 if __name__ == "__main__":
113 113 # deal with unicode
114 114 if sys.version_info < (3,):
115 115 sys.stdout = codecs.getwriter('utf8')(sys.stdout)
116 116
117 117 # Whether to add reST urls for all issues in printout.
118 118 show_urls = True
119 119
120 120 parser = ArgumentParser()
121 121 parser.add_argument('--since-tag', type=str,
122 122 help="The git tag to use for the starting point (typically the last major release)."
123 123 )
124 124 parser.add_argument('--milestone', type=str,
125 125 help="The GitHub milestone to use for filtering issues [optional]."
126 126 )
127 127 parser.add_argument('--days', type=int,
128 128 help="The number of days of data to summarize (use this or --since-tag)."
129 129 )
130 130 parser.add_argument('--project', type=str, default="ipython/ipython",
131 131 help="The project to summarize."
132 132 )
133 133 parser.add_argument('--links', action='store_true', default=False,
134 134 help="Include links to all closed Issues and PRs in the output."
135 135 )
136 136
137 137 opts = parser.parse_args()
138 138 tag = opts.since_tag
139 139
140 140 # set `since` from days or git tag
141 141 if opts.days:
142 142 since = datetime.utcnow() - timedelta(days=opts.days)
143 143 else:
144 144 if not tag:
145 145 tag = check_output(['git', 'describe', '--abbrev=0']).strip().decode('utf8')
146 146 cmd = ['git', 'log', '-1', '--format=%ai', tag]
147 147 tagday, tz = check_output(cmd).strip().decode('utf8').rsplit(' ', 1)
148 148 since = datetime.strptime(tagday, "%Y-%m-%d %H:%M:%S")
149 149 h = int(tz[1:3])
150 150 m = int(tz[3:])
151 151 td = timedelta(hours=h, minutes=m)
152 152 if tz[0] == '-':
153 153 since += td
154 154 else:
155 155 since -= td
156 156
157 157 since = round_hour(since)
158 158
159 159 milestone = opts.milestone
160 160 project = opts.project
161 161
162 162 print("fetching GitHub stats since %s (tag: %s, milestone: %s)" % (since, tag, milestone), file=sys.stderr)
163 163 if milestone:
164 164 milestone_id = get_milestone_id(project=project, milestone=milestone,
165 165 auth=True)
166 166 issues_and_pulls = get_issues_list(project=project,
167 167 milestone=milestone_id,
168 168 state='closed',
169 169 auth=True,
170 170 )
171 171 issues, pulls = split_pulls(issues_and_pulls)
172 172 else:
173 173 issues = issues_closed_since(since, project=project, pulls=False)
174 174 pulls = issues_closed_since(since, project=project, pulls=True)
175 175
176 176 # For regular reports, it's nice to show them in reverse chronological order
177 177 issues = sorted_by_field(issues, reverse=True)
178 178 pulls = sorted_by_field(pulls, reverse=True)
179 179
180 180 n_issues, n_pulls = map(len, (issues, pulls))
181 181 n_total = n_issues + n_pulls
182 182
183 183 # Print summary report we can directly include into release notes.
184 184
185 185 print()
186 186 since_day = since.strftime("%Y/%m/%d")
187 187 today = datetime.today().strftime("%Y/%m/%d")
188 188 print("GitHub stats for %s - %s (tag: %s)" % (since_day, today, tag))
189 189 print()
190 190 print("These lists are automatically generated, and may be incomplete or contain duplicates.")
191 191 print()
192 192
193 193 ncommits = 0
194 194 all_authors = []
195 195 if tag:
196 196 # print git info, in addition to GitHub info:
197 197 since_tag = tag+'..'
198 198 cmd = ['git', 'log', '--oneline', since_tag]
199 199 ncommits += len(check_output(cmd).splitlines())
200 200
201 201 author_cmd = ['git', 'log', '--use-mailmap', "--format=* %aN", since_tag]
202 202 all_authors.extend(check_output(author_cmd).decode('utf-8', 'replace').splitlines())
203 203
204 204 pr_authors = []
205 205 for pr in pulls:
206 206 pr_authors.extend(get_authors(pr))
207 207 ncommits = len(pr_authors) + ncommits - len(pulls)
208 208 author_cmd = ['git', 'check-mailmap'] + pr_authors
209 209 with_email = check_output(author_cmd).decode('utf-8', 'replace').splitlines()
210 210 all_authors.extend([ u'* ' + a.split(' <')[0] for a in with_email ])
211 211 unique_authors = sorted(set(all_authors), key=lambda s: s.lower())
212 212
213 print("We closed %d issues and merged %d pull requests." % (n_pulls, n_issues))
213 print("We closed %d issues and merged %d pull requests." % (n_issues, n_pulls))
214 214 if milestone:
215 215 print("The full list can be seen `on GitHub <https://github.com/%s/milestone/%s>`"
216 216 % (project, milestone)
217 217 )
218 218
219 219 print()
220 220 print("The following %i authors contributed %i commits." % (len(unique_authors), ncommits))
221 221 print()
222 222 print('\n'.join(unique_authors))
223 223
224 224 if opts.links:
225 225 print()
226 226 print("GitHub issues and pull requests:")
227 227 print()
228 228 print('Pull Requests (%d):\n' % n_pulls)
229 229 report(pulls, show_urls)
230 230 print()
231 231 print('Issues (%d):\n' % n_issues)
232 232 report(issues, show_urls)
General Comments 0
You need to be logged in to leave comments. Login now