1+ # Copyright The IETF Trust 2016, All Rights Reserved
2+ # -*- coding: utf-8 -*-
3+ from __future__ import unicode_literals , print_function
4+
15import os
26import re
37import json
48import datetime
59import gzip
10+ from tzparse import tzparse
11+ from calendar import timegm
612
713from django .shortcuts import render
814from django .conf import settings
15+ from django .core .cache import cache
916from django .http import HttpResponse
1017from django .utils .html import escape
18+ from django .utils .safestring import mark_safe
1119
1220import changelog
1321import debug # pyflakes:ignore
1624import time
1725time .strptime ('1984' , '%Y' ) # we do this to force lib loading, instead of it happening lazily when changelog calls tzparse later
1826
27+ import ietf
28+
1929def trac_links (text ):
2030 # changeset links
2131 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):
2434 return text
2535
2636
37+ def get_coverage_data ():
38+ key = 'ietf:release:get_coverage_data:%s' % ietf .__version__
39+ coverage_data = cache .get (key )
40+ if not coverage_data :
41+ coverage_data = {}
42+ if os .path .exists (settings .TEST_COVERAGE_MASTER_FILE ):
43+ if settings .TEST_COVERAGE_MASTER_FILE .endswith (".gz" ):
44+ with gzip .open (settings .TEST_COVERAGE_MASTER_FILE , "rb" ) as file :
45+ coverage_data = json .load (file )
46+ else :
47+ with open (settings .TEST_COVERAGE_MASTER_FILE ) as file :
48+ coverage_data = json .load (file )
49+ cache .set (key , coverage_data , 60 * 60 * 24 )
50+ return coverage_data
51+
52+ def get_changelog_entries ():
53+ key = 'ietf:release:get_changelog_entries:%s' % ietf .__version__
54+ log_entries = cache .get (key )
55+ if not log_entries :
56+ if os .path .exists (settings .CHANGELOG_PATH ):
57+ log_entries = changelog .parse (settings .CHANGELOG_PATH )
58+ cache .set (key , log_entries , 60 * 60 * 24 )
59+ return log_entries
60+
2761def release (request , version = None ):
2862 entries = {}
29- if os .path .exists (settings .CHANGELOG_PATH ):
30- log_entries = changelog .parse (settings .CHANGELOG_PATH )
31- else :
63+ log_entries = get_changelog_entries ()
64+ if not log_entries :
3265 return HttpResponse ("Error: changelog file %s not found" % settings .CHANGELOG_PATH )
3366 next = None
3467 for entry in log_entries :
@@ -48,18 +81,12 @@ def release(request, version=None):
4881 code_coverage_time = datetime .datetime .fromtimestamp (os .path .getmtime (settings .TEST_CODE_COVERAGE_REPORT_FILE ))
4982
5083 coverage = {}
51- if os .path .exists (settings .TEST_COVERAGE_MASTER_FILE ):
52- if settings .TEST_COVERAGE_MASTER_FILE .endswith (".gz" ):
53- with gzip .open (settings .TEST_COVERAGE_MASTER_FILE , "rb" ) as file :
54- coverage_data = json .load (file )
55- else :
56- with open (settings .TEST_COVERAGE_MASTER_FILE ) as file :
57- coverage_data = json .load (file )
58- if version in coverage_data :
59- coverage = coverage_data [version ]
60- for key in coverage :
61- if "coverage" in coverage [key ]:
62- coverage [key ]["percentage" ] = coverage [key ]["coverage" ] * 100
84+ coverage_data = get_coverage_data ()
85+ if version in coverage_data :
86+ coverage = coverage_data [version ]
87+ for key in coverage :
88+ if "coverage" in coverage [key ]:
89+ coverage [key ]["percentage" ] = coverage [key ]["coverage" ] * 100
6390
6491 return render (request , 'release/release.html' ,
6592 {
@@ -72,4 +99,51 @@ def release(request, version=None):
7299 } )
73100
74101
75-
102+ def stats (request ):
103+
104+ coverage_chart_data = []
105+ frequency_chart_data = []
106+
107+ coverage_data = get_coverage_data ()
108+ coverage_series_data = {}
109+ for version in coverage_data :
110+ if 'time' in coverage_data [version ]:
111+ t = coverage_data [version ]['time' ]
112+ secs = timegm (tzparse (t , "%Y-%m-%dT%H:%M:%SZ" ).timetuple ()) * 1000
113+ for coverage_type in coverage_data [version ]:
114+ if 'coverage' in coverage_data [version ][coverage_type ]:
115+ cov = coverage_data [version ][coverage_type ]['coverage' ]
116+ if not coverage_type in coverage_series_data :
117+ coverage_series_data [coverage_type ] = []
118+ coverage_series_data [coverage_type ].append ([secs , cov ])
119+
120+ for coverage_type in coverage_series_data :
121+ coverage_series_data [coverage_type ].sort ()
122+ # skip some early values
123+ coverage_series_data [coverage_type ] = coverage_series_data [coverage_type ][2 :]
124+ coverage_chart_data .append ({
125+ 'data' : coverage_series_data [coverage_type ],
126+ 'name' : coverage_type ,
127+ })
128+
129+ log_entries = get_changelog_entries ()
130+ frequency = {}
131+ frequency_series_data = []
132+ for entry in log_entries :
133+ year = entry .time .year
134+ if not year in frequency :
135+ frequency [year ] = 0
136+ frequency [year ] += 1
137+ for year in frequency :
138+ frequency_series_data .append ([year , frequency [year ]])
139+ frequency_series_data .sort ()
140+ frequency_chart_data .append ({
141+ 'data' : frequency_series_data ,
142+ 'name' : 'Releases' ,
143+ })
144+
145+ return render (request , 'release/stats.html' ,
146+ {
147+ 'coverage_chart_data' : mark_safe (json .dumps (coverage_chart_data )),
148+ 'frequency_chart_data' : mark_safe (json .dumps (frequency_chart_data )),
149+ })
0 commit comments