Skip to content

Commit 213ae49

Browse files
committed
Merged in /personal/lars/6.11.1.dev0@10590 from lars@netapp.com, which brings in a timeline view at the top of document pages.
- Legacy-Id: 10597
2 parents a791d02 + cf49640 commit 213ae49

13 files changed

Lines changed: 371 additions & 20 deletions

.eslintrc.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module.exports = {
2+
rules: {
3+
indent: [2, 4],
4+
camelcase: 0,
5+
"require-jsdoc": 0,
6+
quotes: [2, "double"],
7+
"no-multiple-empty-lines": [2, {max: 2}],
8+
"quote-props": [2, "as-needed"],
9+
"brace-style": [2, "1tbs", {allowSingleLine: true}]
10+
},
11+
env: {
12+
browser: true,
13+
jquery: true
14+
},
15+
globals: {
16+
d3: true
17+
},
18+
extends: "google"
19+
};

ietf/bower.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": [],
66
"dependencies": {
77
"bootstrap-datepicker": "1.5.0",
8+
"d3": "3.5.10",
89
"font-awesome": "4.5.0",
910
"html5shiv": "3.7.3",
1011
"jquery": "1.11.3",

ietf/doc/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/emailposition/$', views_ballot.send_ballot_comment, name='doc_send_ballot_comment'),
6666
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/$', views_doc.document_ballot, name="doc_ballot"),
6767
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/$', views_doc.document_ballot, name="doc_ballot"),
68-
(r'^(?P<name>[A-Za-z0-9._+-]+)/doc.json$', views_doc.document_json),
68+
(r'^(?P<name>[A-Za-z0-9._+-]+)/(?:(?P<rev>[0-9-]+)/)?doc.json$', views_doc.document_json),
6969
(r'^(?P<name>[A-Za-z0-9._+-]+)/ballotpopup/(?P<ballot_id>[0-9]+)/$', views_doc.ballot_popup),
7070

7171
url(r'^(?P<name>[A-Za-z0-9._+-]+)/email-aliases/$', RedirectView.as_view(pattern_name='doc_email', permanent=False),name='doc_specific_email_aliases'),

ietf/doc/utils.py

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.db.models.query import EmptyQuerySet
99
from django.forms import ValidationError
1010
from django.utils.html import strip_tags, escape
11+
from django.core.urlresolvers import reverse as urlreverse
1112

1213
from ietf.doc.models import Document, DocHistory, State
1314
from ietf.doc.models import DocAlias, RelatedDocument, BallotType, DocReminder
@@ -27,7 +28,7 @@ def email_update_telechat(request, doc, text):
2728

2829
if not to:
2930
return
30-
31+
3132
text = strip_tags(text)
3233
send_mail(request, to, None,
3334
"Telechat update notice: %s" % doc.file_tag(),
@@ -41,7 +42,7 @@ def get_state_types(doc):
4142

4243
if not doc:
4344
return res
44-
45+
4546
res.append(doc.type_id)
4647

4748
if doc.type_id == "draft":
@@ -52,7 +53,7 @@ def get_state_types(doc):
5253
res.append("draft-iana-review")
5354
res.append("draft-iana-action")
5455
res.append("draft-rfceditor")
55-
56+
5657
return res
5758

5859
def get_tags_for_stream_id(stream_id):
@@ -144,7 +145,7 @@ def needed_ballot_positions(doc, active_positions):
144145
answer.append("Has enough positions to pass.")
145146

146147
return " ".join(answer)
147-
148+
148149
def create_ballot_if_not_open(doc, by, ballot_slug, time=None):
149150
if not doc.ballot_open(ballot_slug):
150151
if time:
@@ -359,7 +360,7 @@ def make_notify_changed_event(request, doc, by, new_notify, time=None):
359360

360361
def update_telechat(request, doc, by, new_telechat_date, new_returning_item=None):
361362
from ietf.doc.models import TelechatDocEvent
362-
363+
363364
on_agenda = bool(new_telechat_date)
364365

365366
prev = doc.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
@@ -378,7 +379,7 @@ def update_telechat(request, doc, by, new_telechat_date, new_returning_item=None
378379

379380
# auto-set returning item _ONLY_ if the caller did not provide a value
380381
if ( new_returning_item != None
381-
and on_agenda
382+
and on_agenda
382383
and prev_agenda
383384
and new_telechat_date != prev_telechat
384385
and prev_telechat < datetime.date.today()
@@ -392,7 +393,7 @@ def update_telechat(request, doc, by, new_telechat_date, new_returning_item=None
392393
e.doc = doc
393394
e.returning_item = returning
394395
e.telechat_date = new_telechat_date
395-
396+
396397
if on_agenda != prev_agenda:
397398
if on_agenda:
398399
e.desc = "Placed on agenda for telechat - %s" % (new_telechat_date)
@@ -426,7 +427,7 @@ def rebuild_reference_relations(doc,filename=None):
426427
refs = draft.Draft(draft._gettext(filename), filename).get_refs()
427428
except IOError as e:
428429
return { 'errors': ["%s :%s" % (e.strerror, filename)] }
429-
430+
430431
doc.relateddocument_set.filter(relationship__slug__in=['refnorm','refinfo','refold','refunk']).delete()
431432

432433
warnings = []
@@ -449,11 +450,11 @@ def rebuild_reference_relations(doc,filename=None):
449450

450451
ret = {}
451452
if errors:
452-
ret['errors']=errors
453+
ret['errors']=errors
453454
if warnings:
454-
ret['warnings']=warnings
455+
ret['warnings']=warnings
455456
if unfound:
456-
ret['unfound']=list(unfound)
457+
ret['unfound']=list(unfound)
457458

458459
return ret
459460

@@ -539,3 +540,41 @@ def uppercase_std_abbreviated_name(name):
539540
return name.upper()
540541
else:
541542
return name
543+
544+
def crawl_history(doc):
545+
# return document history data for inclusion in doc.json (used by timeline)
546+
def ancestors(doc):
547+
retval = []
548+
if hasattr(doc, 'relateddocument_set'):
549+
for rel in doc.relateddocument_set.filter(relationship__slug='replaces'):
550+
if rel.target.document not in retval:
551+
retval.append(rel.target.document)
552+
retval.extend(ancestors(rel.target.document))
553+
return retval
554+
555+
retval = []
556+
history = ancestors(doc)
557+
if history is not None:
558+
history.append(doc)
559+
for d in history:
560+
for e in d.docevent_set.filter(type='new_revision'):
561+
if hasattr(e, 'newrevisiondocevent'):
562+
retval.append({
563+
'name': d.name,
564+
'rev': e.newrevisiondocevent.rev,
565+
'published': e.time.isoformat(),
566+
'url': urlreverse("doc_view", kwargs=dict(name=d)) + e.newrevisiondocevent.rev + "/"
567+
})
568+
569+
if doc.type_id == "draft":
570+
e = doc.latest_event(type='published_rfc')
571+
else:
572+
e = doc.latest_event(type='iesg_approved')
573+
if e:
574+
retval.append({
575+
'name': e.doc.canonical_name(),
576+
'rev': e.doc.canonical_name(),
577+
'published': e.time.isoformat(),
578+
'url': urlreverse("doc_view", kwargs=dict(name=e.doc))
579+
})
580+
return sorted(retval, key=lambda x: x['published'])

ietf/doc/views_doc.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
from ietf.doc.utils import ( add_links_in_new_revision_events, augment_events_with_revision,
5050
can_adopt_draft, get_chartering_type, get_document_content, get_tags_for_stream_id,
5151
needed_ballot_positions, nice_consensus, prettify_std_name, update_telechat, has_same_ballot,
52-
get_initial_notify, make_notify_changed_event )
52+
get_initial_notify, make_notify_changed_event, crawl_history)
5353
from ietf.community.models import CommunityList
5454
from ietf.group.models import Role
5555
from ietf.group.utils import can_manage_group_type, can_manage_materials
@@ -878,7 +878,7 @@ def ballot_popup(request, name, ballot_id):
878878
context_instance=RequestContext(request))
879879

880880

881-
def document_json(request, name):
881+
def document_json(request, name, rev=None):
882882
doc = get_object_or_404(Document, docalias__name=name)
883883

884884
def extract_name(s):
@@ -911,6 +911,9 @@ def extract_name(s):
911911
data["shepherd"] = doc.shepherd.formatted_email() if doc.shepherd else None
912912
data["ad"] = doc.ad.role_email("ad").formatted_email() if doc.ad else None
913913

914+
latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision")
915+
data["rev_history"] = crawl_history(latest_revision.doc if latest_revision else doc)
916+
914917
if doc.type_id == "draft":
915918
data["iesg_state"] = extract_name(doc.get_state("draft-iesg"))
916919
data["rfceditor_state"] = extract_name(doc.get_state("draft-rfceditor"))
@@ -922,7 +925,7 @@ def extract_name(s):
922925
data["consensus"] = e.consensus if e else None
923926
data["stream"] = extract_name(doc.stream)
924927

925-
return HttpResponse(json.dumps(data, indent=2), content_type='text/plain')
928+
return HttpResponse(json.dumps(data, indent=2), content_type='application/json')
926929

927930
class AddCommentForm(forms.Form):
928931
comment = forms.CharField(required=True, widget=forms.Textarea)

ietf/externals/static/d3/d3.min.js

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ietf/static/ietf/css/ietf.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,32 @@ h1 small .pull-right { margin-top: 10.5px; }
431431
*/
432432
form.navbar-form input.form-control.input-sm { width: 141px; }
433433

434+
435+
436+
/* Styles for d3.js graphical SVG timelines */
437+
#timeline { font-size: small; }
438+
439+
#timeline .axis path, #timeline .axis line {
440+
fill: none;
441+
stroke: black;
442+
}
443+
444+
#timeline .axis.y path, #timeline .axis.y line { stroke: none; }
445+
446+
#timeline .axis.x text { dominant-baseline: central; }
447+
448+
#timeline .bar text {
449+
fill: white;
450+
dominant-baseline: central;
451+
pointer-events: none;
452+
}
453+
454+
/* like label-success */
455+
#timeline .bar:nth-child(odd) rect { fill: #5CB85C; }
456+
457+
/* like label-primary */
458+
#timeline .bar:nth-child(even) rect { fill: #337AB7; }
459+
460+
/* like label-warning */
461+
#timeline .gradient.left { stop-color: #F0AD4E; }
462+
#timeline .gradient.right { stop-color: white; }

0 commit comments

Comments
 (0)