From e3ffb1ec36c5256a6a6424af4f352fd395061199 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 14 Dec 2023 16:30:06 -0400 Subject: [PATCH 1/9] fix: clean up shadowed name in document_referenced_by.html --- ietf/templates/doc/document_referenced_by.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ietf/templates/doc/document_referenced_by.html b/ietf/templates/doc/document_referenced_by.html index 2c1729f99c..e1137768b7 100644 --- a/ietf/templates/doc/document_referenced_by.html +++ b/ietf/templates/doc/document_referenced_by.html @@ -38,10 +38,10 @@

References to {{ name|prettystdname }}

{% for ref in refs %} - {% with ref.source.name as name %} + {% with ref.source.name as src_name %} - {{ name|prettystdname }} + {{ src_name|prettystdname }} {% if ref.target.name != name %}
As {{ ref.target.name }} @@ -51,13 +51,13 @@

References to {{ name|prettystdname }}

{{ ref.source.title }}
References Referenced by From 28a9e2807166f0477e5a172872219447a8a158e0 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 14 Dec 2023 16:43:59 -0400 Subject: [PATCH 2/9] fix: include refs to rfc's came_from_draft() --- ietf/doc/views_doc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 8408f5a0cd..63e2bcc6ae 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -1439,6 +1439,8 @@ def document_references(request, name): def document_referenced_by(request, name): 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.type_id in ["bcp","std","fyi"]: for rfc in doc.contains(): refs |= rfc.referenced_by() From a6a2331dd40fff377f99d7793fcb14fe0ac1175e Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 14 Dec 2023 16:46:36 -0400 Subject: [PATCH 3/9] fix: include refs to draft's became_rfc() --- ietf/doc/views_doc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 63e2bcc6ae..f61694e8bb 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -1441,6 +1441,8 @@ def document_referenced_by(request, 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() From b0c6611a329daac0b130a4cef03af454e4fa661f Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 14 Dec 2023 17:33:18 -0400 Subject: [PATCH 4/9] fix: Count indirect refs by RFCs --- ietf/doc/models.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index fdda23ecd1..daef3150b2 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -651,11 +651,19 @@ def referenced_by(self): | models.Q(source__type__slug="rfc") ) - def referenced_by_rfcs(self): - return self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( + refs_to = self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( source__type__slug="rfc" ) + if self.became_rfc(): + refs_to |= self.became_rfc().relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( + source__type__slug="rfc" + ) + if self.came_from_draft(): + refs_to |= self.came_from_draft().relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( + source__type__slug="rfc" + ) + return refs_to def became_rfc(self): if not hasattr(self, "_cached_became_rfc"): From fe93e35ecc2297e7f3924a4b3c11c14b2ec5b6e4 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 14 Dec 2023 17:56:22 -0400 Subject: [PATCH 5/9] refactor: break indirect ref_by counting to its own fn --- ietf/doc/models.py | 15 ++++++++------- ietf/templates/person/profile.html | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index daef3150b2..7ec60cbe23 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -652,17 +652,18 @@ def referenced_by(self): ) def referenced_by_rfcs(self): - refs_to = self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( + """Get refs to this doc from RFCs""" + return self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( source__type__slug="rfc" ) + + def directly_or_indirectly_referenced_by_rfcs(self): + """Get refs to this doc, or a draft/rfc it came from/became, from an RFC""" + refs_to = self.referenced_by_rfcs() if self.became_rfc(): - refs_to |= self.became_rfc().relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( - source__type__slug="rfc" - ) + refs_to |= self.became_rfc().referenced_by_rfcs() if self.came_from_draft(): - refs_to |= self.came_from_draft().relations_that(("refnorm", "refinfo", "refunk", "refold")).filter( - source__type__slug="rfc" - ) + refs_to |= self.came_from_draft().referenced_by_rfcs() return refs_to def became_rfc(self): diff --git a/ietf/templates/person/profile.html b/ietf/templates/person/profile.html index cc504ebc8d..d71c522c6a 100644 --- a/ietf/templates/person/profile.html +++ b/ietf/templates/person/profile.html @@ -109,7 +109,7 @@

{{ doc.pub_date|date:"b Y"|title }} {{ doc.title|urlize_ietf_docs }} - {% with doc.referenced_by_rfcs.count as refbycount %} + {% with doc.directly_or_indirectly_referenced_by_rfcs.count as refbycount %} {% if refbycount %} Date: Fri, 15 Dec 2023 11:50:58 -0400 Subject: [PATCH 6/9] fix: only count refs to pre-rfc draft, not post-draft rfc (and rename a method) --- ietf/doc/models.py | 16 +++++++--------- ietf/templates/person/profile.html | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 7ec60cbe23..9275b54101 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -657,15 +657,6 @@ def referenced_by_rfcs(self): source__type__slug="rfc" ) - def directly_or_indirectly_referenced_by_rfcs(self): - """Get refs to this doc, or a draft/rfc it came from/became, from an RFC""" - refs_to = self.referenced_by_rfcs() - if self.became_rfc(): - refs_to |= self.became_rfc().referenced_by_rfcs() - if self.came_from_draft(): - refs_to |= self.came_from_draft().referenced_by_rfcs() - return refs_to - def became_rfc(self): if not hasattr(self, "_cached_became_rfc"): doc = self if isinstance(self, Document) else self.doc @@ -684,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 diff --git a/ietf/templates/person/profile.html b/ietf/templates/person/profile.html index d71c522c6a..42e5d2e43a 100644 --- a/ietf/templates/person/profile.html +++ b/ietf/templates/person/profile.html @@ -109,7 +109,7 @@

{{ doc.pub_date|date:"b Y"|title }} {{ doc.title|urlize_ietf_docs }} - {% with doc.directly_or_indirectly_referenced_by_rfcs.count as refbycount %} + {% with doc.referenced_by_rfcs_as_rfc_or_draft.count as refbycount %} {% if refbycount %} Date: Fri, 15 Dec 2023 12:41:33 -0400 Subject: [PATCH 7/9] test: test referenced_by_rfcs methods The test_referenced_by_rfcs_as_rfc_or_draft() test fails because there's a bug! --- ietf/doc/tests.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index f14b5b1af0..5f7faefb6a 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2951,4 +2951,54 @@ 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").exclude(source__relateddocument__target=rfc) + | rfc.targets_related.filter(source__type="rfc") + ), + ) From 6bd9b034289a91b95afbe091a816bf9d6e64b5d4 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 15 Dec 2023 12:43:50 -0400 Subject: [PATCH 8/9] 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. --- ietf/doc/tests.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 5f7faefb6a..8ae588ad1d 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2997,8 +2997,5 @@ def test_referenced_by_rfcs_as_rfc_or_draft(self): self.assertCountEqual( rfc.referenced_by_rfcs_as_rfc_or_draft(), - ( - draft.targets_related.filter(source__type="rfc").exclude(source__relateddocument__target=rfc) - | rfc.targets_related.filter(source__type="rfc") - ), + draft.targets_related.filter(source__type="rfc") | rfc.targets_related.filter(source__type="rfc"), ) From 12868c999aaab99fdde570a778962a5f07011cc0 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Fri, 15 Dec 2023 18:57:31 -0400 Subject: [PATCH 9/9] chore: Add docstring to document_referenced_by view --- ietf/doc/views_doc.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index f61694e8bb..665393c24e 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -1437,6 +1437,20 @@ 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():