Skip to content

Commit d19967a

Browse files
committed
Adds pages to show what a given document refers to and what refers to it.
Fixes bug ietf-tools#1194 commit ready for merge - Legacy-Id: 6701
1 parent a677a70 commit d19967a

9 files changed

Lines changed: 232 additions & 0 deletions

File tree

ietf/doc/models.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,34 @@ def inverse_action():
167167
def __unicode__(self):
168168
return u"%s %s %s" % (self.source.name, self.relationship.name.lower(), self.target.name)
169169

170+
def is_downref(self):
171+
172+
if self.source.type.slug!='draft' or self.relationship.slug not in ['refnorm','refold','refunk']:
173+
return None
174+
175+
if self.source.get_state().slug == 'rfc':
176+
source_lvl = self.source.std_level.slug
177+
else:
178+
source_lvl = self.source.intended_std_level.slug
179+
180+
if source_lvl not in ['bcp','ps','ds','std']:
181+
return None
182+
183+
if self.target.document.get_state().slug == 'rfc':
184+
target_lvl = self.target.document.std_level.slug
185+
else:
186+
target_lvl = self.target.document.intended_std_level.slug
187+
188+
rank = { 'ps':1, 'ds':2, 'std':3, 'bcp':3 }
189+
190+
if ( target_lvl not in rank ) or ( rank[target_lvl] < rank[source_lvl] ):
191+
if self.relationship.slug == 'refnorm' and target_lvl!='unkn':
192+
return "Downref"
193+
else:
194+
return "Possible Downref"
195+
196+
return None
197+
170198
class DocumentAuthor(models.Model):
171199
document = models.ForeignKey('Document')
172200
author = models.ForeignKey(Email, help_text="Email address used by author for submission")

ietf/doc/tests.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,21 @@ def test_template_tags(self):
295295
from ietf.doc.templatetags import ietf_filters
296296
failures, tests = doctest.testmod(ietf_filters)
297297
self.assertEqual(failures, 0)
298+
299+
class ReferencesTest(TestCase):
300+
perma_fixtures = ['names']
301+
302+
def test_references(self):
303+
make_test_data()
304+
doc1 = Document.objects.get(name='draft-ietf-mars-test')
305+
doc2 = DocAlias.objects.get(name='draft-imaginary-independent-submission')
306+
RelatedDocument.objects.get_or_create(source=doc1,target=doc2,relationship=DocRelationshipName.objects.get(slug='refnorm'))
307+
url = urlreverse('doc_references', kwargs=dict(name=doc1.name))
308+
r = self.client.get(url)
309+
self.assertEquals(r.status_code, 200)
310+
self.assertTrue(doc2.name in r.content)
311+
url = urlreverse('doc_referenced_by', kwargs=dict(name=doc2.name))
312+
r = self.client.get(url)
313+
self.assertEquals(r.status_code, 200)
314+
self.assertTrue(doc1.name in r.content)
315+

ietf/doc/urls.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@
6060
url(r'^(?P<name>[A-Za-z0-9._+-]+)/history/$', views_doc.document_history, name="doc_history"),
6161
url(r'^(?P<name>[A-Za-z0-9._+-]+)/writeup/$', views_doc.document_writeup, name="doc_writeup"),
6262
url(r'^(?P<name>[A-Za-z0-9._+-]+)/shepherdwriteup/$', views_doc.document_shepherd_writeup, name="doc_shepherd_writeup"),
63+
url(r'^(?P<name>[A-Za-z0-9._+-]+)/references/$', views_doc.document_references, name="doc_references"),
64+
url(r'^(?P<name>[A-Za-z0-9._+-]+)/referencedby/$', views_doc.document_referenced_by, name="doc_referenced_by"),
6365
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/position/$', views_ballot.edit_position),
6466
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'),
6567
url(r'^(?P<name>[A-Za-z0-9._+-]+)/ballot/(?P<ballot_id>[0-9]+)/$', views_doc.document_ballot, name="doc_ballot"),
@@ -97,6 +99,8 @@
9799
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'),
98100

99101
url(r'^help/state/(?P<type>[\w-]+)/$', 'ietf.doc.views_help.state_help', name="state_help"),
102+
url(r'^help/relationships/$', 'ietf.doc.views_help.relationship_help', name="relationship_help"),
103+
url(r'^help/relationships/(?P<subset>\w+)/$', 'ietf.doc.views_help.relationship_help', name="relationship_subset_help"),
100104

101105
(r'^(?P<name>charter-[A-Za-z0-9._+-]+)/', include('ietf.wgcharter.urls')),
102106
(r'^(?P<name>[A-Za-z0-9._+-]+)/conflict-review/', include('ietf.doc.urls_conflict_review')),

ietf/doc/views_doc.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,29 @@ def document_shepherd_writeup(request, name):
609609
),
610610
context_instance=RequestContext(request))
611611

