Skip to content

Commit 74f7c57

Browse files
committed
Added some basic charting support, using highcharts, and charts showing new-revision timelines for document search results.
- Legacy-Id: 11927
2 parents fdb37c9 + b0906f0 commit 74f7c57

16 files changed

Lines changed: 1526 additions & 16 deletions

File tree

debug.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
# Number of times to indent output
3737
# A list is used to force access by reference
3838
_report_indent = [4]
39-
_mark = [ timeutils.clock() ]
39+
_mark = [ timeutils.time() ]
4040

4141
def set_indent(i):
4242
_report_indent[0] = i
@@ -82,27 +82,29 @@ def wrap(fn, *params,**kwargs):
8282
return fn
8383

8484
def mark():
85-
_mark[0] = timeutils.clock()
85+
_mark[0] = timeutils.time()
8686

8787
def lap(s):
88-
tau = timeutils.clock() - _mark[0]
89-
say("> %s: %.3fs since mark" % (s, tau))
88+
clk = timeutils.time()
89+
tau = clk - _mark[0]
90+
ts = timeutils.strftime("%H:%M:%S", timeutils.localtime(clk))
91+
say("%s: %.3fs since mark: %s" % (ts, tau, s))
9092

9193
def clock(s):
9294
lap(s)
93-
_mark[0] = timeutils.clock()
95+
_mark[0] = timeutils.time()
9496

9597
def time(fn):
9698
"""Decorator to print timing information about a function call.
9799
"""
98100
def wrap(fn, *params,**kwargs):
99-
mark = timeutils.clock()
101+
mark = timeutils.time()
100102

101103
indent = ' ' * _report_indent[0]
102104
fc = "%s.%s()" % (fn.__module__, fn.__name__,)
103105

104106
ret = fn(*params,**kwargs)
105-
tau = timeutils.clock() - mark
107+
tau = timeutils.time() - mark
106108
sys.stderr.write("%s| %s | %.3fs\n" % (indent, fc, tau))
107109

108110
return ret
@@ -160,6 +162,7 @@ def say(s):
160162
if debug:
161163
indent = ' ' * (_report_indent[0])
162164
sys.stderr.write("%s%s\n" % (indent, s))
165+
sys.stderr.flush()
163166

164167
def profile(fn):
165168
def wrapper(*args, **kwargs):

ietf/bower.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"d3": "~3",
99
"font-awesome": "~4",
1010
"html5shiv": "~3",
11+
"highcharts": "~4.2",
1112
"jquery": "~1",
1213
"jquery.cookie": "~1",
1314
"jquery.tablesorter": "~2",
@@ -31,6 +32,16 @@
3132
},
3233
"respond": {
3334
"main": "dest/respond.min.js"
35+
},
36+
"highcharts": {
37+
"main": [
38+
"highcharts.js",
39+
"highcharts-more.js",
40+
"highstock.js",
41+
"highmaps.js",
42+
"modules/exporting.js",
43+
"modules/map.js"
44+
]
3445
}
3546
}
3647
}

ietf/doc/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,8 @@ def latest_event(self, *args, **filter_args):
415415

416416
def canonical_name(self):
417417
from ietf.doc.utils_charter import charter_name_for_group # Imported locally to avoid circular imports
418+
if hasattr(self, '_canonical_name'):
419+
return self._canonical_name
418420
name = self.name
419421
if self.type_id == "draft" and self.get_state_slug() == "rfc":
420422
a = self.docalias_set.filter(name__startswith="rfc")
@@ -459,6 +461,8 @@ def save(self, *args, **kwargs):
459461
super(Document, self).save(*args, **kwargs)
460462

461463
def telechat_date(self, e=None):
464+
if hasattr(self, '_telechat_date'):
465+
return self._telechat_date
462466
if not e:
463467
e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
464468
return e.telechat_date if e and e.telechat_date and e.telechat_date >= datetime.date.today() else None
@@ -574,6 +578,8 @@ def __unicode__(self):
574578
return unicode(self.doc.name)
575579

576580
def canonical_name(self):
581+
if hasattr(self, '_canonical_name'):
582+
return self._canonical_name
577583
return self.name
578584

579585
def latest_event(self, *args, **kwargs):

ietf/doc/tests.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from django.core.urlresolvers import reverse as urlreverse
1616
from django.conf import settings
1717

18+
from tastypie.test import ResourceTestCaseMixin
19+
1820
import debug # pyflakes:ignore
1921

