First step of implementation of Repository.get_stats_xxx (#203)

This commit is contained in:
Vincent Jacques
2013-11-10 18:09:48 -08:00
parent b082414ad0
commit a4910ac922
5 changed files with 330 additions and 0 deletions
+70
View File
@@ -61,6 +61,7 @@ import github.Download
import github.Permissions
import github.Event
import github.Legacy
import github.StatsContributor
class Repository(github.GithubObject.CompletableGithubObject):
@@ -1690,6 +1691,75 @@ class Repository(github.GithubObject.CompletableGithubObject):
None
)
def get_stats_contributors(self):
"""
:calls: `GET /repos/:owner/:repo/stats/contributors <http://developer.github.com/v3/repos/statistics/#get-contributors-list-with-additions-deletions-and-commit-counts>`_
:rtype: None or list of :class:`github.StatsContributor.StatsContributor`
"""
headers, data = self._requester.requestJsonAndCheck(
"GET",
self.url + "/stats/contributors"
)
if data == {}:
return None
else:
return [
github.StatsContributor.StatsContributor(self._requester, headers, attributes, completed=True)
for attributes in data
]
def get_stats_commit_activity(self):
"""
:calls: `GET /repos/:owner/:repo/stats/commit_activity <developer.github.com/v3/repos/statistics/#get-the-number-of-commits-per-hour-in-each-day>`_
"""
headers, data = self._requester.requestJsonAndCheck(
"GET",
self.url + "/stats/commit_activity"
)
if data == {}:
return None
else:
return data # @todo Return something structured
def get_stats_code_frequency(self):
"""
:calls: `GET /repos/:owner/:repo/stats/code_frequency <http://developer.github.com/v3/repos/statistics/#get-the-number-of-additions-and-deletions-per-week>`_
"""
headers, data = self._requester.requestJsonAndCheck(
"GET",
self.url + "/stats/code_frequency"
)
if data == {}:
return None
else:
return data # @todo Return something structured
def get_stats_participation(self):
"""
:calls: `GET /repos/:owner/:repo/stats/participation <http://developer.github.com/v3/repos/statistics/#get-the-weekly-commit-count-for-the-repo-owner-and-everyone-else>`_
"""
headers, data = self._requester.requestJsonAndCheck(
"GET",
self.url + "/stats/participation"
)
if data == {}:
return None
else:
return data # @todo Return something structured
def get_stats_punch_card(self):
"""
:calls: `GET /repos/:owner/:repo/stats/punch_card <http://developer.github.com/v3/repos/statistics/#get-the-number-of-commits-per-hour-in-each-day>`_
"""
headers, data = self._requester.requestJsonAndCheck(
"GET",
self.url + "/stats/punch_card"
)
if data == {}:
return None
else:
return data # @todo Return something structured
def get_subscribers(self):
"""
:calls: `GET /repos/:owner/:repo/subscribers <http://developer.github.com/v3/activity/watching>`_
+119
View File
@@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
############################ Copyrights and license ############################
# #
# Copyright 2012 Vincent Jacques <vincent@vincent-jacques.net> #
# Copyright 2012 Zearin <zearin@gonk.net> #
# Copyright 2013 AKFish <akfish@gmail.com> #
# Copyright 2013 Vincent Jacques <vincent@vincent-jacques.net> #
# Copyright 2013 martinqt <m.ki2@laposte.net> #
# #
# This file is part of PyGithub. http://jacquev6.github.com/PyGithub/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################
import github.GithubObject
import github.NamedUser
class StatsContributor(github.GithubObject.NonCompletableGithubObject):
"""
This class represents statistics of a contibutor. The reference can be found here http://developer.github.com/v3/repos/statistics/#get-contributors-list-with-additions-deletions-and-commit-counts
"""
class Week(github.GithubObject.NonCompletableGithubObject):
"""
This class represents weekly statistics of a contibutor.
"""
@property
def w(self):
"""
:type: int
"""
return self._w.value
@property
def a(self):
"""
:type: int
"""
return self._a.value
@property
def d(self):
"""
:type: int
"""
return self._d.value
@property
def c(self):
"""
:type: int
"""
return self._c.value
def _initAttributes(self):
self._w = github.GithubObject.NotSet
self._a = github.GithubObject.NotSet
self._d = github.GithubObject.NotSet
self._c = github.GithubObject.NotSet
def _useAttributes(self, attributes):
if "w" in attributes: # pragma no branch
self._w = self._makeTimestampAttribute(attributes["w"])
if "a" in attributes: # pragma no branch
self._a = self._makeIntAttribute(attributes["a"])
if "d" in attributes: # pragma no branch
self._d = self._makeIntAttribute(attributes["d"])
if "c" in attributes: # pragma no branch
self._c = self._makeIntAttribute(attributes["c"])
@property
def author(self):
"""
:type: :class:`github.NamedUser.NamedUser`
"""
return self._author.value
@property
def total(self):
"""
:type: string
"""
return self._total.value
@property
def weeks(self):
"""
:type: list of :class:`.Week`
"""
return self._weeks.value
def _initAttributes(self):
self._author = github.GithubObject.NotSet
self._total = github.GithubObject.NotSet
self._weeks = github.GithubObject.NotSet
def _useAttributes(self, attributes):
if "author" in attributes: # pragma no branch
self._author = self._makeClassAttribute(github.NamedUser.NamedUser, attributes["author"])
if "total" in attributes: # pragma no branch
self._total = self._makeIntAttribute(attributes["total"])
if "weeks" in attributes: # pragma no branch
self._weeks = self._makeListOfClassesAttribute(self.Week, attributes["weeks"])
File diff suppressed because one or more lines are too long
@@ -0,0 +1,55 @@
https
GET
api.github.com
None
/repos/jacquev6/PyGithub/stats/contributors
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
null
202
[('status', '202 Accepted'), ('x-ratelimit-remaining', '4965'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('access-control-expose-headers', 'ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes'), ('x-github-request-id', '4C79374B:3446:13B76A5:528030E9'), ('content-length', '2'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('access-control-allow-credentials', 'true'), ('date', 'Mon, 11 Nov 2013 01:20:42 GMT'), ('access-control-allow-origin', '*'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1384134844')]
{}
https
GET
api.github.com
None
/repos/jacquev6/PyGithub/stats/commit_activity
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
null
202
[('status', '202 Accepted'), ('x-ratelimit-remaining', '4964'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('access-control-expose-headers', 'ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes'), ('x-github-request-id', '4C79374B:3448:429A229:528030EA'), ('content-length', '2'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('access-control-allow-credentials', 'true'), ('date', 'Mon, 11 Nov 2013 01:20:42 GMT'), ('access-control-allow-origin', '*'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1384134844')]
{}
https
GET
api.github.com
None
/repos/jacquev6/PyGithub/stats/code_frequency
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
null
202
[('status', '202 Accepted'), ('x-ratelimit-remaining', '4963'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('access-control-expose-headers', 'ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes'), ('x-github-request-id', '4C79374B:3448:429A284:528030EA'), ('content-length', '2'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('access-control-allow-credentials', 'true'), ('date', 'Mon, 11 Nov 2013 01:20:42 GMT'), ('access-control-allow-origin', '*'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1384134844')]
{}
https
GET
api.github.com
None
/repos/jacquev6/PyGithub/stats/participation
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
null
200
[('status', '202 Accepted'), ('x-ratelimit-remaining', '4962'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('access-control-expose-headers', 'ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes'), ('x-github-request-id', '4C79374B:3447:29BC88A:528030EB'), ('access-control-allow-credentials', 'true'), ('vary', 'Accept, Authorization, Cookie, Accept-Encoding'), ('content-length', '260'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('etag', '"a896655765faac08fb15ce0da319416c"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('date', 'Mon, 11 Nov 2013 01:20:43 GMT'), ('access-control-allow-origin', '*'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1384134844')]
{}
https
GET
api.github.com
None
/repos/jacquev6/PyGithub/stats/punch_card
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
null
200
[('status', '202 Accepted'), ('x-ratelimit-remaining', '4961'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('access-control-expose-headers', 'ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes'), ('x-github-request-id', '4C79374B:3448:429A33C:528030EB'), ('access-control-allow-credentials', 'true'), ('vary', 'Accept, Authorization, Cookie, Accept-Encoding'), ('content-length', '1490'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('etag', '"41cbe788b8174e5a98906512e68a825d"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('date', 'Mon, 11 Nov 2013 01:20:43 GMT'), ('access-control-allow-origin', '*'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1384134844')]
{}
+31
View File
@@ -448,3 +448,34 @@ class Repository(Framework.TestCase):
def testUnsubscribePubSubHubbub(self):
self.repo.unsubscribe_from_hub("push", "http://requestb.in/1bc1sc61")
def testStatisticsBeforeCaching(self):
self.assertIsNone(self.repo.get_stats_contributors())
self.assertIsNone(self.repo.get_stats_commit_activity())
self.assertIsNone(self.repo.get_stats_code_frequency())
# ReplayData for those last two get_stats is forged because I was not
# able to find a repo where participation and punch_card had never been
# computed, and pushing to master did not reset the cache for them
self.assertIsNone(self.repo.get_stats_participation())
self.assertIsNone(self.repo.get_stats_punch_card())
def testStatisticsAfterCaching(self):
stats = self.repo.get_stats_contributors()
seenJacquev6 = False
for s in stats:
adTotal = 0
total = 0
for w in s.weeks:
total += w.c
adTotal += w.a + w.d
self.assertEqual(total, s.total)
if s.author.login == "jacquev6":
seenJacquev6 = True
self.assertEqual(adTotal, 282147)
self.assertEqual(s.weeks[0].w, datetime.datetime(2012, 2, 12))
self.assertTrue(seenJacquev6)
self.repo.get_stats_commit_activity()
self.repo.get_stats_code_frequency()
self.repo.get_stats_participation()
self.repo.get_stats_punch_card()