commit
546f438df1
|
@ -517,3 +517,31 @@ class StatementFactory(BaseDocumentFactory):
|
|||
obj.set_state(State.objects.get(type_id=state_type_id, slug=state_slug))
|
||||
else:
|
||||
obj.set_state(State.objects.get(type_id="statement", slug="active"))
|
||||
|
||||
class SubseriesFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Document
|
||||
skip_postgeneration_save = True
|
||||
|
||||
@factory.lazy_attribute_sequence
|
||||
def name(self, n):
|
||||
return f"{self.type_id}{n}"
|
||||
|
||||
@factory.post_generation
|
||||
def contains(obj, create, extracted, **kwargs):
|
||||
if not create:
|
||||
return
|
||||
if extracted:
|
||||
for doc in extracted:
|
||||
obj.relateddocument_set.create(relationship_id="contains",target=doc)
|
||||
else:
|
||||
obj.relateddocument_set.create(relationship_id="contains", target=RfcFactory())
|
||||
|
||||
class BcpFactory(SubseriesFactory):
|
||||
type_id="bcp"
|
||||
|
||||
class StdFactory(SubseriesFactory):
|
||||
type_id="std"
|
||||
|
||||
class FyiFactory(SubseriesFactory):
|
||||
type_id="fyi"
|
||||
|
|
21
ietf/doc/migrations/0018_subseries.py
Normal file
21
ietf/doc/migrations/0018_subseries.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Copyright The IETF Trust 2023, All Rights Reserved
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
StateType = apps.get_model("doc", "StateType")
|
||||
for slug in ["bcp", "std", "fyi"]:
|
||||
StateType.objects.create(slug=slug, label=f"{slug} state")
|
||||
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
StateType = apps.get_model("doc", "StateType")
|
||||
StateType.objects.filter(slug__in=["bcp", "std", "fyi"]).delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("doc", "0017_move_dochistory"),
|
||||
]
|
||||
|
||||
operations = [migrations.RunPython(forward, reverse)]
|
|
@ -673,6 +673,12 @@ class DocumentInfo(models.Model):
|
|||
doc = self if isinstance(self, Document) else self.doc
|
||||
self._cached_came_from_draft = next(iter(doc.related_that("became_rfc")), None)
|
||||
return self._cached_came_from_draft
|
||||
|
||||
def contains(self):
|
||||
return self.related_that_doc("contains")
|
||||
|
||||
def part_of(self):
|
||||
return self.related_that("contains")
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
49
ietf/doc/tests_subseries.py
Normal file
49
ietf/doc/tests_subseries.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Copyright The IETF Trust 2023, All Rights Reserved
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from pyquery import PyQuery
|
||||
|
||||
from django.urls import reverse as urlreverse
|
||||
|
||||
from ietf.doc.factories import SubseriesFactory, RfcFactory
|
||||
from ietf.doc.models import Document
|
||||
from ietf.utils.test_utils import TestCase
|
||||
|
||||
class SubseriesTests(TestCase):
|
||||
|
||||
def test_index_and_view(self):
|
||||
types = ["bcp", "std", "fyi"]
|
||||
for type_id in types:
|
||||
doc = SubseriesFactory(type_id=type_id)
|
||||
self.assertEqual(len(doc.contains()), 1)
|
||||
rfc = doc.contains()[0]
|
||||
# Index
|
||||
url = urlreverse("ietf.doc.views_search.index_subseries", kwargs=dict(type_id=type_id))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertIsNotNone(q(f"#{doc.name}"))
|
||||
self.assertIn(rfc.name,q(f"#{doc.name}").text())
|
||||
# Subseries document view
|
||||
url = urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=doc.name))
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertIn(f"{doc.type_id.upper()} {doc.name[3:]} consists of:",q("h2").text())
|
||||
self.assertIn(f"RFC {rfc.name[3:]}", q("div.row p a").text())
|
||||
# RFC view
|
||||
url = urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))
|
||||
r = self.client.get(url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertIn(f"RFC {rfc.name[3:]} also known as {type_id.upper()} {doc.name[3:]}", q("h1").text())
|
||||
bcp = Document.objects.filter(type_id="bcp").last()
|
||||
bcp.relateddocument_set.create(relationship_id="contains", target=RfcFactory())
|
||||
for rfc in bcp.contains():
|
||||
url = urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))
|
||||
r = self.client.get(url)
|
||||
q = PyQuery(r.content)
|
||||
self.assertIn(f"RFC {rfc.name[3:]} part of BCP {bcp.name[3:]}", q("h1").text())
|
||||
|
||||
|
|
@ -94,6 +94,8 @@ urlpatterns = [
|
|||
url(r'^ballots/irsg/$', views_ballot.irsg_ballot_status),
|
||||
url(r'^ballots/rsab/$', views_ballot.rsab_ballot_status),
|
||||
|
||||
url(r'^(?P<type_id>(bcp|std|fyi))/?$', views_search.index_subseries),
|
||||
|
||||
url(r'^%(name)s(?:/%(rev)s)?/$' % settings.URL_REGEXPS, views_doc.document_main),
|
||||
url(r'^%(name)s(?:/%(rev)s)?/bibtex/$' % settings.URL_REGEXPS, views_doc.document_bibtex),
|
||||
url(r'^%(name)s(?:/%(rev)s)?/idnits2-state/$' % settings.URL_REGEXPS, views_doc.idnits2_state),
|
||||
|
|
|
@ -154,8 +154,8 @@ def render_document_top(request, doc, tab, name):
|
|||
None,
|
||||
)
|
||||
)
|
||||
|
||||
tabs.append(("Email expansions","email",urlreverse('ietf.doc.views_doc.document_email', kwargs=dict(name=name)), True, None))
|
||||
if not doc.type_id in ["bcp", "std", "fyi"]:
|
||||
tabs.append(("Email expansions","email",urlreverse('ietf.doc.views_doc.document_email', kwargs=dict(name=name)), True, None))
|
||||
tabs.append(("History", "history", urlreverse('ietf.doc.views_doc.document_history', kwargs=dict(name=name)), True, None))
|
||||
|
||||
if name.startswith("rfc"):
|
||||
|
@ -163,7 +163,7 @@ def render_document_top(request, doc, tab, name):
|
|||
else:
|
||||
name += "-" + doc.rev
|
||||
|
||||
return render_to_string("doc/document_top.html",
|
||||
return render_to_string("doc/document_top.html" if not doc.type_id in ["bcp", "std", "fyi"] else "doc/document_subseries_top.html",
|
||||
dict(doc=doc,
|
||||
tabs=tabs,
|
||||
selected=tab,
|
||||
|
@ -934,7 +934,7 @@ def document_main(request, name, rev=None, document_html=False):
|
|||
)
|
||||
)
|
||||
|
||||
if doc.type_id == "statement":
|
||||
elif doc.type_id == "statement":
|
||||
if doc.uploaded_filename:
|
||||
basename = doc.uploaded_filename.split(".")[0] # strip extension
|
||||
else:
|
||||
|
@ -955,7 +955,6 @@ def document_main(request, name, rev=None, document_html=False):
|
|||
can_manage = has_role(request.user,["Secretariat"]) # Add IAB or IESG as appropriate
|
||||
interesting_relations_that, interesting_relations_that_doc = interesting_doc_relations(doc)
|
||||
published = doc.latest_event(type="published_statement").time
|
||||
|
||||
return render(request, "doc/document_statement.html",
|
||||
dict(doc=doc,
|
||||
top=top,
|
||||
|
@ -968,6 +967,9 @@ def document_main(request, name, rev=None, document_html=False):
|
|||
replaced_by=interesting_relations_that.filter(relationship="replaces"),
|
||||
can_manage=can_manage,
|
||||
))
|
||||
elif doc.type_id in ["bcp", "std", "fyi"]:
|
||||
return render(request, "doc/document_subseries.html", {"doc": doc, "top": top})
|
||||
|
||||
|
||||
raise Http404("Document not found: %s" % (name + ("-%s"%rev if rev else "")))
|
||||
|
||||
|
@ -1230,13 +1232,6 @@ def document_history(request, name):
|
|||
request.user, ("Area Director", "Secretariat", "IRTF Chair")
|
||||
)
|
||||
|
||||
# Get related docs whose history should be linked
|
||||
if doc.type_id == "draft":
|
||||
related = doc.related_that_doc("became_rfc")
|
||||
elif doc.type_id == "rfc":
|
||||
related = doc.related_that("became_rfc")
|
||||
else:
|
||||
related = []
|
||||
|
||||
return render(
|
||||
request,
|
||||
|
@ -1246,7 +1241,6 @@ def document_history(request, name):
|
|||
"top": top,
|
||||
"diff_revisions": diff_revisions,
|
||||
"events": events,
|
||||
"related": related,
|
||||
"can_add_comment": can_add_comment,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -895,3 +895,11 @@ def ajax_select2_search_docs(request, model_name, doc_type): # TODO - remove mod
|
|||
objs = qs.distinct().order_by("name")[:20]
|
||||
|
||||
return HttpResponse(select2_id_doc_name_json(model, objs), content_type='application/json')
|
||||
|
||||
def index_subseries(request, type_id):
|
||||
docs = sorted(Document.objects.filter(type_id=type_id),key=lambda o: int(o.name[3:]))
|
||||
if len(docs)>0:
|
||||
type = docs[0].type
|
||||
else:
|
||||
type = DocTypeName.objects.get(slug=type_id)
|
||||
return render(request, "doc/index_subseries.html", {"type": type, "docs": docs})
|
||||
|
|
|
@ -2585,6 +2585,13 @@
|
|||
"model": "doc.statetype",
|
||||
"pk": "agenda"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"label": "bcp state"
|
||||
},
|
||||
"model": "doc.statetype",
|
||||
"pk": "bcp"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"label": "State"
|
||||
|
@ -2704,6 +2711,13 @@
|
|||
"model": "doc.statetype",
|
||||
"pk": "draft-stream-ise"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"label": "fyi state"
|
||||
},
|
||||
"model": "doc.statetype",
|
||||
"pk": "fyi"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"label": "State"
|
||||
|
@ -2795,6 +2809,13 @@
|
|||
"model": "doc.statetype",
|
||||
"pk": "statement"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"label": "std state"
|
||||
},
|
||||
"model": "doc.statetype",
|
||||
"pk": "std"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"about_page": "ietf.group.views.group_about",
|
||||
|
@ -5869,7 +5890,7 @@
|
|||
{
|
||||
"fields": {
|
||||
"desc": "The document's authors",
|
||||
"template": "{% if doc.type_id == \"draft\" %}<{{doc.name}}@ietf.org>{% endif %}"
|
||||
"template": "{% if doc.type_id == \"draft\" or doc.type_id == \"rfc\" %}<{{doc.name}}@ietf.org>{% endif %}"
|
||||
},
|
||||
"model": "mailtrigger.recipient",
|
||||
"pk": "doc_authors"
|
||||
|
@ -10040,6 +10061,17 @@
|
|||
"model": "name.docrelationshipname",
|
||||
"pk": "conflrev"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "This document contains other documents (e.g., STDs contain RFCs)",
|
||||
"name": "Contains",
|
||||
"order": 0,
|
||||
"revname": "Is part of",
|
||||
"used": true
|
||||
},
|
||||
"model": "name.docrelationshipname",
|
||||
"pk": "contains"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "Approval for downref",
|
||||
|
@ -10556,6 +10588,17 @@
|
|||
"model": "name.doctypename",
|
||||
"pk": "agenda"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "",
|
||||
"name": "Best Current Practice",
|
||||
"order": 0,
|
||||
"prefix": "bcp",
|
||||
"used": true
|
||||
},
|
||||
"model": "name.doctypename",
|
||||
"pk": "bcp"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "",
|
||||
|
@ -10622,6 +10665,17 @@
|
|||
"model": "name.doctypename",
|
||||
"pk": "draft"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "",
|
||||
"name": "For Your Information",
|
||||
"order": 0,
|
||||
"prefix": "fyi",
|
||||
"used": true
|
||||
},
|
||||
"model": "name.doctypename",
|
||||
"pk": "fyi"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "",
|
||||
|
@ -10754,6 +10808,17 @@
|
|||
"model": "name.doctypename",
|
||||
"pk": "statement"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "",
|
||||
"name": "Standard",
|
||||
"order": 0,
|
||||
"prefix": "std",
|
||||
"used": true
|
||||
},
|
||||
"model": "name.doctypename",
|
||||
"pk": "std"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"desc": "",
|
||||
|
@ -16604,7 +16669,7 @@
|
|||
"fields": {
|
||||
"command": "xym",
|
||||
"switch": "--version",
|
||||
"time": "2023-08-22T07:09:39.542Z",
|
||||
"time": "2023-09-21T07:09:40.201Z",
|
||||
"used": true,
|
||||
"version": "xym 0.7.0"
|
||||
},
|
||||
|
@ -16615,7 +16680,7 @@
|
|||
"fields": {
|
||||
"command": "pyang",
|
||||
"switch": "--version",
|
||||
"time": "2023-08-22T07:09:39.881Z",
|
||||
"time": "2023-09-21T07:09:40.525Z",
|
||||
"used": true,
|
||||
"version": "pyang 2.5.3"
|
||||
},
|
||||
|
@ -16626,7 +16691,7 @@
|
|||
"fields": {
|
||||
"command": "yanglint",
|
||||
"switch": "--version",
|
||||
"time": "2023-08-22T07:09:39.899Z",
|
||||
"time": "2023-09-21T07:09:40.546Z",
|
||||
"used": true,
|
||||
"version": "yanglint SO 1.9.2"
|
||||
},
|
||||
|
@ -16637,7 +16702,7 @@
|
|||
"fields": {
|
||||
"command": "xml2rfc",
|
||||
"switch": "--version",
|
||||
"time": "2023-08-22T07:09:40.791Z",
|
||||
"time": "2023-09-21T07:09:41.465Z",
|
||||
"used": true,
|
||||
"version": "xml2rfc 3.18.0"
|
||||
},
|
||||
|
|
38
ietf/name/migrations/0010_subseries.py
Normal file
38
ietf/name/migrations/0010_subseries.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Copyright The IETF Trust 2023, All Rights Reserved
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
DocTypeName = apps.get_model("name", "DocTypeName")
|
||||
DocRelationshipName = apps.get_model("name", "DocRelationshipName")
|
||||
for slug, name, prefix in [
|
||||
("std", "Standard", "std"),
|
||||
("bcp", "Best Current Practice", "bcp"),
|
||||
("fyi", "For Your Information", "fyi"),
|
||||
]:
|
||||
DocTypeName.objects.create(
|
||||
slug=slug, name=name, prefix=prefix, desc="", used=True
|
||||
)
|
||||
DocRelationshipName.objects.create(
|
||||
slug="contains",
|
||||
name="Contains",
|
||||
revname="Is part of",
|
||||
desc="This document contains other documents (e.g., STDs contain RFCs)",
|
||||
used=True,
|
||||
)
|
||||
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
DocTypeName = apps.get_model("name", "DocTypeName")
|
||||
DocRelationshipName = apps.get_model("name", "DocRelationshipName")
|
||||
DocTypeName.objects.filter(slug__in=["std", "bcp", "fyi"]).delete()
|
||||
DocRelationshipName.objects.filter(slug="contains").delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("name", "0009_rfc_doctype_names"),
|
||||
]
|
||||
|
||||
operations = [migrations.RunPython(forward, reverse)]
|
|
@ -380,6 +380,8 @@ def update_docs_from_rfc_index(
|
|||
|
||||
system = Person.objects.get(name="(System)")
|
||||
|
||||
first_sync_creating_subseries = not Document.objects.filter(type_id__in=["bcp","std","fyi"]).exists()
|
||||
|
||||
for (
|
||||
rfc_number,
|
||||
title,
|
||||
|
@ -511,8 +513,9 @@ def update_docs_from_rfc_index(
|
|||
stream_slug = f"draft-stream-{draft.stream.slug}"
|
||||
prev_state = draft.get_state(stream_slug)
|
||||
if prev_state is None:
|
||||
# TODO: main returns warnings to the caller rather tha logging to the system - look to see if we should be using that instead
|
||||
log(f"Warning while processing {doc.name}: draft {draft.name} stream state was not set")
|
||||
if prev_state.slug != "pub":
|
||||
elif prev_state.slug != "pub":
|
||||
new_state = State.objects.select_related("type").get(used=True, type__slug=stream_slug, slug="pub")
|
||||
draft.set_state(new_state)
|
||||
draft_changes.append(
|
||||
|
@ -650,15 +653,47 @@ def update_docs_from_rfc_index(
|
|||
)
|
||||
)
|
||||
|
||||
# This block attempted to alias subseries names to RFCs.
|
||||
# Handle that differently when we add subseries as a document type.
|
||||
#
|
||||
# if also:
|
||||
# for a in also:
|
||||
# a = a.lower()
|
||||
# if not DocAlias.objects.filter(name=a):
|
||||
# DocAlias.objects.create(name=a).docs.add(doc)
|
||||
# rfc_changes.append(f"created alias {prettify_std_name(a)}")
|
||||
if also:
|
||||
# recondition also to have proper subseries document names:
|
||||
conditioned_also = []
|
||||
for a in also:
|
||||
a = a.lower()
|
||||
subseries_slug = a[:3]
|
||||
if subseries_slug not in ["bcp", "std", "fyi"]:
|
||||
log(f"Unexpected 'also' relationship of {a} encountered for {doc}")
|
||||
next
|
||||
maybe_number = a[3:].strip()
|
||||
if not maybe_number.isdigit():
|
||||
log(f"Unexpected 'also' subseries element identifier {a} encountered for {doc}")
|
||||
next
|
||||
else:
|
||||
subseries_number = int(maybe_number)
|
||||
conditioned_also.append(f"{subseries_slug}{subseries_number}") # Note the lack of leading zeros
|
||||
also = conditioned_also
|
||||
|
||||
for a in also:
|
||||
subseries_doc_name = a
|
||||
subseries_slug=a[:3]
|
||||
# Leaving most things to the default intentionally
|
||||
# Of note, title and stream are left to the defaults of "" and none.
|
||||
subseries_doc, created = Document.objects.get_or_create(type_id=subseries_slug, name=subseries_doc_name)
|
||||
if created:
|
||||
if first_sync_creating_subseries:
|
||||
subseries_doc.docevent_set.create(type=f"{subseries_slug}_history_marker", by=system, desc=f"No history of this {subseries_slug.upper()} document is currently available in the datatracker before this point")
|
||||
subseries_doc.docevent_set.create(type=f"{subseries_slug}_doc_created", by=system, desc=f"Created {subseries_doc_name} via sync to the rfc-index")
|
||||
_, relationship_created = subseries_doc.relateddocument_set.get_or_create(relationship_id="contains", target=doc)
|
||||
if relationship_created:
|
||||
subseries_doc.docevent_set.create(type="sync_from_rfc_editor", by=system, desc=f"Added {doc.name} to {subseries_doc.name}")
|
||||
if first_sync_creating_subseries:
|
||||
rfc_events.append(doc.docevent_set.create(type=f"{subseries_slug}_history_marker", by=system, desc=f"No history of {subseries_doc.name.upper()} is currently available in the datatracker before this point"))
|
||||
rfc_events.append(doc.docevent_set.create(type="sync_from_rfc_editor", by=system, desc=f"Added {doc.name} to {subseries_doc.name}"))
|
||||
|
||||
for subdoc in doc.related_that("contains"):
|
||||
if subdoc.name not in also:
|
||||
assert(not first_sync_creating_subseries)
|
||||
subseries_doc.relateddocument_set.filter(target=subdoc).delete()
|
||||
rfc_events.append(doc.docevent_set.create(type="sync_from_rfc_editor", by=system, desc=f"Removed {doc.name} from {subseries_doc.name}"))
|
||||
subseries_doc.docevent_set.create(type="sync_from_rfc_editor", by=system, desc=f"Removed {doc.name} from {subseries_doc.name}")
|
||||
|
||||
doc_errata = errata.get(f"RFC{rfc_number}", [])
|
||||
all_rejected = doc_errata and all(
|
||||
|
|
|
@ -371,10 +371,26 @@ class RFCSyncTests(TestCase):
|
|||
rfc_doc = Document.objects.filter(rfc_number=1234, type_id="rfc").first()
|
||||
self.assertIsNotNone(rfc_doc, "RFC document should have been created")
|
||||
rfc_events = rfc_doc.docevent_set.all()
|
||||
self.assertEqual(len(rfc_events), 2)
|
||||
self.assertEqual(rfc_events[0].type, "sync_from_rfc_editor")
|
||||
self.assertEqual(rfc_events[1].type, "published_rfc")
|
||||
self.assertEqual(rfc_events[1].time.astimezone(RPC_TZINFO).date(), today)
|
||||
self.assertEqual(len(rfc_events), 8)
|
||||
expected_events = [
|
||||
["sync_from_rfc_editor", f"Received changes through RFC Editor sync (created document RFC 1234, created became rfc relationship between {rfc_doc.came_from_draft().name} and RFC 1234, set title to 'A Testing RFC', set abstract to 'This is some interesting text.', set pages to 42, set standardization level to Proposed Standard, added RFC published event at 2023-09-22, created updates relation between RFC 1234 and RFC 123, added Errata tag)"],
|
||||
["sync_from_rfc_editor", "Added rfc1234 to std1"],
|
||||
["std_history_marker", "No history of STD1 is currently available in the datatracker before this point"],
|
||||
["sync_from_rfc_editor", "Added rfc1234 to fyi1"],
|
||||
["fyi_history_marker", "No history of FYI1 is currently available in the datatracker before this point"],
|
||||
["sync_from_rfc_editor", "Added rfc1234 to bcp1"],
|
||||
["bcp_history_marker", "No history of BCP1 is currently available in the datatracker before this point"],
|
||||
["published_rfc", "RFC published"]
|
||||
]
|
||||
for index, [event_type, desc] in enumerate(expected_events):
|
||||
self.assertEqual(rfc_events[index].type, event_type)
|
||||
self.assertEqual(rfc_events[index].desc, desc)
|
||||
self.assertEqual(rfc_events[7].time.astimezone(RPC_TZINFO).date(), today)
|
||||
for subseries_slug in ["bcp", "fyi", "std"]:
|
||||
sub = Document.objects.filter(type_id=subseries_slug,name=f"{subseries_slug}1").first()
|
||||
self.assertIsNotNone(sub, f"{subseries_slug}1 not created")
|
||||
self.assertTrue(rfc_doc in sub.contains())
|
||||
self.assertTrue(sub in rfc_doc.part_of())
|
||||
self.assertEqual(rfc_doc.get_state_slug(), "published")
|
||||
# Should have an "errata" tag because there is an errata-url in the index XML, but no "verified-errata" tag
|
||||
# because there is no verified item in the errata JSON with doc-id matching the RFC document.
|
||||
|
|
|
@ -20,17 +20,28 @@
|
|||
<h2 class="my-3">Revision differences</h2>
|
||||
{% include "doc/document_history_form.html" with doc=doc diff_revisions=diff_revisions action=rfcdiff_base_url snapshot=snapshot only %}
|
||||
{% endif %}
|
||||
<h2 class="my-3">Document history
|
||||
{% if related %}
|
||||
<div class="float-end">
|
||||
{% for related_document in related %}
|
||||
<a class="btn btn-outline-primary btn-sm"
|
||||
href="{% url 'ietf.doc.views_doc.document_history' name=related_document.name %}">
|
||||
Related history for {{ related_document.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}</h2>
|
||||
<h2 class="my-3">Document history</h2>
|
||||
{% if doc.came_from_draft %}
|
||||
<ul class="nav nav-tabs my-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'ietf.doc.views_doc.document_history' name=doc.came_from_draft.name %}">{{doc.came_from_draft.name}}</a>
|
||||
<li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active">RFC {{doc.name|slice:"3:"}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if doc.became_rfc %}
|
||||
<ul class="nav nav-tabs my-3">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active">{{doc.name}}</a>
|
||||
<li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'ietf.doc.views_doc.document_history' name=doc.became_rfc.name %}">RFC {{doc.became_rfc.name|slice:"3:"}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if can_add_comment %}
|
||||
<div class="buttonlist">
|
||||
<a class="btn btn-primary"
|
||||
|
@ -44,7 +55,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col" data-sort="date">Date</th>
|
||||
{% if doc.type_id != "rfc" %}<th scope="col" data-sort="rev">Rev.</th>{% endif %}
|
||||
{% if doc.type_id not in "rfc,bcp,std,fyi" %}<th scope="col" data-sort="rev">Rev.</th>{% endif %}
|
||||
<th scope="col" data-sort="by">By</th>
|
||||
<th scope="col" data-sort="action">Action</th>
|
||||
</tr>
|
||||
|
@ -55,7 +66,7 @@
|
|||
<td>
|
||||
<div title="{{ e.time|date:'Y-m-d H:i:s O' }}">{{ e.time|date:"Y-m-d" }}</div>
|
||||
</td>
|
||||
{% if doc.type_id != "rfc" %}<td class="text-end">{{ e.rev }}</td>{% endif %}
|
||||
{% if doc.type_id not in "rfc,bcp,std,fyi" %}<td class="text-end">{{ e.rev }}</td>{% endif %}
|
||||
<td>{{ e.by|escape }}</td>
|
||||
<td>{{ e.desc|format_history_text }}</td>
|
||||
</tr>
|
||||
|
|
13
ietf/templates/doc/document_subseries.html
Normal file
13
ietf/templates/doc/document_subseries.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2023, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load static %}
|
||||
{% block title %}{{doc.type.name}}s{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
{{ top|safe }}
|
||||
<h2>{{ doc.name|slice:":3"|upper }} {{ doc.name|slice:"3:"}} consists of:</h2>
|
||||
{% for rfc in doc.contains %}
|
||||
<p><a href="{% url 'ietf.doc.views_doc.document_main' name=rfc.name %}">RFC {{rfc.name|slice:"3:"}}</a> : {{rfc.title}}</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
17
ietf/templates/doc/document_subseries_top.html
Normal file
17
ietf/templates/doc/document_subseries_top.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% origin %}
|
||||
{% load ietf_filters %}
|
||||
<h1>
|
||||
{{ doc.name|slice:":3"|upper }} {{ doc.name|slice:"3:"}}
|
||||
</h1>
|
||||
<ul class="nav nav-tabs my-3">
|
||||
{% for name, t, url, active, tooltip in tabs %}
|
||||
<li {% if tooltip %}title="{{ tooltip }}"{% endif %} class="nav-item">
|
||||
<a class="nav-link {% if t == selected %}active{% endif %}{% if not active %}disabled{% endif %}"
|
||||
{% if active %}href="{{ url }}"{% endif %}>
|
||||
{{ name|capfirst_allcaps }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
|
@ -5,7 +5,9 @@
|
|||
<h1>
|
||||
{{ doc.title|default:"(Untitled)" }}
|
||||
<br>
|
||||
<small class="text-body-secondary">{{ name }}</small>
|
||||
<small class="text-body-secondary">{{ name }}{% if doc.part_of %}
|
||||
{% for sub in doc.part_of %}{% if sub.contains|length_is:"1" %} also known as {% else %} part of {% endif %}<a href="{% url 'ietf.doc.views_doc.document_main' name=sub.name%}">{{sub.name|slice:":3"|upper}} {{sub.name|slice:"3:"}}</a>{% if not forloop.last %}, {%endif%}{% endfor %}
|
||||
{% endif %}</small>
|
||||
</h1>
|
||||
<ul class="nav nav-tabs my-3">
|
||||
{% for name, t, url, active, tooltip in tabs %}
|
||||
|
|
19
ietf/templates/doc/index_subseries.html
Normal file
19
ietf/templates/doc/index_subseries.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2023, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load static %}
|
||||
{% block title %}{{type.name}}s{% endblock %}
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<h1>{{type.name}}s</h1>
|
||||
{% for doc in docs %}
|
||||
<div class="card mb-3" id="{{doc.name}}">
|
||||
<div class="card-header"><a href="{% url 'ietf.doc.views_doc.document_main' name=doc.name %}">{{doc.name}}</a></div>
|
||||
<div class="card-body">
|
||||
{% for rfc in doc.contains %}
|
||||
<p><a href="{% url 'ietf.doc.views_doc.document_main' name=rfc.name %}">{{rfc.name}}</a> : {{rfc.title}}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
Loading…
Reference in a new issue