2424from ietf .group .models import Role , Group
2525from ietf .person .models import Person
2626from ietf .name .models import ReviewRequestStateName , ReviewResultName
27- from ietf .doc .models import DocAlias
27+ from ietf .doc .models import DocAlias , Document
2828from ietf .ietfauth .utils import has_role
2929
3030def stats_index (request ):
@@ -54,49 +54,75 @@ def generate_query_string(query_dict, overrides):
5454
5555 return query_part
5656
57- def document_stats (request , stats_type = None , document_type = None ):
58- def build_document_stats_url (stats_type_override = Ellipsis , document_type_override = Ellipsis , get_overrides = {}):
57+ def get_choice (request , get_parameter , possible_choices , multiple = False ):
58+ # the statistics are built with links to make navigation faster,
59+ # so we don't really have a form in most cases, so just use this
60+ # helper instead to select between the choices
61+ values = request .GET .getlist (get_parameter )
62+ found = [t [0 ] for t in possible_choices if t [0 ] in values ]
63+
64+ if multiple :
65+ return found
66+ else :
67+ if found :
68+ return found [0 ]
69+ else :
70+ return None
71+
72+ def add_url_to_choices (choices , url_builder ):
73+ return [ (slug , label , url_builder (slug )) for slug , label in choices ]
74+
75+ def put_into_bin (value , bin_size ):
76+ if value is None :
77+ return (value , value )
78+
79+ v = (value // bin_size ) * bin_size
80+ return (v , "{} - {}" .format (v , v + bin_size - 1 ))
81+
82+ def document_stats (request , stats_type = None ):
83+ def build_document_stats_url (stats_type_override = Ellipsis , get_overrides = {}):
5984 kwargs = {
6085 "stats_type" : stats_type if stats_type_override is Ellipsis else stats_type_override ,
61- "document_type" : document_type if document_type_override is Ellipsis else document_type_override ,
6286 }
6387
6488 return urlreverse (document_stats , kwargs = { k : v for k , v in kwargs .iteritems () if v is not None }) + generate_query_string (request .GET , get_overrides )
6589
6690 # statistics type - one of the tables or the chart
67- possible_stats_types = [
91+ possible_stats_types = add_url_to_choices ( [
6892 ("authors" , "Authors" ),
6993 ("pages" , "Pages" ),
7094 ("words" , "Words" ),
7195 ("format" , "Format" ),
7296 ("formlang" , "Formal languages" ),
73- ]
74-
75- possible_stats_types = [ (slug , label , build_document_stats_url (stats_type_override = slug ))
76- for slug , label in possible_stats_types ]
97+ ], lambda slug : build_document_stats_url (stats_type_override = slug ))
7798
7899 if not stats_type :
79100 return HttpResponseRedirect (build_document_stats_url (stats_type_override = possible_stats_types [0 ][0 ]))
80101
81- possible_document_types = [
82- ("all" , "All" ),
102+
103+ possible_document_types = add_url_to_choices ([
104+ ("" , "All" ),
83105 ("rfc" , "RFCs" ),
84106 ("draft" , "Drafts" ),
85- ]
107+ ], lambda slug : build_document_stats_url ( get_overrides = { "type" : slug }))
86108
87- possible_document_types = [ (slug , label , build_document_stats_url (document_type_override = slug ))
88- for slug , label in possible_document_types ]
109+ document_type = get_choice (request , "type" , possible_document_types ) or ""
89110
90- if not document_type :
91- return HttpResponseRedirect (build_document_stats_url (document_type_override = possible_document_types [0 ][0 ]))
92-
93111
94- def put_into_bin (value , bin_size ):
95- if value is None :
96- return (value , value )
112+ possible_time_choices = add_url_to_choices ([
113+ ("" , "All time" ),
114+ ("5y" , "Past 5 years" ),
115+ ], lambda slug : build_document_stats_url (get_overrides = { "time" : slug }))
97116
98- v = (value // bin_size ) * bin_size
99- return (v , "{} - {}" .format (v , v + bin_size - 1 ))
117+ time_choice = request .GET .get ("time" ) or ""
118+
119+ from_time = None
120+ if "y" in time_choice :
121+ try :
122+ years = int (time_choice .rstrip ("y" ))
123+ from_time = datetime .datetime .today () - dateutil .relativedelta .relativedelta (years = years )
124+ except ValueError :
125+ pass
100126
101127 def generate_canonical_names (docalias_qs ):
102128 for doc_id , ts in itertools .groupby (docalias_qs .order_by ("document" ), lambda t : t [0 ]):
@@ -120,15 +146,26 @@ def generate_canonical_names(docalias_qs):
120146 elif document_type == "draft" :
121147 docalias_qs = docalias_qs .exclude (document__states__type = "draft" , document__states__slug = "rfc" )
122148
149+ if from_time :
150+ # this is actually faster than joining in the database,
151+ # despite the round-trip back and forth
152+ docs_within_time_constraint = list (Document .objects .filter (
153+ type = "draft" ,
154+ docevent__time__gte = from_time ,
155+ docevent__type__in = ["published_rfc" , "new_revision" ],
156+ ).values_list ("pk" ))
157+
158+ docalias_qs = docalias_qs .filter (document__in = docs_within_time_constraint )
159+
123160 chart_data = []
124161 table_data = []
125162
126- if document_type == "all" :
127- doc_label = "document"
128- elif document_type == "rfc" :
163+ if document_type == "rfc" :
129164 doc_label = "RFC"
130165 elif document_type == "draft" :
131166 doc_label = "draft"
167+ else :
168+ doc_label = "document"
132169
133170 stats_title = ""
134171 bin_size = 1
@@ -280,6 +317,8 @@ def generate_canonical_names(docalias_qs):
280317 "stats_type" : stats_type ,
281318 "possible_document_types" : possible_document_types ,
282319 "document_type" : document_type ,
320+ "possible_time_choices" : possible_time_choices ,
321+ "time_choice" : time_choice ,
283322 "doc_label" : doc_label ,
284323 "bin_size" : bin_size ,
285324 "content_template" : "stats/document_stats_{}.html" .format (stats_type ),
@@ -306,18 +345,6 @@ def build_review_stats_url(stats_type_override=Ellipsis, acronym_override=Ellips
306345
307346 return urlreverse (review_stats , kwargs = kwargs ) + generate_query_string (request .GET , get_overrides )
308347
309- def get_choice (get_parameter , possible_choices , multiple = False ):
310- values = request .GET .getlist (get_parameter )
311- found = [t [0 ] for t in possible_choices if t [0 ] in values ]
312-
313- if multiple :
314- return found
315- else :
316- if found :
317- return found [0 ]
318- else :
319- return None
320-
321348 # which overview - team or reviewer
322349 if acronym :
323350 level = "reviewer"
@@ -334,21 +361,19 @@ def get_choice(get_parameter, possible_choices, multiple=False):
334361 if level == "team" :
335362 possible_stats_types .append (("time" , "Changes over time" ))
336363
337- possible_stats_types = [ ( slug , label , build_review_stats_url ( stats_type_override = slug ))
338- for slug , label in possible_stats_types ]
364+ possible_stats_types = add_url_to_choices ( possible_stats_types ,
365+ lambda slug : build_review_stats_url ( stats_type_override = slug ))
339366
340367 if not stats_type :
341368 return HttpResponseRedirect (build_review_stats_url (stats_type_override = possible_stats_types [0 ][0 ]))
342369
343370 # what to count
344- possible_count_choices = [
371+ possible_count_choices = add_url_to_choices ( [
345372 ("" , "Review requests" ),
346373 ("pages" , "Reviewed pages" ),
347- ]
348-
349- possible_count_choices = [ (slug , label , build_review_stats_url (get_overrides = { "count" : slug })) for slug , label in possible_count_choices ]
374+ ], lambda slug : build_review_stats_url (get_overrides = { "count" : slug }))
350375
351- count = get_choice ("count" , possible_count_choices ) or ""
376+ count = get_choice (request , "count" , possible_count_choices ) or ""
352377
353378 # time range
354379 def parse_date (s ):
@@ -433,7 +458,7 @@ def parse_date(s):
433458
434459 if stats_type == "time" :
435460 possible_teams = [(t .acronym , t .acronym ) for t in teams ]
436- selected_teams = get_choice ("team" , possible_teams , multiple = True )
461+ selected_teams = get_choice (request , "team" , possible_teams , multiple = True )
437462
438463 def add_if_exists_else_subtract (element , l ):
439464 if element in l :
@@ -475,33 +500,28 @@ def time_key_fn(t):
475500
476501 # choice
477502
478- possible_completion_types = [
503+ possible_completion_types = add_url_to_choices ( [
479504 ("completed_in_time" , "Completed in time" ),
480505 ("completed_late" , "Completed late" ),
481506 ("not_completed" , "Not completed" ),
482507 ("average_assignment_to_closure_days" , "Avg. compl. days" ),
483- ]
484-
485- possible_completion_types = [
486- (slug , label , build_review_stats_url (get_overrides = { "completion" : slug , "result" : None , "state" : None }))
487- for slug , label in possible_completion_types
488- ]
508+ ], lambda slug : build_review_stats_url (get_overrides = { "completion" : slug , "result" : None , "state" : None }))
489509
490- selected_completion_type = get_choice ("completion" , possible_completion_types )
510+ selected_completion_type = get_choice (request , "completion" , possible_completion_types )
491511
492- possible_results = [
493- (r .slug , r .name , build_review_stats_url ( get_overrides = { "completion" : None , "result" : r . slug , "state" : None }))
494- for r in results
495- ]
512+ possible_results = add_url_to_choices (
513+ [ (r .slug , r .name ) for r in results ],
514+ lambda slug : build_review_stats_url ( get_overrides = { "completion" : None , "result" : slug , "state" : None })
515+ )
496516
497- selected_result = get_choice ("result" , possible_results )
517+ selected_result = get_choice (request , "result" , possible_results )
498518
499- possible_states = [
500- (s .slug , s .name , build_review_stats_url ( get_overrides = { "completion" : None , "result" : None , "state" : s . slug }))
501- for s in states
502- ]
519+ possible_states = add_url_to_choices (
520+ [ (s .slug , s .name ) for s in states ],
521+ build_review_stats_url ( get_overrides = { "completion" : None , "result" : None , "state" : slug })
522+ )
503523
504- selected_state = get_choice ("state" , possible_states )
524+ selected_state = get_choice (request , "state" , possible_states )
505525
506526 if not selected_completion_type and not selected_result and not selected_state :
507527 selected_completion_type = "completed_in_time"
0 commit comments