From d19967ac933f6b3b1085b4e41bb3b1ca3ed7f97e Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Mon, 11 Nov 2013 23:12:53 +0000 Subject: [PATCH] Adds pages to show what a given document refers to and what refers to it. Fixes bug #1194 commit ready for merge - Legacy-Id: 6701 --- ietf/doc/models.py | 28 +++++++++ ietf/doc/tests.py | 18 ++++++ ietf/doc/urls.py | 4 ++ ietf/doc/views_doc.py | 23 ++++++++ ietf/doc/views_help.py | 15 +++++ ietf/templates/doc/document_draft.html | 2 + .../templates/doc/document_referenced_by.html | 59 +++++++++++++++++++ ietf/templates/doc/document_references.html | 54 +++++++++++++++++ ietf/templates/doc/relationship_help.html | 29 +++++++++ 9 files changed, 232 insertions(+) create mode 100644 ietf/templates/doc/document_referenced_by.html create mode 100644 ietf/templates/doc/document_references.html create mode 100644 ietf/templates/doc/relationship_help.html diff --git a/ietf/doc/models.py b/ietf/doc/models.py index ae6cbcac7..1cf8ae93b 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -167,6 +167,34 @@ class RelatedDocument(models.Model): def __unicode__(self): return u"%s %s %s" % (self.source.name, self.relationship.name.lower(), self.target.name) + def is_downref(self): + + if self.source.type.slug!='draft' or self.relationship.slug not in ['refnorm','refold','refunk']: + return None + + if self.source.get_state().slug == 'rfc': + source_lvl = self.source.std_level.slug + else: + source_lvl = self.source.intended_std_level.slug + + if source_lvl not in ['bcp','ps','ds','std']: + return None + + if self.target.document.get_state().slug == 'rfc': + target_lvl = self.target.document.std_level.slug + else: + target_lvl = self.target.document.intended_std_level.slug + + rank = { 'ps':1, 'ds':2, 'std':3, 'bcp':3 } + + if ( target_lvl not in rank ) or ( rank[target_lvl] < rank[source_lvl] ): + if self.relationship.slug == 'refnorm' and target_lvl!='unkn': + return "Downref" + else: + return "Possible Downref" + + return None + class DocumentAuthor(models.Model): document = models.ForeignKey('Document') author = models.ForeignKey(Email, help_text="Email address used by author for submission") diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 80b6e1f3f..7873da9fb 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -295,3 +295,21 @@ class TemplateTagTest(unittest.TestCase): from ietf.doc.templatetags import ietf_filters failures, tests = doctest.testmod(ietf_filters) self.assertEqual(failures, 0) + +class ReferencesTest(TestCase): + perma_fixtures = ['names'] + + def test_references(self): + make_test_data() + doc1 = Document.objects.get(name='draft-ietf-mars-test') + doc2 = DocAlias.objects.get(name='draft-imaginary-independent-submission') + RelatedDocument.objects.get_or_create(source=doc1,target=doc2,relationship=DocRelationshipName.objects.get(slug='refnorm')) + url = urlreverse('doc_references', kwargs=dict(name=doc1.name)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(doc2.name in r.content) + url = urlreverse('doc_referenced_by', kwargs=dict(name=doc2.name)) + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + self.assertTrue(doc1.name in r.content) + diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index 49df3de21..3ceb97c00 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -60,6 +60,8 @@ urlpatterns += patterns('', url(r'^(?P[A-Za-z0-9._+-]+)/history/$', views_doc.document_history, name="doc_history"), url(r'^(?P[A-Za-z0-9._+-]+)/writeup/$', views_doc.document_writeup, name="doc_writeup"), url(r'^(?P[A-Za-z0-9._+-]+)/shepherdwriteup/$', views_doc.document_shepherd_writeup, name="doc_shepherd_writeup"), + url(r'^(?P[A-Za-z0-9._+-]+)/references/$', views_doc.document_references, name="doc_references"), + url(r'^(?P[A-Za-z0-9._+-]+)/referencedby/$', views_doc.document_referenced_by, name="doc_referenced_by"), url(r'^(?P[A-Za-z0-9._+-]+)/ballot/(?P[0-9]+)/position/$', views_ballot.edit_position), url(r'^(?P[A-Za-z0-9._+-]+)/ballot/(?P[0-9]+)/emailposition/$', views_ballot.send_ballot_comment, name='doc_send_ballot_comment'), url(r'^(?P[A-Za-z0-9._+-]+)/ballot/(?P[0-9]+)/$', views_doc.document_ballot, name="doc_ballot"), @@ -97,6 +99,8 @@ urlpatterns += patterns('', url(r'^(?P[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'), url(r'^help/state/(?P[\w-]+)/$', 'ietf.doc.views_help.state_help', name="state_help"), + url(r'^help/relationships/$', 'ietf.doc.views_help.relationship_help', name="relationship_help"), + url(r'^help/relationships/(?P\w+)/$', 'ietf.doc.views_help.relationship_help', name="relationship_subset_help"), (r'^(?Pcharter-[A-Za-z0-9._+-]+)/', include('ietf.wgcharter.urls')), (r'^(?P[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')), diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 6328fcc1c..e64d90a33 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -609,6 +609,29 @@ def document_shepherd_writeup(request, name): ), context_instance=RequestContext(request)) +def document_references(request, name): + doc = get_object_or_404(Document,docalias__name=name) + refs = doc.relations_that_doc(['refnorm','refinfo','refunk','refold']) + return render_to_response("doc/document_references.html",dict(doc=doc,refs=sorted(refs,key=lambda x:x.target.name),),context_instance=RequestContext(request)) + +def document_referenced_by(request, name): + doc = get_object_or_404(Document,docalias__name=name) + refs = doc.relations_that(['refnorm','refinfo','refunk','refold']).filter(source__states__type__slug='draft',source__states__slug__in=['rfc','active']) + full = ( request.GET.get('full') != None ) + numdocs = refs.count() + if not full and numdocs>250: + refs=refs[:250] + else: + numdocs=None + refs=sorted(refs,key=lambda x:(['refnorm','refinfo','refunk','refold'].index(x.relationship.slug),x.source.canonical_name())) + return render_to_response("doc/document_referenced_by.html", + dict(alias_name=name, + doc=doc, + numdocs=numdocs, + refs=refs, + ), + context_instance=RequestContext(request)) + def document_ballot_content(request, doc, ballot_id, editable=True): """Render HTML string with content of ballot page.""" all_ballots = list(BallotDocEvent.objects.filter(doc=doc, type="created_ballot").order_by("time")) diff --git a/ietf/doc/views_help.py b/ietf/doc/views_help.py index 68104d624..f8493346d 100644 --- a/ietf/doc/views_help.py +++ b/ietf/doc/views_help.py @@ -1,6 +1,7 @@ from django import forms from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext +from django.http import Http404 from ietf.doc.models import * @@ -44,3 +45,17 @@ def state_help(request, type): "tags": tags, }, context_instance=RequestContext(request)) + +def relationship_help(request,subset=None): + subsets = { "reference": ['refnorm','refinfo','refunk','refold'], + "status" : ['tops','tois','tohist','toinf','tobcp','toexp'], + } + if subset and subset not in subsets: + raise Http404() + rels = DocRelationshipName.objects.filter(used=True) + if subset: + rels = rels.filter(slug__in=subsets[subset]) + return render_to_response("doc/relationship_help.html", { + "relations": rels + }, + context_instance=RequestContext(request)) diff --git a/ietf/templates/doc/document_draft.html b/ietf/templates/doc/document_draft.html index d5eb3bd1a..56070bb6a 100644 --- a/ietf/templates/doc/document_draft.html +++ b/ietf/templates/doc/document_draft.html @@ -228,6 +228,8 @@ Email Authors | IPR Disclosures{% if doc.related_ipr %} ({{doc.related_ipr.count}}){% endif %} | Dependencies to this document + | References + | Referenced By | Check nits | History feed | Search Mailing Lists diff --git a/ietf/templates/doc/document_referenced_by.html b/ietf/templates/doc/document_referenced_by.html new file mode 100644 index 000000000..b2f6c650b --- /dev/null +++ b/ietf/templates/doc/document_referenced_by.html @@ -0,0 +1,59 @@ +{% extends "base.html" %} +{% block title %} +References to {{alias_name}} +{% endblock %} +{% block morecss %} +.referencetable {margin-top:1em;} +.referenctable .references {width:100%;} +.referencetable .references .reference .source {width:30%;} +.referencetable .references .reference .title {width:35%;} +.referencetable .references .reference .level {width:15%;} +.referencetable .references .reference .reftype {width:15%;} +.referencetable .references .reference .downref {width:5%;} +.reflinks {font-size:80%;float:right;} +.showmore {margin-top:1em;} +{% endblock %} +{% block content %} +