612+
def document_references(request, name):
613+
doc = get_object_or_404(Document,docalias__name=name)
614+
refs = doc.relations_that_doc(['refnorm','refinfo','refunk','refold'])
615+
return render_to_response("doc/document_references.html",dict(doc=doc,refs=sorted(refs,key=lambda x:x.target.name),),context_instance=RequestContext(request))
616+
617+
def document_referenced_by(request, name):
618+
doc = get_object_or_404(Document,docalias__name=name)
619+
refs = doc.relations_that(['refnorm','refinfo','refunk','refold']).filter(source__states__type__slug='draft',source__states__slug__in=['rfc','active'])
620+
full = ( request.GET.get('full') != None )
621+
numdocs = refs.count()
622+
if not full and numdocs>250:
623+
refs=refs[:250]
624+
else:
625+
numdocs=None
626+
refs=sorted(refs,key=lambda x:(['refnorm','refinfo','refunk','refold'].index(x.relationship.slug),x.source.canonical_name()))
627+
return render_to_response("doc/document_referenced_by.html",
628+
dict(alias_name=name,
629+
doc=doc,
630+
numdocs=numdocs,
631+
refs=refs,
632+
),
633+
context_instance=RequestContext(request))
634+
612635
def document_ballot_content(request, doc, ballot_id, editable=True):
613636
"""Render HTML string with content of ballot page."""
614637
all_ballots = list(BallotDocEvent.objects.filter(doc=doc, type="created_ballot").order_by("time"))

ietf/doc/views_help.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django import forms
22
from django.shortcuts import render_to_response, get_object_or_404
33
from django.template import RequestContext
4+
from django.http import Http404
45

56
from ietf.doc.models import *
67

@@ -44,3 +45,17 @@ def state_help(request, type):
4445
"tags": tags,
4546
},
4647
context_instance=RequestContext(request))
48+
49+
def relationship_help(request,subset=None):
50+
subsets = { "reference": ['refnorm','refinfo','refunk','refold'],
51+
"status" : ['tops','tois','tohist','toinf','tobcp','toexp'],
52+
}
53+
if subset and subset not in subsets:
54+
raise Http404()
55+
rels = DocRelationshipName.objects.filter(used=True)
56+
if subset:
57+
rels = rels.filter(slug__in=subsets[subset])
58+
return render_to_response("doc/relationship_help.html", {
59+
"relations": rels
60+
},
61+
context_instance=RequestContext(request))

