Skip to content

Commit 8bf11a0

Browse files
committed
Fixed django#1119 -- Used GitHub API for commits stats.
1 parent 471587a commit 8bf11a0

7 files changed

+102
-2672
lines changed
-539 Bytes
Binary file not shown.

dashboard/fixtures/dashboard_production_metrics.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@
272272
}
273273
},
274274
{
275-
"model": "dashboard.rssfeedmetric",
275+
"model": "dashboard.githubsearchcountmetric",
276276
"pk": 1,
277277
"fields": {
278278
"name": "Commits today",
@@ -284,12 +284,12 @@
284284
"period": "daily",
285285
"unit": "commit",
286286
"unit_plural": "commits",
287-
"feed_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=0&format=rss",
288-
"link_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=0"
287+
"api_url": "https://api.github.com/search/commits",
288+
"link_url": "https://github.com/django/django/commits"
289289
}
290290
},
291291
{
292-
"model": "dashboard.rssfeedmetric",
292+
"model": "dashboard.githubsearchcountmetric",
293293
"pk": 2,
294294
"fields": {
295295
"name": "Commits in the last week",
@@ -301,8 +301,8 @@
301301
"period": "weekly",
302302
"unit": "commit",
303303
"unit_plural": "commits",
304-
"feed_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=7&format=rss",
305-
"link_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=7"
304+
"api_url": "https://api.github.com/search/commits",
305+
"link_url": "https://github.com/django/django/commits"
306306
}
307307
},
308308
{

dashboard/fixtures/dashboard_test_data.json

+8-8
Original file line numberDiff line numberDiff line change
@@ -180,33 +180,33 @@
180180
"category": 1,
181181
"show_on_dashboard": true,
182182
"name": "Commits today",
183-
"feed_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=0&format=rss",
184-
"link_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=0",
183+
"api_url": "https://api.github.com/search/commits",
184+
"link_url": "https://github.com/django/django/commits",
185185
"period": "daily",
186186
"show_sparkline": true,
187187
"unit_plural": "commits",
188-
"position": 1,
188+
"position": 2,
189189
"slug": "commits-today",
190190
"unit": "commit"
191191
},
192-
"model": "dashboard.rssfeedmetric",
192+
"model": "dashboard.githubsearchcountmetric",
193193
"pk": 1
194194
},
195195
{
196196
"fields": {
197197
"category": 1,
198198
"show_on_dashboard": true,
199199
"name": "Commits in the last week",
200-
"feed_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=7&format=rss",
201-
"link_url": "https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=7",
200+
"api_url": "https://api.github.com/search/commits",
201+
"link_url": "https://github.com/django/django/commits",
202202
"period": "weekly",
203203
"show_sparkline": true,
204204
"unit_plural": "commits",
205-
"position": 1,
205+
"position": 4,
206206
"slug": "commits-week",
207207
"unit": "commit"
208208
},
209-
"model": "dashboard.rssfeedmetric",
209+
"model": "dashboard.githubsearchcountmetric",
210210
"pk": 2
211211
},
212212
{

dashboard/fixtures/rss_feed_metric.xml

-2,626
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Generated by Django 2.2.24 on 2021-10-27 01:33
2+
import django.db.models.deletion
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('dashboard', '0001_initial'),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name='GitHubSearchCountMetric',
15+
fields=[
16+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
17+
('name', models.CharField(max_length=300)),
18+
('slug', models.SlugField()),
19+
('position', models.PositiveSmallIntegerField(default=1)),
20+
('show_on_dashboard', models.BooleanField(default=True)),
21+
('show_sparkline', models.BooleanField(default=True)),
22+
('period', models.CharField(choices=[('instant', 'Instant'), ('daily', 'Daily'), ('weekly', 'Weekly')], default='instant', max_length=15)),
23+
('unit', models.CharField(max_length=100)),
24+
('unit_plural', models.CharField(max_length=100)),
25+
('api_url', models.URLField(max_length=1000)),
26+
('link_url', models.URLField(max_length=1000)),
27+
('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='dashboard.Category')),
28+
],
29+
options={
30+
'abstract': False,
31+
},
32+
),
33+
migrations.DeleteModel(
34+
name='RSSFeedMetric',
35+
),
36+
]

dashboard/models.py

+18-12
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import datetime
44
import xmlrpc.client
55