References to {{alias_name}}

+
+This is an experimental product. These dependencies are extracted using heuristics looking for strings with particular prefixes. Notably, this means that references to I-Ds by title only are not reflected here. If it's really important, please inspect the documents' references sections directly. +
+
+Showing RFCs and active Internet-Drafts, sorted by reference type, then document name. +
+{% if numdocs %} +
+Results restricted to the first 250 of {{ numdocs }} documents. Show All +
+{% endif %} +
+ + +{% for ref in refs %} + + + + + + + +{% endfor %} +
DocumentTitleStatusReference TypeDownref
+{% with ref.source.canonical_name as name %} +{{name}} {% if ref.target.name != alias_name %}(as {{ref.target.name}}){% endif %} (refs, refby) +{% endwith %} + +{{ref.source.title}} + +{% ifequal ref.source.get_state.slug 'rfc' %} + {% with ref.source.std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%} +{% else %} + {% with ref.source.intended_std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%} +{% endifequal %} + +{{ref.relationship.name}} + +{{ref.is_downref|default:''}} +
+
+{% endblock %} diff --git a/ietf/templates/doc/document_references.html b/ietf/templates/doc/document_references.html new file mode 100644 index 000000000..8e413a350 --- /dev/null +++ b/ietf/templates/doc/document_references.html @@ -0,0 +1,54 @@ +{% extends "base.html" %} +{% block title %} +References from {{doc.canonical_name}} +{% endblock %} +{% block morecss %} +.referencetable {margin-top:1em;} +.referenctable .references {width:100%;} +.referencetable .references .reference .source {width:30%;} +.referencetable .references .reference .title {width:35%;} +.referencetable .references .reference .level {width:15%;} +.referencetable .references .reference .reftype {width:15%;} +.referencetable .references .reference .downref {width:5%;} +.reflinks {font-size:80%;float:right;} +{% endblock %} +{% block content %} +

