Skip to content

Commit

Permalink
fix: Include refs to pre-RFC drafts as refs to the RFC (#6784)
Browse files Browse the repository at this point in the history
* fix: clean up shadowed name in document_referenced_by.html

* fix: include refs to rfc's came_from_draft()

* fix: include refs to draft's became_rfc()

* fix: Count indirect refs by RFCs

* refactor: break indirect ref_by counting to its own fn

* fix: only count refs to pre-rfc draft, not post-draft rfc

(and rename a method)

* test: test referenced_by_rfcs methods

The test_referenced_by_rfcs_as_rfc_or_draft() test
fails because there's a bug!

* test: actually, do double-count refs to rfc/draft

Let's do include refs to an rfc and its precursor draft
as separate refs. This almost surely indicates a data
error because it would mean an rfc referenced both an
rfc and the draft that it came from. That should never
be allowed, so at least let some light fall on it if
it happens.

* chore: Add docstring to document_referenced_by view
  • Loading branch information
jennifer-richards authored Dec 18, 2023
1 parent f5bd078 commit 149f82f
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 6 deletions.
9 changes: 8 additions & 1 deletion ietf/doc/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,8 @@ def referenced_by(self):
| models.Q(source__type__slug="rfc")
)


def referenced_by_rfcs(self):
"""Get refs to this doc from RFCs"""
return self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter(
source__type__slug="rfc"
)
Expand All @@ -675,6 +675,13 @@ def contains(self):
def part_of(self):
return self.related_that("contains")

def referenced_by_rfcs_as_rfc_or_draft(self):
"""Get refs to this doc, or a draft/rfc it came from, from an RFC"""
refs_to = self.referenced_by_rfcs()
if self.type_id == "rfc" and self.came_from_draft():
refs_to |= self.came_from_draft().referenced_by_rfcs()
return refs_to

class Meta:
abstract = True

Expand Down
47 changes: 47 additions & 0 deletions ietf/doc/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2951,4 +2951,51 @@ def test_revisions(self):
self.assertEqual(draft.revisions_by_dochistory(),[f"{i:02d}" for i in range(8,10)])
self.assertEqual(draft.revisions_by_newrevisionevent(),[f"{i:02d}" for i in [*range(0,5), *range(6,10)]])

def test_referenced_by_rfcs(self):
# n.b., no significance to the ref* values in this test
referring_draft = WgDraftFactory()
(rfc, referring_rfc) = WgRfcFactory.create_batch(2)
rfc.targets_related.create(relationship_id="refnorm", source=referring_draft)
rfc.targets_related.create(relationship_id="refnorm", source=referring_rfc)
self.assertCountEqual(
rfc.referenced_by_rfcs(),
rfc.targets_related.filter(source=referring_rfc),
)

def test_referenced_by_rfcs_as_rfc_or_draft(self):
# n.b., no significance to the ref* values in this test
draft = WgDraftFactory()
rfc = WgRfcFactory()
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)

# Draft referring to the rfc and the draft - should not be reported at all
draft_referring_to_both = WgDraftFactory()
draft_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=draft)
draft_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=rfc)

# RFC referring only to the draft - should be reported for either the draft or the rfc
rfc_referring_to_draft = WgRfcFactory()
rfc_referring_to_draft.relateddocument_set.create(relationship_id="refinfo", target=draft)

# RFC referring only to the rfc - should be reported only for the rfc
rfc_referring_to_rfc = WgRfcFactory()
rfc_referring_to_rfc.relateddocument_set.create(relationship_id="refinfo", target=rfc)

# RFC referring only to the rfc - should be reported only for the rfc
rfc_referring_to_rfc = WgRfcFactory()
rfc_referring_to_rfc.relateddocument_set.create(relationship_id="refinfo", target=rfc)

# RFC referring to the rfc and the draft - should be reported for both
rfc_referring_to_both = WgRfcFactory()
rfc_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=draft)
rfc_referring_to_both.relateddocument_set.create(relationship_id="refnorm", target=rfc)

self.assertCountEqual(
draft.referenced_by_rfcs_as_rfc_or_draft(),
draft.targets_related.filter(source__type="rfc"),
)

self.assertCountEqual(
rfc.referenced_by_rfcs_as_rfc_or_draft(),
draft.targets_related.filter(source__type="rfc") | rfc.targets_related.filter(source__type="rfc"),
)
18 changes: 18 additions & 0 deletions ietf/doc/views_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1437,8 +1437,26 @@ def document_references(request, name):
return render(request, "doc/document_references.html",dict(doc=doc,refs=sorted(refs,key=lambda x:x.target.name),))

def document_referenced_by(request, name):
"""View documents that reference the named document
The view lists both direct references to a the named document, plus references to
related other documents. For a draft that became an RFC, this will include references
to the RFC. For an RFC, this will include references to the draft it came from, if any.
For a subseries document, this will include references to any of the RFC documents it
contains.
In the rendered output, a badge is applied to indicate the name of the document the
reference actually targeted. E.g., on the display for a draft that became RFC NNN,
references included because they point to that RFC would be shown with a tag "As RFC NNN".
The intention is to make the "Referenced By" page useful for finding related work while
accurately reflecting the actual reference relationships.
"""
doc = get_object_or_404(Document,name=name)
refs = doc.referenced_by()
if doc.came_from_draft():
refs |= doc.came_from_draft().referenced_by()
if doc.became_rfc():
refs |= doc.became_rfc().referenced_by()
if doc.type_id in ["bcp","std","fyi"]:
for rfc in doc.contains():
refs |= rfc.referenced_by()
Expand Down
8 changes: 4 additions & 4 deletions ietf/templates/doc/document_referenced_by.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ <h1>References to {{ name|prettystdname }}</h1>
</thead>
<tbody>
{% for ref in refs %}
{% with ref.source.name as name %}
{% with ref.source.name as src_name %}
<tr>
<td>
<a href="{% url 'ietf.doc.views_doc.document_main' name=name %}">{{ name|prettystdname }}</a>
<a href="{% url 'ietf.doc.views_doc.document_main' name=src_name %}">{{ src_name|prettystdname }}</a>
{% if ref.target.name != name %}
<br>
<span class="badge rounded-pill text-bg-info">As {{ ref.target.name }}</span>
Expand All @@ -51,13 +51,13 @@ <h1>References to {{ name|prettystdname }}</h1>
<b>{{ ref.source.title }}</b>
<br>
<a class="btn btn-primary btn-sm"
href="{% url 'ietf.doc.views_doc.document_references' name %}"
href="{% url 'ietf.doc.views_doc.document_references' src_name %}"
rel="nofollow">
<i class="bi bi-arrow-left"></i>
References
</a>
<a class="btn btn-primary btn-sm"
href="{% url 'ietf.doc.views_doc.document_referenced_by' name %}"
href="{% url 'ietf.doc.views_doc.document_referenced_by' src_name %}"
rel="nofollow">
<i class="bi bi-arrow-right"></i>
Referenced by
Expand Down
2 changes: 1 addition & 1 deletion ietf/templates/person/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ <h2 class="mt-5" id="rfcs-{{ forloop.counter }}">
<td>{{ doc.pub_date|date:"b Y"|title }}</td>
<td>{{ doc.title|urlize_ietf_docs }}</td>
<td class="text-end">
{% with doc.referenced_by_rfcs.count as refbycount %}
{% with doc.referenced_by_rfcs_as_rfc_or_draft.count as refbycount %}
{% if refbycount %}
<a class="text-end"
href="{% url 'ietf.doc.views_doc.document_referenced_by' doc.name %}"
Expand Down

0 comments on commit 149f82f

Please sign in to comment.