ietf/templates/doc/document_draft.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@
228228
<a href="mailto:{{ doc.name }}@tools.ietf.org?subject=Mail%20regarding%20{{ doc.name }}" rel="nofollow">Email Authors</a>
229229
| <a href="{% url ipr_search %}?option=document_search&amp;id={{ doc.name }}" rel="nofollow">IPR Disclosures{% if doc.related_ipr %} ({{doc.related_ipr.count}}){% endif %}</a>
230230
| <a href="http://www.fenron.net/~fenner/ietf/deps/index.cgi?dep={{ name }}" rel="nofollow">Dependencies to this document</a>
231+
| <a href="{% url doc_references doc.canonical_name %}" rel="nofollow">References</a>
232+
| <a href="{% url doc_referenced_by doc.canonical_name %}" rel="nofollow">Referenced By</a>
231233
| <a href="http://tools.ietf.org/idnits?url=http://tools.ietf.org/id/{{ doc.filename_with_rev }}" rel="nofollow" target="_blank">Check nits</a>
232234
| <a href="/feed/document-changes/{{ name }}/">History feed</a>
233235
| <a href="http://www.google.com/search?as_q={{ doc.name }}&as_sitesearch={{ search_archive }}" rel="nofollow" target="_blank">Search Mailing Lists</a>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{% extends "base.html" %}
2+
{% block title %}
3+
References to {{alias_name}}
4+
{% endblock %}
5+
{% block morecss %}
6+
.referencetable {margin-top:1em;}
7+
.referenctable .references {width:100%;}
8+
.referencetable .references .reference .source {width:30%;}
9+
.referencetable .references .reference .title {width:35%;}
10+
.referencetable .references .reference .level {width:15%;}
11+
.referencetable .references .reference .reftype {width:15%;}
12+
.referencetable .references .reference .downref {width:5%;}
13+
.reflinks {font-size:80%;float:right;}
14+
.showmore {margin-top:1em;}
15+
{% endblock %}
16+
{% block content %}
17+
<h1>References to {{alias_name}}</h1>
18+
<div class="info-message-warning">
19+
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.
20+
</div>
21+
<div>
22+
Showing RFCs and active Internet-Drafts, sorted by <a href="{% url relationship_subset_help subset='reference' %}">reference type</a>, then document name.
23+
</div>
24+
{% if numdocs %}
25+
<div class="showmore">
26+
Results restricted to the first 250 of {{ numdocs }} documents. <a href="?full=True">Show All</a>
27+
</div>
28+
{% endif %}
29+
<div class="referencetable">
30+
<table class="references ietf-table">
31+
<tr><th>Document</th><th>Title</th><th>Status</th><th>Reference Type</th><th>Downref</th></tr>
32+
{% for ref in refs %}
33+
<tr class="reference {% cycle 'evenrow' 'oddrow' %}">
34+
<td class="source">
35+
{% with ref.source.canonical_name as name %}
36+
<a href="{% url doc_view name=name %}">{{name}}</a> {% if ref.target.name != alias_name %}(as {{ref.target.name}}){% endif %} <span class="reflinks">(<a href="{% url doc_references name %}">refs</a>, <a href="{% url doc_referenced_by name %}">refby</a>)</span>
37+
{% endwith %}
38+
</td>
39+
<td class="title">
40+
{{ref.source.title}}
41+
</td>
42+
<td class="level">
43+
{% ifequal ref.source.get_state.slug 'rfc' %}
44+
{% with ref.source.std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%}
45+
{% else %}
46+
{% with ref.source.intended_std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%}
47+
{% endifequal %}
48+
</td>
49+
<td class="reftype">
50+
{{ref.relationship.name}}
51+
</td>
52+
<td class="downref">
53+
{{ref.is_downref|default:''}}
54+
</td>
55+
</tr>
56+
{% endfor %}
57+
</table>
58+
</div>
59+
{% endblock %}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{% extends "base.html" %}
2+
{% block title %}
3+
References from {{doc.canonical_name}}
4+
{% endblock %}
5+
{% block morecss %}
6+
.referencetable {margin-top:1em;}
7+
.referenctable .references {width:100%;}
8+
.referencetable .references .reference .source {width:30%;}
9+
.referencetable .references .reference .title {width:35%;}
10+
.referencetable .references .reference .level {width:15%;}
11+
.referencetable .references .reference .reftype {width:15%;}
12+
.referencetable .references .reference .downref {width:5%;}
13+
.reflinks {font-size:80%;float:right;}
14+
{% endblock %}
15+
{% block content %}
16+
<h1>References from {{doc.canonical_name}}</h1>
17+
<div class="info-message-warning">
18+
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.
19+
</div>
20+
<div>
21+
Sorted by document name.
22+
<a href="{% url relationship_subset_help subset='reference' %}">Reference type help</a>
23+
</div>
24+
<div class="referencetable">
25+
<table class="references ietf-table">
26+
<tr><th>Document</th><th>Title</th><th>Status</th><th>Reference Type</th><th>Downref</th></tr>
27+
{% for ref in refs %}
28+
<tr class="reference {% cycle 'evenrow' 'oddrow' %}">
29+
<td class="target">
30+
{% with ref.target.name as name %}
31+
<a href="{% url doc_view name=name %}">{{name}}</a> <span class="reflinks">(<a href="{% url doc_references name %}">refs</a>, <a href="{% url doc_referenced_by name %}">refby</a>)</span>
32+
{% endwith %}
33+
</td>
34+
<td class="title">
35+
{{ref.target.document.title}}
36+
</td>
37+
<td class="level">
38+
{% ifequal ref.target.document.get_state.slug 'rfc' %}
39+
{% with ref.target.document.std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%}
40+
{% else %}
41+
{% with ref.target.document.intended_std_level as lvl %}{% if lvl %}{{lvl}}{% endif %}{%endwith%}
42+
{% endifequal %}
43+
</td>
44+
<td class="reftype">
45+
{{ref.relationship.name}}
46+
</td>
47+
<td class="downref">
48+
{{ref.is_downref|default:''}}
49+
</td>
50+
</tr>
51+
{% endfor %}
52+
</table>
53+
</div>
54+
{% endblock %}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{% extends "base.html" %}
2+
3+
{% block title %}Document Relationships{% endblock %}
4+
5+
{% block morecss %}
6+
.ietf-table .name { white-space: nowrap; padding-right: 1em; }
7+
.ietf-table .desc { max-width: 35em; }
8+
{% endblock %}
9+
10+
{% block content %}
11+
<h1>Document Relationships</h1>
12+
13+
<table class="ietf-table">
14+
<tr>
15+
<th>Relationship</th>
16+
<th>Description</th>
17+
<th>Inverse Relationship</th>
18+
</tr>
19+
20+
{% for rel in relations %}
21+
<tr id="{{ rel.slug }}" class="{{ forloop.counter|divisibleby:2|yesno:"evenrow,oddrow" }}">
22+
<td class="name">{{ rel.name }}</td>
23+
<td class="desc">{{ rel.desc|linebreaksbr }}</td>
24+
<td class="revname">{{ rel.revname }}</td>
25+
</tr>
26+
{% endfor %}
27+
</table>
28+
29+
{% endblock %}

0 commit comments

Comments
 (0)