fix: Include refs to pre-RFC drafts as refs to the RFC (#6784)

* 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
This commit is contained in:
Jennifer Richards 2023-12-18 11:11:53 -04:00 committed by GitHub
parent f5bd078351
commit 149f82f578
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 6 deletions

View file

@ -651,8 +651,8 @@ class DocumentInfo(models.Model):
| 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"
)
@ -675,6 +675,13 @@ class DocumentInfo(models.Model):
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

View file

@ -2951,4 +2951,51 @@ class DocInfoMethodsTests(TestCase):
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"),
)

View file

@ -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()

View file

@ -38,10 +38,10 @@
</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>
@ -51,13 +51,13 @@
<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

View file

@ -109,7 +109,7 @@
<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 %}"