Skip to content

Commit f9e40be

Browse files
committed
Repo statistics presented without tests.
1 parent d95517c commit f9e40be

File tree

4 files changed

+136
-0
lines changed

4 files changed

+136
-0
lines changed

HISTORY.rst

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ History/Changelog
99
- Add ``check_authorization`` to the ``GitHub class`` to cover the `new part
1010
of the API <http://developer.github.com/v3/oauth/#check-an-authorization>`_.
1111

12+
- Add ``Repository.iter_contributor_statistics``,
13+
``Repository.iter_commit_activity``,
14+
1215
- The signature of ``Hook.edit`` has changed since that endpoint has changed
1316
as well. See:
1417
github/developer.github.com@b95f291a47954154a6a8cd7c2296cdda9b610164

github3/repos/repo.py

+88
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from github3.repos.download import Download
2929
from github3.repos.hook import Hook
3030
from github3.repos.status import Status
31+
from github3.repos.stats import ContributorStats
3132
from github3.repos.tag import RepoTag
3233
from github3.users import User, Key
3334

@@ -848,6 +849,28 @@ def iter_branches(self, number=-1, etag=None):
848849
url = self._build_url('branches', base_url=self._api)
849850
return self._iter(int(number), url, Branch, etag=etag)
850851

852+
def iter_code_frequency(self, number=-1, etag=None):
853+
"""Iterate over the code frequency per week.
854+
855+
Returns a weekly aggregate of the number of additions and deletions
856+
pushed to this repository.
857+
858+
:param int number: (optional), number of weeks to return. Default: -1
859+
returns all weeks
860+
:param str etag: (optional), ETag from a previous request to the same
861+
endpoint
862+
:returns: generator of lists ``[seconds_from_epoch, additions,
863+
deletions]``
864+
865+
.. note:: All statistics methods may return a 202. On those occasions,
866+
you will not receive any objects. You should store your
867+
iterator and check the new ``last_status`` attribute. If it
868+
is a 202 you should wait before re-requesting.
869+
870+
"""
871+
url = self._build_url('stats', 'code_frequecy', base_url=self._api)
872+
return self._iter(int(number), url, list, etag=etag)
873+
851874
def iter_comments(self, number=-1, etag=None):
852875
"""Iterate over comments on all commits in the repository.
853876
@@ -874,6 +897,26 @@ def iter_comments_on_commit(self, sha, number=1, etag=None):
874897
url = self._build_url('commits', sha, 'comments', base_url=self._api)
875898
return self._iter(int(number), url, RepoComment, etag=etag)
876899

900+
def iter_commit_activity(self, number=-1, etag=None):
901+
"""Iterate over last year of commit activity by week.
902+
903+
See: http://developer.github.com/v3/repos/statistics/
904+
905+
:param int number: (optional), number of weeks to return. Default -1
906+
will return all of the weeks.
907+
:param str etag: (optional), ETag from a previous request to the same
908+
endpoint
909+
:returns: generator of dictionaries
910+
911+
.. note:: All statistics methods may return a 202. On those occasions,
912+
you will not receive any objects. You should store your
913+
iterator and check the new ``last_status`` attribute. If it
914+
is a 202 you should wait before re-requesting.
915+
916+
"""
917+
url = self._build_url('stats', 'commit_activity', base_url=self._api)
918+
return self._iter(int(number), url, dict, etag=etag)
919+
877920
def iter_commits(self, sha=None, path=None, author=None, number=-1,
878921
etag=None):
879922
"""Iterate over commits in this repository.
@@ -912,6 +955,27 @@ def iter_contributors(self, anon=False, number=-1, etag=None):
912955
params = {'anon': True}
913956
return self._iter(int(number), url, User, params, etag)
914957

