Added charts for test coverage and release frequency.

- Legacy-Id: 13232
This commit is contained in:
Henrik Levkowetz 2017-04-18 16:48:34 +00:00
parent d6b9392f72
commit e6c41879ac
5 changed files with 248 additions and 16 deletions

View file

@ -1,3 +1,7 @@
# Copyright The IETF Trust 2016, All Rights Reserved
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
from pyquery import PyQuery
from django.urls import reverse
@ -32,4 +36,17 @@ class ReleasePagesTest(TestCase):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
def test_stats(self):
url = reverse('ietf.release.views.stats')
r = self.client.get(url)
q = PyQuery(r.content)
# grab the script element text, split off the json data
s = q('#coverage-data').text()
self.assertIn("type: 'line',", s)
self.assertIn('"data": [[1426018457000, ', s)
s = q('#frequency-data').text()
self.assertIn("type: 'column',", s)
self.assertIn('"data": [[2007, 7], ', s)

View file

@ -1,3 +1,7 @@
# Copyright The IETF Trust 2016, All Rights Reserved
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
from django.views.generic import TemplateView
from ietf.release import views
@ -7,6 +11,7 @@ urlpatterns = [
url(r'^$', views.release),
url(r'^(?P<version>[0-9.]+.*)/$', views.release),
url(r'^about/?$', TemplateView.as_view(template_name='release/about.html')),
url(r'^stats/?$', views.stats),
url(r'^todo/?$', TemplateView.as_view(template_name='release/todo.html')),
]

View file

@ -1,13 +1,21 @@
# Copyright The IETF Trust 2016, All Rights Reserved
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import os
import re
import json
import datetime
import gzip
from tzparse import tzparse
from calendar import timegm
from django.shortcuts import render
from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponse
from django.utils.html import escape
from django.utils.safestring import mark_safe
import changelog
import debug # pyflakes:ignore
@ -16,6 +24,8 @@ import debug # pyflakes:ignore
import time
time.strptime('1984', '%Y') # we do this to force lib loading, instead of it happening lazily when changelog calls tzparse later
import ietf
def trac_links(text):
# changeset links
text = re.sub(r'\[(\d+)\]', r'<a href="https://wiki.tools.ietf.org/tools/ietfdb/changeset/\1">[\1]</a>', text)
@ -24,11 +34,34 @@ def trac_links(text):
return text
def get_coverage_data():
key = 'ietf:release:get_coverage_data:%s' % ietf.__version__
coverage_data = cache.get(key)
if not coverage_data:
coverage_data = {}
if os.path.exists(settings.TEST_COVERAGE_MASTER_FILE):
if settings.TEST_COVERAGE_MASTER_FILE.endswith(".gz"):
with gzip.open(settings.TEST_COVERAGE_MASTER_FILE, "rb") as file:
coverage_data = json.load(file)
else:
with open(settings.TEST_COVERAGE_MASTER_FILE) as file:
coverage_data = json.load(file)
cache.set(key, coverage_data, 60*60*24)
return coverage_data
def get_changelog_entries():
key = 'ietf:release:get_changelog_entries:%s' % ietf.__version__
log_entries = cache.get(key)
if not log_entries:
if os.path.exists(settings.CHANGELOG_PATH):
log_entries = changelog.parse(settings.CHANGELOG_PATH)
cache.set(key, log_entries, 60*60*24)
return log_entries
def release(request, version=None):
entries = {}
if os.path.exists(settings.CHANGELOG_PATH):
log_entries = changelog.parse(settings.CHANGELOG_PATH)
else:
log_entries = get_changelog_entries()
if not log_entries:
return HttpResponse("Error: changelog file %s not found" % settings.CHANGELOG_PATH)
next = None
for entry in log_entries:
@ -48,18 +81,12 @@ def release(request, version=None):
code_coverage_time = datetime.datetime.fromtimestamp(os.path.getmtime(settings.TEST_CODE_COVERAGE_REPORT_FILE))
coverage = {}
if os.path.exists(settings.TEST_COVERAGE_MASTER_FILE):
if settings.TEST_COVERAGE_MASTER_FILE.endswith(".gz"):
with gzip.open(settings.TEST_COVERAGE_MASTER_FILE, "rb") as file:
coverage_data = json.load(file)
else:
with open(settings.TEST_COVERAGE_MASTER_FILE) as file:
coverage_data = json.load(file)
if version in coverage_data:
coverage = coverage_data[version]
for key in coverage:
if "coverage" in coverage[key]:
coverage[key]["percentage"] = coverage[key]["coverage"] * 100
coverage_data = get_coverage_data()
if version in coverage_data:
coverage = coverage_data[version]
for key in coverage:
if "coverage" in coverage[key]:
coverage[key]["percentage"] = coverage[key]["coverage"] * 100
return render(request, 'release/release.html',
{
@ -72,4 +99,51 @@ def release(request, version=None):
} )
def stats(request):
coverage_chart_data = []
frequency_chart_data = []
coverage_data = get_coverage_data()
coverage_series_data = {}
for version in coverage_data:
if 'time' in coverage_data[version]:
t = coverage_data[version]['time']
secs = timegm(tzparse(t, "%Y-%m-%dT%H:%M:%SZ").timetuple()) * 1000
for coverage_type in coverage_data[version]:
if 'coverage' in coverage_data[version][coverage_type]:
cov = coverage_data[version][coverage_type]['coverage']
if not coverage_type in coverage_series_data:
coverage_series_data[coverage_type] = []
coverage_series_data[coverage_type].append([secs, cov])
for coverage_type in coverage_series_data:
coverage_series_data[coverage_type].sort()
# skip some early values
coverage_series_data[coverage_type] = coverage_series_data[coverage_type][2:]
coverage_chart_data.append({
'data': coverage_series_data[coverage_type],
'name': coverage_type,
})
log_entries = get_changelog_entries()
frequency = {}
frequency_series_data = []
for entry in log_entries:
year = entry.time.year
if not year in frequency:
frequency[year] = 0
frequency[year] += 1
for year in frequency:
frequency_series_data.append([year, frequency[year]])
frequency_series_data.sort()
frequency_chart_data.append({
'data': frequency_series_data,
'name': 'Releases',
})
return render(request, 'release/stats.html',
{
'coverage_chart_data': mark_safe(json.dumps(coverage_chart_data)),
'frequency_chart_data': mark_safe(json.dumps(frequency_chart_data)),
})