2022
from ietf.doc.models import ( Document, DocAlias, DocRelationshipName, RelatedDocument, State,
@@ -1097,3 +1099,35 @@ def test_add_document_session(self):
10971099
response = self.client.post(url,{'session':self.future.pk,'version':'current'})
10981100
self.assertEqual(response.status_code,302)
10991101
self.assertEqual(2,doc.docevent_set.count())
1102+
1103+
1104+
class ChartTests(ResourceTestCaseMixin, TestCase):
1105+
def test_stats(self):
1106+
doc = DocumentFactory.create(states=[('draft','active')])
1107+
1108+
data_url = urlreverse("ietf.doc.views_stats.chart_data_newrevisiondocevent")
1109+
1110+
# No qurey arguments; expect an empty json object
1111+
r = self.client.get(data_url)
1112+
self.assertValidJSONResponse(r)
1113+
self.assertEqual(r.content, "{}")
1114+
1115+
# No match
1116+
r = self.client.get(data_url + "?activedrafts=on&name=thisisnotadocumentname")
1117+
self.assertValidJSONResponse(r)
1118+
d = json.loads(r.content)
1119+
self.assertEqual(d['series'][0]['data'], [])
1120+
1121+
r = self.client.get(data_url + "?activedrafts=on&name=%s"%doc.name[6:12])
1122+
self.assertValidJSONResponse(r)
1123+
d = json.loads(r.content)
1124+
self.assertEqual(len(d['series'][0]['data']), 1)
1125+
1126+
chart_url = urlreverse("ietf.doc.views_stats.chart_newrevisiondocevent")
1127+
r = self.client.get(chart_url)
1128+
self.assertEqual(r.status_code, 200)
1129+
1130+
r = self.client.get(chart_url + "?activedrafts=on&name=%s"%doc.name[6:12])
1131+
self.assertEqual(r.status_code, 200)
1132+
1133+

ietf/doc/urls.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@
3434
from django.views.generic import RedirectView
3535
from django.conf import settings
3636

37-
from ietf.doc import views_search, views_draft, views_ballot
38-
from ietf.doc import views_status_change
39-
from ietf.doc import views_doc
37+
from ietf.doc import views_search, views_draft, views_ballot, views_status_change, views_doc, views_stats
4038

4139
session_patterns = [
4240
url(r'^add$', views_doc.add_sessionpresentation),
@@ -55,6 +53,8 @@
5553
url(r'^start-rfc-status-change/(?:%(name)s/)?$' % settings.URL_REGEXPS, views_status_change.start_rfc_status_change, name='start_rfc_status_change'),
5654
url(r'^iesg/(?P<last_call_only>[A-Za-z0-9.-]+/)?$', views_search.drafts_in_iesg_process, name="drafts_in_iesg_process"),
5755
url(r'^email-aliases/$', views_doc.email_aliases),
56+
url(r'^stats/newrevisiondocevent/?$', views_stats.chart_newrevisiondocevent),
57+
url(r'^stats/data/newrevisiondocevent/?$', views_stats.chart_data_newrevisiondocevent),
5858

5959
url(r'^all/$', views_search.index_all_drafts, name="index_all_drafts"),
6060
url(r'^active/$', views_search.index_active_drafts, name="index_active_drafts"),

ietf/doc/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import urllib
44
import math
55
import datetime
6+
import hashlib
7+
import json
68

79
from django.conf import settings
810
from django.db.models.query import EmptyQuerySet
@@ -631,3 +633,12 @@ def get_ancestors(doc):
631633
history[url]['pages'] = doc.history_set.filter(rev=e.newrevisiondocevent.rev).first().pages
632634
history = history.values()
633635
return sorted(history, key=lambda x: x['published'])
636+
637+
638+
def get_search_cache_key(params):
639+
from ietf.doc.views_search import SearchForm
640+
fields = set(SearchForm.base_fields) - set(['sort',])
641+
kwargs = dict([ (k,v) for (k,v) in params.items() if k in fields ])
642+
key = "doc:document:search:" + hashlib.sha512(json.dumps(kwargs, sort_keys=True)).hexdigest()
643+
return key
644+

ietf/doc/views_search.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,15 @@
3030
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3131
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3232

33-
import datetime, re
33+
import re
34+
import datetime
3435

3536
from django import forms
3637
from django.conf import settings
3738
from django.core.cache import cache
3839
from django.core.urlresolvers import reverse as urlreverse
3940
from django.db.models import Q
40-
from django.http import Http404, HttpResponseBadRequest, HttpResponse, HttpResponseRedirect
41+
from django.http import Http404, HttpResponseBadRequest, HttpResponse, HttpResponseRedirect, QueryDict
4142
from django.shortcuts import render
4243
from django.utils.cache import _generate_cache_key
4344

@@ -46,6 +47,7 @@
4647
from ietf.doc.models import ( Document, DocHistory, DocAlias, State,
4748
LastCallDocEvent, IESG_SUBSTATE_TAGS )
4849
from ietf.doc.fields import select2_id_doc_name_json
50+
from ietf.doc.utils import get_search_cache_key
4951
from ietf.group.models import Group
5052
from ietf.idindex.index import active_drafts_index_by_group
5153
from ietf.name.models import DocTagName, DocTypeName, StreamName
@@ -164,7 +166,7 @@ def retrieve_search_results(form, all_types=False):
164166
# radio choices
165167
by = query["by"]
166168
if by == "author":
167-
docs = docs.filter(authors__person__name__icontains=query["author"])
169+
docs = docs.filter(authors__person__alias__name__icontains=query["author"])
168170
elif by == "group":
169171
docs = docs.filter(group__acronym=query["group"])
170172
elif by == "area":
@@ -197,16 +199,22 @@ def search(request):
197199
if not form.is_valid():
198200
return HttpResponseBadRequest("form not valid: %s" % form.errors)
199201

200-
results = retrieve_search_results(form)
202+
key = get_search_cache_key(get_params)
203+
results = cache.get(key)
204+
if not results:
205+
results = retrieve_search_results(form)
206+
cache.set(key, results)
207+
201208
results, meta = prepare_document_table(request, results, get_params)
202209
meta['searching'] = True
203210
else:
204211
form = SearchForm()
205212
results = []
206213
meta = { 'by': None, 'searching': False }
214+
get_params = QueryDict('')
207215

208216
return render(request, 'doc/search/search.html', {
209-
'form':form, 'docs':results, 'meta':meta, },
217+
'form':form, 'docs':results, 'meta':meta, 'queryargs':get_params.urlencode() },
210218
)
211219

212220
def frontpage(request):

0 commit comments

Comments
 (0)