feat: subseries
This commit is contained in:
parent
f15960d32d
commit
0390f1868f
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
|
||||
|
|
|
@ -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})
|
||||
|
|
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="subseries_doc_created", by=system, desc=f"Created {subseries_doc_name} via sync to the rfc-index")
|
||||
if not subseries_doc.relateddocument_set.filter(relationship_id="contains", target=doc).exists():
|
||||
subseries_doc.relateddocument_set.create(relationship_id="contains", target=doc)
|
||||
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(
|
||||
|
|
|
@ -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>
|
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