View file

@ -19,6 +19,7 @@
<a href="https://trac.tools.ietf.org/tools/ietfdb/browser/tags/{{entry.version}}">
Version {{ entry.version }}</a>
<br><small>Released {{ entry.date }}</small>
<div class="pull-right"><a href="{% url "ietf.release.views.stats" %}" class="icon-link">&nbsp;<span class="small fa fa-bar-chart">&nbsp;</span></a></div>
</h1>
<ul class="pager">

View file

@ -0,0 +1,135 @@
{# Copyright The IETF Trust 2017, All Rights Reserved #}
{% extends "base.html" %}
{% load origin %}
{% load ietf_filters staticfiles bootstrap3 %}
{% block title %}Release Statistics{% endblock %}
{% block pagehead %}
{% endblock %}
{% block content %}
{% origin %}
<h1>Release Statistics</h1>
<!-- ------------------------------------------------------------------------ -->
<div id="coverage-chart">
</div>
<script id="coverage-data">
var coverageChartConf = {
chart: {
type: 'line',
},
credits: {
enabled: false
},
exporting: {
fallbackToExportServer: false
},
legend: {
align: "right",
verticalAlign: "middle",
layout: "vertical",
enabled: true
},
plotOptions: {
line: {
marker: {
enabled: false
},
animation: false
}
},
title: {
text: 'Test coverage'
},
xAxis: {
type: 'datetime',
title: {
text: 'Release date'
},
},
units: [
[ 'day', [1]],
[ 'week', [1]],
[ 'month', [1, 3, 6]],
[ 'year', null ]
],
yAxis: {
min: 0,
title: {
text: 'Test coverage'
},
labels: {
formatter: function() {
return this.value*100+"%";
}
}
},
series: {{ coverage_chart_data }}
};
</script>
<div id="frequency-chart">
</div>
<script id="frequency-data">
var frequencyChartConf = {
chart: {
type: 'column',
},
credits: {
enabled: false
},
exporting: {
fallbackToExportServer: false
},
legend: {
align: "right",
verticalAlign: "middle",
layout: "vertical",
enabled: true
},
plotOptions: {
column: {
animation: false
}
},
title: {
text: 'Releases per year'
},
xAxis: {
type: 'category',
title: {
text: 'Year'
},
},
yAxis: {
min: 0,
title: {
text: 'Number of releases'
}
},
series: {{ frequency_chart_data }}
};
</script>
{% endblock %}
{% block js %}
<script src="{% static 'highcharts/highcharts.js' %}"></script>
<script src="{% static 'highcharts/modules/exporting.js' %}"></script>
<script src="{% static 'highcharts/modules/offline-exporting.js' %}"></script>
<script>
$(document).ready(function () {
Highcharts.chart('coverage-chart', window.coverageChartConf);
Highcharts.chart('frequency-chart', window.frequencyChartConf);
});
</script>
{% endblock %}