Skip to content

Commit ce21809

Browse files
committed
Revamp choice parsing a bit in statistics section, add time choice to
documents to be able to choose between all time and recent drafts - Legacy-Id: 12751
1 parent 3395d6f commit ce21809

3 files changed

Lines changed: 93 additions & 64 deletions

File tree

ietf/stats/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55

66
urlpatterns = patterns('',
77
url("^$", ietf.stats.views.stats_index),
8-
url("^document/(?:(?P<stats_type>authors|pages|words|format|formlang)/)?(?:(?P<document_type>all|rfc|draft)/)?$", ietf.stats.views.document_stats),
8+
url("^document/(?:(?P<stats_type>authors|pages|words|format|formlang)/)?$", ietf.stats.views.document_stats),
99
url("^review/(?:(?P<stats_type>completion|results|states|time)/)?(?:%(acronym)s/)?$" % settings.URL_REGEXPS, ietf.stats.views.review_stats),
1010
)

ietf/stats/views.py

Lines changed: 83 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from ietf.group.models import Role, Group
2525
from ietf.person.models import Person
2626
from ietf.name.models import ReviewRequestStateName, ReviewResultName
27-
from ietf.doc.models import DocAlias
27+
from ietf.doc.models import DocAlias, Document
2828
from ietf.ietfauth.utils import has_role
2929

3030
def 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"

ietf/templates/stats/document_stats.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ <h1>Document statistics</h1>
3333
{% endfor %}
3434
</div>
3535
</div>
36+
37+
<div>
38+
Time:
39+
<div class="btn-group">
40+
{% for slug, label, url in possible_time_choices %}
41+
<a class="btn btn-default {% if slug == time_choice %}active{% endif %}" href="{{ url }}">{{ label }}</a>
42+
{% endfor %}
43+
</div>
44+
</div>
3645
</div>
3746

3847
{% include content_template %}

0 commit comments

Comments
 (0)