6-
import feedparser
76
import requests
87
from django.conf import settings
98
from django.contrib.contenttypes.fields import (
@@ -127,17 +126,6 @@ def link(self):
127126
return "%squery?%s&desc=1&order=changetime" % (settings.TRAC_URL, self.query)
128127

129128

130-
class RSSFeedMetric(Metric):
131-
feed_url = models.URLField(max_length=1000)
132-
link_url = models.URLField(max_length=1000)
133-
134-
def fetch(self):
135-
return len(feedparser.parse(requests.get(self.feed_url).text).entries)
136-
137-
def link(self):
138-
return self.link_url
139-
140-
141129
class GithubItemCountMetric(Metric):
142130
"""Example: https://api.github.com/repos/django/django/pulls?state=open"""
143131
api_url = models.URLField(max_length=1000)
@@ -165,6 +153,24 @@ def link(self):
165153
return self.link_url
166154

167155

156+
class GitHubSearchCountMetric(Metric):
157+
api_url = models.URLField(max_length=1000)
158+
link_url = models.URLField(max_length=1000)
159+
160+
def fetch(self):
161+
"""Request the specified GitHub API and return a total count."""
162+
today = datetime.date.today()
163+
if self.period == METRIC_PERIOD_WEEKLY:
164+
committer_date = '>%s' % (today - datetime.timedelta(weeks=1)).isoformat()
165+
else:
166+
committer_date = today.isoformat()
167+
r = requests.get(
168+
self.api_url + '?per_page=1&q=repo:django/django+committer-date:%s' % committer_date
169+
)
170+
data = r.json()
171+
return data['total_count']
172+
173+
168174
class JenkinsFailuresMetric(Metric):
169175
"""
170176
Track failures of a job/build. Uses the Python flavor of the Jenkins REST

dashboard/tests.py

+34-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import codecs
1+
import datetime
22
import json
3-
import os
43
from unittest import mock
54

65
import requests_mock
@@ -10,7 +9,8 @@
109
from django_hosts.resolvers import reverse
1110

1211
from .models import (
13-
GithubItemCountMetric, Metric, RSSFeedMetric, TracTicketMetric,
12+
METRIC_PERIOD_DAILY, METRIC_PERIOD_WEEKLY, GithubItemCountMetric,
13+
GitHubSearchCountMetric, Metric, TracTicketMetric,
1414
)
1515
from .views import index, metric_detail, metric_json
1616

@@ -77,23 +77,6 @@ def test_fetch(self, mock_server_proxy):
7777
self.assertTrue(mock_server_proxy.client.query.assert_called_with)
7878

7979

80-
class RSSFeedMetricTestCase(TestCase, MetricMixin):
81-
fixtures = ['dashboard_test_data']
82-
feed_url = 'https://code.djangoproject.com/timeline?changeset=on&max=0&daysback=7&format=rss'
83-
fixtures_path = os.path.join(os.path.dirname(__file__), 'fixtures', 'rss_feed_metric.xml')
84-
85-
def setUp(self):
86-
super().setUp()
87-
self.instance = RSSFeedMetric.objects.last()
88-
89-
@requests_mock.mock()
90-
def test_fetch(self, mocker):
91-
with codecs.open(self.fixtures_path, 'r', 'utf-8') as fixtures:
92-
feed_items = fixtures.read()
93-
mocker.get(self.feed_url, text=feed_items)
94-
self.assertEqual(self.instance.fetch(), 177)
95-
96-
9780
class GithubItemCountMetricTestCase(TestCase, MetricMixin):
9881
fixtures = ['dashboard_test_data']
9982
api_url1 = 'https://api.github.com/repos/django/django/pulls?state=closed&per_page=100&page=1'
@@ -112,6 +95,37 @@ def test_fetch(self, mocker):
11295
self.assertEqual(self.instance.fetch(), 142)
11396

11497

98+
class GitHubSearchCountMetricTestCase(TestCase, MetricMixin):
99+
fixtures = ['dashboard_test_data']
100+
api_url = (
101+
'https://api.github.com/search/commits?'
102+
'per_page=1&q=repo:django/django+committer-date:%s'
103+
)
104+
105+
def setUp(self):
106+
super().setUp()
107+
self.instance = GitHubSearchCountMetric.objects.last()
108+
109+
@requests_mock.mock()
110+
def test_fetch(self, mocker):
111+
today = datetime.date.today()
112+
week_start = today - datetime.timedelta(weeks=1)
113+
# Faking a daily JSON output with 4 items.
114+
mocker.get(
115+
self.api_url % today.isoformat(),
116+
text=json.dumps({'total_count': 4, 'items': []}),
117+
)
118+
metric = GitHubSearchCountMetric.objects.filter(period=METRIC_PERIOD_DAILY).last()
119+
self.assertEqual(metric.fetch(), 4)
120+
# Faking a weekly JSON output with 23 items.
121+
mocker.get(
122+
self.api_url % '>' + week_start.isoformat(),
123+
text=json.dumps({'total_count': 23, 'items': []}),
124+
)
125+
metric = GitHubSearchCountMetric.objects.filter(period=METRIC_PERIOD_WEEKLY).last()
126+
self.assertEqual(metric.fetch(), 23)
127+
128+
115129
class UpdateMetricCommandTestCase(TestCase):
116130
github_url = 'https://api.github.com/repos/django/django/pulls?state=closed&per_page=100&page=1'
117131

0 commit comments

Comments
 (0)