References from {{doc.canonical_name}}

+
+This is an experimental product. These dependencies are extracted using heuristics looking for strings with particular prefixes. Notably, this means that references to I-Ds by title only are not reflected here. If it's really important, please inspect the documents' references sections directly. +
+
+Sorted by document name. +Reference type help +
+
+ + +{% for ref in refs %} + + + + + + + +{% endfor %} +
DocumentTitleStatusReference TypeDownref
+{% with ref.target.name as name %} +{{name}} (refs, refby) +{% endwith %} + +{{ref.target.document.title}} + +{% ifequal ref.target.document.get_state.slug 'rfc' %} + {% with ref.target.document.std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%} +{% else %} + {% with ref.target.document.intended_std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%} +{% endifequal %} + +{{ref.relationship.name}} + +{{ref.is_downref|default:''}} +
+
+{% endblock %} diff --git a/ietf/templates/doc/relationship_help.html b/ietf/templates/doc/relationship_help.html new file mode 100644 index 000000000..e6f970e1f --- /dev/null +++ b/ietf/templates/doc/relationship_help.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} + +{% block title %}Document Relationships{% endblock %} + +{% block morecss %} +.ietf-table .name { white-space: nowrap; padding-right: 1em; } +.ietf-table .desc { max-width: 35em; } +{% endblock %} + +{% block content %} +

Document Relationships

+ + + + + + + + + {% for rel in relations %} + + + + + + {% endfor %} +
RelationshipDescriptionInverse Relationship
{{ rel.name }}{{ rel.desc|linebreaksbr }}{{ rel.revname }}
+ +{% endblock %}