1+ # Copyright The IETF Trust 2016, All Rights Reserved
2+
3+ import copy
14import datetime
25
6+ from django .conf import settings
37from django .core .cache import cache
48from django .core .urlresolvers import reverse as urlreverse
59from django .db .models .aggregates import Count
1923
2024epochday = datetime .datetime .utcfromtimestamp (0 ).date ().toordinal ()
2125
22- def ms (t ):
23- return (t .toordinal () - epochday )* 1000 * 60 * 60 * 24
26+ column_chart_conf = settings .CHART_TYPE_COLUMN_OPTIONS
27+
28+ def dt (s ):
29+ "Convert the date string returned by sqlite's date() to a datetime.date"
30+ ys , ms , ds = s .split ('-' )
31+ return datetime .date (int (ys ), int (ms ), int (ds ))
32+
33+ def model_to_timeline_data (model , field = 'time' , ** kwargs ):
34+ """Takes a Django model and a set of queryset filter arguments, and
35+ returns a dictionary with highchart settings and data, suitable as
36+ a JsonResponse() argument. The model must have a DateTimeField field.
37+ If the time field is named something else than 'time', the name must
38+ be supplied."""
39+ assert field in model ._meta .get_all_field_names ()
40+
41+ objects = ( model .objects .filter (** kwargs )
42+ .order_by ('date' )
43+ .extra (select = {'date' : 'date(%s.%s)' % (model ._meta .db_table , field ) })
44+ .values ('date' )
45+ .annotate (count = Count ('id' )))
46+ if objects .exists ():
47+ obj_list = list (objects )
48+ # This is needed for sqlite, when we're running tests:
49+ if type (obj_list [0 ]['date' ]) != datetime .date :
50+ obj_list = [ {'date' : dt (e ['date' ]), 'count' : e ['count' ]} for e in obj_list ]
51+ today = datetime .date .today ()
52+ if not obj_list [- 1 ]['date' ] == today :
53+ obj_list += [ {'date' : today , 'count' : 0 } ]
54+ data = [ ((e ['date' ].toordinal ()- epochday )* 1000 * 60 * 60 * 24 , e ['count' ]) for e in obj_list ]
55+ else :
56+ data = []
57+
58+ return data
59+
60+
2461
2562def get_doctypes (queryargs , pluralize = False ):
2663 doctypes = []
@@ -47,7 +84,7 @@ def make_title(queryargs):
4784 # radio choices
4885 by = queryargs .get ('by' )
4986 if by == "author" :
50- title += ' with author "%s" ' % queryargs ['author' ].title ()
87+ title += ' with author %s ' % queryargs ['author' ].title ()
5188 elif by == "group" :
5289 group = queryargs ['group' ]
5390 if group :
@@ -76,127 +113,64 @@ def make_title(queryargs):
76113def chart_newrevisiondocevent (request ):
77114 return render_to_response ("doc/stats/highstock.html" , {
78115 "title" : "Document Statistics" ,
116+ "confurl" : urlreverse ("ietf.doc.views_stats.chart_conf_newrevisiondocevent" ),
79117 "dataurl" : urlreverse ("ietf.doc.views_stats.chart_data_newrevisiondocevent" ),
80118 "queryargs" : request .GET .urlencode (),
81119 },
82120 context_instance = RequestContext (request ))
83121
84- def dt (s ):
85- "convert the string from sqlite's date() to a datetime.date"
86- ys , ms , ds = s .split ('-' )
87- return datetime .date (int (ys ), int (ms ), int (ds ))
88-
89- def model_to_timeline (model , ** kwargs ):
90- """Takes a Django model and a set of queryset filter arguments, and
91- returns a dictionary with highchart settings and data, suitable as
92- a JsonResponse() argument. The model must have a time field."""
93- #debug.pprint('model._meta.fields')
94- assert 'time' in model ._meta .get_all_field_names ()
95-
96- objects = ( model .objects .filter (** kwargs )
97- .order_by ('date' )
98- .extra (select = {'date' : 'date(doc_docevent.time)' })
99- .values ('date' )
100- .annotate (count = Count ('id' )))
101- if objects .exists ():
102- # debug.lap('got event query')
103- obj_list = list (objects )
104- # debug.lap('got event list')
105- # This is needed for sqlite, when we're running tests:
106- if type (obj_list [0 ]['date' ]) != datetime .date :
107- # debug.say('converting string dates to datetime.date')
108- obj_list = [ {'date' : dt (e ['date' ]), 'count' : e ['count' ]} for e in obj_list ]
109- points = [ ((e ['date' ].toordinal ()- epochday )* 1000 * 60 * 60 * 24 , e ['count' ]) for e in obj_list ]
110- # debug.lap('got event points')
111- counts = dict (points )
112- # debug.lap('got points dictionary')
113- day_ms = 1000 * 60 * 60 * 24
114- days = range (points [0 ][0 ], points [- 1 ][0 ]+ day_ms , day_ms )
115- # debug.lap('got days array')
116- data = [ (d , counts [d ] if d in counts else 0 ) for d in days ]
117- # debug.lap('merged points into days')
118- else :
119- data = []
120-
121- info = {
122- "chart" : {
123- "type" : 'column'
124- },
125- "rangeSelector" : {
126- "selected" : 4 ,
127- "allButtonsEnabled" : True ,
128- },
129- "title" : {
130- "text" : "%s items over time" % model ._meta .model_name
131- },
132- "credits" : {
133- "enabled" : False ,
134- },
135- "series" : [{
136- "name" : "Items" ,
137- "type" : "column" ,
138- "data" : data ,
139- "dataGrouping" : {
140- "units" : [[
141- 'week' , # unit name
142- [1 ,], # allowed multiples
143- ], [
144- 'month' ,
145- [1 , 4 ,],
146- ]]
147- },
148- "turboThreshold" : 1 , # Only check format of first data point. All others are the same
149- "pointInterval" : 24 * 60 * 60 * 1000 ,
150- "pointPadding" : 0.05 ,
151- }]
152- }
153- return info
154-
155-
156-
157- @cache_page (60 * 15 )
122+ #@cache_page(60*15)
158123def chart_data_newrevisiondocevent (request ):
159- # debug.mark()
160124 queryargs = request .GET
161125 if queryargs :
162- # debug.lap('got queryargs')
163126 key = get_search_cache_key (queryargs )
164- # debug.lap('got cache key')
165127 results = cache .get (key )
166- # debug.lap('did cache lookup')
167128 if not results :
168- # debug.say('doing new search')
169129 form = SearchForm (queryargs )
170- # debug.lap('set up search form')
171130 if not form .is_valid ():
172131 return HttpResponseBadRequest ("form not valid: %s" % form .errors )
173132 results = retrieve_search_results (form )
174- # debug.lap('got search result')
175133 if results .exists ():
176134 cache .set (key , results )
177- # debug.lap('cached search result')
178135 if results .exists ():
179- info = model_to_timeline (DocEvent , doc__in = results , type = 'new_revision' )
180- info ['title' ]['text' ] = make_title (queryargs )
181- info ['series' ][0 ]['name' ] = "Submitted %s" % get_doctypes (queryargs , pluralize = True ).lower (),
136+ data = model_to_timeline_data (DocEvent , doc__in = results , type = 'new_revision' )
182137 else :
183- info = {}
184- # debug.clock('set up info dict')
138+ data = []
185139 else :
186- info = {}
187- return JsonResponse (info )
140+ data = []
141+ return JsonResponse (data , safe = False )
188142
189143
144+ @cache_page (60 * 15 )
145+ def chart_conf_newrevisiondocevent (request ):
146+ queryargs = request .GET
147+ if queryargs :
148+ conf = copy .deepcopy (settings .CHART_TYPE_COLUMN_OPTIONS )
149+ conf ['title' ]['text' ] = make_title (queryargs )
150+ conf ['series' ][0 ]['name' ] = "Submitted %s" % get_doctypes (queryargs , pluralize = True ).lower (),
151+ else :
152+ conf = {}
153+ return JsonResponse (conf )
154+
155+
156+ @cache_page (60 * 15 )
157+ def chart_conf_person_drafts (request , id ):
158+ person = Person .objects .filter (id = id ).first ()
159+ if not person :
160+ conf = {}
161+ else :
162+ conf = copy .deepcopy (settings .CHART_TYPE_COLUMN_OPTIONS )
163+ conf ['title' ]['text' ] = "New draft revisions over time for %s" % person .name
164+ conf ['series' ][0 ]['name' ] = "Submitted drafts"
165+ return JsonResponse (conf )
166+
190167@cache_page (60 * 15 )
191168def chart_data_person_drafts (request , id ):
192- # debug.mark()
193169 person = Person .objects .filter (id = id ).first ()
194170 if not person :
195- info = {}
171+ data = []
196172 else :
197- info = model_to_timeline (DocEvent , doc__authors__person = person , type = 'new_revision' )
198- info ['title' ]['text' ] = "New draft revisions over time for %s" % person .name
199- info ['series' ][0 ]['name' ] = "Submitted drafts"
200- return JsonResponse (info )
173+ data = model_to_timeline_data (DocEvent , doc__authors__person = person , type = 'new_revision' )
174+ return JsonResponse (data , safe = False )
201175
202176
0 commit comments