958+
def iter_contributor_statistics(self, number=-1, etag=None):
959+
"""Iterate over the contributors list.
960+
961+
See also: http://developer.github.com/v3/repos/statistics/
962+
963+
:param int number: (optional), number of weeks to return. Default -1
964+
will return all of the weeks.
965+
:param str etag: (optional), ETag from a previous request to the same
966+
endpoint
967+
:returns: generator of
968+
:class:`ContributorStats <github3.repos.stats.ContributorStats>`
969+
970+
.. note:: All statistics methods may return a 202. On those occasions,
971+
you will not receive any objects. You should store your
972+
iterator and check the new ``last_status`` attribute. If it
973+
is a 202 you should wait before re-requesting.
974+
975+
"""
976+
url = self._build_url('stats', 'contributors', base_url=self._api)
977+
return self._iter(int(number), url, ContributorStats, etag=etag)
978+
915979
def iter_downloads(self, number=-1, etag=None):
916980
"""Iterate over available downloads for this repository.
917981
@@ -1398,3 +1462,27 @@ def update_label(self, name, color, new_name=''):
13981462
upd = label.update
13991463
resp = upd(new_name, color) if new_name else upd(name, color)
14001464
return resp
1465+
1466+
def weekly_commit_count(self):
1467+
"""Returns the total commit counts.
1468+
1469+
The dictionary returned has two entries: ``all`` and ``owner``. Each
1470+
has a fifty-two element long list of commit counts. (Note: ``all``
1471+
includes the owner.) ``d['all'][0]`` will be the oldest week,
1472+
``d['all'][51]`` will be the most recent.
1473+
1474+
:returns: dict
1475+
1476+
.. note:: All statistics methods may return a 202. If github3.py
1477+
receives a 202 in this case, it will return an emtpy dictionary.
1478+
You should give the API a moment to compose the data and then re
1479+
-request it via this method.
1480+
1481+
"""
1482+
url = self._build_url('stats', 'participation', base_url=self._api)
1483+
resp = self._get(url)
1484+
if resp.status_code == 202:
1485+
return {}
1486+
json = self._json(resp, 200)
1487+
del(json['Etag'], json['Last-Modified'])
1488+
return json

github3/repos/stats.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from datetime import datetime
2+
from github3.models import GitHubCore
3+
from github3.users import User
4+
5+
6+
def alternate_week(week):
7+
return {
8+
'start of week': datetime.fromtimestamp(int(week['w'])),
9+
'additions': week['a'],
10+
'deletions': week['d'],
11+
'commits': week['c'],
12+
}
13+
14+
15+
class ContributorStats(GitHubCore):
16+
17+
"""This object provides easy access to information returned by the
18+
statistics section of the API.
19+
20+
See http://developer.github.com/v3/repos/statistics/ for specifics.
21+
22+
"""
23+
24+
def __init__(self, stats_object, session=None):
25+
super(ContributorStats, self).__init__(stats_object, session)
26+
#: Contributor in particular that this relates to
27+
self.author = User(stats_object.get('author', {}), session)
28+
#: Total number of commits authored by ``author``.
29+
self.total = stats_object.get('total')
30+
#: List of weekly dictionaries.
31+
self.weeks = stats_object.get('weeks', [])
32+
#: Alternative collection of weekly dictionaries
33+
#: This provides a datetime object and easy to remember keys for each
34+
#: element in the list. 'w' -> 'start of week', 'a' -> 'additions',
35+
#: 'd' -> 'deletions', 'c' -> 'commits'
36+
self.alt_weeks = [alternate_week(w) for w in self.weeks]
37+
38+
def __repr__(self):
39+
return '<Contributor Statistics [{0}]>'.format(self.author)

github3/structs.py

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ def __init__(self, count, url, cls, session, params=None, etag=None):
2525
self.etag = None
2626
#: Headers generated for the GET request
2727
self.headers = {}
28+
#: The last response seen
29+
self.last_response = None
30+
#: Last status code received
31+
self.last_status = 0
2832

2933
if etag:
3034
self.headers = {'If-None-Match': etag}
@@ -39,6 +43,8 @@ def __iter__(self):
3943

4044
while (self.count == -1 or self.count > 0) and url:
4145
response = self._get(url, params=params, headers=headers)
46+
self.last_response = response
47+
self.last_status = response.status_code
4248
if params:
4349
params = None # rel_next already has the params
4450

0 commit comments

Comments
 (0)