Merge branch 'feat/rfc' into main-merge
# Conflicts: # ietf/doc/factories.py # ietf/doc/tests_status_change.py # ietf/name/fixtures/names.json # ietf/templates/doc/document_info.html
This commit is contained in:
commit
48d4072eeb
1
.github/workflows/ci-run-tests.yml
vendored
1
.github/workflows/ci-run-tests.yml
vendored
|
@ -4,6 +4,7 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
|
- 'feat/rfc'
|
||||||
paths:
|
paths:
|
||||||
- 'client/**'
|
- 'client/**'
|
||||||
- 'ietf/**'
|
- 'ietf/**'
|
||||||
|
|
|
@ -24,7 +24,7 @@ import debug # pyflakes:ignore
|
||||||
import ietf
|
import ietf
|
||||||
from ietf.doc.utils import get_unicode_document_content
|
from ietf.doc.utils import get_unicode_document_content
|
||||||
from ietf.doc.models import RelatedDocument, State
|
from ietf.doc.models import RelatedDocument, State
|
||||||
from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory
|
from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory, WgRfcFactory
|
||||||
from ietf.group.factories import RoleFactory
|
from ietf.group.factories import RoleFactory
|
||||||
from ietf.meeting.factories import MeetingFactory, SessionFactory
|
from ietf.meeting.factories import MeetingFactory, SessionFactory
|
||||||
from ietf.meeting.models import Session
|
from ietf.meeting.models import Session
|
||||||
|
@ -943,7 +943,7 @@ class RfcdiffSupportTests(TestCase):
|
||||||
self.assertNotIn('previous', received, 'Rev 00 has no previous name when not replacing a draft')
|
self.assertNotIn('previous', received, 'Rev 00 has no previous name when not replacing a draft')
|
||||||
|
|
||||||
replaced = IndividualDraftFactory()
|
replaced = IndividualDraftFactory()
|
||||||
RelatedDocument.objects.create(relationship_id='replaces',source=draft,target=replaced.docalias.first())
|
RelatedDocument.objects.create(relationship_id='replaces',source=draft,target=replaced)
|
||||||
received = self.getJson(dict(name=draft.name, rev='00'))
|
received = self.getJson(dict(name=draft.name, rev='00'))
|
||||||
self.assertEqual(received['previous'], f'{replaced.name}-{replaced.rev}',
|
self.assertEqual(received['previous'], f'{replaced.name}-{replaced.rev}',
|
||||||
'Rev 00 has a previous name when replacing a draft')
|
'Rev 00 has a previous name when replacing a draft')
|
||||||
|
@ -973,19 +973,19 @@ class RfcdiffSupportTests(TestCase):
|
||||||
|
|
||||||
def do_rfc_test(self, draft_name):
|
def do_rfc_test(self, draft_name):
|
||||||
draft = WgDraftFactory(name=draft_name, create_revisions=range(0,2))
|
draft = WgDraftFactory(name=draft_name, create_revisions=range(0,2))
|
||||||
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
|
rfc = WgRfcFactory(group=draft.group, rfc_number=self.next_rfc_number())
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
||||||
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
||||||
draft = reload_db_objects(draft)
|
draft, rfc = reload_db_objects(draft, rfc)
|
||||||
rfc = draft
|
|
||||||
|
|
||||||
number = rfc.rfc_number()
|
number = rfc.rfc_number
|
||||||
received = self.getJson(dict(name=number))
|
received = self.getJson(dict(name=number))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
received,
|
received,
|
||||||
dict(
|
dict(
|
||||||
content_url=rfc.get_href(),
|
content_url=rfc.get_href(),
|
||||||
name=rfc.canonical_name(),
|
name=rfc.name,
|
||||||
previous=f'{draft.name}-{draft.rev}',
|
previous=f'{draft.name}-{draft.rev}',
|
||||||
previous_url= draft.history_set.get(rev=draft.rev).get_href(),
|
previous_url= draft.history_set.get(rev=draft.rev).get_href(),
|
||||||
),
|
),
|
||||||
|
@ -1025,11 +1025,11 @@ class RfcdiffSupportTests(TestCase):
|
||||||
|
|
||||||
def test_rfc_with_tombstone(self):
|
def test_rfc_with_tombstone(self):
|
||||||
draft = WgDraftFactory(create_revisions=range(0,2))
|
draft = WgDraftFactory(create_revisions=range(0,2))
|
||||||
draft.docalias.create(name='rfc3261') # See views_doc.HAS_TOMBSTONE
|
rfc = WgRfcFactory(rfc_number=3261,group=draft.group)# See views_doc.HAS_TOMBSTONE
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
||||||
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
||||||
draft = reload_db_objects(draft)
|
draft = reload_db_objects(draft)
|
||||||
rfc = draft
|
|
||||||
|
|
||||||
# Some old rfcs had tombstones that shouldn't be used for comparisons
|
# Some old rfcs had tombstones that shouldn't be used for comparisons
|
||||||
received = self.getJson(dict(name=rfc.canonical_name()))
|
received = self.getJson(dict(name=rfc.canonical_name()))
|
||||||
|
@ -1037,11 +1037,11 @@ class RfcdiffSupportTests(TestCase):
|
||||||
|
|
||||||
def do_rfc_with_broken_history_test(self, draft_name):
|
def do_rfc_with_broken_history_test(self, draft_name):
|
||||||
draft = WgDraftFactory(rev='10', name=draft_name)
|
draft = WgDraftFactory(rev='10', name=draft_name)
|
||||||
draft.docalias.create(name=f'rfc{self.next_rfc_number():04}')
|
rfc = WgRfcFactory(group=draft.group, rfc_number=self.next_rfc_number())
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
draft.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
||||||
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
||||||
draft = reload_db_objects(draft)
|
draft = reload_db_objects(draft)
|
||||||
rfc = draft
|
|
||||||
|
|
||||||
received = self.getJson(dict(name=draft.name))
|
received = self.getJson(dict(name=draft.name))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
@ -317,12 +317,9 @@ def get_previous_url(name, rev=None):
|
||||||
previous_url = ''
|
previous_url = ''
|
||||||
if condition in ('historic version', 'current version'):
|
if condition in ('historic version', 'current version'):
|
||||||
doc = history if history else document
|
doc = history if history else document
|
||||||
if found_rev:
|
|
||||||
doc.is_rfc = lambda: False
|
|
||||||
previous_url = doc.get_href()
|
previous_url = doc.get_href()
|
||||||
elif condition == 'version dochistory not found':
|
elif condition == 'version dochistory not found':
|
||||||
document.rev = found_rev
|
document.rev = found_rev
|
||||||
document.is_rfc = lambda: False
|
|
||||||
previous_url = document.get_href()
|
previous_url = document.get_href()
|
||||||
return previous_url
|
return previous_url
|
||||||
|
|
||||||
|
@ -330,32 +327,39 @@ def get_previous_url(name, rev=None):
|
||||||
def rfcdiff_latest_json(request, name, rev=None):
|
def rfcdiff_latest_json(request, name, rev=None):
|
||||||
response = dict()
|
response = dict()
|
||||||
condition, document, history, found_rev = find_doc_for_rfcdiff(name, rev)
|
condition, document, history, found_rev = find_doc_for_rfcdiff(name, rev)
|
||||||
|
if document.type_id == "rfc":
|
||||||
|
draft_alias = next(iter(document.related_that('became_rfc')), None)
|
||||||
if condition == 'no such document':
|
if condition == 'no such document':
|
||||||
raise Http404
|
raise Http404
|
||||||
elif condition in ('historic version', 'current version'):
|
elif condition in ('historic version', 'current version'):
|
||||||
doc = history if history else document
|
doc = history if history else document
|
||||||
if not found_rev and doc.is_rfc():
|
if doc.type_id == "rfc":
|
||||||
response['content_url'] = doc.get_href()
|
response['content_url'] = doc.get_href()
|
||||||
response['name']=doc.canonical_name()
|
response['name']=doc.name
|
||||||
if doc.name != doc.canonical_name():
|
if draft_alias:
|
||||||
|
draft = draft_alias.document
|
||||||
|
prev_rev = draft.rev
|
||||||
|
if doc.rfc_number in HAS_TOMBSTONE and prev_rev != '00':
|
||||||
|
prev_rev = f'{(int(draft.rev)-1):02d}'
|
||||||
|
response['previous'] = f'{draft.name}-{prev_rev}'
|
||||||
|
response['previous_url'] = get_previous_url(draft.name, prev_rev)
|
||||||
|
elif doc.type_id == "draft" and not found_rev and doc.relateddocument_set.filter(relationship_id="became_rfc").exists():
|
||||||
|
rfc = doc.related_that_doc("became_rfc")[0]
|
||||||
|
response['content_url'] = rfc.get_href()
|
||||||
|
response['name']=rfc.name
|
||||||
prev_rev = doc.rev
|
prev_rev = doc.rev
|
||||||
# not sure what to do if non-numeric values come back, so at least log it
|
if rfc.rfc_number in HAS_TOMBSTONE and prev_rev != '00':
|
||||||
log.assertion('doc.rfc_number().isdigit()') # .rfc_number() is expensive...
|
|
||||||
log.assertion('doc.rev.isdigit()')
|
|
||||||
if int(doc.rfc_number()) in HAS_TOMBSTONE and prev_rev != '00':
|
|
||||||
prev_rev = f'{(int(doc.rev)-1):02d}'
|
prev_rev = f'{(int(doc.rev)-1):02d}'
|
||||||
response['previous'] = f'{doc.name}-{prev_rev}'
|
response['previous'] = f'{doc.name}-{prev_rev}'
|
||||||
response['previous_url'] = get_previous_url(doc.name, prev_rev)
|
response['previous_url'] = get_previous_url(doc.name, prev_rev)
|
||||||
else:
|
else:
|
||||||
doc.is_rfc = lambda: False
|
|
||||||
response['content_url'] = doc.get_href()
|
response['content_url'] = doc.get_href()
|
||||||
response['rev'] = doc.rev
|
response['rev'] = doc.rev
|
||||||
response['name'] = doc.name
|
response['name'] = doc.name
|
||||||
if doc.rev == '00':
|
if doc.rev == '00':
|
||||||
replaces_docs = (history.doc if condition=='historic version' else doc).related_that_doc('replaces')
|
replaces_docs = (history.doc if condition=='historic version' else doc).related_that_doc('replaces')
|
||||||
if replaces_docs:
|
if replaces_docs:
|
||||||
replaces = replaces_docs[0].document
|
replaces = replaces_docs[0]
|
||||||
response['previous'] = f'{replaces.name}-{replaces.rev}'
|
response['previous'] = f'{replaces.name}-{replaces.rev}'
|
||||||
response['previous_url'] = get_previous_url(replaces.name, replaces.rev)
|
response['previous_url'] = get_previous_url(replaces.name, replaces.rev)
|
||||||
else:
|
else:
|
||||||
|
@ -374,7 +378,6 @@ def rfcdiff_latest_json(request, name, rev=None):
|
||||||
response['name'] = document.name
|
response['name'] = document.name
|
||||||
response['rev'] = found_rev
|
response['rev'] = found_rev
|
||||||
document.rev = found_rev
|
document.rev = found_rev
|
||||||
document.is_rfc = lambda: False
|
|
||||||
response['content_url'] = document.get_href()
|
response['content_url'] = document.get_href()
|
||||||
# not sure what to do if non-numeric values come back, so at least log it
|
# not sure what to do if non-numeric values come back, so at least log it
|
||||||
log.assertion('found_rev.isdigit()')
|
log.assertion('found_rev.isdigit()')
|
||||||
|
|
|
@ -84,7 +84,7 @@ for changes, doc, rfc_published in ietf.sync.rfceditor.update_docs_from_rfc_inde
|
||||||
new_rfcs.append(doc)
|
new_rfcs.append(doc)
|
||||||
|
|
||||||
for c in changes:
|
for c in changes:
|
||||||
log("RFC%s, %s: %s" % (doc.rfcnum, doc.name, c))
|
log("RFC%s, %s: %s" % (doc.rfc_number, doc.name, c))
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ class SearchRuleForm(forms.ModelForm):
|
||||||
super(SearchRuleForm, self).__init__(*args, **kwargs)
|
super(SearchRuleForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def restrict_state(state_type, slug=None):
|
def restrict_state(state_type, slug=None):
|
||||||
|
if "state" not in self.fields:
|
||||||
|
raise RuntimeError(f"Rule type {rule_type} cannot include state filtering")
|
||||||
f = self.fields['state']
|
f = self.fields['state']
|
||||||
f.queryset = f.queryset.filter(used=True).filter(type=state_type)
|
f.queryset = f.queryset.filter(used=True).filter(type=state_type)
|
||||||
if slug:
|
if slug:
|
||||||
|
@ -38,11 +40,15 @@ class SearchRuleForm(forms.ModelForm):
|
||||||
f.initial = f.queryset[0].pk
|
f.initial = f.queryset[0].pk
|
||||||
f.widget = forms.HiddenInput()
|
f.widget = forms.HiddenInput()
|
||||||
|
|
||||||
|
if rule_type.endswith("_rfc"):
|
||||||
|
del self.fields["state"] # rfc rules must not look at document states
|
||||||
|
|
||||||
if rule_type in ["group", "group_rfc", "area", "area_rfc", "group_exp"]:
|
if rule_type in ["group", "group_rfc", "area", "area_rfc", "group_exp"]:
|
||||||
if rule_type == "group_exp":
|
if rule_type == "group_exp":
|
||||||
restrict_state("draft", "expired")
|
restrict_state("draft", "expired")
|
||||||
else:
|
else:
|
||||||
restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active")
|
if not rule_type.endswith("_rfc"):
|
||||||
|
restrict_state("draft", "active")
|
||||||
|
|
||||||
if rule_type.startswith("area"):
|
if rule_type.startswith("area"):
|
||||||
self.fields["group"].label = "Area"
|
self.fields["group"].label = "Area"
|
||||||
|
@ -70,7 +76,8 @@ class SearchRuleForm(forms.ModelForm):
|
||||||
del self.fields["text"]
|
del self.fields["text"]
|
||||||
|
|
||||||
elif rule_type in ["author", "author_rfc", "shepherd", "ad"]:
|
elif rule_type in ["author", "author_rfc", "shepherd", "ad"]:
|
||||||
restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active")
|
if not rule_type.endswith("_rfc"):
|
||||||
|
restrict_state("draft", "active")
|
||||||
|
|
||||||
if rule_type.startswith("author"):
|
if rule_type.startswith("author"):
|
||||||
self.fields["person"].label = "Author"
|
self.fields["person"].label = "Author"
|
||||||
|
@ -84,7 +91,8 @@ class SearchRuleForm(forms.ModelForm):
|
||||||
del self.fields["text"]
|
del self.fields["text"]
|
||||||
|
|
||||||
elif rule_type == "name_contains":
|
elif rule_type == "name_contains":
|
||||||
restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active")
|
if not rule_type.endswith("_rfc"):
|
||||||
|
restrict_state("draft", "active")
|
||||||
|
|
||||||
del self.fields["person"]
|
del self.fields["person"]
|
||||||
del self.fields["group"]
|
del self.fields["group"]
|
||||||
|
|
50
ietf/community/migrations/0003_track_rfcs.py
Normal file
50
ietf/community/migrations/0003_track_rfcs.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Generated by Django 4.2.3 on 2023-07-07 18:33
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
"""Track any RFCs that were created from tracked drafts"""
|
||||||
|
CommunityList = apps.get_model("community", "CommunityList")
|
||||||
|
RelatedDocument = apps.get_model("doc", "RelatedDocument")
|
||||||
|
|
||||||
|
# Handle individually tracked documents
|
||||||
|
for cl in CommunityList.objects.all():
|
||||||
|
for rfc in set(
|
||||||
|
RelatedDocument.objects.filter(
|
||||||
|
source__in=cl.added_docs.all(),
|
||||||
|
relationship__slug="became_rfc",
|
||||||
|
).values_list("target__docs", flat=True)
|
||||||
|
):
|
||||||
|
cl.added_docs.add(rfc)
|
||||||
|
|
||||||
|
# Handle rules - rules ending with _rfc should no longer filter by state.
|
||||||
|
# There are 9 CommunityLists with invalid author_rfc rules that are filtering
|
||||||
|
# by (draft, active) instead of (draft, rfc) state before migration. All but one
|
||||||
|
# also includes an author rule for (draft, active), so these will start following
|
||||||
|
# RFCs as well. The one exception will start tracking RFCs instead of I-Ds, which
|
||||||
|
# is probably what was intended, but will be a change in their user experience.
|
||||||
|
SearchRule = apps.get_model("community", "SearchRule")
|
||||||
|
rfc_rules = SearchRule.objects.filter(rule_type__endswith="_rfc")
|
||||||
|
rfc_rules.update(state=None)
|
||||||
|
|
||||||
|
def reverse(apps, schema_editor):
|
||||||
|
Document = apps.get_model("doc", "Document")
|
||||||
|
for rfc in Document.objects.filter(type__slug="rfc"):
|
||||||
|
rfc.communitylist_set.clear()
|
||||||
|
|
||||||
|
# See the comment above regarding author_rfc
|
||||||
|
SearchRule = apps.get_model("community", "SearchRule")
|
||||||
|
State = apps.get_model("doc", "State")
|
||||||
|
SearchRule.objects.filter(rule_type__endswith="_rfc").update(
|
||||||
|
state=State.objects.get(type_id="draft", slug="rfc")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("community", "0002_auto_20230320_1222"),
|
||||||
|
("doc", "0010_move_rfc_docaliases"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [migrations.RunPython(forward, reverse)]
|
|
@ -151,7 +151,7 @@ class CommunityListTests(WebTest):
|
||||||
"action": "add_rule",
|
"action": "add_rule",
|
||||||
"rule_type": "author_rfc",
|
"rule_type": "author_rfc",
|
||||||
"author_rfc-person": Person.objects.filter(documentauthor__document=draft).first().pk,
|
"author_rfc-person": Person.objects.filter(documentauthor__document=draft).first().pk,
|
||||||
"author_rfc-state": State.objects.get(type="draft", slug="rfc").pk,
|
"author_rfc-state": State.objects.get(type="rfc", slug="published").pk,
|
||||||
})
|
})
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
clist = CommunityList.objects.get(user__username="plain")
|
clist = CommunityList.objects.get(user__username="plain")
|
||||||
|
@ -408,4 +408,4 @@ class CommunityListTests(WebTest):
|
||||||
self.assertEqual(len(outbox), mailbox_before + 1)
|
self.assertEqual(len(outbox), mailbox_before + 1)
|
||||||
self.assertTrue(draft.name in outbox[-1]["Subject"])
|
self.assertTrue(draft.name in outbox[-1]["Subject"])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -71,70 +71,103 @@ def update_name_contains_indexes_with_new_doc(doc):
|
||||||
if re.search(r.text, doc.name) and not doc in r.name_contains_index.all():
|
if re.search(r.text, doc.name) and not doc in r.name_contains_index.all():
|
||||||
r.name_contains_index.add(doc)
|
r.name_contains_index.add(doc)
|
||||||
|
|
||||||
|
|
||||||
def docs_matching_community_list_rule(rule):
|
def docs_matching_community_list_rule(rule):
|
||||||
docs = Document.objects.all()
|
docs = Document.objects.all()
|
||||||
|
|
||||||
|
if rule.rule_type.endswith("_rfc"):
|
||||||
|
docs = docs.filter(type_id="rfc") # rule.state is ignored for RFCs
|
||||||
|
else:
|
||||||
|
docs = docs.filter(type_id="draft", states=rule.state)
|
||||||
|
|
||||||
if rule.rule_type in ['group', 'area', 'group_rfc', 'area_rfc']:
|
if rule.rule_type in ['group', 'area', 'group_rfc', 'area_rfc']:
|
||||||
return docs.filter(Q(group=rule.group_id) | Q(group__parent=rule.group_id), states=rule.state)
|
return docs.filter(Q(group=rule.group_id) | Q(group__parent=rule.group_id))
|
||||||
elif rule.rule_type in ['group_exp']:
|
elif rule.rule_type in ['group_exp']:
|
||||||
return docs.filter(group=rule.group_id, states=rule.state)
|
return docs.filter(group=rule.group_id)
|
||||||
elif rule.rule_type.startswith("state_"):
|
elif rule.rule_type.startswith("state_"):
|
||||||
return docs.filter(states=rule.state)
|
return docs
|
||||||
elif rule.rule_type in ["author", "author_rfc"]:
|
elif rule.rule_type in ["author", "author_rfc"]:
|
||||||
return docs.filter(states=rule.state, documentauthor__person=rule.person)
|
return docs.filter(documentauthor__person=rule.person)
|
||||||
elif rule.rule_type == "ad":
|
elif rule.rule_type == "ad":
|
||||||
return docs.filter(states=rule.state, ad=rule.person)
|
return docs.filter(ad=rule.person)
|
||||||
elif rule.rule_type == "shepherd":
|
elif rule.rule_type == "shepherd":
|
||||||
return docs.filter(states=rule.state, shepherd__person=rule.person)
|
return docs.filter(shepherd__person=rule.person)
|
||||||
elif rule.rule_type == "name_contains":
|
elif rule.rule_type == "name_contains":
|
||||||
return docs.filter(states=rule.state, searchrule=rule)
|
return docs.filter(searchrule=rule)
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
def community_list_rules_matching_doc(doc):
|
def community_list_rules_matching_doc(doc):
|
||||||
|
rules = SearchRule.objects.none()
|
||||||
|
if doc.type_id not in ["draft", "rfc"]:
|
||||||
|
return rules # none
|
||||||
states = list(doc.states.values_list("pk", flat=True))
|
states = list(doc.states.values_list("pk", flat=True))
|
||||||
|
|
||||||
rules = SearchRule.objects.none()
|
# group and area rules
|
||||||
|
|
||||||
if doc.group_id:
|
if doc.group_id:
|
||||||
groups = [doc.group_id]
|
groups = [doc.group_id]
|
||||||
if doc.group.parent_id:
|
if doc.group.parent_id:
|
||||||
groups.append(doc.group.parent_id)
|
groups.append(doc.group.parent_id)
|
||||||
|
rules_to_add = SearchRule.objects.filter(group__in=groups)
|
||||||
|
if doc.type_id == "rfc":
|
||||||
|
rules_to_add = rules_to_add.filter(rule_type__in=["group_rfc", "area_rfc"])
|
||||||
|
else:
|
||||||
|
rules_to_add = rules_to_add.filter(
|
||||||
|
rule_type__in=["group", "area", "group_exp"],
|
||||||
|
state__in=states,
|
||||||
|
)
|
||||||
|
rules |= rules_to_add
|
||||||
|
|
||||||
|
# state rules (only relevant for I-Ds)
|
||||||
|
if doc.type_id == "draft":
|
||||||
rules |= SearchRule.objects.filter(
|
rules |= SearchRule.objects.filter(
|
||||||
rule_type__in=['group', 'area', 'group_rfc', 'area_rfc', 'group_exp'],
|
rule_type__in=[
|
||||||
|
"state_iab",
|
||||||
|
"state_iana",
|
||||||
|
"state_iesg",
|
||||||
|
"state_irtf",
|
||||||
|
"state_ise",
|
||||||
|
"state_rfceditor",
|
||||||
|
"state_ietf",
|
||||||
|
],
|
||||||
state__in=states,
|
state__in=states,
|
||||||
group__in=groups
|
|
||||||
)
|
)
|
||||||
|
|
||||||
rules |= SearchRule.objects.filter(
|
# author rules
|
||||||
rule_type__in=['state_iab', 'state_iana', 'state_iesg', 'state_irtf', 'state_ise', 'state_rfceditor', 'state_ietf'],
|
if doc.type_id == "rfc":
|
||||||
state__in=states,
|
|
||||||
)
|
|
||||||
|
|
||||||
rules |= SearchRule.objects.filter(
|
|
||||||
rule_type__in=["author", "author_rfc"],
|
|
||||||
state__in=states,
|
|
||||||
person__in=list(Person.objects.filter(documentauthor__document=doc)),
|
|
||||||
)
|
|
||||||
|
|
||||||
if doc.ad_id:
|
|
||||||
rules |= SearchRule.objects.filter(
|
rules |= SearchRule.objects.filter(
|
||||||
rule_type="ad",
|
rule_type="author_rfc",
|
||||||
|
person__in=list(Person.objects.filter(documentauthor__document=doc)),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
rules |= SearchRule.objects.filter(
|
||||||
|
rule_type="author",
|
||||||
state__in=states,
|
state__in=states,
|
||||||
person=doc.ad_id,
|
person__in=list(Person.objects.filter(documentauthor__document=doc)),
|
||||||
)
|
)
|
||||||
|
|
||||||
if doc.shepherd_id:
|
# Other draft-only rules rules
|
||||||
rules |= SearchRule.objects.filter(
|
if doc.type_id == "draft":
|
||||||
rule_type="shepherd",
|
if doc.ad_id:
|
||||||
state__in=states,
|
rules |= SearchRule.objects.filter(
|
||||||
person__email=doc.shepherd_id,
|
rule_type="ad",
|
||||||
)
|
state__in=states,
|
||||||
|
person=doc.ad_id,
|
||||||
|
)
|
||||||
|
|
||||||
rules |= SearchRule.objects.filter(
|
if doc.shepherd_id:
|
||||||
rule_type="name_contains",
|
rules |= SearchRule.objects.filter(
|
||||||
state__in=states,
|
rule_type="shepherd",
|
||||||
name_contains_index=doc, # search our materialized index to avoid full scan
|
state__in=states,
|
||||||
)
|
person__email=doc.shepherd_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
rules |= SearchRule.objects.filter(
|
||||||
|
rule_type="name_contains",
|
||||||
|
state__in=states,
|
||||||
|
name_contains_index=doc, # search our materialized index to avoid full scan
|
||||||
|
)
|
||||||
|
|
||||||
return rules
|
return rules
|
||||||
|
|
||||||
|
@ -146,7 +179,11 @@ def docs_tracked_by_community_list(clist):
|
||||||
# in theory, we could use an OR query, but databases seem to have
|
# in theory, we could use an OR query, but databases seem to have
|
||||||
# trouble with OR queries and complicated joins so do the OR'ing
|
# trouble with OR queries and complicated joins so do the OR'ing
|
||||||
# manually
|
# manually
|
||||||
doc_ids = set(clist.added_docs.values_list("pk", flat=True))
|
doc_ids = set()
|
||||||
|
for doc in clist.added_docs.all():
|
||||||
|
doc_ids.add(doc.pk)
|
||||||
|
doc_ids.update(alias.docs.first().pk for alias in doc.related_that_doc("became_rfc"))
|
||||||
|
|
||||||
for rule in clist.searchrule_set.all():
|
for rule in clist.searchrule_set.all():
|
||||||
doc_ids = doc_ids | set(docs_matching_community_list_rule(rule).values_list("pk", flat=True))
|
doc_ids = doc_ids | set(docs_matching_community_list_rule(rule).values_list("pk", flat=True))
|
||||||
|
|
||||||
|
|
|
@ -79,19 +79,18 @@ def manage_list(request, username=None, acronym=None, group_type=None):
|
||||||
rule_type_form = SearchRuleTypeForm(request.POST)
|
rule_type_form = SearchRuleTypeForm(request.POST)
|
||||||
if rule_type_form.is_valid():
|
if rule_type_form.is_valid():
|
||||||
rule_type = rule_type_form.cleaned_data['rule_type']
|
rule_type = rule_type_form.cleaned_data['rule_type']
|
||||||
|
if rule_type:
|
||||||
if rule_type:
|
rule_form = SearchRuleForm(clist, rule_type, request.POST)
|
||||||
rule_form = SearchRuleForm(clist, rule_type, request.POST)
|
if rule_form.is_valid():
|
||||||
if rule_form.is_valid():
|
if clist.pk is None:
|
||||||
if clist.pk is None:
|
clist.save()
|
||||||
clist.save()
|
|
||||||
|
rule = rule_form.save(commit=False)
|
||||||
rule = rule_form.save(commit=False)
|
rule.community_list = clist
|
||||||
rule.community_list = clist
|
rule.rule_type = rule_type
|
||||||
rule.rule_type = rule_type
|
rule.save()
|
||||||
rule.save()
|
if rule.rule_type == "name_contains":
|
||||||
if rule.rule_type == "name_contains":
|
reset_name_contains_index_for_rule(rule)
|
||||||
reset_name_contains_index_for_rule(rule)
|
|
||||||
|
|
||||||
return HttpResponseRedirect("")
|
return HttpResponseRedirect("")
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -43,6 +43,7 @@ class DocActionHolderInline(admin.TabularInline):
|
||||||
|
|
||||||
class RelatedDocumentInline(admin.TabularInline):
|
class RelatedDocumentInline(admin.TabularInline):
|
||||||
model = RelatedDocument
|
model = RelatedDocument
|
||||||
|
fk_name= 'source'
|
||||||
def this(self, instance):
|
def this(self, instance):
|
||||||
return instance.source.canonical_name()
|
return instance.source.canonical_name()
|
||||||
readonly_fields = ['this', ]
|
readonly_fields = ['this', ]
|
||||||
|
@ -125,7 +126,7 @@ admin.site.register(DocReminder, DocReminderAdmin)
|
||||||
class RelatedDocumentAdmin(admin.ModelAdmin):
|
class RelatedDocumentAdmin(admin.ModelAdmin):
|
||||||
list_display = ['source', 'target', 'relationship', ]
|
list_display = ['source', 'target', 'relationship', ]
|
||||||
list_filter = ['relationship', ]
|
list_filter = ['relationship', ]
|
||||||
search_fields = ['source__name', 'target__name', 'target__docs__name', ]
|
search_fields = ['source__name', 'target__name', ]
|
||||||
raw_id_fields = ['source', 'target', ]
|
raw_id_fields = ['source', 'target', ]
|
||||||
admin.site.register(RelatedDocument, RelatedDocumentAdmin)
|
admin.site.register(RelatedDocument, RelatedDocumentAdmin)
|
||||||
|
|
||||||
|
|
|
@ -82,13 +82,7 @@ class BaseDocumentFactory(factory.django.DjangoModelFactory):
|
||||||
def relations(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
|
def relations(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
|
||||||
if create and extracted:
|
if create and extracted:
|
||||||
for (rel_id, doc) in extracted:
|
for (rel_id, doc) in extracted:
|
||||||
if isinstance(doc, Document):
|
obj.relateddocument_set.create(relationship_id=rel_id, target=doc)
|
||||||
docalias = doc.docalias.first()
|
|
||||||
elif isinstance(doc, DocAlias):
|
|
||||||
docalias = doc
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
obj.relateddocument_set.create(relationship_id=rel_id, target=docalias)
|
|
||||||
|
|
||||||
@factory.post_generation
|
@factory.post_generation
|
||||||
def create_revisions(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
|
def create_revisions(obj, create, extracted, **kwargs): # pylint: disable=no-self-argument
|
||||||
|
@ -118,6 +112,29 @@ class DocumentFactory(BaseDocumentFactory):
|
||||||
group = factory.SubFactory('ietf.group.factories.GroupFactory',acronym='none')
|
group = factory.SubFactory('ietf.group.factories.GroupFactory',acronym='none')
|
||||||
|
|
||||||
|
|
||||||
|
class RfcFactory(BaseDocumentFactory):
|
||||||
|
type_id = "rfc"
|
||||||
|
rfc_number = factory.Sequence(lambda n: n + 1000)
|
||||||
|
name = factory.LazyAttribute(lambda o: f"rfc{o.rfc_number:d}")
|
||||||
|
expires = None
|
||||||
|
|
||||||
|
@factory.post_generation
|
||||||
|
def states(obj, create, extracted, **kwargs):
|
||||||
|
if not create:
|
||||||
|
return
|
||||||
|
if extracted:
|
||||||
|
for (state_type_id,state_slug) in extracted:
|
||||||
|
obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
|
||||||
|
else:
|
||||||
|
obj.set_state(State.objects.get(type_id='rfc',slug='published'))
|
||||||
|
|
||||||
|
@factory.post_generation
|
||||||
|
def reset_canonical_name(obj, create, extracted, **kwargs):
|
||||||
|
if hasattr(obj, '_canonical_name'):
|
||||||
|
del obj._canonical_name
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class IndividualDraftFactory(BaseDocumentFactory):
|
class IndividualDraftFactory(BaseDocumentFactory):
|
||||||
|
|
||||||
type_id = 'draft'
|
type_id = 'draft'
|
||||||
|
@ -136,28 +153,11 @@ class IndividualDraftFactory(BaseDocumentFactory):
|
||||||
obj.set_state(State.objects.get(type_id='draft',slug='active'))
|
obj.set_state(State.objects.get(type_id='draft',slug='active'))
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
||||||
|
|
||||||
class IndividualRfcFactory(IndividualDraftFactory):
|
class IndividualRfcFactory(RfcFactory):
|
||||||
|
group = factory.SubFactory('ietf.group.factories.GroupFactory',acronym='none')
|
||||||
|
|
||||||
alias2 = factory.RelatedFactory('ietf.doc.factories.DocAliasFactory','document',name=factory.Sequence(lambda n: 'rfc%04d'%(n+1000)))
|
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def states(obj, create, extracted, **kwargs):
|
|
||||||
if not create:
|
|
||||||
return
|
|
||||||
if extracted:
|
|
||||||
for (state_type_id,state_slug) in extracted:
|
|
||||||
obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
|
|
||||||
else:
|
|
||||||
obj.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def reset_canonical_name(obj, create, extracted, **kwargs):
|
|
||||||
if hasattr(obj, '_canonical_name'):
|
|
||||||
del obj._canonical_name
|
|
||||||
return None
|
|
||||||
|
|
||||||
class WgDraftFactory(BaseDocumentFactory):
|
class WgDraftFactory(BaseDocumentFactory):
|
||||||
|
|
||||||
type_id = 'draft'
|
type_id = 'draft'
|
||||||
group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='wg')
|
group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='wg')
|
||||||
stream_id = 'ietf'
|
stream_id = 'ietf'
|
||||||
|
@ -176,30 +176,12 @@ class WgDraftFactory(BaseDocumentFactory):
|
||||||
obj.set_state(State.objects.get(type_id='draft-stream-ietf',slug='wg-doc'))
|
obj.set_state(State.objects.get(type_id='draft-stream-ietf',slug='wg-doc'))
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
||||||
|
|
||||||
class WgRfcFactory(WgDraftFactory):
|
|
||||||
|
|
||||||
alias2 = factory.RelatedFactory('ietf.doc.factories.DocAliasFactory','document',name=factory.Sequence(lambda n: 'rfc%04d'%(n+1000)))
|
|
||||||
|
|
||||||
|
class WgRfcFactory(RfcFactory):
|
||||||
|
group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='wg')
|
||||||
|
stream_id = 'ietf'
|
||||||
std_level_id = 'ps'
|
std_level_id = 'ps'
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def states(obj, create, extracted, **kwargs):
|
|
||||||
if not create:
|
|
||||||
return
|
|
||||||
if extracted:
|
|
||||||
for (state_type_id,state_slug) in extracted:
|
|
||||||
obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
|
|
||||||
if not obj.get_state('draft-iesg'):
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
|
||||||
else:
|
|
||||||
obj.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg', slug='pub'))
|
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def reset_canonical_name(obj, create, extracted, **kwargs):
|
|
||||||
if hasattr(obj, '_canonical_name'):
|
|
||||||
del obj._canonical_name
|
|
||||||
return None
|
|
||||||
|
|
||||||
class RgDraftFactory(BaseDocumentFactory):
|
class RgDraftFactory(BaseDocumentFactory):
|
||||||
|
|
||||||
|
@ -222,34 +204,11 @@ class RgDraftFactory(BaseDocumentFactory):
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
||||||
|
|
||||||
|
|
||||||
class RgRfcFactory(RgDraftFactory):
|
class RgRfcFactory(RfcFactory):
|
||||||
|
group = factory.SubFactory('ietf.group.factories.GroupFactory',type_id='rg')
|
||||||
alias2 = factory.RelatedFactory('ietf.doc.factories.DocAliasFactory','document',name=factory.Sequence(lambda n: 'rfc%04d'%(n+1000)))
|
stream_id = 'irtf'
|
||||||
|
|
||||||
std_level_id = 'inf'
|
std_level_id = 'inf'
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def states(obj, create, extracted, **kwargs):
|
|
||||||
if not create:
|
|
||||||
return
|
|
||||||
if extracted:
|
|
||||||
for (state_type_id,state_slug) in extracted:
|
|
||||||
obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
|
|
||||||
if not obj.get_state('draft-stream-irtf'):
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-stream-irtf', slug='pub'))
|
|
||||||
if not obj.get_state('draft-iesg'):
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
|
||||||
else:
|
|
||||||
obj.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-stream-irtf', slug='pub'))
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def reset_canonical_name(obj, create, extracted, **kwargs):
|
|
||||||
if hasattr(obj, '_canonical_name'):
|
|
||||||
del obj._canonical_name
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class CharterFactory(BaseDocumentFactory):
|
class CharterFactory(BaseDocumentFactory):
|
||||||
|
|
||||||
|
@ -278,7 +237,7 @@ class StatusChangeFactory(BaseDocumentFactory):
|
||||||
for (rel, target) in extracted:
|
for (rel, target) in extracted:
|
||||||
obj.relateddocument_set.create(relationship_id=rel,target=target)
|
obj.relateddocument_set.create(relationship_id=rel,target=target)
|
||||||
else:
|
else:
|
||||||
obj.relateddocument_set.create(relationship_id='tobcp', target=WgRfcFactory().docalias.first())
|
obj.relateddocument_set.create(relationship_id='tobcp', target=WgRfcFactory())
|
||||||
|
|
||||||
@factory.post_generation
|
@factory.post_generation
|
||||||
def states(obj, create, extracted, **kwargs):
|
def states(obj, create, extracted, **kwargs):
|
||||||
|
@ -305,9 +264,9 @@ class ConflictReviewFactory(BaseDocumentFactory):
|
||||||
if not create:
|
if not create:
|
||||||
return
|
return
|
||||||
if extracted:
|
if extracted:
|
||||||
obj.relateddocument_set.create(relationship_id='conflrev',target=extracted.docalias.first())
|
obj.relateddocument_set.create(relationship_id='conflrev',target=extracted)
|
||||||
else:
|
else:
|
||||||
obj.relateddocument_set.create(relationship_id='conflrev',target=DocumentFactory(name=obj.name.replace('conflict-review-','draft-'),type_id='draft',group=Group.objects.get(type_id='individ')).docalias.first())
|
obj.relateddocument_set.create(relationship_id='conflrev',target=DocumentFactory(name=obj.name.replace('conflict-review-','draft-'),type_id='draft',group=Group.objects.get(type_id='individ')))
|
||||||
|
|
||||||
|
|
||||||
@factory.post_generation
|
@factory.post_generation
|
||||||
|
@ -550,33 +509,8 @@ class EditorialDraftFactory(BaseDocumentFactory):
|
||||||
obj.set_state(State.objects.get(type_id='draft-stream-editorial',slug='active'))
|
obj.set_state(State.objects.get(type_id='draft-stream-editorial',slug='active'))
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
||||||
|
|
||||||
class EditorialRfcFactory(RgDraftFactory):
|
class EditorialRfcFactory(RgRfcFactory):
|
||||||
|
pass
|
||||||
alias2 = factory.RelatedFactory('ietf.doc.factories.DocAliasFactory','document',name=factory.Sequence(lambda n: 'rfc%04d'%(n+1000)))
|
|
||||||
|
|
||||||
std_level_id = 'inf'
|
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def states(obj, create, extracted, **kwargs):
|
|
||||||
if not create:
|
|
||||||
return
|
|
||||||
if extracted:
|
|
||||||
for (state_type_id,state_slug) in extracted:
|
|
||||||
obj.set_state(State.objects.get(type_id=state_type_id,slug=state_slug))
|
|
||||||
if not obj.get_state('draft-stream-editorial'):
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-stream-editorial', slug='pub'))
|
|
||||||
if not obj.get_state('draft-iesg'):
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
|
||||||
else:
|
|
||||||
obj.set_state(State.objects.get(type_id='draft',slug='rfc'))
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-stream-editorial', slug='pub'))
|
|
||||||
obj.set_state(State.objects.get(type_id='draft-iesg',slug='idexists'))
|
|
||||||
|
|
||||||
@factory.post_generation
|
|
||||||
def reset_canonical_name(obj, create, extracted, **kwargs):
|
|
||||||
if hasattr(obj, '_canonical_name'):
|
|
||||||
del obj._canonical_name
|
|
||||||
return None
|
|
||||||
|
|
||||||
class StatementFactory(BaseDocumentFactory):
|
class StatementFactory(BaseDocumentFactory):
|
||||||
type_id = "statement"
|
type_id = "statement"
|
||||||
|
|
|
@ -224,8 +224,8 @@ class RfcFeed(Feed):
|
||||||
extra.update({"dcterms_accessRights": "gratis"})
|
extra.update({"dcterms_accessRights": "gratis"})
|
||||||
extra.update({"dcterms_format": "text/html"})
|
extra.update({"dcterms_format": "text/html"})
|
||||||
media_contents = []
|
media_contents = []
|
||||||
if int(item.rfc_number()) < 8650:
|
if item.rfc_number < 8650:
|
||||||
if int(item.rfc_number()) not in [8, 9, 51, 418, 500, 530, 589]:
|
if item.rfc_number not in [8, 9, 51, 418, 500, 530, 589]:
|
||||||
for fmt, media_type in [("txt", "text/plain"), ("html", "text/html")]:
|
for fmt, media_type in [("txt", "text/plain"), ("html", "text/html")]:
|
||||||
media_contents.append(
|
media_contents.append(
|
||||||
{
|
{
|
||||||
|
@ -234,7 +234,7 @@ class RfcFeed(Feed):
|
||||||
"is_format_of": self.item_link(item),
|
"is_format_of": self.item_link(item),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if int(item.rfc_number()) not in [571, 587]:
|
if item.rfc_number not in [571, 587]:
|
||||||
media_contents.append(
|
media_contents.append(
|
||||||
{
|
{
|
||||||
"url": f"https://www.rfc-editor.org/rfc/pdfrfc/{item.canonical_name()}.txt.pdf",
|
"url": f"https://www.rfc-editor.org/rfc/pdfrfc/{item.canonical_name()}.txt.pdf",
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django import forms
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
|
|
||||||
from ietf.doc.fields import SearchableDocAliasesField, SearchableDocAliasField
|
from ietf.doc.fields import SearchableDocumentField, SearchableDocumentsField
|
||||||
from ietf.doc.models import RelatedDocument, DocExtResource
|
from ietf.doc.models import RelatedDocument, DocExtResource
|
||||||
from ietf.iesg.models import TelechatDate
|
from ietf.iesg.models import TelechatDate
|
||||||
from ietf.iesg.utils import telechat_page_count
|
from ietf.iesg.utils import telechat_page_count
|
||||||
|
@ -134,11 +134,11 @@ class ActionHoldersForm(forms.Form):
|
||||||
IESG_APPROVED_STATE_LIST = ("ann", "rfcqueue", "pub")
|
IESG_APPROVED_STATE_LIST = ("ann", "rfcqueue", "pub")
|
||||||
|
|
||||||
class AddDownrefForm(forms.Form):
|
class AddDownrefForm(forms.Form):
|
||||||
rfc = SearchableDocAliasField(
|
rfc = SearchableDocumentField(
|
||||||
label="Referenced RFC",
|
label="Referenced RFC",
|
||||||
help_text="The RFC that is approved for downref",
|
help_text="The RFC that is approved for downref",
|
||||||
required=True)
|
required=True)
|
||||||
drafts = SearchableDocAliasesField(
|
drafts = SearchableDocumentsField(
|
||||||
label="Internet-Drafts that makes the reference",
|
label="Internet-Drafts that makes the reference",
|
||||||
help_text="The Internet-Drafts that approve the downref in their Last Call",
|
help_text="The Internet-Drafts that approve the downref in their Last Call",
|
||||||
required=True)
|
required=True)
|
||||||
|
@ -148,7 +148,7 @@ class AddDownrefForm(forms.Form):
|
||||||
raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft")
|
raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft")
|
||||||
|
|
||||||
rfc = self.cleaned_data['rfc']
|
rfc = self.cleaned_data['rfc']
|
||||||
if not rfc.document.is_rfc():
|
if rfc.type_id != "rfc":
|
||||||
raise forms.ValidationError("Cannot find the RFC: " + rfc.name)
|
raise forms.ValidationError("Cannot find the RFC: " + rfc.name)
|
||||||
return rfc
|
return rfc
|
||||||
|
|
||||||
|
@ -158,10 +158,10 @@ class AddDownrefForm(forms.Form):
|
||||||
|
|
||||||
v_err_names = []
|
v_err_names = []
|
||||||
drafts = self.cleaned_data['drafts']
|
drafts = self.cleaned_data['drafts']
|
||||||
for da in drafts:
|
for d in drafts:
|
||||||
state = da.document.get_state("draft-iesg")
|
state = d.get_state("draft-iesg")
|
||||||
if not state or state.slug not in IESG_APPROVED_STATE_LIST:
|
if not state or state.slug not in IESG_APPROVED_STATE_LIST:
|
||||||
v_err_names.append(da.name)
|
v_err_names.append(d.name)
|
||||||
if v_err_names:
|
if v_err_names:
|
||||||
raise forms.ValidationError("Internet-Draft is not yet approved: " + ", ".join(v_err_names))
|
raise forms.ValidationError("Internet-Draft is not yet approved: " + ", ".join(v_err_names))
|
||||||
return drafts
|
return drafts
|
||||||
|
@ -173,23 +173,23 @@ class AddDownrefForm(forms.Form):
|
||||||
v_err_pairs = []
|
v_err_pairs = []
|
||||||
rfc = self.cleaned_data['rfc']
|
rfc = self.cleaned_data['rfc']
|
||||||
drafts = self.cleaned_data['drafts']
|
drafts = self.cleaned_data['drafts']
|
||||||
for da in drafts:
|
for d in drafts:
|
||||||
if RelatedDocument.objects.filter(source=da.document, target=rfc, relationship_id='downref-approval'):
|
if RelatedDocument.objects.filter(source=d, target=rfc, relationship_id='downref-approval'):
|
||||||
v_err_pairs.append(da.name + " --> RFC " + rfc.document.rfc_number())
|
v_err_pairs.append(f"{d.name} --> RFC {rfc.rfc_number}")
|
||||||
if v_err_pairs:
|
if v_err_pairs:
|
||||||
raise forms.ValidationError("Downref is already in the registry: " + ", ".join(v_err_pairs))
|
raise forms.ValidationError("Downref is already in the registry: " + ", ".join(v_err_pairs))
|
||||||
|
|
||||||
if 'save_downref_anyway' not in self.data:
|
if 'save_downref_anyway' not in self.data:
|
||||||
# this check is skipped if the save_downref_anyway button is used
|
# this check is skipped if the save_downref_anyway button is used
|
||||||
v_err_refnorm = ""
|
v_err_refnorm = ""
|
||||||
for da in drafts:
|
for d in drafts:
|
||||||
if not RelatedDocument.objects.filter(source=da.document, target=rfc, relationship_id='refnorm'):
|
if not RelatedDocument.objects.filter(source=d, target=rfc, relationship_id='refnorm'):
|
||||||
if v_err_refnorm:
|
if v_err_refnorm:
|
||||||
v_err_refnorm = v_err_refnorm + " or " + da.name
|
v_err_refnorm = v_err_refnorm + " or " + d.name
|
||||||
else:
|
else:
|
||||||
v_err_refnorm = da.name
|
v_err_refnorm = d.name
|
||||||
if v_err_refnorm:
|
if v_err_refnorm:
|
||||||
v_err_refnorm_prefix = "There does not seem to be a normative reference to RFC " + rfc.document.rfc_number() + " by "
|
v_err_refnorm_prefix = f"There does not seem to be a normative reference to RFC {rfc.rfc_number} by "
|
||||||
raise forms.ValidationError(v_err_refnorm_prefix + v_err_refnorm)
|
raise forms.ValidationError(v_err_refnorm_prefix + v_err_refnorm)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ def email_ad_approved_doc(request, doc, text):
|
||||||
|
|
||||||
def email_ad_approved_conflict_review(request, review, ok_to_publish):
|
def email_ad_approved_conflict_review(request, review, ok_to_publish):
|
||||||
"""Email notification when AD approves a conflict review"""
|
"""Email notification when AD approves a conflict review"""
|
||||||
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document
|
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target
|
||||||
(to, cc) = gather_address_lists("ad_approved_conflict_review")
|
(to, cc) = gather_address_lists("ad_approved_conflict_review")
|
||||||
frm = request.user.person.formatted_email()
|
frm = request.user.person.formatted_email()
|
||||||
send_mail(request,
|
send_mail(request,
|
||||||
|
|
|
@ -24,6 +24,7 @@ from ietf.doc.models import Document
|
||||||
from ietf.group.utils import get_group_role_emails, get_group_ad_emails
|
from ietf.group.utils import get_group_role_emails, get_group_ad_emails
|
||||||
from ietf.utils.aliases import dump_sublist
|
from ietf.utils.aliases import dump_sublist
|
||||||
from utils.mail import parseaddr
|
from utils.mail import parseaddr
|
||||||
|
from ietf.utils import log
|
||||||
|
|
||||||
DEFAULT_YEARS = 2
|
DEFAULT_YEARS = 2
|
||||||
|
|
||||||
|
@ -120,16 +121,18 @@ class Command(BaseCommand):
|
||||||
vfile.write("%s anything\n" % settings.DRAFT_VIRTUAL_DOMAIN)
|
vfile.write("%s anything\n" % settings.DRAFT_VIRTUAL_DOMAIN)
|
||||||
|
|
||||||
# Internet-Drafts with active status or expired within DEFAULT_YEARS
|
# Internet-Drafts with active status or expired within DEFAULT_YEARS
|
||||||
drafts = Document.objects.filter(name__startswith='draft-')
|
drafts = Document.objects.filter(type_id="draft")
|
||||||
active_drafts = drafts.filter(states__slug='active')
|
active_drafts = drafts.filter(states__slug='active')
|
||||||
inactive_recent_drafts = drafts.exclude(states__slug='active').filter(expires__gte=show_since)
|
inactive_recent_drafts = drafts.exclude(states__slug='active').filter(expires__gte=show_since)
|
||||||
interesting_drafts = active_drafts | inactive_recent_drafts
|
interesting_drafts = active_drafts | inactive_recent_drafts
|
||||||
|
|
||||||
alias_domains = ['ietf.org', ]
|
alias_domains = ['ietf.org', ]
|
||||||
for draft in interesting_drafts.distinct().iterator():
|
for draft in interesting_drafts.distinct().iterator():
|
||||||
# Omit RFCs, unless they were published in the last DEFAULT_YEARS
|
# Omit drafts that became RFCs, unless they were published in the last DEFAULT_YEARS
|
||||||
if draft.docalias.filter(name__startswith='rfc'):
|
if draft.get_state_slug()=="rfc":
|
||||||
if draft.latest_event(type='published_rfc').time < show_since:
|
rfc = next(iter(draft.related_that_doc("became_rfc")), None)
|
||||||
|
log.assertion("rfc is not None")
|
||||||
|
if rfc.latest_event(type='published_rfc').time < show_since:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
alias = draft.name
|
alias = draft.name
|
||||||
|
|
23
ietf/doc/migrations/0005_add_rfc_states.py
Normal file
23
ietf/doc/migrations/0005_add_rfc_states.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-14 20:57
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
StateType = apps.get_model("doc", "StateType")
|
||||||
|
rfc_statetype, _ = StateType.objects.get_or_create(slug="rfc", label="State")
|
||||||
|
|
||||||
|
State = apps.get_model("doc", "State")
|
||||||
|
State.objects.get_or_create(
|
||||||
|
type=rfc_statetype, slug="published", name="Published", used=True, order=1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0004_alter_dochistory_ad_alter_dochistory_shepherd_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward),
|
||||||
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-14 22:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0005_add_rfc_states"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="dochistory",
|
||||||
|
name="rfc_number",
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="document",
|
||||||
|
name="rfc_number",
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
79
ietf/doc/migrations/0007_create_rfc_documents.py
Normal file
79
ietf/doc/migrations/0007_create_rfc_documents.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-15 15:27
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
Document = apps.get_model("doc", "Document")
|
||||||
|
DocAlias = apps.get_model("doc", "DocAlias")
|
||||||
|
DocumentAuthor = apps.get_model("doc", "DocumentAuthor")
|
||||||
|
|
||||||
|
State = apps.get_model("doc", "State")
|
||||||
|
draft_rfc_state = State.objects.get(type_id="draft", slug="rfc")
|
||||||
|
rfc_published_state = State.objects.get(type_id="rfc", slug="published")
|
||||||
|
|
||||||
|
DocTypeName = apps.get_model("name", "DocTypeName")
|
||||||
|
rfc_doctype = DocTypeName(slug="rfc")
|
||||||
|
|
||||||
|
# Find draft Documents in the "rfc" state
|
||||||
|
found_by_state = Document.objects.filter(states=draft_rfc_state).distinct()
|
||||||
|
|
||||||
|
# Find Documents with an "rfc..." alias and confirm they're the same set
|
||||||
|
rfc_docaliases = DocAlias.objects.filter(name__startswith="rfc")
|
||||||
|
found_by_name = Document.objects.filter(docalias__in=rfc_docaliases).distinct()
|
||||||
|
assert set(found_by_name) == set(found_by_state), "mismatch between rfcs identified by state and docalias"
|
||||||
|
|
||||||
|
# As of 2023-06-15, there is one Document with two rfc aliases: rfc6312 and rfc6342 are the same Document. This
|
||||||
|
# was due to a publication error. Because we go alias-by-alias, no special handling is needed in this migration.
|
||||||
|
|
||||||
|
for rfc_alias in rfc_docaliases.order_by("name"):
|
||||||
|
assert rfc_alias.docs.count() == 1, f"DocAlias {rfc_alias} is linked to more than 1 Document"
|
||||||
|
draft = rfc_alias.docs.first()
|
||||||
|
if draft.name.startswith("rfc"):
|
||||||
|
rfc = draft
|
||||||
|
rfc.type = rfc_doctype
|
||||||
|
rfc.rfc_number = int(draft.name[3:])
|
||||||
|
rfc.save()
|
||||||
|
rfc.states.set([rfc_published_state])
|
||||||
|
else:
|
||||||
|
rfc = Document.objects.create(
|
||||||
|
type=rfc_doctype,
|
||||||
|
name=rfc_alias.name,
|
||||||
|
rfc_number=int(rfc_alias.name[3:]),
|
||||||
|
time=draft.time,
|
||||||
|
title=draft.title,
|
||||||
|
stream=draft.stream,
|
||||||
|
group=draft.group,
|
||||||
|
abstract=draft.abstract,
|
||||||
|
pages=draft.pages,
|
||||||
|
words=draft.words,
|
||||||
|
std_level=draft.std_level,
|
||||||
|
ad=draft.ad,
|
||||||
|
external_url=draft.external_url,
|
||||||
|
uploaded_filename=draft.uploaded_filename,
|
||||||
|
note=draft.note,
|
||||||
|
)
|
||||||
|
rfc.states.set([rfc_published_state])
|
||||||
|
rfc.formal_languages.set(draft.formal_languages.all())
|
||||||
|
|
||||||
|
# Copy Authors
|
||||||
|
for da in draft.documentauthor_set.all():
|
||||||
|
DocumentAuthor.objects.create(
|
||||||
|
document=rfc,
|
||||||
|
person=da.person,
|
||||||
|
email=da.email,
|
||||||
|
affiliation=da.affiliation,
|
||||||
|
country=da.country,
|
||||||
|
order=da.order,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0006_dochistory_rfc_number_document_rfc_number"),
|
||||||
|
("name", "0004_rfc_doctype_names"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward),
|
||||||
|
]
|
98
ietf/doc/migrations/0008_move_rfc_docevents.py
Normal file
98
ietf/doc/migrations/0008_move_rfc_docevents.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-20 18:36
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
"""Move RFC events from the draft to the rfc Document"""
|
||||||
|
DocAlias = apps.get_model("doc", "DocAlias")
|
||||||
|
DocEvent = apps.get_model("doc", "DocEvent")
|
||||||
|
Document = apps.get_model("doc", "Document")
|
||||||
|
|
||||||
|
# queryset with events migrated regardless of whether before or after the "published_rfc" event
|
||||||
|
events_always_migrated = DocEvent.objects.filter(
|
||||||
|
Q(
|
||||||
|
type__in=[
|
||||||
|
"published_rfc", # do not remove this one!
|
||||||
|
"sync_from_rfc_editor",
|
||||||
|
"rfc_editor_received_announcement", # problematic for new RFCs until RPC tools enhancements come in?
|
||||||
|
]
|
||||||
|
)
|
||||||
|
| Q(
|
||||||
|
type="changed_state",
|
||||||
|
desc__startswith="RFC Editor state",
|
||||||
|
)
|
||||||
|
| Q(
|
||||||
|
type="changed_state",
|
||||||
|
desc__startswith="IANA Action state",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# queryset with events migrated only after the "published_rfc" event
|
||||||
|
events_migrated_after_pub = DocEvent.objects.exclude(
|
||||||
|
type__in=[
|
||||||
|
"created_ballot",
|
||||||
|
"closed_ballot",
|
||||||
|
"sent_ballot_announcement",
|
||||||
|
"changed_ballot_position",
|
||||||
|
"changed_ballot_approval_text",
|
||||||
|
"changed_ballot_writeup_text",
|
||||||
|
]
|
||||||
|
).exclude(
|
||||||
|
type="added_comment",
|
||||||
|
desc__contains="ballot set", # excludes 311 comments that all apply to drafts
|
||||||
|
)
|
||||||
|
|
||||||
|
# special case for rfc 6312/6342 draft, which has two published_rfc events
|
||||||
|
ignore = ["rfc6312", "rfc6342"] # do not reprocess these later
|
||||||
|
rfc6312 = Document.objects.get(name="rfc6312")
|
||||||
|
rfc6342 = Document.objects.get(name="rfc6342")
|
||||||
|
draft = DocAlias.objects.get(name="rfc6312").docs.first()
|
||||||
|
assert draft == DocAlias.objects.get(name="rfc6342").docs.first()
|
||||||
|
published_events = list(
|
||||||
|
DocEvent.objects.filter(doc=draft, type="published_rfc").order_by("time")
|
||||||
|
)
|
||||||
|
assert len(published_events) == 2
|
||||||
|
(
|
||||||
|
pub_event_6312,
|
||||||
|
pub_event_6342,
|
||||||
|
) = published_events # order matches pub dates at rfc-editor.org
|
||||||
|
|
||||||
|
pub_event_6312.doc = rfc6312
|
||||||
|
pub_event_6312.save()
|
||||||
|
events_migrated_after_pub.filter(
|
||||||
|
doc=draft,
|
||||||
|
time__gte=pub_event_6312.time,
|
||||||
|
time__lt=pub_event_6342.time,
|
||||||
|
).update(doc=rfc6312)
|
||||||
|
|
||||||
|
pub_event_6342.doc = rfc6342
|
||||||
|
pub_event_6342.save()
|
||||||
|
events_migrated_after_pub.filter(
|
||||||
|
doc=draft,
|
||||||
|
time__gte=pub_event_6342.time,
|
||||||
|
).update(doc=rfc6342)
|
||||||
|
|
||||||
|
# Now handle all the rest
|
||||||
|
for rfc in Document.objects.filter(type_id="rfc").exclude(name__in=ignore):
|
||||||
|
draft = DocAlias.objects.get(name=rfc.name).docs.first()
|
||||||
|
assert draft is not None
|
||||||
|
published_event = DocEvent.objects.get(doc=draft, type="published_rfc")
|
||||||
|
events_always_migrated.filter(
|
||||||
|
doc=draft,
|
||||||
|
).update(doc=rfc)
|
||||||
|
events_migrated_after_pub.filter(
|
||||||
|
doc=draft,
|
||||||
|
time__gte=published_event.time,
|
||||||
|
).update(doc=rfc)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0007_create_rfc_documents"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward),
|
||||||
|
]
|
45
ietf/doc/migrations/0009_rfc_relateddocuments.py
Normal file
45
ietf/doc/migrations/0009_rfc_relateddocuments.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# Generated by Django 4.2.3 on 2023-07-05 22:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
DocAlias = apps.get_model("doc", "DocAlias")
|
||||||
|
Document = apps.get_model("doc", "Document")
|
||||||
|
RelatedDocument = apps.get_model("doc", "RelatedDocument")
|
||||||
|
for rfc_alias in DocAlias.objects.filter(name__startswith="rfc").exclude(
|
||||||
|
docs__type__slug="rfc"
|
||||||
|
):
|
||||||
|
# Move these over to the RFC
|
||||||
|
RelatedDocument.objects.filter(
|
||||||
|
relationship__slug__in=(
|
||||||
|
"tobcp",
|
||||||
|
"toexp",
|
||||||
|
"tohist",
|
||||||
|
"toinf",
|
||||||
|
"tois",
|
||||||
|
"tops",
|
||||||
|
"obs",
|
||||||
|
"updates",
|
||||||
|
),
|
||||||
|
source__docalias=rfc_alias,
|
||||||
|
).update(source=Document.objects.get(name=rfc_alias.name))
|
||||||
|
# Duplicate references on the RFC but keep the ones on the draft as well
|
||||||
|
originals = list(
|
||||||
|
RelatedDocument.objects.filter(
|
||||||
|
relationship__slug__in=("refinfo", "refnorm", "refold", "refunk"),
|
||||||
|
source__docalias=rfc_alias,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for o in originals:
|
||||||
|
o.pk = None
|
||||||
|
o.source = Document.objects.get(name=rfc_alias.name)
|
||||||
|
RelatedDocument.objects.bulk_create(originals)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0008_move_rfc_docevents"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [migrations.RunPython(forward)]
|
38
ietf/doc/migrations/0010_move_rfc_docaliases.py
Normal file
38
ietf/doc/migrations/0010_move_rfc_docaliases.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-20 18:36
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
"""Point "rfc..." DocAliases at the rfc-type Document
|
||||||
|
|
||||||
|
Creates a became_rfc RelatedDocument to preserve the connection between the draft and the rfc.
|
||||||
|
"""
|
||||||
|
DocAlias = apps.get_model("doc", "DocAlias")
|
||||||
|
Document = apps.get_model("doc", "Document")
|
||||||
|
RelatedDocument = apps.get_model("doc", "RelatedDocument")
|
||||||
|
|
||||||
|
for rfc_alias in DocAlias.objects.filter(name__startswith="rfc"):
|
||||||
|
rfc = Document.objects.get(name=rfc_alias.name)
|
||||||
|
aliased_doc = rfc_alias.docs.get() # implicitly confirms only one value in rfc_alias.docs
|
||||||
|
if aliased_doc != rfc:
|
||||||
|
# If the DocAlias was not already pointing at the rfc, it was pointing at the draft
|
||||||
|
# it came from. Create the relationship between draft and rfc Documents.
|
||||||
|
assert aliased_doc.type_id == "draft", f"Alias for {rfc.name} should be pointing at a draft"
|
||||||
|
RelatedDocument.objects.create(
|
||||||
|
source=aliased_doc,
|
||||||
|
target=rfc_alias,
|
||||||
|
relationship_id="became_rfc",
|
||||||
|
)
|
||||||
|
# Now move the alias from the draft to the rfc
|
||||||
|
rfc_alias.docs.set([rfc])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0009_rfc_relateddocuments"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward),
|
||||||
|
]
|
77
ietf/doc/migrations/0011_relate_no_aliases.py
Normal file
77
ietf/doc/migrations/0011_relate_no_aliases.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-16 13:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db.models import F, Subquery, OuterRef
|
||||||
|
import ietf.utils.models
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
RelatedDocument = apps.get_model("doc", "RelatedDocument")
|
||||||
|
DocAlias = apps.get_model("doc", "DocAlias")
|
||||||
|
subquery = Subquery(DocAlias.objects.filter(pk=OuterRef("deprecated_target")).values("docs")[:1])
|
||||||
|
RelatedDocument.objects.annotate(firstdoc=subquery).update(target=F("firstdoc"))
|
||||||
|
|
||||||
|
def reverse(apps, schema_editor):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0010_move_rfc_docaliases"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='relateddocument',
|
||||||
|
name='target',
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
db_index=False,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to='doc.docalias',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="relateddocument",
|
||||||
|
old_name="target",
|
||||||
|
new_name="deprecated_target"
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='relateddocument',
|
||||||
|
name='deprecated_target',
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
db_index=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to='doc.docalias',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="relateddocument",
|
||||||
|
name="target",
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
default=1, # A lie, but a convenient one - no relations point here.
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="targets_related",
|
||||||
|
to="doc.document",
|
||||||
|
db_index=False,
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.RunPython(forward, reverse),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="relateddocument",
|
||||||
|
name="target",
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="targets_related",
|
||||||
|
to="doc.document",
|
||||||
|
db_index=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="relateddocument",
|
||||||
|
name="deprecated_target",
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to='doc.DocAlias',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
80
ietf/doc/migrations/0012_relate_hist_no_aliases.py
Normal file
80
ietf/doc/migrations/0012_relate_hist_no_aliases.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-16 13:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db.models import F, Subquery, OuterRef
|
||||||
|
import ietf.utils.models
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
RelatedDocHistory = apps.get_model("doc", "RelatedDocHistory")
|
||||||
|
DocAlias = apps.get_model("doc", "DocAlias")
|
||||||
|
subquery = Subquery(DocAlias.objects.filter(pk=OuterRef("deprecated_target")).values("docs")[:1])
|
||||||
|
RelatedDocHistory.objects.annotate(firstdoc=subquery).update(target=F("firstdoc"))
|
||||||
|
|
||||||
|
def reverse(apps, schema_editor):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("doc", "0011_relate_no_aliases"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='relateddochistory',
|
||||||
|
name='target',
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
db_index=False,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to='doc.docalias',
|
||||||
|
related_name='reversely_related_document_history_set',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="relateddochistory",
|
||||||
|
old_name="target",
|
||||||
|
new_name="deprecated_target"
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='relateddochistory',
|
||||||
|
name='deprecated_target',
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
db_index=True,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to='doc.docalias',
|
||||||
|
related_name='deprecated_reversely_related_document_history_set',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="relateddochistory",
|
||||||
|
name="target",
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
default=1, # A lie, but a convenient one - no relations point here.
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="doc.document",
|
||||||
|
db_index=False,
|
||||||
|
related_name='reversely_related_document_history_set',
|
||||||
|
),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.RunPython(forward, reverse),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="relateddochistory",
|
||||||
|
name="target",
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="doc.document",
|
||||||
|
db_index=True,
|
||||||
|
related_name='reversely_related_document_history_set',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="relateddochistory",
|
||||||
|
name="deprecated_target",
|
||||||
|
field=ietf.utils.models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to='doc.DocAlias',
|
||||||
|
related_name='deprecated_reversely_related_document_history_set',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -124,6 +124,7 @@ class DocumentInfo(models.Model):
|
||||||
uploaded_filename = models.TextField(blank=True)
|
uploaded_filename = models.TextField(blank=True)
|
||||||
note = models.TextField(blank=True)
|
note = models.TextField(blank=True)
|
||||||
internal_comments = models.TextField(blank=True)
|
internal_comments = models.TextField(blank=True)
|
||||||
|
rfc_number = models.PositiveIntegerField(blank=True, null=True) # only valid for type="rfc"
|
||||||
|
|
||||||
def file_extension(self):
|
def file_extension(self):
|
||||||
if not hasattr(self, '_cached_extension'):
|
if not hasattr(self, '_cached_extension'):
|
||||||
|
@ -136,18 +137,17 @@ class DocumentInfo(models.Model):
|
||||||
|
|
||||||
def get_file_path(self):
|
def get_file_path(self):
|
||||||
if not hasattr(self, '_cached_file_path'):
|
if not hasattr(self, '_cached_file_path'):
|
||||||
if self.type_id == "draft":
|
if self.type_id == "rfc":
|
||||||
|
self._cached_file_path = settings.RFC_PATH
|
||||||
|
elif self.type_id == "draft":
|
||||||
if self.is_dochistory():
|
if self.is_dochistory():
|
||||||
self._cached_file_path = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
|
self._cached_file_path = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
|
||||||
else:
|
else:
|
||||||
if self.get_state_slug() == "rfc":
|
draft_state = self.get_state('draft')
|
||||||
self._cached_file_path = settings.RFC_PATH
|
if draft_state and draft_state.slug == 'active':
|
||||||
|
self._cached_file_path = settings.INTERNET_DRAFT_PATH
|
||||||
else:
|
else:
|
||||||
draft_state = self.get_state('draft')
|
self._cached_file_path = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
|
||||||
if draft_state and draft_state.slug == 'active':
|
|
||||||
self._cached_file_path = settings.INTERNET_DRAFT_PATH
|
|
||||||
else:
|
|
||||||
self._cached_file_path = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
|
|
||||||
elif self.meeting_related() and self.type_id in (
|
elif self.meeting_related() and self.type_id in (
|
||||||
"agenda", "minutes", "slides", "bluesheets", "procmaterials", "chatlog", "polls"
|
"agenda", "minutes", "slides", "bluesheets", "procmaterials", "chatlog", "polls"
|
||||||
):
|
):
|
||||||
|
@ -172,14 +172,13 @@ class DocumentInfo(models.Model):
|
||||||
if not hasattr(self, '_cached_base_name'):
|
if not hasattr(self, '_cached_base_name'):
|
||||||
if self.uploaded_filename:
|
if self.uploaded_filename:
|
||||||
self._cached_base_name = self.uploaded_filename
|
self._cached_base_name = self.uploaded_filename
|
||||||
|
elif self.type_id == 'rfc':
|
||||||
|
self._cached_base_name = "%s.txt" % self.canonical_name()
|
||||||
elif self.type_id == 'draft':
|
elif self.type_id == 'draft':
|
||||||
if self.is_dochistory():
|
if self.is_dochistory():
|
||||||
self._cached_base_name = "%s-%s.txt" % (self.doc.name, self.rev)
|
self._cached_base_name = "%s-%s.txt" % (self.doc.name, self.rev)
|
||||||
else:
|
else:
|
||||||
if self.get_state_slug() == 'rfc':
|
self._cached_base_name = "%s-%s.txt" % (self.name, self.rev)
|
||||||
self._cached_base_name = "%s.txt" % self.canonical_name()
|
|
||||||
else:
|
|
||||||
self._cached_base_name = "%s-%s.txt" % (self.name, self.rev)
|
|
||||||
elif self.type_id in ["slides", "agenda", "minutes", "bluesheets", "procmaterials", ] and self.meeting_related():
|
elif self.type_id in ["slides", "agenda", "minutes", "bluesheets", "procmaterials", ] and self.meeting_related():
|
||||||
ext = 'pdf' if self.type_id == 'procmaterials' else 'txt'
|
ext = 'pdf' if self.type_id == 'procmaterials' else 'txt'
|
||||||
self._cached_base_name = f'{self.canonical_name()}-{self.rev}.{ext}'
|
self._cached_base_name = f'{self.canonical_name()}-{self.rev}.{ext}'
|
||||||
|
@ -244,7 +243,7 @@ class DocumentInfo(models.Model):
|
||||||
format = settings.DOC_HREFS[self.type_id]
|
format = settings.DOC_HREFS[self.type_id]
|
||||||
elif self.type_id in settings.DOC_HREFS:
|
elif self.type_id in settings.DOC_HREFS:
|
||||||
self.is_meeting_related = False
|
self.is_meeting_related = False
|
||||||
if self.is_rfc():
|
if self.type_id == "rfc":
|
||||||
format = settings.DOC_HREFS['rfc']
|
format = settings.DOC_HREFS['rfc']
|
||||||
else:
|
else:
|
||||||
format = settings.DOC_HREFS[self.type_id]
|
format = settings.DOC_HREFS[self.type_id]
|
||||||
|
@ -334,7 +333,9 @@ class DocumentInfo(models.Model):
|
||||||
if not state:
|
if not state:
|
||||||
return "Unknown state"
|
return "Unknown state"
|
||||||
|
|
||||||
if self.type_id == 'draft':
|
if self.type_id == "rfc":
|
||||||
|
return f"RFC {self.rfc_number} ({self.std_level})"
|
||||||
|
elif self.type_id == 'draft':
|
||||||
iesg_state = self.get_state("draft-iesg")
|
iesg_state = self.get_state("draft-iesg")
|
||||||
iesg_state_summary = None
|
iesg_state_summary = None
|
||||||
if iesg_state:
|
if iesg_state:
|
||||||
|
@ -345,7 +346,12 @@ class DocumentInfo(models.Model):
|
||||||
iesg_state_summary = iesg_state_summary + "::"+"::".join(tag.name for tag in iesg_substate)
|
iesg_state_summary = iesg_state_summary + "::"+"::".join(tag.name for tag in iesg_substate)
|
||||||
|
|
||||||
if state.slug == "rfc":
|
if state.slug == "rfc":
|
||||||
return "RFC %s (%s)" % (self.rfc_number(), self.std_level)
|
rfcs = self.related_that_doc("became_rfc") # should be only one
|
||||||
|
if len(rfcs) > 0:
|
||||||
|
rfc = rfcs[0].document
|
||||||
|
return f"Became RFC {rfc.rfc_number} ({rfc.std_level})"
|
||||||
|
else:
|
||||||
|
return "Became RFC"
|
||||||
elif state.slug == "repl":
|
elif state.slug == "repl":
|
||||||
rs = self.related_that("replaces")
|
rs = self.related_that("replaces")
|
||||||
if rs:
|
if rs:
|
||||||
|
@ -375,27 +381,6 @@ class DocumentInfo(models.Model):
|
||||||
else:
|
else:
|
||||||
return state.name
|
return state.name
|
||||||
|
|
||||||
def is_rfc(self):
|
|
||||||
if not hasattr(self, '_cached_is_rfc'):
|
|
||||||
self._cached_is_rfc = self.pk and self.type_id == 'draft' and self.states.filter(type='draft',slug='rfc').exists()
|
|
||||||
return self._cached_is_rfc
|
|
||||||
|
|
||||||
def rfc_number(self):
|
|
||||||
if not hasattr(self, '_cached_rfc_number'):
|
|
||||||
self._cached_rfc_number = None
|
|
||||||
if self.is_rfc():
|
|
||||||
n = self.canonical_name()
|
|
||||||
if n.startswith("rfc"):
|
|
||||||
self._cached_rfc_number = n[3:]
|
|
||||||
else:
|
|
||||||
if isinstance(self,Document):
|
|
||||||
logger.error("Document self.is_rfc() is True but self.canonical_name() is %s" % n)
|
|
||||||
return self._cached_rfc_number
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rfcnum(self):
|
|
||||||
return self.rfc_number()
|
|
||||||
|
|
||||||
def author_list(self):
|
def author_list(self):
|
||||||
best_addresses = []
|
best_addresses = []
|
||||||
for author in self.documentauthor_set.all():
|
for author in self.documentauthor_set.all():
|
||||||
|
@ -468,9 +453,9 @@ class DocumentInfo(models.Model):
|
||||||
if not isinstance(relationship, tuple):
|
if not isinstance(relationship, tuple):
|
||||||
raise TypeError("Expected a string or tuple, received %s" % type(relationship))
|
raise TypeError("Expected a string or tuple, received %s" % type(relationship))
|
||||||
if isinstance(self, Document):
|
if isinstance(self, Document):
|
||||||
return RelatedDocument.objects.filter(target__docs=self, relationship__in=relationship).select_related('source')
|
return RelatedDocument.objects.filter(target=self, relationship__in=relationship).select_related('source')
|
||||||
elif isinstance(self, DocHistory):
|
elif isinstance(self, DocHistory):
|
||||||
return RelatedDocHistory.objects.filter(target__docs=self.doc, relationship__in=relationship).select_related('source')
|
return RelatedDocHistory.objects.filter(target=self.doc, relationship__in=relationship).select_related('source')
|
||||||
else:
|
else:
|
||||||
raise TypeError("Expected method called on Document or DocHistory")
|
raise TypeError("Expected method called on Document or DocHistory")
|
||||||
|
|
||||||
|
@ -504,8 +489,7 @@ class DocumentInfo(models.Model):
|
||||||
for r in rels:
|
for r in rels:
|
||||||
if not r in related:
|
if not r in related:
|
||||||
related += ( r, )
|
related += ( r, )
|
||||||
for doc in r.target.docs.all():
|
related = r.target.all_relations_that_doc(relationship, related)
|
||||||
related = doc.all_relations_that_doc(relationship, related)
|
|
||||||
return related
|
return related
|
||||||
|
|
||||||
def related_that(self, relationship):
|
def related_that(self, relationship):
|
||||||
|
@ -656,10 +640,20 @@ class DocumentInfo(models.Model):
|
||||||
return self.relations_that_doc(('refnorm','refinfo','refunk','refold'))
|
return self.relations_that_doc(('refnorm','refinfo','refunk','refold'))
|
||||||
|
|
||||||
def referenced_by(self):
|
def referenced_by(self):
|
||||||
return self.relations_that(('refnorm','refinfo','refunk','refold')).filter(source__states__type__slug='draft',source__states__slug__in=['rfc','active'])
|
return self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter(
|
||||||
|
models.Q(
|
||||||
|
source__type__slug="draft",
|
||||||
|
source__states__type__slug="draft",
|
||||||
|
source__states__slug="active",
|
||||||
|
)
|
||||||
|
| models.Q(source__type__slug="rfc")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def referenced_by_rfcs(self):
|
def referenced_by_rfcs(self):
|
||||||
return self.relations_that(('refnorm','refinfo','refunk','refold')).filter(source__states__type__slug='draft',source__states__slug='rfc')
|
return self.relations_that(("refnorm", "refinfo", "refunk", "refold")).filter(
|
||||||
|
source__type__slug="rfc"
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
@ -668,7 +662,7 @@ STATUSCHANGE_RELATIONS = ('tops','tois','tohist','toinf','tobcp','toexp')
|
||||||
|
|
||||||
class RelatedDocument(models.Model):
|
class RelatedDocument(models.Model):
|
||||||
source = ForeignKey('Document')
|
source = ForeignKey('Document')
|
||||||
target = ForeignKey('DocAlias')
|
target = ForeignKey('Document', related_name='targets_related')
|
||||||
relationship = ForeignKey(DocRelationshipName)
|
relationship = ForeignKey(DocRelationshipName)
|
||||||
def action(self):
|
def action(self):
|
||||||
return self.relationship.name
|
return self.relationship.name
|
||||||
|
@ -691,16 +685,16 @@ class RelatedDocument(models.Model):
|
||||||
if source_lvl not in ['bcp','ps','ds','std']:
|
if source_lvl not in ['bcp','ps','ds','std']:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if self.target.document.get_state().slug == 'rfc':
|
if self.target.type_id == 'rfc':
|
||||||
if not self.target.document.std_level:
|
if not self.target.std_level:
|
||||||
target_lvl = 'unkn'
|
target_lvl = 'unkn'
|
||||||
else:
|
else:
|
||||||
target_lvl = self.target.document.std_level.slug
|
target_lvl = self.target.std_level.slug
|
||||||
else:
|
else:
|
||||||
if not self.target.document.intended_std_level:
|
if not self.target.intended_std_level:
|
||||||
target_lvl = 'unkn'
|
target_lvl = 'unkn'
|
||||||
else:
|
else:
|
||||||
target_lvl = self.target.document.intended_std_level.slug
|
target_lvl = self.target.intended_std_level.slug
|
||||||
|
|
||||||
rank = { 'ps':1, 'ds':2, 'std':3, 'bcp':3 }
|
rank = { 'ps':1, 'ds':2, 'std':3, 'bcp':3 }
|
||||||
|
|
||||||
|
@ -714,8 +708,8 @@ class RelatedDocument(models.Model):
|
||||||
|
|
||||||
def is_approved_downref(self):
|
def is_approved_downref(self):
|
||||||
|
|
||||||
if self.target.document.get_state().slug == 'rfc':
|
if self.target.type_id == 'rfc':
|
||||||
if RelatedDocument.objects.filter(relationship_id='downref-approval', target=self.target):
|
if RelatedDocument.objects.filter(relationship_id='downref-approval', target=self.target).exists():
|
||||||
return "Approved Downref"
|
return "Approved Downref"
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -856,12 +850,6 @@ class Document(DocumentInfo):
|
||||||
a = self.docalias.filter(name__startswith="rfc").order_by('-name').first()
|
a = self.docalias.filter(name__startswith="rfc").order_by('-name').first()
|
||||||
if a:
|
if a:
|
||||||
name = a.name
|
name = a.name
|
||||||
elif self.type_id == "charter":
|
|
||||||
from ietf.doc.utils_charter import charter_name_for_group # Imported locally to avoid circular imports
|
|
||||||
try:
|
|
||||||
name = charter_name_for_group(self.chartered_group)
|
|
||||||
except Group.DoesNotExist:
|
|
||||||
pass
|
|
||||||
self._canonical_name = name
|
self._canonical_name = name
|
||||||
return self._canonical_name
|
return self._canonical_name
|
||||||
|
|
||||||
|
@ -973,7 +961,15 @@ class Document(DocumentInfo):
|
||||||
document directly or indirectly obsoletes or replaces
|
document directly or indirectly obsoletes or replaces
|
||||||
"""
|
"""
|
||||||
from ietf.ipr.models import IprDocRel
|
from ietf.ipr.models import IprDocRel
|
||||||
iprs = IprDocRel.objects.filter(document__in=list(self.docalias.all())+self.all_related_that_doc(('obs','replaces'))).filter(disclosure__state__in=('posted','removed')).values_list('disclosure', flat=True).distinct()
|
iprs = (
|
||||||
|
IprDocRel.objects.filter(
|
||||||
|
document__in=list(self.docalias.all())
|
||||||
|
+ [x.docalias.first() for x in self.all_related_that_doc(("obs", "replaces"))] # this really is docalias until IprDocRel changes
|
||||||
|
)
|
||||||
|
.filter(disclosure__state__in=("posted", "removed"))
|
||||||
|
.values_list("disclosure", flat=True)
|
||||||
|
.distinct()
|
||||||
|
)
|
||||||
return iprs
|
return iprs
|
||||||
|
|
||||||
def future_presentations(self):
|
def future_presentations(self):
|
||||||
|
@ -1010,7 +1006,7 @@ class Document(DocumentInfo):
|
||||||
|
|
||||||
This is the rfc publication date for RFCs, and the new-revision date for other documents.
|
This is the rfc publication date for RFCs, and the new-revision date for other documents.
|
||||||
"""
|
"""
|
||||||
if self.get_state_slug() == "rfc":
|
if self.type_id == "rfc":
|
||||||
# As of Sept 2022, in ietf.sync.rfceditor.update_docs_from_rfc_index() `published_rfc` events are
|
# As of Sept 2022, in ietf.sync.rfceditor.update_docs_from_rfc_index() `published_rfc` events are
|
||||||
# created with a timestamp whose date *in the PST8PDT timezone* is the official publication date
|
# created with a timestamp whose date *in the PST8PDT timezone* is the official publication date
|
||||||
# assigned by the RFC editor.
|
# assigned by the RFC editor.
|
||||||
|
@ -1112,7 +1108,7 @@ class DocExtResource(ExtResource):
|
||||||
|
|
||||||
class RelatedDocHistory(models.Model):
|
class RelatedDocHistory(models.Model):
|
||||||
source = ForeignKey('DocHistory')
|
source = ForeignKey('DocHistory')
|
||||||
target = ForeignKey('DocAlias', related_name="reversely_related_document_history_set")
|
target = ForeignKey('Document', related_name="reversely_related_document_history_set")
|
||||||
relationship = ForeignKey(DocRelationshipName)
|
relationship = ForeignKey(DocRelationshipName)
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return u"%s %s %s" % (self.source.doc.name, self.relationship.name.lower(), self.target.name)
|
return u"%s %s %s" % (self.source.doc.name, self.relationship.name.lower(), self.target.name)
|
||||||
|
|
|
@ -288,8 +288,8 @@ def urlize_related_target_list(related, document_html=False):
|
||||||
"""Convert a list of RelatedDocuments into list of links using the target document's canonical name"""
|
"""Convert a list of RelatedDocuments into list of links using the target document's canonical name"""
|
||||||
links = []
|
links = []
|
||||||
for rel in related:
|
for rel in related:
|
||||||
name=rel.target.document.canonical_name()
|
name=rel.target.canonical_name()
|
||||||
title = rel.target.document.title
|
title = rel.target.title
|
||||||
url = urlreverse('ietf.doc.views_doc.document_main' if document_html is False else 'ietf.doc.views_doc.document_html', kwargs=dict(name=name))
|
url = urlreverse('ietf.doc.views_doc.document_main' if document_html is False else 'ietf.doc.views_doc.document_html', kwargs=dict(name=name))
|
||||||
name = escape(name)
|
name = escape(name)
|
||||||
title = escape(title)
|
title = escape(title)
|
||||||
|
@ -556,7 +556,7 @@ def consensus(doc):
|
||||||
@register.filter
|
@register.filter
|
||||||
def std_level_to_label_format(doc):
|
def std_level_to_label_format(doc):
|
||||||
"""Returns valid Bootstrap classes to label a status level badge."""
|
"""Returns valid Bootstrap classes to label a status level badge."""
|
||||||
if doc.is_rfc():
|
if doc.type_id == "rfc":
|
||||||
if doc.related_that("obs"):
|
if doc.related_that("obs"):
|
||||||
return "obs"
|
return "obs"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -630,23 +630,22 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
def test_document_draft(self):
|
def test_document_draft(self):
|
||||||
draft = WgDraftFactory(name='draft-ietf-mars-test',rev='01', create_revisions=range(0,2))
|
draft = WgDraftFactory(name='draft-ietf-mars-test',rev='01', create_revisions=range(0,2))
|
||||||
|
|
||||||
|
|
||||||
HolderIprDisclosureFactory(docs=[draft])
|
HolderIprDisclosureFactory(docs=[draft])
|
||||||
|
|
||||||
# Docs for testing relationships. Does not test 'possibly-replaces'. The 'replaced_by' direction
|
# Docs for testing relationships. Does not test 'possibly-replaces'. The 'replaced_by' direction
|
||||||
# is tested separately below.
|
# is tested separately below.
|
||||||
replaced = IndividualDraftFactory()
|
replaced = IndividualDraftFactory()
|
||||||
draft.relateddocument_set.create(relationship_id='replaces',source=draft,target=replaced.docalias.first())
|
draft.relateddocument_set.create(relationship_id='replaces',source=draft,target=replaced)
|
||||||
obsoleted = IndividualDraftFactory()
|
obsoleted = IndividualDraftFactory()
|
||||||
draft.relateddocument_set.create(relationship_id='obs',source=draft,target=obsoleted.docalias.first())
|
draft.relateddocument_set.create(relationship_id='obs',source=draft,target=obsoleted)
|
||||||
obsoleted_by = IndividualDraftFactory()
|
obsoleted_by = IndividualDraftFactory()
|
||||||
obsoleted_by.relateddocument_set.create(relationship_id='obs',source=obsoleted_by,target=draft.docalias.first())
|
obsoleted_by.relateddocument_set.create(relationship_id='obs',source=obsoleted_by,target=draft)
|
||||||
updated = IndividualDraftFactory()
|
updated = IndividualDraftFactory()
|
||||||
draft.relateddocument_set.create(relationship_id='updates',source=draft,target=updated.docalias.first())
|
draft.relateddocument_set.create(relationship_id='updates',source=draft,target=updated)
|
||||||
updated_by = IndividualDraftFactory()
|
updated_by = IndividualDraftFactory()
|
||||||
updated_by.relateddocument_set.create(relationship_id='updates',source=obsoleted_by,target=draft.docalias.first())
|
updated_by.relateddocument_set.create(relationship_id='updates',source=obsoleted_by,target=draft)
|
||||||
|
|
||||||
external_resource = DocExtResourceFactory(doc=draft)
|
DocExtResourceFactory(doc=draft)
|
||||||
|
|
||||||
# these tests aren't testing all attributes yet, feel free to
|
# these tests aren't testing all attributes yet, feel free to
|
||||||
# expand them
|
# expand them
|
||||||
|
@ -659,16 +658,6 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertNotContains(r, "Deimos street")
|
self.assertNotContains(r, "Deimos street")
|
||||||
self.assertContains(r, replaced.canonical_name())
|
self.assertContains(r, replaced.canonical_name())
|
||||||
self.assertContains(r, replaced.title)
|
self.assertContains(r, replaced.title)
|
||||||
# obs/updates not included until draft is RFC
|
|
||||||
self.assertNotContains(r, obsoleted.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted.title)
|
|
||||||
self.assertNotContains(r, obsoleted_by.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted_by.title)
|
|
||||||
self.assertNotContains(r, updated.canonical_name())
|
|
||||||
self.assertNotContains(r, updated.title)
|
|
||||||
self.assertNotContains(r, updated_by.canonical_name())
|
|
||||||
self.assertNotContains(r, updated_by.title)
|
|
||||||
self.assertContains(r, external_resource.value)
|
|
||||||
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)) + "?include_text=0")
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)) + "?include_text=0")
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -677,15 +666,6 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertNotContains(r, "Deimos street")
|
self.assertNotContains(r, "Deimos street")
|
||||||
self.assertContains(r, replaced.canonical_name())
|
self.assertContains(r, replaced.canonical_name())
|
||||||
self.assertContains(r, replaced.title)
|
self.assertContains(r, replaced.title)
|
||||||
# obs/updates not included until draft is RFC
|
|
||||||
self.assertNotContains(r, obsoleted.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted.title)
|
|
||||||
self.assertNotContains(r, obsoleted_by.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted_by.title)
|
|
||||||
self.assertNotContains(r, updated.canonical_name())
|
|
||||||
self.assertNotContains(r, updated.title)
|
|
||||||
self.assertNotContains(r, updated_by.canonical_name())
|
|
||||||
self.assertNotContains(r, updated_by.title)
|
|
||||||
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)) + "?include_text=foo")
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)) + "?include_text=foo")
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -694,15 +674,6 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertContains(r, "Deimos street")
|
self.assertContains(r, "Deimos street")
|
||||||
self.assertContains(r, replaced.canonical_name())
|
self.assertContains(r, replaced.canonical_name())
|
||||||
self.assertContains(r, replaced.title)
|
self.assertContains(r, replaced.title)
|
||||||
# obs/updates not included until draft is RFC
|
|
||||||
self.assertNotContains(r, obsoleted.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted.title)
|
|
||||||
self.assertNotContains(r, obsoleted_by.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted_by.title)
|
|
||||||
self.assertNotContains(r, updated.canonical_name())
|
|
||||||
self.assertNotContains(r, updated.title)
|
|
||||||
self.assertNotContains(r, updated_by.canonical_name())
|
|
||||||
self.assertNotContains(r, updated_by.title)
|
|
||||||
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)) + "?include_text=1")
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)) + "?include_text=1")
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -711,15 +682,6 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertContains(r, "Deimos street")
|
self.assertContains(r, "Deimos street")
|
||||||
self.assertContains(r, replaced.canonical_name())
|
self.assertContains(r, replaced.canonical_name())
|
||||||
self.assertContains(r, replaced.title)
|
self.assertContains(r, replaced.title)
|
||||||
# obs/updates not included until draft is RFC
|
|
||||||
self.assertNotContains(r, obsoleted.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted.title)
|
|
||||||
self.assertNotContains(r, obsoleted_by.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted_by.title)
|
|
||||||
self.assertNotContains(r, updated.canonical_name())
|
|
||||||
self.assertNotContains(r, updated.title)
|
|
||||||
self.assertNotContains(r, updated_by.canonical_name())
|
|
||||||
self.assertNotContains(r, updated_by.title)
|
|
||||||
|
|
||||||
self.client.cookies = SimpleCookie({str('full_draft'): str('on')})
|
self.client.cookies = SimpleCookie({str('full_draft'): str('on')})
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
||||||
|
@ -729,15 +691,6 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertContains(r, "Deimos street")
|
self.assertContains(r, "Deimos street")
|
||||||
self.assertContains(r, replaced.canonical_name())
|
self.assertContains(r, replaced.canonical_name())
|
||||||
self.assertContains(r, replaced.title)
|
self.assertContains(r, replaced.title)
|
||||||
# obs/updates not included until draft is RFC
|
|
||||||
self.assertNotContains(r, obsoleted.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted.title)
|
|
||||||
self.assertNotContains(r, obsoleted_by.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted_by.title)
|
|
||||||
self.assertNotContains(r, updated.canonical_name())
|
|
||||||
self.assertNotContains(r, updated.title)
|
|
||||||
self.assertNotContains(r, updated_by.canonical_name())
|
|
||||||
self.assertNotContains(r, updated_by.title)
|
|
||||||
|
|
||||||
self.client.cookies = SimpleCookie({str('full_draft'): str('off')})
|
self.client.cookies = SimpleCookie({str('full_draft'): str('off')})
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
||||||
|
@ -747,15 +700,6 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertNotContains(r, "Deimos street")
|
self.assertNotContains(r, "Deimos street")
|
||||||
self.assertContains(r, replaced.canonical_name())
|
self.assertContains(r, replaced.canonical_name())
|
||||||
self.assertContains(r, replaced.title)
|
self.assertContains(r, replaced.title)
|
||||||
# obs/updates not included until draft is RFC
|
|
||||||
self.assertNotContains(r, obsoleted.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted.title)
|
|
||||||
self.assertNotContains(r, obsoleted_by.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted_by.title)
|
|
||||||
self.assertNotContains(r, updated.canonical_name())
|
|
||||||
self.assertNotContains(r, updated.title)
|
|
||||||
self.assertNotContains(r, updated_by.canonical_name())
|
|
||||||
self.assertNotContains(r, updated_by.title)
|
|
||||||
|
|
||||||
self.client.cookies = SimpleCookie({str('full_draft'): str('foo')})
|
self.client.cookies = SimpleCookie({str('full_draft'): str('foo')})
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
||||||
|
@ -766,15 +710,6 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertNotContains(r, "Deimos street")
|
self.assertNotContains(r, "Deimos street")
|
||||||
self.assertContains(r, replaced.canonical_name())
|
self.assertContains(r, replaced.canonical_name())
|
||||||
self.assertContains(r, replaced.title)
|
self.assertContains(r, replaced.title)
|
||||||
# obs/updates not included until draft is RFC
|
|
||||||
self.assertNotContains(r, obsoleted.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted.title)
|
|
||||||
self.assertNotContains(r, obsoleted_by.canonical_name())
|
|
||||||
self.assertNotContains(r, obsoleted_by.title)
|
|
||||||
self.assertNotContains(r, updated.canonical_name())
|
|
||||||
self.assertNotContains(r, updated.title)
|
|
||||||
self.assertNotContains(r, updated_by.canonical_name())
|
|
||||||
self.assertNotContains(r, updated_by.title)
|
|
||||||
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=draft.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=draft.name)))
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -803,12 +738,12 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.canonical_name())))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.canonical_name())))
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
self.assertEqual(q('title').text(), f'RFC {rfc.rfc_number()} - {rfc.title}')
|
self.assertEqual(q('title').text(), f'RFC {rfc.rfc_number} - {rfc.title}')
|
||||||
|
|
||||||
# synonyms for the rfc should be redirected to its canonical view
|
# synonyms for the rfc should be redirected to its canonical view
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.rfc_number())))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.rfc_number)))
|
||||||
self.assertRedirects(r, urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.canonical_name())))
|
self.assertRedirects(r, urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.canonical_name())))
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=f'RFC {rfc.rfc_number()}')))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=f'RFC {rfc.rfc_number}')))
|
||||||
self.assertRedirects(r, urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.canonical_name())))
|
self.assertRedirects(r, urlreverse("ietf.doc.views_doc.document_html", kwargs=dict(name=rfc.canonical_name())))
|
||||||
|
|
||||||
# expired draft
|
# expired draft
|
||||||
|
@ -830,7 +765,7 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
shepherd_id=draft.shepherd_id, ad_id=draft.ad_id, expires=draft.expires,
|
shepherd_id=draft.shepherd_id, ad_id=draft.ad_id, expires=draft.expires,
|
||||||
notify=draft.notify, note=draft.note)
|
notify=draft.notify, note=draft.note)
|
||||||
rel = RelatedDocument.objects.create(source=replacement,
|
rel = RelatedDocument.objects.create(source=replacement,
|
||||||
target=draft.docalias.get(name__startswith="draft"),
|
target=draft,
|
||||||
relationship_id="replaces")
|
relationship_id="replaces")
|
||||||
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
||||||
|
@ -842,26 +777,29 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
|
|
||||||
# draft published as RFC
|
# draft published as RFC
|
||||||
draft.set_state(State.objects.get(type="draft", slug="rfc"))
|
draft.set_state(State.objects.get(type="draft", slug="rfc"))
|
||||||
draft.std_level_id = "bcp"
|
draft.std_level_id = "ps"
|
||||||
draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="published_rfc", by=Person.objects.get(name="(System)"))])
|
|
||||||
|
|
||||||
|
rfc = WgRfcFactory(group=draft.group, name="rfc123456")
|
||||||
|
rfc.save_with_history([DocEvent.objects.create(doc=rfc, rev=None, type="published_rfc", by=Person.objects.get(name="(System)"))])
|
||||||
|
|
||||||
rfc_alias = DocAlias.objects.create(name="rfc123456")
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
rfc_alias.docs.add(draft)
|
|
||||||
bcp_alias = DocAlias.objects.create(name="bcp123456")
|
obsoleted = IndividualRfcFactory()
|
||||||
bcp_alias.docs.add(draft)
|
rfc.relateddocument_set.create(relationship_id='obs',target=obsoleted)
|
||||||
|
obsoleted_by = IndividualRfcFactory()
|
||||||
|
obsoleted_by.relateddocument_set.create(relationship_id='obs',target=rfc)
|
||||||
|
updated = IndividualRfcFactory()
|
||||||
|
rfc.relateddocument_set.create(relationship_id='updates',target=updated)
|
||||||
|
updated_by = IndividualRfcFactory()
|
||||||
|
updated_by.relateddocument_set.create(relationship_id='updates',target=rfc)
|
||||||
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name)))
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=bcp_alias.name)))
|
|
||||||
self.assertEqual(r.status_code, 302)
|
|
||||||
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_alias.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name)))
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, "RFC 123456")
|
self.assertContains(r, "RFC 123456")
|
||||||
self.assertContains(r, draft.name)
|
self.assertContains(r, draft.name)
|
||||||
self.assertContains(r, replaced.canonical_name())
|
|
||||||
self.assertContains(r, replaced.title)
|
|
||||||
# obs/updates included with RFC
|
# obs/updates included with RFC
|
||||||
self.assertContains(r, obsoleted.canonical_name())
|
self.assertContains(r, obsoleted.canonical_name())
|
||||||
self.assertContains(r, obsoleted.title)
|
self.assertContains(r, obsoleted.title)
|
||||||
|
@ -902,7 +840,7 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
draft = WgRfcFactory()
|
draft = WgRfcFactory()
|
||||||
status_change_doc = StatusChangeFactory(
|
status_change_doc = StatusChangeFactory(
|
||||||
group=draft.group,
|
group=draft.group,
|
||||||
changes_status_of=[('tops', draft.docalias.first())],
|
changes_status_of=[('tops', draft)],
|
||||||
)
|
)
|
||||||
status_change_url = urlreverse(
|
status_change_url = urlreverse(
|
||||||
'ietf.doc.views_doc.document_main',
|
'ietf.doc.views_doc.document_main',
|
||||||
|
@ -910,7 +848,7 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
)
|
)
|
||||||
proposed_status_change_doc = StatusChangeFactory(
|
proposed_status_change_doc = StatusChangeFactory(
|
||||||
group=draft.group,
|
group=draft.group,
|
||||||
changes_status_of=[('tobcp', draft.docalias.first())],
|
changes_status_of=[('tobcp', draft)],
|
||||||
states=[State.objects.get(slug='needshep', type='statchg')],
|
states=[State.objects.get(slug='needshep', type='statchg')],
|
||||||
)
|
)
|
||||||
proposed_status_change_url = urlreverse(
|
proposed_status_change_url = urlreverse(
|
||||||
|
@ -1501,11 +1439,11 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assert_correct_wg_group_link(r, group)
|
self.assert_correct_wg_group_link(r, group)
|
||||||
|
|
||||||
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
|
rfc = WgRfcFactory(group=group)
|
||||||
|
draft = WgDraftFactory(group=group)
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
|
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
|
||||||
# get the rfc name to avoid a redirect
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name)))
|
||||||
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
|
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assert_correct_wg_group_link(r, group)
|
self.assert_correct_wg_group_link(r, group)
|
||||||
|
|
||||||
|
@ -1516,11 +1454,11 @@ Man Expires September 22, 2015 [Page 3]
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assert_correct_non_wg_group_link(r, group)
|
self.assert_correct_non_wg_group_link(r, group)
|
||||||
|
|
||||||
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
|
rfc = WgRfcFactory(group=group)
|
||||||
|
draft = WgDraftFactory(name='draft-rfc-document-%s'% group_type_id, group=group)
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
|
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
|
||||||
# get the rfc name to avoid a redirect
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name)))
|
||||||
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
|
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
|
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assert_correct_non_wg_group_link(r, group)
|
self.assert_correct_non_wg_group_link(r, group)
|
||||||
|
|
||||||
|
@ -1621,8 +1559,8 @@ class DocTestCase(TestCase):
|
||||||
statchg = StatusChangeFactory()
|
statchg = StatusChangeFactory()
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=statchg.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=statchg.name)))
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=statchg.relateddocument_set.first().target.document)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=statchg.relateddocument_set.first().target)))
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
||||||
def test_document_charter(self):
|
def test_document_charter(self):
|
||||||
CharterFactory(name='charter-ietf-mars')
|
CharterFactory(name='charter-ietf-mars')
|
||||||
|
@ -1786,8 +1724,8 @@ class DocTestCase(TestCase):
|
||||||
self.assertNotContains(r, 'more YES or NO')
|
self.assertNotContains(r, 'more YES or NO')
|
||||||
|
|
||||||
# status change
|
# status change
|
||||||
DocAlias.objects.create(name='rfc9998').docs.add(IndividualDraftFactory())
|
Document.objects.create(name='rfc9998')
|
||||||
DocAlias.objects.create(name='rfc9999').docs.add(IndividualDraftFactory())
|
Document.objects.create(name='rfc9999')
|
||||||
doc = DocumentFactory(type_id='statchg',name='status-change-imaginary-mid-review')
|
doc = DocumentFactory(type_id='statchg',name='status-change-imaginary-mid-review')
|
||||||
iesgeval_pk = str(State.objects.get(slug='iesgeval',type__slug='statchg').pk)
|
iesgeval_pk = str(State.objects.get(slug='iesgeval',type__slug='statchg').pk)
|
||||||
empty_outbox()
|
empty_outbox()
|
||||||
|
@ -1800,12 +1738,12 @@ class DocTestCase(TestCase):
|
||||||
self.assertIn('iesg-secretary',outbox[0]['To'])
|
self.assertIn('iesg-secretary',outbox[0]['To'])
|
||||||
self.assertIn('drafts-eval',outbox[1]['To'])
|
self.assertIn('drafts-eval',outbox[1]['To'])
|
||||||
|
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9998'),relationship_id='tohist')
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name)))
|
||||||
self.assertNotContains(r, 'Needs a YES')
|
self.assertNotContains(r, 'Needs a YES')
|
||||||
self.assertNotContains(r, 'more YES or NO')
|
self.assertNotContains(r, 'more YES or NO')
|
||||||
|
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9999'),relationship_id='tois')
|
||||||
r = self.client.get(urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name)))
|
r = self.client.get(urlreverse("ietf.doc.views_doc.document_ballot", kwargs=dict(name=doc.name)))
|
||||||
self.assertContains(r, 'more YES or NO')
|
self.assertContains(r, 'more YES or NO')
|
||||||
|
|
||||||
|
@ -1868,15 +1806,14 @@ class DocTestCase(TestCase):
|
||||||
self.assertContains(r, e.desc)
|
self.assertContains(r, e.desc)
|
||||||
|
|
||||||
def test_history_bis_00(self):
|
def test_history_bis_00(self):
|
||||||
rfcname='rfc9090'
|
rfc = WgRfcFactory(rfc_number=9090)
|
||||||
rfc = WgRfcFactory(alias2=rfcname)
|
bis_draft = WgDraftFactory(name='draft-ietf-{}-{}bis'.format(rfc.group.acronym,rfc.name))
|
||||||
bis_draft = WgDraftFactory(name='draft-ietf-{}-{}bis'.format(rfc.group.acronym,rfcname))
|
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_doc.document_history', kwargs=dict(name=bis_draft.name))
|
url = urlreverse('ietf.doc.views_doc.document_history', kwargs=dict(name=bis_draft.name))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
q = PyQuery(unicontent(r))
|
q = PyQuery(unicontent(r))
|
||||||
attr1='value="{}"'.format(rfcname)
|
attr1='value="{}"'.format(rfc.name)
|
||||||
self.assertEqual(len(q('option['+attr1+'][selected="selected"]')), 1)
|
self.assertEqual(len(q('option['+attr1+'][selected="selected"]')), 1)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1926,7 +1863,7 @@ class DocTestCase(TestCase):
|
||||||
self.assertContains(r, doc.name)
|
self.assertContains(r, doc.name)
|
||||||
|
|
||||||
def test_rfc_feed(self):
|
def test_rfc_feed(self):
|
||||||
rfc = WgRfcFactory(alias2__name="rfc9000")
|
rfc = WgRfcFactory(rfc_number=9000)
|
||||||
DocEventFactory(doc=rfc, type="published_rfc")
|
DocEventFactory(doc=rfc, type="published_rfc")
|
||||||
r = self.client.get("/feed/rfc/")
|
r = self.client.get("/feed/rfc/")
|
||||||
self.assertTrue(r.status_code, 200)
|
self.assertTrue(r.status_code, 200)
|
||||||
|
@ -1989,7 +1926,7 @@ class DocTestCase(TestCase):
|
||||||
std_level_id = 'ps',
|
std_level_id = 'ps',
|
||||||
time = datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo(settings.TIME_ZONE)),
|
time = datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo(settings.TIME_ZONE)),
|
||||||
)
|
)
|
||||||
num = rfc.rfc_number()
|
num = rfc.rfc_number
|
||||||
DocEventFactory.create(
|
DocEventFactory.create(
|
||||||
doc=rfc,
|
doc=rfc,
|
||||||
type='published_rfc',
|
type='published_rfc',
|
||||||
|
@ -2000,7 +1937,7 @@ class DocTestCase(TestCase):
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
entry = self._parse_bibtex_response(r)["rfc%s"%num]
|
entry = self._parse_bibtex_response(r)["rfc%s"%num]
|
||||||
self.assertEqual(entry['series'], 'Request for Comments')
|
self.assertEqual(entry['series'], 'Request for Comments')
|
||||||
self.assertEqual(entry['number'], num)
|
self.assertEqual(int(entry['number']), num)
|
||||||
self.assertEqual(entry['doi'], '10.17487/RFC%s'%num)
|
self.assertEqual(entry['doi'], '10.17487/RFC%s'%num)
|
||||||
self.assertEqual(entry['year'], '2010')
|
self.assertEqual(entry['year'], '2010')
|
||||||
self.assertEqual(entry['month'].lower()[0:3], 'oct')
|
self.assertEqual(entry['month'].lower()[0:3], 'oct')
|
||||||
|
@ -2020,7 +1957,7 @@ class DocTestCase(TestCase):
|
||||||
std_level_id = 'inf',
|
std_level_id = 'inf',
|
||||||
time = datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo(settings.TIME_ZONE)),
|
time = datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo(settings.TIME_ZONE)),
|
||||||
)
|
)
|
||||||
num = april1.rfc_number()
|
num = april1.rfc_number
|
||||||
DocEventFactory.create(
|
DocEventFactory.create(
|
||||||
doc=april1,
|
doc=april1,
|
||||||
type='published_rfc',
|
type='published_rfc',
|
||||||
|
@ -2032,7 +1969,7 @@ class DocTestCase(TestCase):
|
||||||
self.assertEqual(r.get('Content-Type'), 'text/plain; charset=utf-8')
|
self.assertEqual(r.get('Content-Type'), 'text/plain; charset=utf-8')
|
||||||
entry = self._parse_bibtex_response(r)["rfc%s"%num]
|
entry = self._parse_bibtex_response(r)["rfc%s"%num]
|
||||||
self.assertEqual(entry['series'], 'Request for Comments')
|
self.assertEqual(entry['series'], 'Request for Comments')
|
||||||
self.assertEqual(entry['number'], num)
|
self.assertEqual(int(entry['number']), num)
|
||||||
self.assertEqual(entry['doi'], '10.17487/RFC%s'%num)
|
self.assertEqual(entry['doi'], '10.17487/RFC%s'%num)
|
||||||
self.assertEqual(entry['year'], '1990')
|
self.assertEqual(entry['year'], '1990')
|
||||||
self.assertEqual(entry['month'].lower()[0:3], 'apr')
|
self.assertEqual(entry['month'].lower()[0:3], 'apr')
|
||||||
|
@ -2129,7 +2066,7 @@ class ReferencesTest(TestCase):
|
||||||
|
|
||||||
def test_references(self):
|
def test_references(self):
|
||||||
doc1 = WgDraftFactory(name='draft-ietf-mars-test')
|
doc1 = WgDraftFactory(name='draft-ietf-mars-test')
|
||||||
doc2 = IndividualDraftFactory(name='draft-imaginary-independent-submission').docalias.first()
|
doc2 = IndividualDraftFactory(name='draft-imaginary-independent-submission')
|
||||||
RelatedDocument.objects.get_or_create(source=doc1,target=doc2,relationship=DocRelationshipName.objects.get(slug='refnorm'))
|
RelatedDocument.objects.get_or_create(source=doc1,target=doc2,relationship=DocRelationshipName.objects.get(slug='refnorm'))
|
||||||
url = urlreverse('ietf.doc.views_doc.document_references', kwargs=dict(name=doc1.name))
|
url = urlreverse('ietf.doc.views_doc.document_references', kwargs=dict(name=doc1.name))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -2141,124 +2078,168 @@ class ReferencesTest(TestCase):
|
||||||
self.assertContains(r, doc1.name)
|
self.assertContains(r, doc1.name)
|
||||||
|
|
||||||
class GenerateDraftAliasesTests(TestCase):
|
class GenerateDraftAliasesTests(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.doc_aliases_file = NamedTemporaryFile(delete=False, mode='w+')
|
self.doc_aliases_file = NamedTemporaryFile(delete=False, mode="w+")
|
||||||
self.doc_aliases_file.close()
|
self.doc_aliases_file.close()
|
||||||
self.doc_virtual_file = NamedTemporaryFile(delete=False, mode='w+')
|
self.doc_virtual_file = NamedTemporaryFile(delete=False, mode="w+")
|
||||||
self.doc_virtual_file.close()
|
self.doc_virtual_file.close()
|
||||||
self.saved_draft_aliases_path = settings.DRAFT_ALIASES_PATH
|
self.saved_draft_aliases_path = settings.DRAFT_ALIASES_PATH
|
||||||
self.saved_draft_virtual_path = settings.DRAFT_VIRTUAL_PATH
|
self.saved_draft_virtual_path = settings.DRAFT_VIRTUAL_PATH
|
||||||
settings.DRAFT_ALIASES_PATH = self.doc_aliases_file.name
|
settings.DRAFT_ALIASES_PATH = self.doc_aliases_file.name
|
||||||
settings.DRAFT_VIRTUAL_PATH = self.doc_virtual_file.name
|
settings.DRAFT_VIRTUAL_PATH = self.doc_virtual_file.name
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
settings.DRAFT_ALIASES_PATH = self.saved_draft_aliases_path
|
settings.DRAFT_ALIASES_PATH = self.saved_draft_aliases_path
|
||||||
settings.DRAFT_VIRTUAL_PATH = self.saved_draft_virtual_path
|
settings.DRAFT_VIRTUAL_PATH = self.saved_draft_virtual_path
|
||||||
os.unlink(self.doc_aliases_file.name)
|
os.unlink(self.doc_aliases_file.name)
|
||||||
os.unlink(self.doc_virtual_file.name)
|
os.unlink(self.doc_virtual_file.name)
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
|
||||||
def testManagementCommand(self):
|
def testManagementCommand(self):
|
||||||
a_month_ago = (timezone.now() - datetime.timedelta(30)).astimezone(RPC_TZINFO)
|
a_month_ago = (timezone.now() - datetime.timedelta(30)).astimezone(RPC_TZINFO)
|
||||||
a_month_ago = a_month_ago.replace(hour=0, minute=0, second=0, microsecond=0)
|
a_month_ago = a_month_ago.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
ad = RoleFactory(name_id='ad', group__type_id='area', group__state_id='active').person
|
ad = RoleFactory(
|
||||||
shepherd = PersonFactory()
|
name_id="ad", group__type_id="area", group__state_id="active"
|
||||||
author1 = PersonFactory()
|
).person
|
||||||
author2 = PersonFactory()
|
shepherd = PersonFactory()
|
||||||
author3 = PersonFactory()
|
author1 = PersonFactory()
|
||||||
author4 = PersonFactory()
|
author2 = PersonFactory()
|
||||||
author5 = PersonFactory()
|
author3 = PersonFactory()
|
||||||
author6 = PersonFactory()
|
author4 = PersonFactory()
|
||||||
mars = GroupFactory(type_id='wg', acronym='mars')
|
author5 = PersonFactory()
|
||||||
marschairman = PersonFactory(user__username='marschairman')
|
author6 = PersonFactory()
|
||||||
mars.role_set.create(name_id='chair', person=marschairman, email=marschairman.email())
|
mars = GroupFactory(type_id="wg", acronym="mars")
|
||||||
doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), ad=ad)
|
marschairman = PersonFactory(user__username="marschairman")
|
||||||
doc2 = WgDraftFactory(name='draft-ietf-mars-test', group__acronym='mars', authors=[author2], ad=ad)
|
mars.role_set.create(
|
||||||
doc3 = WgRfcFactory.create(name='draft-ietf-mars-finished', group__acronym='mars', authors=[author3], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=a_month_ago)
|
name_id="chair", person=marschairman, email=marschairman.email()
|
||||||
DocEventFactory.create(doc=doc3, type='published_rfc', time=a_month_ago)
|
)
|
||||||
doc4 = WgRfcFactory.create(authors=[author4,author5], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=datetime.datetime(2010,10,10, tzinfo=ZoneInfo(settings.TIME_ZONE)))
|
doc1 = IndividualDraftFactory(
|
||||||
DocEventFactory.create(doc=doc4, type='published_rfc', time=datetime.datetime(2010, 10, 10, tzinfo=RPC_TZINFO))
|
authors=[author1], shepherd=shepherd.email(), ad=ad
|
||||||
doc5 = IndividualDraftFactory(authors=[author6])
|
)
|
||||||
|
doc2 = WgDraftFactory(
|
||||||
|
name="draft-ietf-mars-test", group__acronym="mars", authors=[author2], ad=ad
|
||||||
|
)
|
||||||
|
doc3 = WgDraftFactory.create(
|
||||||
|
name="draft-ietf-mars-finished",
|
||||||
|
group__acronym="mars",
|
||||||
|
authors=[author3],
|
||||||
|
ad=ad,
|
||||||
|
std_level_id="ps",
|
||||||
|
states=[("draft", "rfc"), ("draft-iesg", "pub")],
|
||||||
|
time=a_month_ago,
|
||||||
|
)
|
||||||
|
rfc3 = WgRfcFactory()
|
||||||
|
DocEventFactory.create(doc=rfc3, type="published_rfc", time=a_month_ago)
|
||||||
|
doc3.relateddocument_set.create(
|
||||||
|
relationship_id="became_rfc", target=rfc3
|
||||||
|
)
|
||||||
|
doc4 = WgDraftFactory.create(
|
||||||
|
authors=[author4, author5],
|
||||||
|
ad=ad,
|
||||||
|
std_level_id="ps",
|
||||||
|
states=[("draft", "rfc"), ("draft-iesg", "pub")],
|
||||||
|
time=datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo(settings.TIME_ZONE)),
|
||||||
|
)
|
||||||
|
rfc4 = WgRfcFactory()
|
||||||
|
DocEventFactory.create(
|
||||||
|
doc=rfc4,
|
||||||
|
type="published_rfc",
|
||||||
|
time=datetime.datetime(2010, 10, 10, tzinfo=RPC_TZINFO),
|
||||||
|
)
|
||||||
|
doc4.relateddocument_set.create(
|
||||||
|
relationship_id="became_rfc", target=rfc4
|
||||||
|
)
|
||||||
|
doc5 = IndividualDraftFactory(authors=[author6])
|
||||||
|
|
||||||
args = [ ]
|
args = []
|
||||||
kwargs = { }
|
kwargs = {}
|
||||||
out = io.StringIO()
|
out = io.StringIO()
|
||||||
call_command("generate_draft_aliases", *args, **kwargs, stdout=out, stderr=out)
|
call_command("generate_draft_aliases", *args, **kwargs, stdout=out, stderr=out)
|
||||||
self.assertFalse(out.getvalue())
|
self.assertFalse(out.getvalue())
|
||||||
|
|
||||||
with open(settings.DRAFT_ALIASES_PATH) as afile:
|
with open(settings.DRAFT_ALIASES_PATH) as afile:
|
||||||
acontent = afile.read()
|
acontent = afile.read()
|
||||||
self.assertTrue(all([x in acontent for x in [
|
for x in [
|
||||||
'xfilter-' + doc1.name,
|
"xfilter-" + doc1.name,
|
||||||
'xfilter-' + doc1.name + '.ad',
|
"xfilter-" + doc1.name + ".ad",
|
||||||
'xfilter-' + doc1.name + '.authors',
|
"xfilter-" + doc1.name + ".authors",
|
||||||
'xfilter-' + doc1.name + '.shepherd',
|
"xfilter-" + doc1.name + ".shepherd",
|
||||||
'xfilter-' + doc1.name + '.all',
|
"xfilter-" + doc1.name + ".all",
|
||||||
'xfilter-' + doc2.name,
|
"xfilter-" + doc2.name,
|
||||||
'xfilter-' + doc2.name + '.ad',
|
"xfilter-" + doc2.name + ".ad",
|
||||||
'xfilter-' + doc2.name + '.authors',
|
"xfilter-" + doc2.name + ".authors",
|
||||||
'xfilter-' + doc2.name + '.chairs',
|
"xfilter-" + doc2.name + ".chairs",
|
||||||
'xfilter-' + doc2.name + '.all',
|
"xfilter-" + doc2.name + ".all",
|
||||||
'xfilter-' + doc3.name,
|
"xfilter-" + doc3.name,
|
||||||
'xfilter-' + doc3.name + '.ad',
|
"xfilter-" + doc3.name + ".ad",
|
||||||
'xfilter-' + doc3.name + '.authors',
|
"xfilter-" + doc3.name + ".authors",
|
||||||
'xfilter-' + doc3.name + '.chairs',
|
"xfilter-" + doc3.name + ".chairs",
|
||||||
'xfilter-' + doc5.name,
|
"xfilter-" + doc5.name,
|
||||||
'xfilter-' + doc5.name + '.authors',
|
"xfilter-" + doc5.name + ".authors",
|
||||||
'xfilter-' + doc5.name + '.all',
|
"xfilter-" + doc5.name + ".all",
|
||||||
]]))
|
]:
|
||||||
self.assertFalse(all([x in acontent for x in [
|
self.assertIn(x, acontent)
|
||||||
'xfilter-' + doc1.name + '.chairs',
|
|
||||||
'xfilter-' + doc2.name + '.shepherd',
|
|
||||||
'xfilter-' + doc3.name + '.shepherd',
|
|
||||||
'xfilter-' + doc4.name,
|
|
||||||
'xfilter-' + doc5.name + '.shepherd',
|
|
||||||
'xfilter-' + doc5.name + '.ad',
|
|
||||||
]]))
|
|
||||||
|
|
||||||
with open(settings.DRAFT_VIRTUAL_PATH) as vfile:
|
for x in [
|
||||||
vcontent = vfile.read()
|
"xfilter-" + doc1.name + ".chairs",
|
||||||
self.assertTrue(all([x in vcontent for x in [
|
"xfilter-" + doc2.name + ".shepherd",
|
||||||
ad.email_address(),
|
"xfilter-" + doc3.name + ".shepherd",
|
||||||
shepherd.email_address(),
|
"xfilter-" + doc4.name,
|
||||||
marschairman.email_address(),
|
"xfilter-" + doc5.name + ".shepherd",
|
||||||
author1.email_address(),
|
"xfilter-" + doc5.name + ".ad",
|
||||||
author2.email_address(),
|
]:
|
||||||
author3.email_address(),
|
self.assertNotIn(x, acontent)
|
||||||
author6.email_address(),
|
|
||||||
]]))
|
with open(settings.DRAFT_VIRTUAL_PATH) as vfile:
|
||||||
self.assertFalse(all([x in vcontent for x in [
|
vcontent = vfile.read()
|
||||||
author4.email_address(),
|
for x in [
|
||||||
author5.email_address(),
|
ad.email_address(),
|
||||||
]]))
|
shepherd.email_address(),
|
||||||
self.assertTrue(all([x in vcontent for x in [
|
marschairman.email_address(),
|
||||||
'xfilter-' + doc1.name,
|
author1.email_address(),
|
||||||
'xfilter-' + doc1.name + '.ad',
|
author2.email_address(),
|
||||||
'xfilter-' + doc1.name + '.authors',
|
author3.email_address(),
|
||||||
'xfilter-' + doc1.name + '.shepherd',
|
author6.email_address(),
|
||||||
'xfilter-' + doc1.name + '.all',
|
]:
|
||||||
'xfilter-' + doc2.name,
|
self.assertIn(x, vcontent)
|
||||||
'xfilter-' + doc2.name + '.ad',
|
|
||||||
'xfilter-' + doc2.name + '.authors',
|
for x in [
|
||||||
'xfilter-' + doc2.name + '.chairs',
|
author4.email_address(),
|
||||||
'xfilter-' + doc2.name + '.all',
|
author5.email_address(),
|
||||||
'xfilter-' + doc3.name,
|
]:
|
||||||
'xfilter-' + doc3.name + '.ad',
|
self.assertNotIn(x, vcontent)
|
||||||
'xfilter-' + doc3.name + '.authors',
|
|
||||||
'xfilter-' + doc3.name + '.chairs',
|
for x in [
|
||||||
'xfilter-' + doc5.name,
|
"xfilter-" + doc1.name,
|
||||||
'xfilter-' + doc5.name + '.authors',
|
"xfilter-" + doc1.name + ".ad",
|
||||||
'xfilter-' + doc5.name + '.all',
|
"xfilter-" + doc1.name + ".authors",
|
||||||
]]))
|
"xfilter-" + doc1.name + ".shepherd",
|
||||||
self.assertFalse(all([x in vcontent for x in [
|
"xfilter-" + doc1.name + ".all",
|
||||||
'xfilter-' + doc1.name + '.chairs',
|
"xfilter-" + doc2.name,
|
||||||
'xfilter-' + doc2.name + '.shepherd',
|
"xfilter-" + doc2.name + ".ad",
|
||||||
'xfilter-' + doc3.name + '.shepherd',
|
"xfilter-" + doc2.name + ".authors",
|
||||||
'xfilter-' + doc4.name,
|
"xfilter-" + doc2.name + ".chairs",
|
||||||
'xfilter-' + doc5.name + '.shepherd',
|
"xfilter-" + doc2.name + ".all",
|
||||||
'xfilter-' + doc5.name + '.ad',
|
"xfilter-" + doc3.name,
|
||||||
]]))
|
"xfilter-" + doc3.name + ".ad",
|
||||||
|
"xfilter-" + doc3.name + ".authors",
|
||||||
|
"xfilter-" + doc3.name + ".chairs",
|
||||||
|
"xfilter-" + doc5.name,
|
||||||
|
"xfilter-" + doc5.name + ".authors",
|
||||||
|
"xfilter-" + doc5.name + ".all",
|
||||||
|
]:
|
||||||
|
self.assertIn(x, vcontent)
|
||||||
|
|
||||||
|
for x in [
|
||||||
|
"xfilter-" + doc1.name + ".chairs",
|
||||||
|
"xfilter-" + doc2.name + ".shepherd",
|
||||||
|
"xfilter-" + doc3.name + ".shepherd",
|
||||||
|
"xfilter-" + doc4.name,
|
||||||
|
"xfilter-" + doc5.name + ".shepherd",
|
||||||
|
"xfilter-" + doc5.name + ".ad",
|
||||||
|
]:
|
||||||
|
self.assertNotIn(x, vcontent)
|
||||||
|
|
||||||
class EmailAliasesTests(TestCase):
|
class EmailAliasesTests(TestCase):
|
||||||
|
|
||||||
|
@ -2692,10 +2673,10 @@ class Idnits2SupportTests(TestCase):
|
||||||
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['DERIVED_DIR']
|
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['DERIVED_DIR']
|
||||||
|
|
||||||
def test_obsoleted(self):
|
def test_obsoleted(self):
|
||||||
rfc = WgRfcFactory(alias2__name='rfc1001')
|
rfc = WgRfcFactory(rfc_number=1001)
|
||||||
WgRfcFactory(alias2__name='rfc1003',relations=[('obs',rfc)])
|
WgRfcFactory(rfc_number=1003,relations=[('obs',rfc)])
|
||||||
rfc = WgRfcFactory(alias2__name='rfc1005')
|
rfc = WgRfcFactory(rfc_number=1005)
|
||||||
WgRfcFactory(alias2__name='rfc1007',relations=[('obs',rfc)])
|
WgRfcFactory(rfc_number=1007,relations=[('obs',rfc)])
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_doc.idnits2_rfcs_obsoleted')
|
url = urlreverse('ietf.doc.views_doc.idnits2_rfcs_obsoleted')
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -2720,6 +2701,8 @@ class Idnits2SupportTests(TestCase):
|
||||||
|
|
||||||
def test_idnits2_state(self):
|
def test_idnits2_state(self):
|
||||||
rfc = WgRfcFactory()
|
rfc = WgRfcFactory()
|
||||||
|
draft = WgDraftFactory()
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
url = urlreverse('ietf.doc.views_doc.idnits2_state', kwargs=dict(name=rfc.canonical_name()))
|
url = urlreverse('ietf.doc.views_doc.idnits2_state', kwargs=dict(name=rfc.canonical_name()))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -2778,16 +2761,12 @@ class RawIdTests(TestCase):
|
||||||
self.should_succeed(dict(name=draft.name, rev='00',ext='txt'))
|
self.should_succeed(dict(name=draft.name, rev='00',ext='txt'))
|
||||||
self.should_404(dict(name=draft.name, rev='00',ext='html'))
|
self.should_404(dict(name=draft.name, rev='00',ext='html'))
|
||||||
|
|
||||||
def test_raw_id_rfc(self):
|
# test_raw_id_rfc intentionally removed
|
||||||
rfc = WgRfcFactory()
|
# an rfc is no longer a pseudo-version of a draft.
|
||||||
dir = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
|
|
||||||
(Path(dir) / f'{rfc.name}-{rfc.rev}.txt').touch()
|
|
||||||
self.should_succeed(dict(name=rfc.name))
|
|
||||||
self.should_404(dict(name=rfc.canonical_name()))
|
|
||||||
|
|
||||||
def test_non_draft(self):
|
def test_non_draft(self):
|
||||||
charter = CharterFactory()
|
for doc in [CharterFactory(), WgRfcFactory()]:
|
||||||
self.should_404(dict(name=charter.name))
|
self.should_404(dict(name=doc.name))
|
||||||
|
|
||||||
class PdfizedTests(TestCase):
|
class PdfizedTests(TestCase):
|
||||||
|
|
||||||
|
@ -2806,24 +2785,27 @@ class PdfizedTests(TestCase):
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 404)
|
self.assertEqual(r.status_code, 404)
|
||||||
|
|
||||||
|
# This takes a _long_ time (32s on a 2022 m1 macbook pro) - is it worth what it covers?
|
||||||
def test_pdfized(self):
|
def test_pdfized(self):
|
||||||
rfc = WgRfcFactory(create_revisions=range(0,2))
|
rfc = WgRfcFactory()
|
||||||
|
draft = WgDraftFactory(create_revisions=range(0,2))
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
|
|
||||||
dir = settings.RFC_PATH
|
dir = settings.RFC_PATH
|
||||||
with (Path(dir) / f'{rfc.canonical_name()}.txt').open('w') as f:
|
with (Path(dir) / f'{rfc.name}.txt').open('w') as f:
|
||||||
f.write('text content')
|
f.write('text content')
|
||||||
dir = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
|
dir = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR
|
||||||
for r in range(0,2):
|
for r in range(0,2):
|
||||||
with (Path(dir) / f'{rfc.name}-{r:02d}.txt').open('w') as f:
|
with (Path(dir) / f'{draft.name}-{r:02d}.txt').open('w') as f:
|
||||||
f.write('text content')
|
f.write('text content')
|
||||||
|
|
||||||
self.should_succeed(dict(name=rfc.canonical_name()))
|
|
||||||
self.should_succeed(dict(name=rfc.name))
|
self.should_succeed(dict(name=rfc.name))
|
||||||
|
self.should_succeed(dict(name=draft.name))
|
||||||
for r in range(0,2):
|
for r in range(0,2):
|
||||||
self.should_succeed(dict(name=rfc.name,rev=f'{r:02d}'))
|
self.should_succeed(dict(name=draft.name,rev=f'{r:02d}'))
|
||||||
for ext in ('pdf','txt','html','anythingatall'):
|
for ext in ('pdf','txt','html','anythingatall'):
|
||||||
self.should_succeed(dict(name=rfc.name,rev=f'{r:02d}',ext=ext))
|
self.should_succeed(dict(name=draft.name,rev=f'{r:02d}',ext=ext))
|
||||||
self.should_404(dict(name=rfc.name,rev='02'))
|
self.should_404(dict(name=draft.name,rev='02'))
|
||||||
|
|
||||||
class NotifyValidationTests(TestCase):
|
class NotifyValidationTests(TestCase):
|
||||||
def test_notify_validation(self):
|
def test_notify_validation(self):
|
||||||
|
|
|
@ -803,8 +803,8 @@ class ApproveBallotTests(TestCase):
|
||||||
desc='Last call announcement was changed',
|
desc='Last call announcement was changed',
|
||||||
text='this is simple last call text.' )
|
text='this is simple last call text.' )
|
||||||
rfc = IndividualRfcFactory.create(
|
rfc = IndividualRfcFactory.create(
|
||||||
|
name = "rfc6666",
|
||||||
stream_id='ise',
|
stream_id='ise',
|
||||||
other_aliases=['rfc6666',],
|
|
||||||
states=[('draft','rfc'),('draft-iesg','pub')],
|
states=[('draft','rfc'),('draft-iesg','pub')],
|
||||||
std_level_id='inf', )
|
std_level_id='inf', )
|
||||||
|
|
||||||
|
@ -821,7 +821,7 @@ class ApproveBallotTests(TestCase):
|
||||||
self.assertContains(r, "No downward references for")
|
self.assertContains(r, "No downward references for")
|
||||||
|
|
||||||
# Add a downref, the page should ask if it should be added to the registry
|
# Add a downref, the page should ask if it should be added to the registry
|
||||||
rel = draft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'),relationship_id='refnorm')
|
rel = draft.relateddocument_set.create(target=rfc, relationship_id='refnorm')
|
||||||
d = [rdoc for rdoc in draft.relateddocument_set.all() if rel.is_approved_downref()]
|
d = [rdoc for rdoc in draft.relateddocument_set.all() if rel.is_approved_downref()]
|
||||||
original_len = len(d)
|
original_len = len(d)
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -1121,13 +1121,13 @@ class RegenerateLastCallTestCase(TestCase):
|
||||||
self.assertFalse("contains these normative down" in lc_text)
|
self.assertFalse("contains these normative down" in lc_text)
|
||||||
|
|
||||||
rfc = IndividualRfcFactory.create(
|
rfc = IndividualRfcFactory.create(
|
||||||
|
rfc_number=6666,
|
||||||
stream_id='ise',
|
stream_id='ise',
|
||||||
other_aliases=['rfc6666',],
|
|
||||||
states=[('draft','rfc'),('draft-iesg','pub')],
|
states=[('draft','rfc'),('draft-iesg','pub')],
|
||||||
std_level_id='inf',
|
std_level_id='inf',
|
||||||
)
|
)
|
||||||
|
|
||||||
draft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'),relationship_id='refnorm')
|
draft.relateddocument_set.create(target=rfc,relationship_id='refnorm')
|
||||||
|
|
||||||
r = self.client.post(url, dict(regenerate_last_call_text="1"))
|
r = self.client.post(url, dict(regenerate_last_call_text="1"))
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
@ -1137,7 +1137,7 @@ class RegenerateLastCallTestCase(TestCase):
|
||||||
self.assertTrue("rfc6666" in lc_text)
|
self.assertTrue("rfc6666" in lc_text)
|
||||||
self.assertTrue("Independent Submission" in lc_text)
|
self.assertTrue("Independent Submission" in lc_text)
|
||||||
|
|
||||||
draft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'), relationship_id='downref-approval')
|
draft.relateddocument_set.create(target=rfc, relationship_id='downref-approval')
|
||||||
|
|
||||||
r = self.client.post(url, dict(regenerate_last_call_text="1"))
|
r = self.client.post(url, dict(regenerate_last_call_text="1"))
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
|
|
|
@ -88,10 +88,7 @@ class EditCharterTests(TestCase):
|
||||||
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['CHARTER_PATH']
|
settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['CHARTER_PATH']
|
||||||
|
|
||||||
def write_charter_file(self, charter):
|
def write_charter_file(self, charter):
|
||||||
with (Path(settings.CHARTER_PATH) /
|
(Path(settings.CHARTER_PATH) / f"{charter.name}-{charter.rev}.txt").write_text("This is a charter.")
|
||||||
("%s-%s.txt" % (charter.canonical_name(), charter.rev))
|
|
||||||
).open("w") as f:
|
|
||||||
f.write("This is a charter.")
|
|
||||||
|
|
||||||
def test_startstop_process(self):
|
def test_startstop_process(self):
|
||||||
CharterFactory(group__acronym='mars')
|
CharterFactory(group__acronym='mars')
|
||||||
|
@ -509,8 +506,13 @@ class EditCharterTests(TestCase):
|
||||||
self.assertEqual(charter.rev, next_revision(prev_rev))
|
self.assertEqual(charter.rev, next_revision(prev_rev))
|
||||||
self.assertTrue("new_revision" in charter.latest_event().type)
|
self.assertTrue("new_revision" in charter.latest_event().type)
|
||||||
|
|
||||||
with (Path(settings.CHARTER_PATH) / (charter.canonical_name() + "-" + charter.rev + ".txt")).open(encoding='utf-8') as f:
|
file_contents = (
|
||||||
self.assertEqual(f.read(), "Windows line\nMac line\nUnix line\n" + utf_8_snippet.decode('utf-8'))
|
Path(settings.CHARTER_PATH) / (charter.name + "-" + charter.rev + ".txt")
|
||||||
|
).read_text("utf-8")
|
||||||
|
self.assertEqual(
|
||||||
|
file_contents,
|
||||||
|
"Windows line\nMac line\nUnix line\n" + utf_8_snippet.decode("utf-8"),
|
||||||
|
)
|
||||||
|
|
||||||
def test_submit_initial_charter(self):
|
def test_submit_initial_charter(self):
|
||||||
group = GroupFactory(type_id='wg',acronym='mars',list_email='mars-wg@ietf.org')
|
group = GroupFactory(type_id='wg',acronym='mars',list_email='mars-wg@ietf.org')
|
||||||
|
@ -538,6 +540,24 @@ class EditCharterTests(TestCase):
|
||||||
group = Group.objects.get(pk=group.pk)
|
group = Group.objects.get(pk=group.pk)
|
||||||
self.assertEqual(group.charter, charter)
|
self.assertEqual(group.charter, charter)
|
||||||
|
|
||||||
|
def test_submit_charter_with_invalid_name(self):
|
||||||
|
self.client.login(username="secretary", password="secretary+password")
|
||||||
|
ietf_group = GroupFactory(type_id="wg")
|
||||||
|
for bad_name in ("charter-irtf-{}", "charter-randomjunk-{}", "charter-ietf-thisisnotagroup"):
|
||||||
|
url = urlreverse("ietf.doc.views_charter.submit", kwargs={"name": bad_name.format(ietf_group.acronym)})
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 404, f"GET of charter named {bad_name} should 404")
|
||||||
|
r = self.client.post(url, {})
|
||||||
|
self.assertEqual(r.status_code, 404, f"POST of charter named {bad_name} should 404")
|
||||||
|
|
||||||
|
irtf_group = GroupFactory(type_id="rg")
|
||||||
|
for bad_name in ("charter-ietf-{}", "charter-whatisthis-{}", "charter-irtf-thisisnotagroup"):
|
||||||
|
url = urlreverse("ietf.doc.views_charter.submit", kwargs={"name": bad_name.format(irtf_group.acronym)})
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 404, f"GET of charter named {bad_name} should 404")
|
||||||
|
r = self.client.post(url, {})
|
||||||
|
self.assertEqual(r.status_code, 404, f"POST of charter named {bad_name} should 404")
|
||||||
|
|
||||||
def test_edit_review_announcement_text(self):
|
def test_edit_review_announcement_text(self):
|
||||||
area = GroupFactory(type_id='area')
|
area = GroupFactory(type_id='area')
|
||||||
RoleFactory(name_id='ad',group=area,person=Person.objects.get(user__username='ad'))
|
RoleFactory(name_id='ad',group=area,person=Person.objects.get(user__username='ad'))
|
||||||
|
|
|
@ -70,12 +70,12 @@ class ConflictReviewTests(TestCase):
|
||||||
self.assertEqual(review_doc.ad.name,'Areað Irector')
|
self.assertEqual(review_doc.ad.name,'Areað Irector')
|
||||||
self.assertEqual(review_doc.notify,'ipu@ietf.org')
|
self.assertEqual(review_doc.notify,'ipu@ietf.org')
|
||||||
doc = Document.objects.get(name='draft-imaginary-independent-submission')
|
doc = Document.objects.get(name='draft-imaginary-independent-submission')
|
||||||
self.assertTrue(doc in [x.target.document for x in review_doc.relateddocument_set.filter(relationship__slug='conflrev')])
|
self.assertTrue(doc in [x.target for x in review_doc.relateddocument_set.filter(relationship__slug='conflrev')])
|
||||||
|
|
||||||
self.assertTrue(review_doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review requested"))
|
self.assertTrue(review_doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review requested"))
|
||||||
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review initiated"))
|
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith("IETF conflict review initiated"))
|
||||||
self.assertTrue('Conflict Review requested' in outbox[-1]['Subject'])
|
self.assertTrue('Conflict Review requested' in outbox[-1]['Subject'])
|
||||||
|
|
||||||
# verify you can't start a review when a review is already in progress
|
# verify you can't start a review when a review is already in progress
|
||||||
r = self.client.post(url,dict(ad="Areað Irector",create_in_state="Needs Shepherd",notify='ipu@ietf.org'))
|
r = self.client.post(url,dict(ad="Areað Irector",create_in_state="Needs Shepherd",notify='ipu@ietf.org'))
|
||||||
self.assertEqual(r.status_code, 404)
|
self.assertEqual(r.status_code, 404)
|
||||||
|
@ -119,7 +119,7 @@ class ConflictReviewTests(TestCase):
|
||||||
self.assertEqual(review_doc.ad.name,'Ietf Chair')
|
self.assertEqual(review_doc.ad.name,'Ietf Chair')
|
||||||
self.assertEqual(review_doc.notify,'ipu@ietf.org')
|
self.assertEqual(review_doc.notify,'ipu@ietf.org')
|
||||||
doc = Document.objects.get(name='draft-imaginary-independent-submission')
|
doc = Document.objects.get(name='draft-imaginary-independent-submission')
|
||||||
self.assertTrue(doc in [x.target.document for x in review_doc.relateddocument_set.filter(relationship__slug='conflrev')])
|
self.assertTrue(doc in [x.target for x in review_doc.relateddocument_set.filter(relationship__slug='conflrev')])
|
||||||
|
|
||||||
self.assertEqual(len(outbox), messages_before + 2)
|
self.assertEqual(len(outbox), messages_before + 2)
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,9 @@ class Downref(TestCase):
|
||||||
self.draftalias = self.draft.docalias.get(name='draft-ietf-mars-test')
|
self.draftalias = self.draft.docalias.get(name='draft-ietf-mars-test')
|
||||||
self.doc = WgDraftFactory(name='draft-ietf-mars-approved-document',states=[('draft-iesg','rfcqueue')])
|
self.doc = WgDraftFactory(name='draft-ietf-mars-approved-document',states=[('draft-iesg','rfcqueue')])
|
||||||
self.docalias = self.doc.docalias.get(name='draft-ietf-mars-approved-document')
|
self.docalias = self.doc.docalias.get(name='draft-ietf-mars-approved-document')
|
||||||
self.rfc = WgRfcFactory(alias2__name='rfc9998')
|
self.rfc = WgRfcFactory(rfc_number=9998)
|
||||||
self.rfcalias = self.rfc.docalias.get(name='rfc9998')
|
self.rfcalias = self.rfc.docalias.get(name='rfc9998')
|
||||||
RelatedDocument.objects.create(source=self.doc, target=self.rfcalias, relationship_id='downref-approval')
|
RelatedDocument.objects.create(source=self.doc, target=self.rfc, relationship_id='downref-approval')
|
||||||
|
|
||||||
def test_downref_registry(self):
|
def test_downref_registry(self):
|
||||||
url = urlreverse('ietf.doc.views_downref.downref_registry')
|
url = urlreverse('ietf.doc.views_downref.downref_registry')
|
||||||
|
@ -64,44 +64,44 @@ class Downref(TestCase):
|
||||||
self.assertContains(r, 'Save downref')
|
self.assertContains(r, 'Save downref')
|
||||||
|
|
||||||
# error - already in the downref registry
|
# error - already in the downref registry
|
||||||
r = self.client.post(url, dict(rfc=self.rfcalias.pk, drafts=(self.docalias.pk, )))
|
r = self.client.post(url, dict(rfc=self.rfc.pk, drafts=(self.doc.pk, )))
|
||||||
self.assertContains(r, 'Downref is already in the registry')
|
self.assertContains(r, 'Downref is already in the registry')
|
||||||
|
|
||||||
# error - source is not in an approved state
|
# error - source is not in an approved state
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
r = self.client.post(url, dict(rfc=self.rfcalias.pk, drafts=(self.draftalias.pk, )))
|
r = self.client.post(url, dict(rfc=self.rfc.pk, drafts=(self.draft.pk, )))
|
||||||
self.assertContains(r, 'Draft is not yet approved')
|
self.assertContains(r, 'Draft is not yet approved')
|
||||||
|
|
||||||
# error - the target is not a normative reference of the source
|
# error - the target is not a normative reference of the source
|
||||||
self.draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="pub"))
|
self.draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="pub"))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
r = self.client.post(url, dict(rfc=self.rfcalias.pk, drafts=(self.draftalias.pk, )))
|
r = self.client.post(url, dict(rfc=self.rfc.pk, drafts=(self.draft.pk, )))
|
||||||
self.assertContains(r, 'There does not seem to be a normative reference to RFC')
|
self.assertContains(r, 'There does not seem to be a normative reference to RFC')
|
||||||
self.assertContains(r, 'Save downref anyway')
|
self.assertContains(r, 'Save downref anyway')
|
||||||
|
|
||||||
# normal - approve the document so the downref is now okay
|
# normal - approve the document so the downref is now okay
|
||||||
RelatedDocument.objects.create(source=self.draft, target=self.rfcalias, relationship_id='refnorm')
|
RelatedDocument.objects.create(source=self.draft, target=self.rfc, relationship_id='refnorm')
|
||||||
draft_de_count_before = self.draft.docevent_set.count()
|
draft_de_count_before = self.draft.docevent_set.count()
|
||||||
rfc_de_count_before = self.rfc.docevent_set.count()
|
rfc_de_count_before = self.rfc.docevent_set.count()
|
||||||
|
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
r = self.client.post(url, dict(rfc=self.rfcalias.pk, drafts=(self.draftalias.pk, )))
|
r = self.client.post(url, dict(rfc=self.rfc.pk, drafts=(self.draft.pk, )))
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
newurl = urlreverse('ietf.doc.views_downref.downref_registry')
|
newurl = urlreverse('ietf.doc.views_downref.downref_registry')
|
||||||
r = self.client.get(newurl)
|
r = self.client.get(newurl)
|
||||||
self.assertContains(r, '<a href="/doc/draft-ietf-mars-test')
|
self.assertContains(r, '<a href="/doc/draft-ietf-mars-test')
|
||||||
self.assertTrue(RelatedDocument.objects.filter(source=self.draft, target=self.rfcalias, relationship_id='downref-approval'))
|
self.assertTrue(RelatedDocument.objects.filter(source=self.draft, target=self.rfc, relationship_id='downref-approval'))
|
||||||
self.assertEqual(self.draft.docevent_set.count(), draft_de_count_before + 1)
|
self.assertEqual(self.draft.docevent_set.count(), draft_de_count_before + 1)
|
||||||
self.assertEqual(self.rfc.docevent_set.count(), rfc_de_count_before + 1)
|
self.assertEqual(self.rfc.docevent_set.count(), rfc_de_count_before + 1)
|
||||||
|
|
||||||
def test_downref_last_call(self):
|
def test_downref_last_call(self):
|
||||||
draft = WgDraftFactory(name='draft-ietf-mars-ready-for-lc-document',intended_std_level_id='ps',states=[('draft-iesg','iesg-eva')])
|
draft = WgDraftFactory(name='draft-ietf-mars-ready-for-lc-document',intended_std_level_id='ps',states=[('draft-iesg','iesg-eva')])
|
||||||
WgDraftFactory(name='draft-ietf-mars-another-approved-document',states=[('draft-iesg','rfcqueue')])
|
WgDraftFactory(name='draft-ietf-mars-another-approved-document',states=[('draft-iesg','rfcqueue')])
|
||||||
rfc9999 = WgRfcFactory(alias2__name='rfc9999', std_level_id=None)
|
rfc9999 = WgRfcFactory(rfc_number=9999, std_level_id=None)
|
||||||
RelatedDocument.objects.create(source=draft, target=rfc9999.docalias.get(name='rfc9999'), relationship_id='refnorm')
|
RelatedDocument.objects.create(source=draft, target=rfc9999, relationship_id='refnorm')
|
||||||
url = urlreverse('ietf.doc.views_ballot.lastcalltext', kwargs=dict(name=draft.name))
|
url = urlreverse('ietf.doc.views_ballot.lastcalltext', kwargs=dict(name=draft.name))
|
||||||
login_testing_unauthorized(self, "secretary", url)
|
login_testing_unauthorized(self, "secretary", url)
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ class Downref(TestCase):
|
||||||
self.assertIn('The document contains these normative downward references', text)
|
self.assertIn('The document contains these normative downward references', text)
|
||||||
|
|
||||||
# now, the announcement text about the downref to RFC 9999 should be gone
|
# now, the announcement text about the downref to RFC 9999 should be gone
|
||||||
RelatedDocument.objects.create(source=draft, target=rfc9999.docalias.get(name='rfc9999'),relationship_id='downref-approval')
|
RelatedDocument.objects.create(source=draft, target=rfc9999, relationship_id='downref-approval')
|
||||||
r = self.client.post(url, dict(regenerate_last_call_text="1"))
|
r = self.client.post(url, dict(regenerate_last_call_text="1"))
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
|
|
|
@ -2017,10 +2017,10 @@ class ChangeReplacesTests(TestCase):
|
||||||
|
|
||||||
# Post that says replacea replaces base a
|
# Post that says replacea replaces base a
|
||||||
empty_outbox()
|
empty_outbox()
|
||||||
RelatedDocument.objects.create(source=self.replacea, target=self.basea.docalias.first(),
|
RelatedDocument.objects.create(source=self.replacea, target=self.basea,
|
||||||
relationship=DocRelationshipName.objects.get(slug="possibly-replaces"))
|
relationship=DocRelationshipName.objects.get(slug="possibly-replaces"))
|
||||||
self.assertEqual(self.basea.get_state().slug,'active')
|
self.assertEqual(self.basea.get_state().slug,'active')
|
||||||
r = self.client.post(url, dict(replaces=self.basea.docalias.first().pk))
|
r = self.client.post(url, dict(replaces=self.basea.pk))
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
self.assertEqual(RelatedDocument.objects.filter(relationship__slug='replaces',source=self.replacea).count(),1)
|
self.assertEqual(RelatedDocument.objects.filter(relationship__slug='replaces',source=self.replacea).count(),1)
|
||||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
||||||
|
@ -2034,7 +2034,7 @@ class ChangeReplacesTests(TestCase):
|
||||||
# Post that says replaceboth replaces both base a and base b
|
# Post that says replaceboth replaces both base a and base b
|
||||||
url = urlreverse('ietf.doc.views_draft.replaces', kwargs=dict(name=self.replaceboth.name))
|
url = urlreverse('ietf.doc.views_draft.replaces', kwargs=dict(name=self.replaceboth.name))
|
||||||
self.assertEqual(self.baseb.get_state().slug,'expired')
|
self.assertEqual(self.baseb.get_state().slug,'expired')
|
||||||
r = self.client.post(url, dict(replaces=[self.basea.docalias.first().pk, self.baseb.docalias.first().pk]))
|
r = self.client.post(url, dict(replaces=[self.basea.pk, self.baseb.pk]))
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
self.assertEqual(Document.objects.get(name='draft-test-base-a').get_state().slug,'repl')
|
||||||
self.assertEqual(Document.objects.get(name='draft-test-base-b').get_state().slug,'repl')
|
self.assertEqual(Document.objects.get(name='draft-test-base-b').get_state().slug,'repl')
|
||||||
|
@ -2065,7 +2065,7 @@ class ChangeReplacesTests(TestCase):
|
||||||
|
|
||||||
|
|
||||||
def test_review_possibly_replaces(self):
|
def test_review_possibly_replaces(self):
|
||||||
replaced = self.basea.docalias.first()
|
replaced = self.basea
|
||||||
RelatedDocument.objects.create(source=self.replacea, target=replaced,
|
RelatedDocument.objects.create(source=self.replacea, target=replaced,
|
||||||
relationship=DocRelationshipName.objects.get(slug="possibly-replaces"))
|
relationship=DocRelationshipName.objects.get(slug="possibly-replaces"))
|
||||||
|
|
||||||
|
@ -2093,7 +2093,7 @@ class MoreReplacesTests(TestCase):
|
||||||
new_doc = IndividualDraftFactory(stream_id=stream)
|
new_doc = IndividualDraftFactory(stream_id=stream)
|
||||||
|
|
||||||
url = urlreverse('ietf.doc.views_draft.replaces', kwargs=dict(name=new_doc.name))
|
url = urlreverse('ietf.doc.views_draft.replaces', kwargs=dict(name=new_doc.name))
|
||||||
r = self.client.post(url, dict(replaces=old_doc.docalias.first().pk))
|
r = self.client.post(url, dict(replaces=old_doc.pk))
|
||||||
self.assertEqual(r.status_code,302)
|
self.assertEqual(r.status_code,302)
|
||||||
old_doc = Document.objects.get(name=old_doc.name)
|
old_doc = Document.objects.get(name=old_doc.name)
|
||||||
self.assertEqual(old_doc.get_state_slug('draft'),'repl')
|
self.assertEqual(old_doc.get_state_slug('draft'),'repl')
|
||||||
|
|
|
@ -137,10 +137,18 @@ class ReviewTests(TestCase):
|
||||||
url = urlreverse('ietf.doc.views_review.request_review', kwargs={ "name": doc.name })
|
url = urlreverse('ietf.doc.views_review.request_review', kwargs={ "name": doc.name })
|
||||||
login_testing_unauthorized(self, "ad", url)
|
login_testing_unauthorized(self, "ad", url)
|
||||||
|
|
||||||
# get should fail
|
# get should fail - all non draft types 404
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code, 404)
|
||||||
|
|
||||||
|
# Can only request reviews on active draft documents
|
||||||
|
doc = WgDraftFactory(states=[("draft","rfc")])
|
||||||
|
url = urlreverse('ietf.doc.views_review.request_review', kwargs={ "name": doc.name })
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
self.assertEqual(r.status_code, 403)
|
self.assertEqual(r.status_code, 403)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_doc_page(self):
|
def test_doc_page(self):
|
||||||
|
|
||||||
doc = WgDraftFactory(group__acronym='mars',rev='01')
|
doc = WgDraftFactory(group__acronym='mars',rev='01')
|
||||||
|
@ -153,8 +161,8 @@ class ReviewTests(TestCase):
|
||||||
# check we can fish it out
|
# check we can fish it out
|
||||||
old_doc = WgDraftFactory(name="draft-foo-mars-test")
|
old_doc = WgDraftFactory(name="draft-foo-mars-test")
|
||||||
older_doc = WgDraftFactory(name="draft-older")
|
older_doc = WgDraftFactory(name="draft-older")
|
||||||
RelatedDocument.objects.create(source=old_doc, target=older_doc.docalias.first(), relationship_id='replaces')
|
RelatedDocument.objects.create(source=old_doc, target=older_doc, relationship_id='replaces')
|
||||||
RelatedDocument.objects.create(source=doc, target=old_doc.docalias.first(), relationship_id='replaces')
|
RelatedDocument.objects.create(source=doc, target=old_doc, relationship_id='replaces')
|
||||||
review_req.doc = older_doc
|
review_req.doc = older_doc
|
||||||
review_req.save()
|
review_req.save()
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,9 @@ from textwrap import wrap
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse as urlreverse
|
from django.urls import reverse as urlreverse
|
||||||
|
|
||||||
from ietf.doc.factories import DocumentFactory, IndividualRfcFactory, WgRfcFactory, DocEventFactory
|
from ietf.doc.factories import ( DocumentFactory, IndividualRfcFactory,
|
||||||
from ietf.doc.models import ( Document, DocAlias, State, DocEvent,
|
WgRfcFactory, DocEventFactory, WgDraftFactory )
|
||||||
|
from ietf.doc.models import ( Document, State, DocEvent,
|
||||||
BallotPositionDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent )
|
BallotPositionDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent )
|
||||||
from ietf.doc.utils import create_ballot_if_not_open
|
from ietf.doc.utils import create_ballot_if_not_open
|
||||||
from ietf.doc.views_status_change import default_approval_text
|
from ietf.doc.views_status_change import default_approval_text
|
||||||
|
@ -74,7 +75,7 @@ class StatusChangeTests(TestCase):
|
||||||
self.assertEqual(status_change.rev,'00')
|
self.assertEqual(status_change.rev,'00')
|
||||||
self.assertEqual(status_change.ad.name,'Areað Irector')
|
self.assertEqual(status_change.ad.name,'Areað Irector')
|
||||||
self.assertEqual(status_change.notify,'ipu@ietf.org')
|
self.assertEqual(status_change.notify,'ipu@ietf.org')
|
||||||
self.assertTrue(status_change.relateddocument_set.filter(relationship__slug='tois',target__docs__name='draft-ietf-random-thing'))
|
self.assertTrue(status_change.relateddocument_set.filter(relationship__slug='tois',target__name='rfc9999'))
|
||||||
|
|
||||||
# Verify that it's possible to start a status change without a responsible ad.
|
# Verify that it's possible to start a status change without a responsible ad.
|
||||||
r = self.client.post(url,dict(
|
r = self.client.post(url,dict(
|
||||||
|
@ -169,8 +170,8 @@ class StatusChangeTests(TestCase):
|
||||||
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith('Notification list changed'))
|
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith('Notification list changed'))
|
||||||
|
|
||||||
# Some additional setup so there's something to put in a generated notify list
|
# Some additional setup so there's something to put in a generated notify list
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9999'),relationship_id='tois')
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9998'),relationship_id='tohist')
|
||||||
|
|
||||||
# Ask the form to regenerate the list
|
# Ask the form to regenerate the list
|
||||||
r = self.client.post(url,dict(regenerate_addresses="1"))
|
r = self.client.post(url,dict(regenerate_addresses="1"))
|
||||||
|
@ -273,8 +274,8 @@ class StatusChangeTests(TestCase):
|
||||||
login_testing_unauthorized(self, "ad", url)
|
login_testing_unauthorized(self, "ad", url)
|
||||||
|
|
||||||
# additional setup
|
# additional setup
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9999'),relationship_id='tois')
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9998'),relationship_id='tohist')
|
||||||
doc.ad = Person.objects.get(name='Ad No2')
|
doc.ad = Person.objects.get(name='Ad No2')
|
||||||
doc.save_with_history([DocEvent.objects.create(doc=doc, rev=doc.rev, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
|
doc.save_with_history([DocEvent.objects.create(doc=doc, rev=doc.rev, type="changed_document", by=Person.objects.get(user__username="secretary"), desc="Test")])
|
||||||
|
|
||||||
|
@ -329,8 +330,8 @@ class StatusChangeTests(TestCase):
|
||||||
login_testing_unauthorized(self, "secretary", url)
|
login_testing_unauthorized(self, "secretary", url)
|
||||||
|
|
||||||
# Some additional setup
|
# Some additional setup
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9999'),relationship_id='tois')
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9998'),relationship_id='tohist')
|
||||||
create_ballot_if_not_open(None, doc, Person.objects.get(user__username="secretary"), "statchg")
|
create_ballot_if_not_open(None, doc, Person.objects.get(user__username="secretary"), "statchg")
|
||||||
doc.set_state(State.objects.get(slug='appr-pend',type='statchg'))
|
doc.set_state(State.objects.get(slug='appr-pend',type='statchg'))
|
||||||
|
|
||||||
|
@ -370,10 +371,10 @@ class StatusChangeTests(TestCase):
|
||||||
url = urlreverse('ietf.doc.views_status_change.change_state',kwargs=dict(name=doc.name))
|
url = urlreverse('ietf.doc.views_status_change.change_state',kwargs=dict(name=doc.name))
|
||||||
|
|
||||||
# Add some status change related documents
|
# Add some status change related documents
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9999'),relationship_id='tois')
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9998'),relationship_id='tohist')
|
||||||
# And a non-status change related document
|
# And a non-status change related document
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc14'),relationship_id='updates')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc14'),relationship_id='updates')
|
||||||
|
|
||||||
login_testing_unauthorized(self, role, url)
|
login_testing_unauthorized(self, role, url)
|
||||||
empty_outbox()
|
empty_outbox()
|
||||||
|
@ -395,9 +396,9 @@ class StatusChangeTests(TestCase):
|
||||||
self.assertTrue(notification['Subject'].startswith('Approved:'))
|
self.assertTrue(notification['Subject'].startswith('Approved:'))
|
||||||
notification_text = get_payload_text(notification)
|
notification_text = get_payload_text(notification)
|
||||||
self.assertIn('The AD has approved changing the status', notification_text)
|
self.assertIn('The AD has approved changing the status', notification_text)
|
||||||
self.assertIn(DocAlias.objects.get(name='rfc9999').document.canonical_name(), notification_text)
|
self.assertIn(Document.objects.get(name='rfc9999').canonical_name(), notification_text)
|
||||||
self.assertIn(DocAlias.objects.get(name='rfc9998').document.canonical_name(), notification_text)
|
self.assertIn(Document.objects.get(name='rfc9998').canonical_name(), notification_text)
|
||||||
self.assertNotIn(DocAlias.objects.get(name='rfc14').document.canonical_name(), notification_text)
|
self.assertNotIn(Document.objects.get(name='rfc14').canonical_name(), notification_text)
|
||||||
self.assertNotIn('No value found for', notification_text) # make sure all interpolation values were set
|
self.assertNotIn('No value found for', notification_text) # make sure all interpolation values were set
|
||||||
else:
|
else:
|
||||||
self.assertEqual(len(outbox), 0)
|
self.assertEqual(len(outbox), 0)
|
||||||
|
@ -417,8 +418,8 @@ class StatusChangeTests(TestCase):
|
||||||
login_testing_unauthorized(self, "secretary", url)
|
login_testing_unauthorized(self, "secretary", url)
|
||||||
|
|
||||||
# Some additional setup
|
# Some additional setup
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9999'),relationship_id='tois')
|
||||||
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist')
|
doc.relateddocument_set.create(target=Document.objects.get(name='rfc9998'),relationship_id='tohist')
|
||||||
|
|
||||||
# get
|
# get
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -471,9 +472,16 @@ class StatusChangeTests(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
IndividualRfcFactory(alias2__name='rfc14',name='draft-was-never-issued',std_level_id='unkn')
|
IndividualRfcFactory(rfc_number=14,std_level_id='unkn') # draft was never issued
|
||||||
WgRfcFactory(alias2__name='rfc9999',name='draft-ietf-random-thing',std_level_id='ps')
|
|
||||||
WgRfcFactory(alias2__name='rfc9998',name='draft-ietf-random-other-thing',std_level_id='inf')
|
rfc = WgRfcFactory(rfc_number=9999,std_level_id='ps')
|
||||||
|
draft = WgDraftFactory(name='draft-ietf-random-thing')
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
|
|
||||||
|
rfc = WgRfcFactory(rfc_number=9998,std_level_id='inf')
|
||||||
|
draft = WgDraftFactory(name='draft-ietf-random-other-thing')
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
|
|
||||||
DocumentFactory(type_id='statchg',name='status-change-imaginary-mid-review',notify='notify@example.org')
|
DocumentFactory(type_id='statchg',name='status-change-imaginary-mid-review',notify='notify@example.org')
|
||||||
|
|
||||||
class StatusChangeSubmitTests(TestCase):
|
class StatusChangeSubmitTests(TestCase):
|
||||||
|
|
|
@ -11,10 +11,10 @@ from django.utils import timezone
|
||||||
from ietf.group.factories import GroupFactory, RoleFactory
|
from ietf.group.factories import GroupFactory, RoleFactory
|
||||||
from ietf.name.models import DocTagName
|
from ietf.name.models import DocTagName
|
||||||
from ietf.person.factories import PersonFactory
|
from ietf.person.factories import PersonFactory
|
||||||
from ietf.utils.test_utils import TestCase, name_of_file_containing
|
from ietf.utils.test_utils import TestCase, name_of_file_containing, reload_db_objects
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.doc.factories import DocumentFactory, WgRfcFactory, WgDraftFactory
|
from ietf.doc.factories import DocumentFactory, WgRfcFactory, WgDraftFactory
|
||||||
from ietf.doc.models import State, DocumentActionHolder, DocumentAuthor, Document
|
from ietf.doc.models import State, DocumentActionHolder, DocumentAuthor
|
||||||
from ietf.doc.utils import (update_action_holders, add_state_change_event, update_documentauthors,
|
from ietf.doc.utils import (update_action_holders, add_state_change_event, update_documentauthors,
|
||||||
fuzzy_find_documents, rebuild_reference_relations, build_file_urls)
|
fuzzy_find_documents, rebuild_reference_relations, build_file_urls)
|
||||||
from ietf.utils.draft import Draft, PlaintextDraft
|
from ietf.utils.draft import Draft, PlaintextDraft
|
||||||
|
@ -251,40 +251,42 @@ class MiscTests(TestCase):
|
||||||
self.assertEqual(docauth.country, '')
|
self.assertEqual(docauth.country, '')
|
||||||
|
|
||||||
def do_fuzzy_find_documents_rfc_test(self, name):
|
def do_fuzzy_find_documents_rfc_test(self, name):
|
||||||
rfc = WgRfcFactory(name=name, create_revisions=(0, 1, 2))
|
draft = WgDraftFactory(name=name, create_revisions=(0, 1, 2))
|
||||||
rfc = Document.objects.get(pk=rfc.pk) # clear out any cached values
|
rfc = WgRfcFactory()
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
|
draft, rfc = reload_db_objects(draft, rfc)
|
||||||
|
|
||||||
# by canonical name
|
# by canonical name
|
||||||
found = fuzzy_find_documents(rfc.canonical_name(), None)
|
|
||||||
self.assertCountEqual(found.documents, [rfc])
|
|
||||||
self.assertEqual(found.matched_rev, None)
|
|
||||||
self.assertEqual(found.matched_name, rfc.canonical_name())
|
|
||||||
|
|
||||||
# by draft name, no rev
|
|
||||||
found = fuzzy_find_documents(rfc.name, None)
|
found = fuzzy_find_documents(rfc.name, None)
|
||||||
self.assertCountEqual(found.documents, [rfc])
|
self.assertCountEqual(found.documents, [rfc])
|
||||||
self.assertEqual(found.matched_rev, None)
|
self.assertEqual(found.matched_rev, None)
|
||||||
self.assertEqual(found.matched_name, rfc.name)
|
self.assertEqual(found.matched_name, rfc.name)
|
||||||
|
|
||||||
|
# by draft name, no rev
|
||||||
|
found = fuzzy_find_documents(draft.name, None)
|
||||||
|
self.assertCountEqual(found.documents, [draft])
|
||||||
|
self.assertEqual(found.matched_rev, None)
|
||||||
|
self.assertEqual(found.matched_name, draft.name)
|
||||||
|
|
||||||
# by draft name, latest rev
|
# by draft name, latest rev
|
||||||
found = fuzzy_find_documents(rfc.name, '02')
|
found = fuzzy_find_documents(draft.name, '02')
|
||||||
self.assertCountEqual(found.documents, [rfc])
|
self.assertCountEqual(found.documents, [draft])
|
||||||
self.assertEqual(found.matched_rev, '02')
|
self.assertEqual(found.matched_rev, '02')
|
||||||
self.assertEqual(found.matched_name, rfc.name)
|
self.assertEqual(found.matched_name, draft.name)
|
||||||
|
|
||||||
# by draft name, earlier rev
|
# by draft name, earlier rev
|
||||||
found = fuzzy_find_documents(rfc.name, '01')
|
found = fuzzy_find_documents(draft.name, '01')
|
||||||
self.assertCountEqual(found.documents, [rfc])
|
self.assertCountEqual(found.documents, [draft])
|
||||||
self.assertEqual(found.matched_rev, '01')
|
self.assertEqual(found.matched_rev, '01')
|
||||||
self.assertEqual(found.matched_name, rfc.name)
|
self.assertEqual(found.matched_name, draft.name)
|
||||||
|
|
||||||
# wrong name or revision
|
# wrong name or revision
|
||||||
found = fuzzy_find_documents(rfc.name + '-incorrect')
|
found = fuzzy_find_documents(draft.name + '-incorrect')
|
||||||
self.assertCountEqual(found.documents, [], 'Should not find document that does not match')
|
self.assertCountEqual(found.documents, [], 'Should not find document that does not match')
|
||||||
found = fuzzy_find_documents(rfc.name + '-incorrect', '02')
|
found = fuzzy_find_documents(draft.name + '-incorrect', '02')
|
||||||
self.assertCountEqual(found.documents, [], 'Still should not find document, even with a version')
|
self.assertCountEqual(found.documents, [], 'Still should not find document, even with a version')
|
||||||
found = fuzzy_find_documents(rfc.name, '22')
|
found = fuzzy_find_documents(draft.name, '22')
|
||||||
self.assertCountEqual(found.documents, [rfc],
|
self.assertCountEqual(found.documents, [draft],
|
||||||
'Should find document even if rev does not exist')
|
'Should find document even if rev does not exist')
|
||||||
|
|
||||||
|
|
||||||
|
@ -325,14 +327,14 @@ class RebuildReferenceRelationsTests(TestCase):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.doc = WgDraftFactory() # document under test
|
self.doc = WgDraftFactory() # document under test
|
||||||
# Other documents that should be found by rebuild_reference_relations
|
# Other documents that should be found by rebuild_reference_relations
|
||||||
self.normative, self.informative, self.unknown = WgRfcFactory.create_batch(3)
|
self.normative, self.informative, self.unknown = WgRfcFactory.create_batch(3) # AMHERE - these need to have rfc names.
|
||||||
for relationship in ['refnorm', 'refinfo', 'refunk', 'refold']:
|
for relationship in ['refnorm', 'refinfo', 'refunk', 'refold']:
|
||||||
self.doc.relateddocument_set.create(
|
self.doc.relateddocument_set.create(
|
||||||
target=WgRfcFactory().docalias.first(),
|
target=WgRfcFactory(),
|
||||||
relationship_id=relationship,
|
relationship_id=relationship,
|
||||||
)
|
)
|
||||||
self.updated = WgRfcFactory() # related document that should be left alone
|
self.updated = WgRfcFactory() # related document that should be left alone
|
||||||
self.doc.relateddocument_set.create(target=self.updated.docalias.first(), relationship_id='updates')
|
self.doc.relateddocument_set.create(target=self.updated, relationship_id='updates')
|
||||||
self.assertCountEqual(self.doc.relateddocument_set.values_list('relationship__slug', flat=True),
|
self.assertCountEqual(self.doc.relateddocument_set.values_list('relationship__slug', flat=True),
|
||||||
['refnorm', 'refinfo', 'refold', 'refunk', 'updates'],
|
['refnorm', 'refinfo', 'refold', 'refunk', 'updates'],
|
||||||
'Test conditions set up incorrectly: wrong prior document relationships')
|
'Test conditions set up incorrectly: wrong prior document relationships')
|
||||||
|
@ -378,7 +380,7 @@ class RebuildReferenceRelationsTests(TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
result,
|
result,
|
||||||
{
|
{
|
||||||
'warnings': ['There were 1 references with no matching DocAlias'],
|
'warnings': ['There were 1 references with no matching Document'],
|
||||||
'unfound': ['draft-not-found'],
|
'unfound': ['draft-not-found'],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -389,7 +391,7 @@ class RebuildReferenceRelationsTests(TestCase):
|
||||||
(self.normative.canonical_name(), 'refnorm'),
|
(self.normative.canonical_name(), 'refnorm'),
|
||||||
(self.informative.canonical_name(), 'refinfo'),
|
(self.informative.canonical_name(), 'refinfo'),
|
||||||
(self.unknown.canonical_name(), 'refunk'),
|
(self.unknown.canonical_name(), 'refunk'),
|
||||||
(self.updated.docalias.first().name, 'updates'),
|
(self.updated.name, 'updates'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -409,7 +411,7 @@ class RebuildReferenceRelationsTests(TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
result,
|
result,
|
||||||
{
|
{
|
||||||
'warnings': ['There were 1 references with no matching DocAlias'],
|
'warnings': ['There were 1 references with no matching Document'],
|
||||||
'unfound': ['draft-not-found'],
|
'unfound': ['draft-not-found'],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -420,7 +422,7 @@ class RebuildReferenceRelationsTests(TestCase):
|
||||||
(self.normative.canonical_name(), 'refnorm'),
|
(self.normative.canonical_name(), 'refnorm'),
|
||||||
(self.informative.canonical_name(), 'refinfo'),
|
(self.informative.canonical_name(), 'refinfo'),
|
||||||
(self.unknown.canonical_name(), 'refunk'),
|
(self.unknown.canonical_name(), 'refunk'),
|
||||||
(self.updated.docalias.first().name, 'updates'),
|
(self.updated.name, 'updates'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -441,7 +443,7 @@ class RebuildReferenceRelationsTests(TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
result,
|
result,
|
||||||
{
|
{
|
||||||
'warnings': ['There were 1 references with no matching DocAlias'],
|
'warnings': ['There were 1 references with no matching Document'],
|
||||||
'unfound': ['draft-not-found'],
|
'unfound': ['draft-not-found'],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -452,6 +454,6 @@ class RebuildReferenceRelationsTests(TestCase):
|
||||||
(self.normative.canonical_name(), 'refnorm'),
|
(self.normative.canonical_name(), 'refnorm'),
|
||||||
(self.informative.canonical_name(), 'refinfo'),
|
(self.informative.canonical_name(), 'refinfo'),
|
||||||
(self.unknown.canonical_name(), 'refunk'),
|
(self.unknown.canonical_name(), 'refunk'),
|
||||||
(self.updated.docalias.first().name, 'updates'),
|
(self.updated.name, 'updates'),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,7 +31,7 @@ from ietf.community.models import CommunityList
|
||||||
from ietf.community.utils import docs_tracked_by_community_list
|
from ietf.community.utils import docs_tracked_by_community_list
|
||||||
|
|
||||||
from ietf.doc.models import Document, DocHistory, State, DocumentAuthor, DocHistoryAuthor
|
from ietf.doc.models import Document, DocHistory, State, DocumentAuthor, DocHistoryAuthor
|
||||||
from ietf.doc.models import DocAlias, RelatedDocument, RelatedDocHistory, BallotType, DocReminder
|
from ietf.doc.models import RelatedDocument, RelatedDocHistory, BallotType, DocReminder
|
||||||
from ietf.doc.models import DocEvent, ConsensusDocEvent, BallotDocEvent, IRSGBallotDocEvent, NewRevisionDocEvent, StateDocEvent
|
from ietf.doc.models import DocEvent, ConsensusDocEvent, BallotDocEvent, IRSGBallotDocEvent, NewRevisionDocEvent, StateDocEvent
|
||||||
from ietf.doc.models import TelechatDocEvent, DocumentActionHolder, EditedAuthorsDocEvent
|
from ietf.doc.models import TelechatDocEvent, DocumentActionHolder, EditedAuthorsDocEvent
|
||||||
from ietf.name.models import DocReminderTypeName, DocRelationshipName
|
from ietf.name.models import DocReminderTypeName, DocRelationshipName
|
||||||
|
@ -218,7 +218,7 @@ def needed_ballot_positions(doc, active_positions):
|
||||||
else:
|
else:
|
||||||
related_set = RelatedDocHistory.objects.none()
|
related_set = RelatedDocHistory.objects.none()
|
||||||
for rel in related_set.filter(relationship__slug__in=['tops', 'tois', 'tohist', 'toinf', 'tobcp', 'toexp']):
|
for rel in related_set.filter(relationship__slug__in=['tops', 'tois', 'tohist', 'toinf', 'tobcp', 'toexp']):
|
||||||
if (rel.target.document.std_level_id in ['bcp','ps','ds','std']) or (rel.relationship_id in ['tops','tois','tobcp']):
|
if (rel.target.std_level_id in ['bcp','ps','ds','std']) or (rel.relationship_id in ['tops','tois','tobcp']):
|
||||||
needed = two_thirds_rule(recused=len(recuse))
|
needed = two_thirds_rule(recused=len(recuse))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -771,22 +771,22 @@ def rebuild_reference_relations(doc, filenames):
|
||||||
errors = []
|
errors = []
|
||||||
unfound = set()
|
unfound = set()
|
||||||
for ( ref, refType ) in refs.items():
|
for ( ref, refType ) in refs.items():
|
||||||
refdoc = DocAlias.objects.filter(name=ref)
|
refdoc = Document.objects.filter(name=ref)
|
||||||
if not refdoc and re.match(r"^draft-.*-\d{2}$", ref):
|
if not refdoc and re.match(r"^draft-.*-\d{2}$", ref):
|
||||||
refdoc = DocAlias.objects.filter(name=ref[:-3])
|
refdoc = Document.objects.filter(name=ref[:-3])
|
||||||
count = refdoc.count()
|
count = refdoc.count()
|
||||||
# As of Dec 2021, DocAlias has a unique constraint on the name field, so count > 1 should not occur
|
# As of Dec 2021, DocAlias has a unique constraint on the name field, so count > 1 should not occur
|
||||||
if count == 0:
|
if count == 0:
|
||||||
unfound.add( "%s" % ref )
|
unfound.add( "%s" % ref )
|
||||||
continue
|
continue
|
||||||
elif count > 1:
|
elif count > 1:
|
||||||
errors.append("Too many DocAlias objects found for %s"%ref)
|
errors.append("Too many Document objects found for %s"%ref)
|
||||||
else:
|
else:
|
||||||
# Don't add references to ourself
|
# Don't add references to ourself
|
||||||
if doc != refdoc[0].document:
|
if doc != refdoc[0]:
|
||||||
RelatedDocument.objects.get_or_create( source=doc, target=refdoc[ 0 ], relationship=DocRelationshipName.objects.get( slug='ref%s' % refType ) )
|
RelatedDocument.objects.get_or_create( source=doc, target=refdoc[ 0 ], relationship=DocRelationshipName.objects.get( slug='ref%s' % refType ) )
|
||||||
if unfound:
|
if unfound:
|
||||||
warnings.append('There were %d references with no matching DocAlias'%len(unfound))
|
warnings.append('There were %d references with no matching Document'%len(unfound))
|
||||||
|
|
||||||
ret = {}
|
ret = {}
|
||||||
if errors:
|
if errors:
|
||||||
|
@ -821,26 +821,26 @@ def set_replaces_for_document(request, doc, new_replaces, by, email_subject, com
|
||||||
|
|
||||||
for d in old_replaces:
|
for d in old_replaces:
|
||||||
if d not in new_replaces:
|
if d not in new_replaces:
|
||||||
other_addrs = gather_address_lists('doc_replacement_changed',doc=d.document)
|
other_addrs = gather_address_lists('doc_replacement_changed',doc=d)
|
||||||
to.update(other_addrs.to)
|
to.update(other_addrs.to)
|
||||||
cc.update(other_addrs.cc)
|
cc.update(other_addrs.cc)
|
||||||
RelatedDocument.objects.filter(source=doc, target=d, relationship=relationship).delete()
|
RelatedDocument.objects.filter(source=doc, target=d, relationship=relationship).delete()
|
||||||
if not RelatedDocument.objects.filter(target=d, relationship=relationship):
|
if not RelatedDocument.objects.filter(target=d, relationship=relationship):
|
||||||
s = 'active' if d.document.expires > timezone.now() else 'expired'
|
s = 'active' if d.expires > timezone.now() else 'expired'
|
||||||
d.document.set_state(State.objects.get(type='draft', slug=s))
|
d.set_state(State.objects.get(type='draft', slug=s))
|
||||||
|
|
||||||
for d in new_replaces:
|
for d in new_replaces:
|
||||||
if d not in old_replaces:
|
if d not in old_replaces:
|
||||||
other_addrs = gather_address_lists('doc_replacement_changed',doc=d.document)
|
other_addrs = gather_address_lists('doc_replacement_changed',doc=d)
|
||||||
to.update(other_addrs.to)
|
to.update(other_addrs.to)
|
||||||
cc.update(other_addrs.cc)
|
cc.update(other_addrs.cc)
|
||||||
RelatedDocument.objects.create(source=doc, target=d, relationship=relationship)
|
RelatedDocument.objects.create(source=doc, target=d, relationship=relationship)
|
||||||
d.document.set_state(State.objects.get(type='draft', slug='repl'))
|
d.set_state(State.objects.get(type='draft', slug='repl'))
|
||||||
|
|
||||||
if d.document.stream_id in ('irtf','ise','iab'):
|
if d.stream_id in ('irtf','ise','iab'):
|
||||||
repl_state = State.objects.get(type_id='draft-stream-%s'%d.document.stream_id, slug='repl')
|
repl_state = State.objects.get(type_id='draft-stream-%s'%d.stream_id, slug='repl')
|
||||||
d.document.set_state(repl_state)
|
d.set_state(repl_state)
|
||||||
events.append(StateDocEvent.objects.create(doc=d.document, rev=d.document.rev, by=by, type='changed_state', desc="Set stream state to Replaced",state_type=repl_state.type, state=repl_state))
|
events.append(StateDocEvent.objects.create(doc=d, rev=d.rev, by=by, type='changed_state', desc="Set stream state to Replaced",state_type=repl_state.type, state=repl_state))
|
||||||
|
|
||||||
# make sure there are no lingering suggestions duplicating new replacements
|
# make sure there are no lingering suggestions duplicating new replacements
|
||||||
RelatedDocument.objects.filter(source=doc, target__in=new_replaces, relationship="possibly-replaces").delete()
|
RelatedDocument.objects.filter(source=doc, target__in=new_replaces, relationship="possibly-replaces").delete()
|
||||||
|
@ -910,7 +910,7 @@ def extract_complete_replaces_ancestor_mapping_for_docs(names):
|
||||||
break
|
break
|
||||||
|
|
||||||
relations = ( RelatedDocument.objects.filter(source__name__in=front, relationship="replaces")
|
relations = ( RelatedDocument.objects.filter(source__name__in=front, relationship="replaces")
|
||||||
.select_related("target").values_list("source__name", "target__docs__name") )
|
.select_related("target").values_list("source__name", "target__name") )
|
||||||
if not relations:
|
if not relations:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -998,14 +998,11 @@ def get_search_cache_key(params):
|
||||||
kwargs = dict([ (k,v) for (k,v) in list(params.items()) if k in fields ])
|
kwargs = dict([ (k,v) for (k,v) in list(params.items()) if k in fields ])
|
||||||
key = "doc:document:search:" + hashlib.sha512(json.dumps(kwargs, sort_keys=True).encode('utf-8')).hexdigest()
|
key = "doc:document:search:" + hashlib.sha512(json.dumps(kwargs, sort_keys=True).encode('utf-8')).hexdigest()
|
||||||
return key
|
return key
|
||||||
|
|
||||||
def build_file_urls(doc: Union[Document, DocHistory]):
|
|
||||||
if doc.type_id != 'draft':
|
|
||||||
return [], []
|
|
||||||
|
|
||||||
if doc.get_state_slug() == "rfc":
|
|
||||||
name = doc.canonical_name()
|
def build_file_urls(doc: Union[Document, DocHistory]):
|
||||||
base_path = os.path.join(settings.RFC_PATH, name + ".")
|
if doc.type_id == "rfc":
|
||||||
|
base_path = os.path.join(settings.RFC_PATH, doc.name + ".")
|
||||||
possible_types = settings.RFC_FILE_TYPES
|
possible_types = settings.RFC_FILE_TYPES
|
||||||
found_types = [t for t in possible_types if os.path.exists(base_path + t)]
|
found_types = [t for t in possible_types if os.path.exists(base_path + t)]
|
||||||
|
|
||||||
|
@ -1014,17 +1011,17 @@ def build_file_urls(doc: Union[Document, DocHistory]):
|
||||||
file_urls = []
|
file_urls = []
|
||||||
for t in found_types:
|
for t in found_types:
|
||||||
label = "plain text" if t == "txt" else t
|
label = "plain text" if t == "txt" else t
|
||||||
file_urls.append((label, base + name + "." + t))
|
file_urls.append((label, base + doc.name + "." + t))
|
||||||
|
|
||||||
if "pdf" not in found_types and "txt" in found_types:
|
if "pdf" not in found_types and "txt" in found_types:
|
||||||
file_urls.append(("pdf", base + "pdfrfc/" + name + ".txt.pdf"))
|
file_urls.append(("pdf", base + "pdfrfc/" + doc.name + ".txt.pdf"))
|
||||||
|
|
||||||
if "txt" in found_types:
|
if "txt" in found_types:
|
||||||
file_urls.append(("htmlized", urlreverse('ietf.doc.views_doc.document_html', kwargs=dict(name=name))))
|
file_urls.append(("htmlized", urlreverse('ietf.doc.views_doc.document_html', kwargs=dict(name=doc.name))))
|
||||||
if doc.tags.filter(slug="verified-errata").exists():
|
if doc.tags.filter(slug="verified-errata").exists():
|
||||||
file_urls.append(("with errata", settings.RFC_EDITOR_INLINE_ERRATA_URL.format(rfc_number=doc.rfc_number())))
|
file_urls.append(("with errata", settings.RFC_EDITOR_INLINE_ERRATA_URL.format(rfc_number=doc.rfc_number)))
|
||||||
file_urls.append(("bibtex", urlreverse('ietf.doc.views_doc.document_bibtex',kwargs=dict(name=name))))
|
file_urls.append(("bibtex", urlreverse('ietf.doc.views_doc.document_bibtex',kwargs=dict(name=doc.name))))
|
||||||
elif doc.rev:
|
elif doc.type_id == "draft" and doc.rev != "":
|
||||||
base_path = os.path.join(settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR, doc.name + "-" + doc.rev + ".")
|
base_path = os.path.join(settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR, doc.name + "-" + doc.rev + ".")
|
||||||
possible_types = settings.IDSUBMIT_FILE_TYPES
|
possible_types = settings.IDSUBMIT_FILE_TYPES
|
||||||
found_types = [t for t in possible_types if os.path.exists(base_path + t)]
|
found_types = [t for t in possible_types if os.path.exists(base_path + t)]
|
||||||
|
@ -1040,12 +1037,14 @@ def build_file_urls(doc: Union[Document, DocHistory]):
|
||||||
file_urls.append(("bibtex", urlreverse('ietf.doc.views_doc.document_bibtex',kwargs=dict(name=doc.name,rev=doc.rev))))
|
file_urls.append(("bibtex", urlreverse('ietf.doc.views_doc.document_bibtex',kwargs=dict(name=doc.name,rev=doc.rev))))
|
||||||
file_urls.append(("bibxml", urlreverse('ietf.doc.views_doc.document_bibxml',kwargs=dict(name=doc.name,rev=doc.rev))))
|
file_urls.append(("bibxml", urlreverse('ietf.doc.views_doc.document_bibxml',kwargs=dict(name=doc.name,rev=doc.rev))))
|
||||||
else:
|
else:
|
||||||
# As of 2022-12-14, there are 1463 Document and 3136 DocHistory records with type='draft' and rev=''.
|
if doc.type_id == "draft":
|
||||||
# All of these are in the rfc state and are covered by the above cases.
|
# TODO: look at the state of the database post migration and update this comment, or remove the block
|
||||||
log.unreachable('2022-12-14')
|
# As of 2022-12-14, there are 1463 Document and 3136 DocHistory records with type='draft' and rev=''.
|
||||||
|
# All of these are in the rfc state and are covered by the above cases.
|
||||||
|
log.unreachable('2022-12-14')
|
||||||
file_urls = []
|
file_urls = []
|
||||||
found_types = []
|
found_types = []
|
||||||
|
|
||||||
return file_urls, found_types
|
return file_urls, found_types
|
||||||
|
|
||||||
def augment_docs_and_user_with_user_info(docs, user):
|
def augment_docs_and_user_with_user_info(docs, user):
|
||||||
|
@ -1112,16 +1111,16 @@ def generate_idnits2_rfc_status():
|
||||||
'unkn': 'U',
|
'unkn': 'U',
|
||||||
}
|
}
|
||||||
|
|
||||||
rfcs = Document.objects.filter(type_id='draft',states__slug='rfc',states__type='draft')
|
rfcs = Document.objects.filter(type_id='rfc')
|
||||||
for rfc in rfcs:
|
for rfc in rfcs:
|
||||||
offset = int(rfc.rfcnum)-1
|
offset = int(rfc.rfc_number)-1
|
||||||
blob[offset] = symbols[rfc.std_level_id]
|
blob[offset] = symbols[rfc.std_level_id]
|
||||||
if rfc.related_that('obs'):
|
if rfc.related_that('obs'):
|
||||||
blob[offset] = 'O'
|
blob[offset] = 'O'
|
||||||
|
|
||||||
# Workarounds for unusual states in the datatracker
|
# Workarounds for unusual states in the datatracker
|
||||||
|
|
||||||
# Document.get(docalias='rfc6312').rfcnum == 6342
|
# Document.get(docalias='rfc6312').rfc_number == 6342
|
||||||
# 6312 was published with the wrong rfc number in it
|
# 6312 was published with the wrong rfc number in it
|
||||||
# weird workaround in the datatracker - there are two
|
# weird workaround in the datatracker - there are two
|
||||||
# DocAliases starting with rfc - the canonical name code
|
# DocAliases starting with rfc - the canonical name code
|
||||||
|
@ -1142,7 +1141,7 @@ def generate_idnits2_rfc_status():
|
||||||
def generate_idnits2_rfcs_obsoleted():
|
def generate_idnits2_rfcs_obsoleted():
|
||||||
obsdict = defaultdict(list)
|
obsdict = defaultdict(list)
|
||||||
for r in RelatedDocument.objects.filter(relationship_id='obs'):
|
for r in RelatedDocument.objects.filter(relationship_id='obs'):
|
||||||
obsdict[int(r.target.document.rfc_number())].append(int(r.source.rfc_number()))
|
obsdict[int(r.target.rfc_number)].append(int(r.source.rfc_number)) # Aren't these already guaranteed to be ints?
|
||||||
for k in obsdict:
|
for k in obsdict:
|
||||||
obsdict[k] = sorted(obsdict[k])
|
obsdict[k] = sorted(obsdict[k])
|
||||||
return render_to_string('doc/idnits2-rfcs-obsoleted.txt', context={'obsitems':sorted(obsdict.items())})
|
return render_to_string('doc/idnits2-rfcs-obsoleted.txt', context={'obsitems':sorted(obsdict.items())})
|
||||||
|
@ -1171,8 +1170,14 @@ def fuzzy_find_documents(name, rev=None):
|
||||||
if re.match("^[0-9]+$", name):
|
if re.match("^[0-9]+$", name):
|
||||||
name = f'rfc{name}'
|
name = f'rfc{name}'
|
||||||
|
|
||||||
|
if name.startswith("rfc"):
|
||||||
|
sought_type = "rfc"
|
||||||
|
log.assertion("rev is None")
|
||||||
|
else:
|
||||||
|
sought_type = "draft"
|
||||||
|
|
||||||
# see if we can find a document using this name
|
# see if we can find a document using this name
|
||||||
docs = Document.objects.filter(docalias__name=name, type_id='draft')
|
docs = Document.objects.filter(docalias__name=name, type_id=sought_type)
|
||||||
if rev and not docs.exists():
|
if rev and not docs.exists():
|
||||||
# No document found, see if the name/rev split has been misidentified.
|
# No document found, see if the name/rev split has been misidentified.
|
||||||
# Handles some special cases, like draft-ietf-tsvwg-ieee-802-11.
|
# Handles some special cases, like draft-ietf-tsvwg-ieee-802-11.
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse as urlreverse
|
from django.urls import reverse as urlreverse
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
@ -62,10 +63,9 @@ def next_approved_revision(rev):
|
||||||
return "%#02d" % (int(m.group('major')) + 1)
|
return "%#02d" % (int(m.group('major')) + 1)
|
||||||
|
|
||||||
def read_charter_text(doc):
|
def read_charter_text(doc):
|
||||||
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (doc.canonical_name(), doc.rev))
|
filename = Path(settings.CHARTER_PATH) / f"{doc.name}-{doc.rev}.txt"
|
||||||
try:
|
try:
|
||||||
with io.open(filename, 'r') as f:
|
return filename.read_text()
|
||||||
return f.read()
|
|
||||||
except IOError:
|
except IOError:
|
||||||
return "Error: couldn't read charter text"
|
return "Error: couldn't read charter text"
|
||||||
|
|
||||||
|
@ -92,8 +92,8 @@ def change_group_state_after_charter_approval(group, by):
|
||||||
def fix_charter_revision_after_approval(charter, by):
|
def fix_charter_revision_after_approval(charter, by):
|
||||||
# according to spec, 00-02 becomes 01, so copy file and record new revision
|
# according to spec, 00-02 becomes 01, so copy file and record new revision
|
||||||
try:
|
try:
|
||||||
old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), charter.rev))
|
old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.name, charter.rev))
|
||||||
new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), next_approved_revision(charter.rev)))
|
new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.name, next_approved_revision(charter.rev)))
|
||||||
shutil.copy(old, new)
|
shutil.copy(old, new)
|
||||||
except IOError:
|
except IOError:
|
||||||
log("There was an error copying %s to %s" % (old, new))
|
log("There was an error copying %s to %s" % (old, new))
|
||||||
|
@ -101,7 +101,7 @@ def fix_charter_revision_after_approval(charter, by):
|
||||||
events = []
|
events = []
|
||||||
e = NewRevisionDocEvent(doc=charter, by=by, type="new_revision")
|
e = NewRevisionDocEvent(doc=charter, by=by, type="new_revision")
|
||||||
e.rev = next_approved_revision(charter.rev)
|
e.rev = next_approved_revision(charter.rev)
|
||||||
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), e.rev)
|
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.name, e.rev)
|
||||||
e.save()
|
e.save()
|
||||||
events.append(e)
|
events.append(e)
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ def fill_in_document_table_attributes(docs, have_telechat_date=False):
|
||||||
# emulate canonical name which is used by a lot of the utils
|
# emulate canonical name which is used by a lot of the utils
|
||||||
# d.canonical_name = wrap_value(rfc_aliases[d.pk] if d.pk in rfc_aliases else d.name)
|
# d.canonical_name = wrap_value(rfc_aliases[d.pk] if d.pk in rfc_aliases else d.name)
|
||||||
|
|
||||||
if d.rfc_number() != None and d.latest_event_cache["published_rfc"]:
|
if d.type_id == "rfc" and d.latest_event_cache["published_rfc"]:
|
||||||
d.latest_revision_date = d.latest_event_cache["published_rfc"].time
|
d.latest_revision_date = d.latest_event_cache["published_rfc"].time
|
||||||
elif d.latest_event_cache["new_revision"]:
|
elif d.latest_event_cache["new_revision"]:
|
||||||
d.latest_revision_date = d.latest_event_cache["new_revision"].time
|
d.latest_revision_date = d.latest_event_cache["new_revision"].time
|
||||||
|
@ -164,12 +164,11 @@ def fill_in_document_table_attributes(docs, have_telechat_date=False):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for rel in xed_by:
|
for rel in xed_by:
|
||||||
d = doc_dict[rel.target.document.id]
|
d = doc_dict[rel.target.id]
|
||||||
s = rel_rfc_aliases[rel.source_id]
|
|
||||||
if rel.relationship_id == "obs":
|
if rel.relationship_id == "obs":
|
||||||
d.obsoleted_by_list.append(s)
|
d.obsoleted_by_list.append(rel.source)
|
||||||
elif rel.relationship_id == "updates":
|
elif rel.relationship_id == "updates":
|
||||||
d.updated_by_list.append(s)
|
d.updated_by_list.append(rel.source)
|
||||||
|
|
||||||
def augment_docs_with_related_docs_info(docs):
|
def augment_docs_with_related_docs_info(docs):
|
||||||
"""Augment all documents with related documents information.
|
"""Augment all documents with related documents information.
|
||||||
|
@ -217,7 +216,7 @@ def prepare_document_table(request, docs, query=None, max_results=200):
|
||||||
|
|
||||||
res = []
|
res = []
|
||||||
|
|
||||||
rfc_num = d.rfc_number()
|
rfc_num = num(d.rfc_number) if d.rfc_number else None
|
||||||
|
|
||||||
if d.type_id == "draft":
|
if d.type_id == "draft":
|
||||||
res.append(num(["Active", "Expired", "Replaced", "Withdrawn", "RFC"].index(d.search_heading.split()[0])))
|
res.append(num(["Active", "Expired", "Replaced", "Withdrawn", "RFC"].index(d.search_heading.split()[0])))
|
||||||
|
@ -232,23 +231,23 @@ def prepare_document_table(request, docs, query=None, max_results=200):
|
||||||
elif sort_key == "date":
|
elif sort_key == "date":
|
||||||
res.append(str(d.latest_revision_date.astimezone(ZoneInfo(settings.TIME_ZONE))))
|
res.append(str(d.latest_revision_date.astimezone(ZoneInfo(settings.TIME_ZONE))))
|
||||||
elif sort_key == "status":
|
elif sort_key == "status":
|
||||||
if rfc_num != None:
|
if rfc_num is not None:
|
||||||
res.append(num(rfc_num))
|
res.append(rfc_num)
|
||||||
else:
|
else:
|
||||||
res.append(num(d.get_state().order) if d.get_state() else None)
|
res.append(num(d.get_state().order) if d.get_state() else None)
|
||||||
elif sort_key == "ipr":
|
elif sort_key == "ipr":
|
||||||
res.append(len(d.ipr()))
|
res.append(len(d.ipr()))
|
||||||
elif sort_key == "ad":
|
elif sort_key == "ad":
|
||||||
if rfc_num != None:
|
if rfc_num is not None:
|
||||||
res.append(num(rfc_num))
|
res.append(rfc_num)
|
||||||
elif d.get_state_slug() == "active":
|
elif d.get_state_slug() == "active":
|
||||||
if d.get_state("draft-iesg"):
|
if d.get_state("draft-iesg"):
|
||||||
res.append(d.get_state("draft-iesg").order)
|
res.append(d.get_state("draft-iesg").order)
|
||||||
else:
|
else:
|
||||||
res.append(0)
|
res.append(0)
|
||||||
else:
|
else:
|
||||||
if rfc_num != None:
|
if rfc_num is not None:
|
||||||
res.append(num(rfc_num))
|
res.append(rfc_num)
|
||||||
else:
|
else:
|
||||||
res.append(d.canonical_name())
|
res.append(d.canonical_name())
|
||||||
|
|
||||||
|
|
|
@ -953,7 +953,13 @@ def approve_downrefs(request, name):
|
||||||
|
|
||||||
login = request.user.person
|
login = request.user.person
|
||||||
|
|
||||||
downrefs_to_rfc = [rel for rel in doc.relateddocument_set.all() if rel.is_downref() and not rel.is_approved_downref() and rel.target.document.is_rfc()]
|
downrefs_to_rfc = [
|
||||||
|
rel
|
||||||
|
for rel in doc.relateddocument_set.all()
|
||||||
|
if rel.is_downref()
|
||||||
|
and not rel.is_approved_downref()
|
||||||
|
and rel.target.type_id == "rfc"
|
||||||
|
]
|
||||||
|
|
||||||
downrefs_to_rfc_qs = RelatedDocument.objects.filter(pk__in=[r.pk for r in downrefs_to_rfc])
|
downrefs_to_rfc_qs = RelatedDocument.objects.filter(pk__in=[r.pk for r in downrefs_to_rfc])
|
||||||
|
|
||||||
|
@ -968,12 +974,12 @@ def approve_downrefs(request, name):
|
||||||
c = DocEvent(type="downref_approved", doc=rel.source,
|
c = DocEvent(type="downref_approved", doc=rel.source,
|
||||||
rev=rel.source.rev, by=login)
|
rev=rel.source.rev, by=login)
|
||||||
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
||||||
rel.target.document.rfc_number(), rel.source, rel.source.rev)
|
rel.target.rfc_number, rel.source, rel.source.rev)
|
||||||
c.save()
|
c.save()
|
||||||
c = DocEvent(type="downref_approved", doc=rel.target.document,
|
c = DocEvent(type="downref_approved", doc=rel.target,
|
||||||
rev=rel.target.document.rev, by=login)
|
rev=rel.target.rev, by=login)
|
||||||
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
||||||
rel.target.document.rfc_number(), rel.source, rel.source.rev)
|
rel.target.rfc_number, rel.source, rel.source.rev)
|
||||||
c.save()
|
c.save()
|
||||||
|
|
||||||
return HttpResponseRedirect(doc.get_absolute_url())
|
return HttpResponseRedirect(doc.get_absolute_url())
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import io
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect, HttpResponseNotFound, Http404
|
from django.http import HttpResponseRedirect, HttpResponseNotFound, Http404
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.urls import reverse as urlreverse
|
from django.urls import reverse as urlreverse
|
||||||
|
@ -32,7 +32,7 @@ from ietf.doc.utils_charter import ( historic_milestones_for_charter,
|
||||||
generate_ballot_writeup, generate_issue_ballot_mail, next_revision,
|
generate_ballot_writeup, generate_issue_ballot_mail, next_revision,
|
||||||
derive_new_work_text,
|
derive_new_work_text,
|
||||||
change_group_state_after_charter_approval, fix_charter_revision_after_approval,
|
change_group_state_after_charter_approval, fix_charter_revision_after_approval,
|
||||||
split_charter_name)
|
split_charter_name, charter_name_for_group)
|
||||||
from ietf.doc.mails import email_state_changed, email_charter_internal_review
|
from ietf.doc.mails import email_state_changed, email_charter_internal_review
|
||||||
from ietf.group.mails import email_admin_re_charter
|
from ietf.group.mails import email_admin_re_charter
|
||||||
from ietf.group.models import Group, ChangeStateGroupEvent, MilestoneGroupEvent
|
from ietf.group.models import Group, ChangeStateGroupEvent, MilestoneGroupEvent
|
||||||
|
@ -42,6 +42,7 @@ from ietf.ietfauth.utils import has_role, role_required
|
||||||
from ietf.name.models import GroupStateName
|
from ietf.name.models import GroupStateName
|
||||||
from ietf.person.models import Person
|
from ietf.person.models import Person
|
||||||
from ietf.utils.history import find_history_active_at
|
from ietf.utils.history import find_history_active_at
|
||||||
|
from ietf.utils.log import assertion
|
||||||
from ietf.utils.mail import send_mail_preformatted
|
from ietf.utils.mail import send_mail_preformatted
|
||||||
from ietf.utils.textupload import get_cleaned_text_file_content
|
from ietf.utils.textupload import get_cleaned_text_file_content
|
||||||
from ietf.utils.response import permission_denied
|
from ietf.utils.response import permission_denied
|
||||||
|
@ -362,38 +363,41 @@ class UploadForm(forms.Form):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def submit(request, name, option=None):
|
def submit(request, name, option=None):
|
||||||
if not name.startswith('charter-'):
|
# Charters are named "charter-<ietf|irtf>-<group acronym>"
|
||||||
raise Http404
|
|
||||||
|
|
||||||
charter = Document.objects.filter(type="charter", name=name).first()
|
charter = Document.objects.filter(type="charter", name=name).first()
|
||||||
if charter:
|
if charter:
|
||||||
group = charter.group
|
group = charter.group
|
||||||
charter_canonical_name = charter.canonical_name()
|
assertion("charter.name == charter_name_for_group(group)")
|
||||||
charter_rev = charter.rev
|
charter_rev = charter.rev
|
||||||
else:
|
else:
|
||||||
top_org, group_acronym = split_charter_name(name)
|
top_org, group_acronym = split_charter_name(name)
|
||||||
group = get_object_or_404(Group, acronym=group_acronym)
|
group = get_object_or_404(Group, acronym=group_acronym)
|
||||||
charter_canonical_name = name
|
if name != charter_name_for_group(group):
|
||||||
|
raise Http404 # do not allow creation of misnamed charters
|
||||||
charter_rev = "00-00"
|
charter_rev = "00-00"
|
||||||
|
|
||||||
if not can_manage_all_groups_of_type(request.user, group.type_id) or not group.features.has_chartering_process:
|
if (
|
||||||
|
not can_manage_all_groups_of_type(request.user, group.type_id)
|
||||||
|
or not group.features.has_chartering_process
|
||||||
|
):
|
||||||
permission_denied(request, "You don't have permission to access this view.")
|
permission_denied(request, "You don't have permission to access this view.")
|
||||||
|
|
||||||
|
charter_filename = Path(settings.CHARTER_PATH) / f"{name}-{charter_rev}.txt"
|
||||||
path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter_canonical_name, charter_rev))
|
not_uploaded_yet = charter_rev.endswith("-00") and not charter_filename.exists()
|
||||||
not_uploaded_yet = charter_rev.endswith("-00") and not os.path.exists(path)
|
|
||||||
|
|
||||||
if not_uploaded_yet or not charter:
|
if not_uploaded_yet or not charter:
|
||||||
# this case is special - we recently chartered or rechartered and have no file yet
|
# this case is special - we recently chartered or rechartered and have no file yet
|
||||||
next_rev = charter_rev
|
next_rev = charter_rev
|
||||||
else:
|
else:
|
||||||
# search history for possible collisions with abandoned efforts
|
# search history for possible collisions with abandoned efforts
|
||||||
prev_revs = list(charter.history_set.order_by('-time').values_list('rev', flat=True))
|
prev_revs = list(
|
||||||
|
charter.history_set.order_by("-time").values_list("rev", flat=True)
|
||||||
|
)
|
||||||
next_rev = next_revision(charter.rev)
|
next_rev = next_revision(charter.rev)
|
||||||
while next_rev in prev_revs:
|
while next_rev in prev_revs:
|
||||||
next_rev = next_revision(next_rev)
|
next_rev = next_revision(next_rev)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
form = UploadForm(request.POST, request.FILES)
|
form = UploadForm(request.POST, request.FILES)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# Also save group history so we can search for it
|
# Also save group history so we can search for it
|
||||||
|
@ -408,9 +412,11 @@ def submit(request, name, option=None):
|
||||||
abstract=group.name,
|
abstract=group.name,
|
||||||
rev=next_rev,
|
rev=next_rev,
|
||||||
)
|
)
|
||||||
DocAlias.objects.create(name=charter.name).docs.add(charter)
|
DocAlias.objects.create(name=name).docs.add(charter)
|
||||||
|
|
||||||
charter.set_state(State.objects.get(used=True, type="charter", slug="notrev"))
|
charter.set_state(
|
||||||
|
State.objects.get(used=True, type="charter", slug="notrev")
|
||||||
|
)
|
||||||
|
|
||||||
group.charter = charter
|
group.charter = charter
|
||||||
group.save()
|
group.save()
|
||||||
|
@ -418,56 +424,74 @@ def submit(request, name, option=None):
|
||||||
charter.rev = next_rev
|
charter.rev = next_rev
|
||||||
|
|
||||||
events = []
|
events = []
|
||||||
e = NewRevisionDocEvent(doc=charter, by=request.user.person, type="new_revision")
|
e = NewRevisionDocEvent(
|
||||||
e.desc = "New version available: <b>%s-%s.txt</b>" % (charter.canonical_name(), charter.rev)
|
doc=charter, by=request.user.person, type="new_revision"
|
||||||
|
)
|
||||||
|
e.desc = "New version available: <b>%s-%s.txt</b>" % (
|
||||||
|
charter.name,
|
||||||
|
charter.rev,
|
||||||
|
)
|
||||||
e.rev = charter.rev
|
e.rev = charter.rev
|
||||||
e.save()
|
e.save()
|
||||||
events.append(e)
|
events.append(e)
|
||||||
|
|
||||||
# Save file on disk
|
# Save file on disk
|
||||||
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter.canonical_name(), charter.rev))
|
charter_filename = charter_filename.with_name(
|
||||||
with io.open(filename, 'w', encoding='utf-8') as destination:
|
f"{name}-{charter.rev}.txt"
|
||||||
if form.cleaned_data['txt']:
|
) # update rev
|
||||||
destination.write(form.cleaned_data['txt'])
|
with charter_filename.open("w", encoding="utf-8") as destination:
|
||||||
|
if form.cleaned_data["txt"]:
|
||||||
|
destination.write(form.cleaned_data["txt"])
|
||||||
else:
|
else:
|
||||||
destination.write(form.cleaned_data['content'])
|
destination.write(form.cleaned_data["content"])
|
||||||
|
|
||||||
if option in ['initcharter','recharter'] and charter.ad == None:
|
if option in ["initcharter", "recharter"] and charter.ad == None:
|
||||||
charter.ad = getattr(group.ad_role(),'person',None)
|
charter.ad = getattr(group.ad_role(), "person", None)
|
||||||
|
|
||||||
charter.save_with_history(events)
|
charter.save_with_history(events)
|
||||||
|
|
||||||
if option:
|
if option:
|
||||||
return redirect('ietf.doc.views_charter.change_state', name=charter.name, option=option)
|
return redirect(
|
||||||
|
"ietf.doc.views_charter.change_state",
|
||||||
|
name=charter.name,
|
||||||
|
option=option,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return redirect("ietf.doc.views_doc.document_main", name=charter.name)
|
return redirect("ietf.doc.views_doc.document_main", name=charter.name)
|
||||||
else:
|
else:
|
||||||
init = { "content": "" }
|
init = {"content": ""}
|
||||||
|
|
||||||
if not_uploaded_yet and charter:
|
if not_uploaded_yet and charter:
|
||||||
# use text from last approved revision
|
# use text from last approved revision
|
||||||
last_approved = charter.rev.split("-")[0]
|
last_approved = charter.rev.split("-")[0]
|
||||||
h = charter.history_set.filter(rev=last_approved).order_by("-time", "-id").first()
|
h = (
|
||||||
|
charter.history_set.filter(rev=last_approved)
|
||||||
|
.order_by("-time", "-id")
|
||||||
|
.first()
|
||||||
|
)
|
||||||
if h:
|
if h:
|
||||||
charter_canonical_name = h.canonical_name()
|
assertion("h.name == charter_name_for_group(group)")
|
||||||
charter_rev = h.rev
|
charter_filename = charter_filename.with_name(
|
||||||
|
f"{name}-{h.rev}.txt"
|
||||||
filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter_canonical_name, charter_rev))
|
) # update rev
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with io.open(filename, 'r') as f:
|
init["content"] = charter_filename.read_text()
|
||||||
init["content"] = f.read()
|
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
form = UploadForm(initial=init)
|
form = UploadForm(initial=init)
|
||||||
fill_in_charter_info(group)
|
fill_in_charter_info(group)
|
||||||
|
|
||||||
return render(request, 'doc/charter/submit.html', {
|
return render(
|
||||||
'form': form,
|
request,
|
||||||
'next_rev': next_rev,
|
"doc/charter/submit.html",
|
||||||
'group': group,
|
{
|
||||||
'name': name,
|
"form": form,
|
||||||
})
|
"next_rev": next_rev,
|
||||||
|
"group": group,
|
||||||
|
"name": name,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
class ActionAnnouncementTextForm(forms.Form):
|
class ActionAnnouncementTextForm(forms.Form):
|
||||||
announcement_text = forms.CharField(widget=forms.Textarea, required=True, strip=False)
|
announcement_text = forms.CharField(widget=forms.Textarea, required=True, strip=False)
|
||||||
|
@ -484,7 +508,7 @@ class ReviewAnnouncementTextForm(forms.Form):
|
||||||
return self.cleaned_data["announcement_text"].replace("\r", "")
|
return self.cleaned_data["announcement_text"].replace("\r", "")
|
||||||
|
|
||||||
|
|
||||||
@role_required('Area Director','Secretariat')
|
@role_required("Area Director", "Secretariat")
|
||||||
def review_announcement_text(request, name):
|
def review_announcement_text(request, name):
|
||||||
"""Editing of review announcement text"""
|
"""Editing of review announcement text"""
|
||||||
charter = get_object_or_404(Document, type="charter", name=name)
|
charter = get_object_or_404(Document, type="charter", name=name)
|
||||||
|
@ -493,7 +517,9 @@ def review_announcement_text(request, name):
|
||||||
by = request.user.person
|
by = request.user.person
|
||||||
|
|
||||||
existing = charter.latest_event(WriteupDocEvent, type="changed_review_announcement")
|
existing = charter.latest_event(WriteupDocEvent, type="changed_review_announcement")
|
||||||
existing_new_work = charter.latest_event(WriteupDocEvent, type="changed_new_work_text")
|
existing_new_work = charter.latest_event(
|
||||||
|
WriteupDocEvent, type="changed_new_work_text"
|
||||||
|
)
|
||||||
|
|
||||||
if not existing:
|
if not existing:
|
||||||
(existing, existing_new_work) = default_review_text(group, charter, by)
|
(existing, existing_new_work) = default_review_text(group, charter, by)
|
||||||
|
@ -506,19 +532,23 @@ def review_announcement_text(request, name):
|
||||||
existing_new_work.by = by
|
existing_new_work.by = by
|
||||||
existing_new_work.type = "changed_new_work_text"
|
existing_new_work.type = "changed_new_work_text"
|
||||||
existing_new_work.desc = "%s review text was changed" % group.type.name
|
existing_new_work.desc = "%s review text was changed" % group.type.name
|
||||||
existing_new_work.text = derive_new_work_text(existing.text,group)
|
existing_new_work.text = derive_new_work_text(existing.text, group)
|
||||||
existing_new_work.time = timezone.now()
|
existing_new_work.time = timezone.now()
|
||||||
|
|
||||||
form = ReviewAnnouncementTextForm(initial=dict(announcement_text=escape(existing.text),new_work_text=escape(existing_new_work.text)))
|
form = ReviewAnnouncementTextForm(
|
||||||
|
initial=dict(
|
||||||
|
announcement_text=escape(existing.text),
|
||||||
|
new_work_text=escape(existing_new_work.text),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
form = ReviewAnnouncementTextForm(request.POST)
|
form = ReviewAnnouncementTextForm(request.POST)
|
||||||
if "save_text" in request.POST and form.is_valid():
|
if "save_text" in request.POST and form.is_valid():
|
||||||
|
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
events = []
|
events = []
|
||||||
|
|
||||||
t = form.cleaned_data['announcement_text']
|
t = form.cleaned_data["announcement_text"]
|
||||||
if t != existing.text:
|
if t != existing.text:
|
||||||
e = WriteupDocEvent(doc=charter, rev=charter.rev)
|
e = WriteupDocEvent(doc=charter, rev=charter.rev)
|
||||||
e.by = by
|
e.by = by
|
||||||
|
@ -532,11 +562,11 @@ def review_announcement_text(request, name):
|
||||||
existing.save()
|
existing.save()
|
||||||
events.append(existing)
|
events.append(existing)
|
||||||
|
|
||||||
t = form.cleaned_data['new_work_text']
|
t = form.cleaned_data["new_work_text"]
|
||||||
if t != existing_new_work.text:
|
if t != existing_new_work.text:
|
||||||
e = WriteupDocEvent(doc=charter, rev=charter.rev)
|
e = WriteupDocEvent(doc=charter, rev=charter.rev)
|
||||||
e.by = by
|
e.by = by
|
||||||
e.type = "changed_new_work_text"
|
e.type = "changed_new_work_text"
|
||||||
e.desc = "%s new work message text was changed" % (group.type.name)
|
e.desc = "%s new work message text was changed" % (group.type.name)
|
||||||
e.text = t
|
e.text = t
|
||||||
e.time = now
|
e.time = now
|
||||||
|
@ -549,33 +579,71 @@ def review_announcement_text(request, name):
|
||||||
charter.save_with_history(events)
|
charter.save_with_history(events)
|
||||||
|
|
||||||
if request.GET.get("next", "") == "approve":
|
if request.GET.get("next", "") == "approve":
|
||||||
return redirect('ietf.doc.views_charter.approve', name=charter.canonical_name())
|
return redirect(
|
||||||
|
"ietf.doc.views_charter.approve", name=charter.name
|
||||||
|
)
|
||||||
|
|
||||||
return redirect('ietf.doc.views_doc.document_writeup', name=charter.canonical_name())
|
return redirect(
|
||||||
|
"ietf.doc.views_doc.document_writeup", name=charter.name
|
||||||
|
)
|
||||||
|
|
||||||
if "regenerate_text" in request.POST:
|
if "regenerate_text" in request.POST:
|
||||||
(existing, existing_new_work) = default_review_text(group, charter, by)
|
(existing, existing_new_work) = default_review_text(group, charter, by)
|
||||||
existing.save()
|
existing.save()
|
||||||
existing_new_work.save()
|
existing_new_work.save()
|
||||||
form = ReviewAnnouncementTextForm(initial=dict(announcement_text=escape(existing.text),
|
form = ReviewAnnouncementTextForm(
|
||||||
new_work_text=escape(existing_new_work.text)))
|
initial=dict(
|
||||||
|
announcement_text=escape(existing.text),
|
||||||
|
new_work_text=escape(existing_new_work.text),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if any(x in request.POST for x in ['send_annc_only','send_nw_only','send_both']) and form.is_valid():
|
if (
|
||||||
if any(x in request.POST for x in ['send_annc_only','send_both']):
|
any(
|
||||||
parsed_msg = send_mail_preformatted(request, form.cleaned_data['announcement_text'])
|
x in request.POST
|
||||||
messages.success(request, "The email To: '%s' with Subject: '%s' has been sent." % (parsed_msg["To"],parsed_msg["Subject"],))
|
for x in ["send_annc_only", "send_nw_only", "send_both"]
|
||||||
if any(x in request.POST for x in ['send_nw_only','send_both']):
|
)
|
||||||
parsed_msg = send_mail_preformatted(request, form.cleaned_data['new_work_text'])
|
and form.is_valid()
|
||||||
messages.success(request, "The email To: '%s' with Subject: '%s' has been sent." % (parsed_msg["To"],parsed_msg["Subject"],))
|
):
|
||||||
return redirect('ietf.doc.views_doc.document_writeup', name=charter.name)
|
if any(x in request.POST for x in ["send_annc_only", "send_both"]):
|
||||||
|
parsed_msg = send_mail_preformatted(
|
||||||
|
request, form.cleaned_data["announcement_text"]
|
||||||
|
)
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
"The email To: '%s' with Subject: '%s' has been sent."
|
||||||
|
% (
|
||||||
|
parsed_msg["To"],
|
||||||
|
parsed_msg["Subject"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if any(x in request.POST for x in ["send_nw_only", "send_both"]):
|
||||||
|
parsed_msg = send_mail_preformatted(
|
||||||
|
request, form.cleaned_data["new_work_text"]
|
||||||
|
)
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
"The email To: '%s' with Subject: '%s' has been sent."
|
||||||
|
% (
|
||||||
|
parsed_msg["To"],
|
||||||
|
parsed_msg["Subject"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return redirect("ietf.doc.views_doc.document_writeup", name=charter.name)
|
||||||
|
|
||||||
return render(request, 'doc/charter/review_announcement_text.html',
|
return render(
|
||||||
dict(charter=charter,
|
request,
|
||||||
back_url=urlreverse('ietf.doc.views_doc.document_writeup', kwargs=dict(name=charter.name)),
|
"doc/charter/review_announcement_text.html",
|
||||||
announcement_text_form=form,
|
dict(
|
||||||
))
|
charter=charter,
|
||||||
|
back_url=urlreverse(
|
||||||
|
"ietf.doc.views_doc.document_writeup", kwargs=dict(name=charter.name)
|
||||||
|
),
|
||||||
|
announcement_text_form=form,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
@role_required('Area Director','Secretariat')
|
@role_required("Area Director", "Secretariat")
|
||||||
def action_announcement_text(request, name):
|
def action_announcement_text(request, name):
|
||||||
"""Editing of action announcement text"""
|
"""Editing of action announcement text"""
|
||||||
charter = get_object_or_404(Document, type="charter", name=name)
|
charter = get_object_or_404(Document, type="charter", name=name)
|
||||||
|
@ -590,16 +658,18 @@ def action_announcement_text(request, name):
|
||||||
if not existing:
|
if not existing:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
form = ActionAnnouncementTextForm(initial=dict(announcement_text=escape(existing.text)))
|
form = ActionAnnouncementTextForm(
|
||||||
|
initial=dict(announcement_text=escape(existing.text))
|
||||||
|
)
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == "POST":
|
||||||
form = ActionAnnouncementTextForm(request.POST)
|
form = ActionAnnouncementTextForm(request.POST)
|
||||||
if "save_text" in request.POST and form.is_valid():
|
if "save_text" in request.POST and form.is_valid():
|
||||||
t = form.cleaned_data['announcement_text']
|
t = form.cleaned_data["announcement_text"]
|
||||||
if t != existing.text:
|
if t != existing.text:
|
||||||
e = WriteupDocEvent(doc=charter, rev=charter.rev)
|
e = WriteupDocEvent(doc=charter, rev=charter.rev)
|
||||||
e.by = by
|
e.by = by
|
||||||
e.type = "changed_action_announcement"
|
e.type = "changed_action_announcement"
|
||||||
e.desc = "%s action text was changed" % group.type.name
|
e.desc = "%s action text was changed" % group.type.name
|
||||||
e.text = t
|
e.text = t
|
||||||
e.save()
|
e.save()
|
||||||
|
@ -607,25 +677,46 @@ def action_announcement_text(request, name):
|
||||||
existing.save()
|
existing.save()
|
||||||
|
|
||||||
if request.GET.get("next", "") == "approve":
|
if request.GET.get("next", "") == "approve":
|
||||||
return redirect('ietf.doc.views_charter.approve', name=charter.canonical_name())
|
return redirect(
|
||||||
|
"ietf.doc.views_charter.approve", name=charter.name
|
||||||
|
)
|
||||||
|
|
||||||
return redirect('ietf.doc.views_doc.document_writeup', name=charter.canonical_name())
|
return redirect(
|
||||||
|
"ietf.doc.views_doc.document_writeup", name=charter.name
|
||||||
|
)
|
||||||
|
|
||||||
if "regenerate_text" in request.POST:
|
if "regenerate_text" in request.POST:
|
||||||
e = default_action_text(group, charter, by)
|
e = default_action_text(group, charter, by)
|
||||||
e.save()
|
e.save()
|
||||||
form = ActionAnnouncementTextForm(initial=dict(announcement_text=escape(e.text)))
|
form = ActionAnnouncementTextForm(
|
||||||
|
initial=dict(announcement_text=escape(e.text))
|
||||||
|
)
|
||||||
|
|
||||||
if "send_text" in request.POST and form.is_valid():
|
if "send_text" in request.POST and form.is_valid():
|
||||||
parsed_msg = send_mail_preformatted(request, form.cleaned_data['announcement_text'])
|
parsed_msg = send_mail_preformatted(
|
||||||
messages.success(request, "The email To: '%s' with Subject: '%s' has been sent." % (parsed_msg["To"],parsed_msg["Subject"],))
|
request, form.cleaned_data["announcement_text"]
|
||||||
return redirect('ietf.doc.views_doc.document_writeup', name=charter.name)
|
)
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
"The email To: '%s' with Subject: '%s' has been sent."
|
||||||
|
% (
|
||||||
|
parsed_msg["To"],
|
||||||
|
parsed_msg["Subject"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return redirect("ietf.doc.views_doc.document_writeup", name=charter.name)
|
||||||
|
|
||||||
return render(request, 'doc/charter/action_announcement_text.html',
|
return render(
|
||||||
dict(charter=charter,
|
request,
|
||||||
back_url=urlreverse('ietf.doc.views_doc.document_writeup', kwargs=dict(name=charter.name)),
|
"doc/charter/action_announcement_text.html",
|
||||||
announcement_text_form=form,
|
dict(
|
||||||
))
|
charter=charter,
|
||||||
|
back_url=urlreverse(
|
||||||
|
"ietf.doc.views_doc.document_writeup", kwargs=dict(name=charter.name)
|
||||||
|
),
|
||||||
|
announcement_text_form=form,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
class BallotWriteupForm(forms.Form):
|
class BallotWriteupForm(forms.Form):
|
||||||
ballot_writeup = forms.CharField(widget=forms.Textarea, required=True, strip=False)
|
ballot_writeup = forms.CharField(widget=forms.Textarea, required=True, strip=False)
|
||||||
|
@ -806,33 +897,37 @@ def approve(request, name):
|
||||||
dict(charter=charter,
|
dict(charter=charter,
|
||||||
announcement=escape(announcement)))
|
announcement=escape(announcement)))
|
||||||
|
|
||||||
def charter_with_milestones_txt(request, name, rev):
|
|
||||||
charter = get_object_or_404(Document, type="charter", docalias__name=name)
|
|
||||||
|
|
||||||
revision_event = charter.latest_event(NewRevisionDocEvent, type="new_revision", rev=rev)
|
def charter_with_milestones_txt(request, name, rev):
|
||||||
|
charter = get_object_or_404(Document, type="charter", name=name)
|
||||||
|
|
||||||
|
revision_event = charter.latest_event(
|
||||||
|
NewRevisionDocEvent, type="new_revision", rev=rev
|
||||||
|
)
|
||||||
if not revision_event:
|
if not revision_event:
|
||||||
return HttpResponseNotFound("Revision %s not found in database" % rev)
|
return HttpResponseNotFound("Revision %s not found in database" % rev)
|
||||||
|
|
||||||
# read charter text
|
# read charter text
|
||||||
c = find_history_active_at(charter, revision_event.time) or charter
|
c = find_history_active_at(charter, revision_event.time) or charter
|
||||||
filename = '%s-%s.txt' % (c.canonical_name(), rev)
|
filename = Path(settings.CHARTER_PATH) / f"{c.name}-{rev}.txt"
|
||||||
|
|
||||||
charter_text = ""
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with io.open(os.path.join(settings.CHARTER_PATH, filename), 'r') as f:
|
with filename.open() as f:
|
||||||
charter_text = force_str(f.read(), errors='ignore')
|
charter_text = force_str(f.read(), errors="ignore")
|
||||||
except IOError:
|
except IOError:
|
||||||
charter_text = "Error reading charter text %s" % filename
|
charter_text = f"Error reading charter text {filename.name}"
|
||||||
|
|
||||||
milestones = historic_milestones_for_charter(charter, rev)
|
milestones = historic_milestones_for_charter(charter, rev)
|
||||||
|
|
||||||
# wrap the output nicely
|
# wrap the output nicely
|
||||||
wrapper = textwrap.TextWrapper(initial_indent="", subsequent_indent=" " * 11, width=80, break_long_words=False)
|
wrapper = textwrap.TextWrapper(
|
||||||
|
initial_indent="", subsequent_indent=" " * 11, width=80, break_long_words=False
|
||||||
|
)
|
||||||
for m in milestones:
|
for m in milestones:
|
||||||
m.desc_filled = wrapper.fill(m.desc)
|
m.desc_filled = wrapper.fill(m.desc)
|
||||||
|
|
||||||
return render(request, 'doc/charter/charter_with_milestones.txt',
|
return render(
|
||||||
dict(charter_text=charter_text,
|
request,
|
||||||
milestones=milestones),
|
"doc/charter/charter_with_milestones.txt",
|
||||||
content_type="text/plain; charset=%s"%settings.DEFAULT_CHARSET)
|
dict(charter_text=charter_text, milestones=milestones),
|
||||||
|
content_type="text/plain; charset=%s" % settings.DEFAULT_CHARSET,
|
||||||
|
)
|
||||||
|
|
|
@ -113,7 +113,7 @@ def send_conflict_review_ad_changed_email(request, review, event):
|
||||||
by = request.user.person,
|
by = request.user.person,
|
||||||
event = event,
|
event = event,
|
||||||
review = review,
|
review = review,
|
||||||
reviewed_doc = review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
reviewed_doc = review.relateddocument_set.get(relationship__slug='conflrev').target,
|
||||||
review_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
review_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -128,7 +128,7 @@ def send_conflict_review_started_email(request, review):
|
||||||
cc = addrs.cc,
|
cc = addrs.cc,
|
||||||
by = request.user.person,
|
by = request.user.person,
|
||||||
review = review,
|
review = review,
|
||||||
reviewed_doc = review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
reviewed_doc = review.relateddocument_set.get(relationship__slug='conflrev').target,
|
||||||
review_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
review_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -137,7 +137,7 @@ def send_conflict_review_started_email(request, review):
|
||||||
|
|
||||||
addrs = gather_address_lists('conflrev_requested_iana',doc=review).as_strings(compact=False)
|
addrs = gather_address_lists('conflrev_requested_iana',doc=review).as_strings(compact=False)
|
||||||
email_iana(request,
|
email_iana(request,
|
||||||
review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
review.relateddocument_set.get(relationship__slug='conflrev').target,
|
||||||
addrs.to,
|
addrs.to,
|
||||||
msg,
|
msg,
|
||||||
cc=addrs.cc)
|
cc=addrs.cc)
|
||||||
|
@ -155,7 +155,7 @@ def send_conflict_eval_email(request,review):
|
||||||
send_mail_preformatted(request,msg,override=override)
|
send_mail_preformatted(request,msg,override=override)
|
||||||
addrs = gather_address_lists('ballot_issued_iana',doc=review).as_strings()
|
addrs = gather_address_lists('ballot_issued_iana',doc=review).as_strings()
|
||||||
email_iana(request,
|
email_iana(request,
|
||||||
review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
review.relateddocument_set.get(relationship__slug='conflrev').target,
|
||||||
addrs.to,
|
addrs.to,
|
||||||
msg,
|
msg,
|
||||||
addrs.cc)
|
addrs.cc)
|
||||||
|
@ -247,7 +247,7 @@ def submit(request, name):
|
||||||
{'form': form,
|
{'form': form,
|
||||||
'next_rev': next_rev,
|
'next_rev': next_rev,
|
||||||
'review' : review,
|
'review' : review,
|
||||||
'conflictdoc' : review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
'conflictdoc' : review.relateddocument_set.get(relationship__slug='conflrev').target,
|
||||||
})
|
})
|
||||||
|
|
||||||
@role_required("Area Director", "Secretariat")
|
@role_required("Area Director", "Secretariat")
|
||||||
|
@ -275,7 +275,7 @@ def edit_ad(request, name):
|
||||||
form = AdForm(initial=init)
|
form = AdForm(initial=init)
|
||||||
|
|
||||||
|
|
||||||
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document
|
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target
|
||||||
titletext = 'the conflict review of %s-%s' % (conflictdoc.canonical_name(),conflictdoc.rev)
|
titletext = 'the conflict review of %s-%s' % (conflictdoc.canonical_name(),conflictdoc.rev)
|
||||||
return render(request, 'doc/change_ad.html',
|
return render(request, 'doc/change_ad.html',
|
||||||
{'form': form,
|
{'form': form,
|
||||||
|
@ -287,7 +287,7 @@ def edit_ad(request, name):
|
||||||
def default_approval_text(review):
|
def default_approval_text(review):
|
||||||
|
|
||||||
current_text = review.text_or_error() # pyflakes:ignore
|
current_text = review.text_or_error() # pyflakes:ignore
|
||||||
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document
|
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target
|
||||||
if conflictdoc.stream_id=='ise':
|
if conflictdoc.stream_id=='ise':
|
||||||
receiver = 'Independent Submissions Editor'
|
receiver = 'Independent Submissions Editor'
|
||||||
elif conflictdoc.stream_id=='irtf':
|
elif conflictdoc.stream_id=='irtf':
|
||||||
|
@ -365,7 +365,7 @@ def approve_conflict_review(request, name):
|
||||||
return render(request, 'doc/conflict_review/approve.html',
|
return render(request, 'doc/conflict_review/approve.html',
|
||||||
dict(
|
dict(
|
||||||
review = review,
|
review = review,
|
||||||
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target,
|
||||||
form = form,
|
form = form,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
@ -416,7 +416,7 @@ def start_review_sanity_check(request, name):
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
# sanity check that there's not already a conflict review document for this document
|
# sanity check that there's not already a conflict review document for this document
|
||||||
if [ rel.source for alias in doc_to_review.docalias.all() for rel in alias.relateddocument_set.filter(relationship='conflrev') ]:
|
if [ rel.source for rel in doc_to_review.targets_related.filter(relationship='conflrev') ]:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
return doc_to_review
|
return doc_to_review
|
||||||
|
@ -452,7 +452,7 @@ def build_conflict_review_document(login, doc_to_review, ad, notify, create_in_s
|
||||||
DocAlias.objects.create( name=review_name).docs.add( conflict_review )
|
DocAlias.objects.create( name=review_name).docs.add( conflict_review )
|
||||||
|
|
||||||
|
|
||||||
conflict_review.relateddocument_set.create(target=DocAlias.objects.get(name=doc_to_review.name),relationship_id='conflrev')
|
conflict_review.relateddocument_set.create(target=doc_to_review, relationship_id='conflrev')
|
||||||
|
|
||||||
c = DocEvent(type="added_comment", doc=conflict_review, rev=conflict_review.rev, by=login)
|
c = DocEvent(type="added_comment", doc=conflict_review, rev=conflict_review.rev, by=login)
|
||||||
c.desc = "IETF conflict review requested"
|
c.desc = "IETF conflict review requested"
|
||||||
|
|
|
@ -54,13 +54,13 @@ from django.contrib.staticfiles import finders
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.doc.models import ( Document, DocAlias, DocHistory, DocEvent, BallotDocEvent, BallotType,
|
from ietf.doc.models import ( Document, DocHistory, DocEvent, BallotDocEvent, BallotType,
|
||||||
ConsensusDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent, IanaExpertDocEvent,
|
ConsensusDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent, IanaExpertDocEvent,
|
||||||
IESG_BALLOT_ACTIVE_STATES, STATUSCHANGE_RELATIONS, DocumentActionHolder, DocumentAuthor,
|
IESG_BALLOT_ACTIVE_STATES, STATUSCHANGE_RELATIONS, DocumentActionHolder, DocumentAuthor,
|
||||||
RelatedDocument, RelatedDocHistory)
|
RelatedDocument, RelatedDocHistory)
|
||||||
from ietf.doc.utils import (augment_events_with_revision,
|
from ietf.doc.utils import (augment_events_with_revision,
|
||||||
can_adopt_draft, can_unadopt_draft, get_chartering_type, get_tags_for_stream_id,
|
can_adopt_draft, can_unadopt_draft, get_chartering_type, get_tags_for_stream_id,
|
||||||
needed_ballot_positions, nice_consensus, prettify_std_name, update_telechat, has_same_ballot,
|
needed_ballot_positions, nice_consensus, update_telechat, has_same_ballot,
|
||||||
get_initial_notify, make_notify_changed_event, make_rev_history, default_consensus,
|
get_initial_notify, make_notify_changed_event, make_rev_history, default_consensus,
|
||||||
add_events_message_info, get_unicode_document_content,
|
add_events_message_info, get_unicode_document_content,
|
||||||
augment_docs_and_user_with_user_info, irsg_needed_ballot_positions, add_action_holder_change_event,
|
augment_docs_and_user_with_user_info, irsg_needed_ballot_positions, add_action_holder_change_event,
|
||||||
|
@ -180,13 +180,12 @@ def interesting_doc_relations(doc):
|
||||||
else:
|
else:
|
||||||
raise TypeError("Expected this method to be called with a Document or DocHistory object")
|
raise TypeError("Expected this method to be called with a Document or DocHistory object")
|
||||||
|
|
||||||
that_relationships = STATUSCHANGE_RELATIONS + ('conflrev', 'replaces', 'possibly_replaces', 'updates', 'obs')
|
that_relationships = STATUSCHANGE_RELATIONS + ('conflrev', 'replaces', 'possibly_replaces', 'updates', 'obs', 'became_rfc')
|
||||||
|
|
||||||
that_doc_relationships = ('replaces', 'possibly_replaces', 'updates', 'obs')
|
that_doc_relationships = ('replaces', 'possibly_replaces', 'updates', 'obs', 'became_rfc')
|
||||||
|
|
||||||
# TODO: This returns the relationships in database order, which may not be the order we want to display them in.
|
interesting_relations_that = cls.objects.filter(target=target, relationship__in=that_relationships).select_related('source')
|
||||||
interesting_relations_that = cls.objects.filter(target__docs=target, relationship__in=that_relationships).select_related('source')
|
interesting_relations_that_doc = cls.objects.filter(source=doc, relationship__in=that_doc_relationships).prefetch_related('target')
|
||||||
interesting_relations_that_doc = cls.objects.filter(source=doc, relationship__in=that_doc_relationships).prefetch_related('target__docs')
|
|
||||||
|
|
||||||
return interesting_relations_that, interesting_relations_that_doc
|
return interesting_relations_that, interesting_relations_that_doc
|
||||||
|
|
||||||
|
@ -197,11 +196,10 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
doc = get_object_or_404(Document.objects.select_related(), docalias__name=name)
|
doc = get_object_or_404(Document.objects.select_related(), docalias__name=name)
|
||||||
|
|
||||||
# take care of possible redirections
|
# take care of possible redirections
|
||||||
aliases = DocAlias.objects.filter(docs=doc).values_list("name", flat=True)
|
if document_html is False and rev is None:
|
||||||
if document_html is False and rev==None and doc.type_id == "draft" and not name.startswith("rfc"):
|
became_rfc = next(iter(doc.related_that_doc("became_rfc")), None)
|
||||||
for a in aliases:
|
if became_rfc:
|
||||||
if a.startswith("rfc"):
|
return redirect("ietf.doc.views_doc.document_main", name=became_rfc.name)
|
||||||
return redirect("ietf.doc.views_doc.document_main", name=a)
|
|
||||||
|
|
||||||
revisions = []
|
revisions = []
|
||||||
for h in doc.history_set.order_by("time", "id"):
|
for h in doc.history_set.order_by("time", "id"):
|
||||||
|
@ -243,7 +241,163 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
|
|
||||||
|
|
||||||
# specific document types
|
# specific document types
|
||||||
if doc.type_id == "draft":
|
if doc.type_id == "rfc":
|
||||||
|
split_content = request.COOKIES.get("full_draft", settings.USER_PREFERENCE_DEFAULTS["full_draft"]) == "off"
|
||||||
|
if request.GET.get('include_text') == "0":
|
||||||
|
split_content = True
|
||||||
|
elif request.GET.get('include_text') == "1":
|
||||||
|
split_content = False
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
interesting_relations_that, interesting_relations_that_doc = interesting_doc_relations(doc)
|
||||||
|
|
||||||
|
can_edit = has_role(request.user, ("Area Director", "Secretariat"))
|
||||||
|
can_edit_authors = has_role(request.user, ("Secretariat"))
|
||||||
|
|
||||||
|
stream_slugs = StreamName.objects.values_list("slug", flat=True)
|
||||||
|
# For some reason, AnonymousUser has __iter__, but is not iterable,
|
||||||
|
# which causes problems in the filter() below. Work around this:
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
roles = Role.objects.filter(group__acronym__in=stream_slugs, person__user=request.user)
|
||||||
|
roles = group_features_role_filter(roles, request.user.person, 'docman_roles')
|
||||||
|
else:
|
||||||
|
roles = []
|
||||||
|
|
||||||
|
can_change_stream = bool(can_edit or roles)
|
||||||
|
|
||||||
|
file_urls, found_types = build_file_urls(doc)
|
||||||
|
content = doc.text_or_error() # pyflakes:ignore
|
||||||
|
content = markup_txt.markup(maybe_split(content, split=split_content))
|
||||||
|
|
||||||
|
if not found_types:
|
||||||
|
content = "This RFC is not currently available online."
|
||||||
|
split_content = False
|
||||||
|
elif "txt" not in found_types:
|
||||||
|
content = "This RFC is not available in plain text format."
|
||||||
|
split_content = False
|
||||||
|
|
||||||
|
# mailing list search archive
|
||||||
|
search_archive = "www.ietf.org/mail-archive/web/"
|
||||||
|
if doc.stream_id == "ietf" and group.type_id == "wg" and group.list_archive:
|
||||||
|
search_archive = group.list_archive
|
||||||
|
|
||||||
|
search_archive = quote(search_archive, safe="~")
|
||||||
|
|
||||||
|
# status changes
|
||||||
|
status_changes = []
|
||||||
|
proposed_status_changes = []
|
||||||
|
for r in interesting_relations_that.filter(relationship__in=STATUSCHANGE_RELATIONS):
|
||||||
|
state_slug = r.source.get_state_slug()
|
||||||
|
if state_slug in ('appr-sent', 'appr-pend'):
|
||||||
|
status_changes.append(r)
|
||||||
|
elif state_slug in ('needshep','adrev','iesgeval','defer','appr-pr'):
|
||||||
|
proposed_status_changes.append(r)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
presentations = doc.future_presentations()
|
||||||
|
|
||||||
|
augment_docs_and_user_with_user_info([doc], request.user)
|
||||||
|
|
||||||
|
exp_comment = doc.latest_event(IanaExpertDocEvent,type="comment")
|
||||||
|
iana_experts_comment = exp_comment and exp_comment.desc
|
||||||
|
|
||||||
|
# Do not show the Auth48 URL in the "Additional URLs" section
|
||||||
|
additional_urls = doc.documenturl_set.exclude(tag_id='auth48')
|
||||||
|
|
||||||
|
html = None
|
||||||
|
js = None
|
||||||
|
css = None
|
||||||
|
diff_revisions = None
|
||||||
|
simple_diff_revisions = None
|
||||||
|
if document_html:
|
||||||
|
diff_revisions=get_diff_revisions(request, name, doc if isinstance(doc,Document) else doc.doc)
|
||||||
|
simple_diff_revisions = [t[1] for t in diff_revisions if t[0] == doc.name]
|
||||||
|
simple_diff_revisions.reverse()
|
||||||
|
if rev and rev != doc.rev:
|
||||||
|
# No DocHistory was found matching rev - snapshot will be false
|
||||||
|
# and doc will be a Document object, not a DocHistory
|
||||||
|
snapshot = True
|
||||||
|
doc = doc.fake_history_obj(rev)
|
||||||
|
else:
|
||||||
|
html = doc.html_body()
|
||||||
|
if request.COOKIES.get("pagedeps") == "inline":
|
||||||
|
js = Path(finders.find("ietf/js/document_html.js")).read_text()
|
||||||
|
css = Path(finders.find("ietf/css/document_html_inline.css")).read_text()
|
||||||
|
if html:
|
||||||
|
css += Path(finders.find("ietf/css/document_html_txt.css")).read_text()
|
||||||
|
draft_that_became_rfc = None
|
||||||
|
became_rfc_alias = next(iter(doc.related_that("became_rfc")), None)
|
||||||
|
if became_rfc_alias:
|
||||||
|
draft_that_became_rfc = became_rfc_alias.document
|
||||||
|
# submission
|
||||||
|
submission = ""
|
||||||
|
if group is None:
|
||||||
|
submission = "unknown"
|
||||||
|
elif group.type_id == "individ":
|
||||||
|
submission = "individual"
|
||||||
|
elif group.type_id == "area" and doc.stream_id == "ietf":
|
||||||
|
submission = "individual in %s area" % group.acronym
|
||||||
|
else:
|
||||||
|
if group.features.acts_like_wg and not group.type_id == "edwg":
|
||||||
|
submission = "%s %s" % (group.acronym, group.type)
|
||||||
|
else:
|
||||||
|
submission = group.acronym
|
||||||
|
submission = '<a href="%s">%s</a>' % (group.about_url(), submission)
|
||||||
|
# Should be unreachable?
|
||||||
|
if (
|
||||||
|
draft_that_became_rfc
|
||||||
|
and draft_that_became_rfc.stream_id
|
||||||
|
and draft_that_became_rfc.get_state_slug(
|
||||||
|
"draft-stream-%s" % draft_that_became_rfc.stream_id
|
||||||
|
)
|
||||||
|
== "c-adopt"
|
||||||
|
):
|
||||||
|
submission = "candidate for %s" % submission
|
||||||
|
|
||||||
|
|
||||||
|
# todo replace document_html?
|
||||||
|
return render(request, "doc/document_rfc.html" if document_html is False else "doc/document_html.html",
|
||||||
|
dict(doc=doc,
|
||||||
|
document_html=document_html,
|
||||||
|
css=css,
|
||||||
|
js=js,
|
||||||
|
html=html,
|
||||||
|
group=group,
|
||||||
|
top=top,
|
||||||
|
name=doc.name,
|
||||||
|
content=content,
|
||||||
|
split_content=split_content,
|
||||||
|
revisions=simple_diff_revisions if document_html else revisions,
|
||||||
|
snapshot=snapshot,
|
||||||
|
latest_rev=latest_rev,
|
||||||
|
can_edit=can_edit,
|
||||||
|
can_edit_authors=can_edit_authors,
|
||||||
|
can_change_stream=can_change_stream,
|
||||||
|
rfc_number=doc.rfc_number,
|
||||||
|
draft_name=draft_that_became_rfc and draft_that_became_rfc.name,
|
||||||
|
updates=interesting_relations_that_doc.filter(relationship="updates"),
|
||||||
|
updated_by=interesting_relations_that.filter(relationship="updates"),
|
||||||
|
obsoletes=interesting_relations_that_doc.filter(relationship="obs"),
|
||||||
|
obsoleted_by=interesting_relations_that.filter(relationship="obs"),
|
||||||
|
status_changes=status_changes,
|
||||||
|
proposed_status_changes=proposed_status_changes,
|
||||||
|
has_errata=doc.pk and doc.tags.filter(slug="errata"), # doc.pk == None if using a fake_history_obj
|
||||||
|
file_urls=file_urls,
|
||||||
|
additional_urls=additional_urls,
|
||||||
|
rfc_editor_state=doc.get_state("draft-rfceditor"),
|
||||||
|
iana_review_state=doc.get_state("draft-iana-review"),
|
||||||
|
iana_action_state=doc.get_state("draft-iana-action"),
|
||||||
|
iana_experts_state=doc.get_state("draft-iana-experts"),
|
||||||
|
iana_experts_comment=iana_experts_comment,
|
||||||
|
search_archive=search_archive,
|
||||||
|
presentations=presentations,
|
||||||
|
diff_revisions=diff_revisions,
|
||||||
|
submission=submission
|
||||||
|
))
|
||||||
|
|
||||||
|
elif doc.type_id == "draft":
|
||||||
split_content = request.COOKIES.get("full_draft", settings.USER_PREFERENCE_DEFAULTS["full_draft"]) == "off"
|
split_content = request.COOKIES.get("full_draft", settings.USER_PREFERENCE_DEFAULTS["full_draft"]) == "off"
|
||||||
if request.GET.get('include_text') == "0":
|
if request.GET.get('include_text') == "0":
|
||||||
split_content = True
|
split_content = True
|
||||||
|
@ -281,43 +435,13 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
is_author = request.user.is_authenticated and doc.documentauthor_set.filter(person__user=request.user).exists()
|
is_author = request.user.is_authenticated and doc.documentauthor_set.filter(person__user=request.user).exists()
|
||||||
can_view_possibly_replaces = can_edit_replaces or is_author
|
can_view_possibly_replaces = can_edit_replaces or is_author
|
||||||
|
|
||||||
rfc_number = name[3:] if name.startswith("rfc") else None
|
|
||||||
draft_name = None
|
|
||||||
for a in aliases:
|
|
||||||
if a.startswith("draft"):
|
|
||||||
draft_name = a
|
|
||||||
|
|
||||||
rfc_aliases = [prettify_std_name(a) for a in aliases
|
|
||||||
if a.startswith("fyi") or a.startswith("std") or a.startswith("bcp")]
|
|
||||||
|
|
||||||
latest_revision = None
|
latest_revision = None
|
||||||
|
|
||||||
# Workaround to allow displaying last rev of draft that became rfc as a draft
|
|
||||||
# This should be unwound when RFCs become their own documents.
|
|
||||||
if snapshot:
|
|
||||||
doc.name = doc.doc.name
|
|
||||||
name = doc.doc.name
|
|
||||||
else:
|
|
||||||
name = doc.name
|
|
||||||
|
|
||||||
file_urls, found_types = build_file_urls(doc)
|
file_urls, found_types = build_file_urls(doc)
|
||||||
if not snapshot and doc.get_state_slug() == "rfc":
|
|
||||||
# content
|
|
||||||
content = doc.text_or_error() # pyflakes:ignore
|
|
||||||
content = markup_txt.markup(maybe_split(content, split=split_content))
|
|
||||||
|
|
||||||
content = doc.text_or_error() # pyflakes:ignore
|
content = doc.text_or_error() # pyflakes:ignore
|
||||||
content = markup_txt.markup(maybe_split(content, split=split_content))
|
content = markup_txt.markup(maybe_split(content, split=split_content))
|
||||||
|
|
||||||
if not snapshot and doc.get_state_slug() == "rfc":
|
latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision")
|
||||||
if not found_types:
|
|
||||||
content = "This RFC is not currently available online."
|
|
||||||
split_content = False
|
|
||||||
elif "txt" not in found_types:
|
|
||||||
content = "This RFC is not available in plain text format."
|
|
||||||
split_content = False
|
|
||||||
else:
|
|
||||||
latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision")
|
|
||||||
|
|
||||||
# ballot
|
# ballot
|
||||||
iesg_ballot_summary = None
|
iesg_ballot_summary = None
|
||||||
|
@ -497,7 +621,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
|
|
||||||
augment_docs_and_user_with_user_info([doc], request.user)
|
augment_docs_and_user_with_user_info([doc], request.user)
|
||||||
|
|
||||||
published = doc.latest_event(type="published_rfc")
|
published = doc.latest_event(type="published_rfc") # todo rethink this now that published_rfc is on rfc
|
||||||
started_iesg_process = doc.latest_event(type="started_iesg_process")
|
started_iesg_process = doc.latest_event(type="started_iesg_process")
|
||||||
|
|
||||||
review_assignments = review_assignments_to_list_for_docs([doc]).get(doc.name, [])
|
review_assignments = review_assignments_to_list_for_docs([doc]).get(doc.name, [])
|
||||||
|
@ -516,12 +640,6 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
# Do not show the Auth48 URL in the "Additional URLs" section
|
# Do not show the Auth48 URL in the "Additional URLs" section
|
||||||
additional_urls = doc.documenturl_set.exclude(tag_id='auth48')
|
additional_urls = doc.documenturl_set.exclude(tag_id='auth48')
|
||||||
|
|
||||||
# Stream description passing test
|
|
||||||
if doc.stream != None:
|
|
||||||
stream_desc = doc.stream.desc
|
|
||||||
else:
|
|
||||||
stream_desc = "(None)"
|
|
||||||
|
|
||||||
html = None
|
html = None
|
||||||
js = None
|
js = None
|
||||||
css = None
|
css = None
|
||||||
|
@ -552,12 +670,11 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
html=html,
|
html=html,
|
||||||
group=group,
|
group=group,
|
||||||
top=top,
|
top=top,
|
||||||
name=name,
|
name=doc.name,
|
||||||
content=content,
|
content=content,
|
||||||
split_content=split_content,
|
split_content=split_content,
|
||||||
revisions=simple_diff_revisions if document_html else revisions,
|
revisions=simple_diff_revisions if document_html else revisions,
|
||||||
snapshot=snapshot,
|
snapshot=snapshot,
|
||||||
stream_desc=stream_desc,
|
|
||||||
latest_revision=latest_revision,
|
latest_revision=latest_revision,
|
||||||
latest_rev=latest_rev,
|
latest_rev=latest_rev,
|
||||||
can_edit=can_edit,
|
can_edit=can_edit,
|
||||||
|
@ -575,8 +692,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
can_request_review=can_request_review,
|
can_request_review=can_request_review,
|
||||||
can_submit_unsolicited_review_for_teams=can_submit_unsolicited_review_for_teams,
|
can_submit_unsolicited_review_for_teams=can_submit_unsolicited_review_for_teams,
|
||||||
|
|
||||||
rfc_number=rfc_number,
|
draft_name=doc.name,
|
||||||
draft_name=draft_name,
|
|
||||||
telechat=telechat,
|
telechat=telechat,
|
||||||
iesg_ballot_summary=iesg_ballot_summary,
|
iesg_ballot_summary=iesg_ballot_summary,
|
||||||
submission=submission,
|
submission=submission,
|
||||||
|
@ -593,7 +709,6 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
conflict_reviews=conflict_reviews,
|
conflict_reviews=conflict_reviews,
|
||||||
status_changes=status_changes,
|
status_changes=status_changes,
|
||||||
proposed_status_changes=proposed_status_changes,
|
proposed_status_changes=proposed_status_changes,
|
||||||
rfc_aliases=rfc_aliases,
|
|
||||||
has_errata=doc.pk and doc.tags.filter(slug="errata"), # doc.pk == None if using a fake_history_obj
|
has_errata=doc.pk and doc.tags.filter(slug="errata"), # doc.pk == None if using a fake_history_obj
|
||||||
published=published,
|
published=published,
|
||||||
file_urls=file_urls,
|
file_urls=file_urls,
|
||||||
|
@ -623,7 +738,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
diff_revisions=diff_revisions
|
diff_revisions=diff_revisions
|
||||||
))
|
))
|
||||||
|
|
||||||
if doc.type_id == "charter":
|
elif doc.type_id == "charter":
|
||||||
content = doc.text_or_error() # pyflakes:ignore
|
content = doc.text_or_error() # pyflakes:ignore
|
||||||
content = markup_txt.markup(content)
|
content = markup_txt.markup(content)
|
||||||
|
|
||||||
|
@ -660,7 +775,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
can_manage=can_manage,
|
can_manage=can_manage,
|
||||||
))
|
))
|
||||||
|
|
||||||
if doc.type_id == "bofreq":
|
elif doc.type_id == "bofreq":
|
||||||
content = markdown.markdown(doc.text_or_error())
|
content = markdown.markdown(doc.text_or_error())
|
||||||
editors = bofreq_editors(doc)
|
editors = bofreq_editors(doc)
|
||||||
responsible = bofreq_responsible(doc)
|
responsible = bofreq_responsible(doc)
|
||||||
|
@ -680,7 +795,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
editor_can_manage=editor_can_manage,
|
editor_can_manage=editor_can_manage,
|
||||||
))
|
))
|
||||||
|
|
||||||
if doc.type_id == "conflrev":
|
elif doc.type_id == "conflrev":
|
||||||
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
|
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
|
||||||
pathname = os.path.join(settings.CONFLICT_REVIEW_PATH,filename)
|
pathname = os.path.join(settings.CONFLICT_REVIEW_PATH,filename)
|
||||||
|
|
||||||
|
@ -695,7 +810,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
if doc.get_state_slug() in ("iesgeval", ) and doc.active_ballot():
|
if doc.get_state_slug() in ("iesgeval", ) and doc.active_ballot():
|
||||||
ballot_summary = needed_ballot_positions(doc, list(doc.active_ballot().active_balloter_positions().values()))
|
ballot_summary = needed_ballot_positions(doc, list(doc.active_ballot().active_balloter_positions().values()))
|
||||||
|
|
||||||
conflictdoc = doc.related_that_doc('conflrev')[0].document
|
conflictdoc = doc.related_that_doc('conflrev')[0]
|
||||||
|
|
||||||
return render(request, "doc/document_conflict_review.html",
|
return render(request, "doc/document_conflict_review.html",
|
||||||
dict(doc=doc,
|
dict(doc=doc,
|
||||||
|
@ -710,7 +825,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
approved_states=('appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent'),
|
approved_states=('appr-reqnopub-pend','appr-reqnopub-sent','appr-noprob-pend','appr-noprob-sent'),
|
||||||
))
|
))
|
||||||
|
|
||||||
if doc.type_id == "statchg":
|
elif doc.type_id == "statchg":
|
||||||
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
|
filename = "%s-%s.txt" % (doc.canonical_name(), doc.rev)
|
||||||
pathname = os.path.join(settings.STATUS_CHANGE_PATH,filename)
|
pathname = os.path.join(settings.STATUS_CHANGE_PATH,filename)
|
||||||
|
|
||||||
|
@ -744,7 +859,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
sorted_relations=sorted_relations,
|
sorted_relations=sorted_relations,
|
||||||
))
|
))
|
||||||
|
|
||||||
if doc.type_id in ("slides", "agenda", "minutes", "bluesheets", "procmaterials",):
|
elif doc.type_id in ("slides", "agenda", "minutes", "bluesheets", "procmaterials",):
|
||||||
can_manage_material = can_manage_materials(request.user, doc.group)
|
can_manage_material = can_manage_materials(request.user, doc.group)
|
||||||
presentations = doc.future_presentations()
|
presentations = doc.future_presentations()
|
||||||
if doc.uploaded_filename:
|
if doc.uploaded_filename:
|
||||||
|
@ -800,7 +915,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
if doc.type_id == "review":
|
elif doc.type_id == "review":
|
||||||
basename = "{}.txt".format(doc.name)
|
basename = "{}.txt".format(doc.name)
|
||||||
pathname = os.path.join(doc.get_file_path(), basename)
|
pathname = os.path.join(doc.get_file_path(), basename)
|
||||||
content = get_unicode_document_content(basename, pathname)
|
content = get_unicode_document_content(basename, pathname)
|
||||||
|
@ -826,7 +941,7 @@ def document_main(request, name, rev=None, document_html=False):
|
||||||
assignments=assignments,
|
assignments=assignments,
|
||||||
))
|
))
|
||||||
|
|
||||||
if doc.type_id in ("chatlog", "polls"):
|
elif doc.type_id in ("chatlog", "polls"):
|
||||||
if isinstance(doc,DocHistory):
|
if isinstance(doc,DocHistory):
|
||||||
session = doc.doc.sessionpresentation_set.last().session
|
session = doc.doc.sessionpresentation_set.last().session
|
||||||
else:
|
else:
|
||||||
|
@ -934,7 +1049,7 @@ def document_html(request, name, rev=None):
|
||||||
doc = found.documents.get()
|
doc = found.documents.get()
|
||||||
rev = found.matched_rev
|
rev = found.matched_rev
|
||||||
|
|
||||||
if not requested_rev and doc.is_rfc(): # Someone asked for /doc/html/8989
|
if not requested_rev and doc.type_id == "rfc": # Someone asked for /doc/html/8989
|
||||||
if not name.startswith('rfc'):
|
if not name.startswith('rfc'):
|
||||||
return redirect('ietf.doc.views_doc.document_html', name=doc.canonical_name())
|
return redirect('ietf.doc.views_doc.document_html', name=doc.canonical_name())
|
||||||
|
|
||||||
|
@ -944,7 +1059,12 @@ def document_html(request, name, rev=None):
|
||||||
if not os.path.exists(doc.get_file_name()):
|
if not os.path.exists(doc.get_file_name()):
|
||||||
raise Http404("File not found: %s" % doc.get_file_name())
|
raise Http404("File not found: %s" % doc.get_file_name())
|
||||||
|
|
||||||
return document_main(request, name=doc.name if requested_rev else doc.canonical_name(), rev=doc.rev if requested_rev or not doc.is_rfc() else None, document_html=True)
|
return document_main(
|
||||||
|
request,
|
||||||
|
name=doc.name if requested_rev else doc.canonical_name(),
|
||||||
|
rev=doc.rev if requested_rev or doc.type_id != "rfc" else None,
|
||||||
|
document_html=True,
|
||||||
|
)
|
||||||
|
|
||||||
def document_pdfized(request, name, rev=None, ext=None):
|
def document_pdfized(request, name, rev=None, ext=None):
|
||||||
|
|
||||||
|
@ -1044,8 +1164,8 @@ def get_diff_revisions(request, name, doc):
|
||||||
diff_documents = [doc]
|
diff_documents = [doc]
|
||||||
diff_documents.extend(
|
diff_documents.extend(
|
||||||
Document.objects.filter(
|
Document.objects.filter(
|
||||||
docalias__relateddocument__source=doc,
|
relateddocument__source=doc,
|
||||||
docalias__relateddocument__relationship="replaces",
|
relateddocument__relationship="replaces",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1100,21 +1220,46 @@ def document_history(request, name):
|
||||||
add_events_message_info(events)
|
add_events_message_info(events)
|
||||||
|
|
||||||
# figure out if the current user can add a comment to the history
|
# figure out if the current user can add a comment to the history
|
||||||
if doc.type_id == "draft" and doc.group != None:
|
if doc.type_id in ("draft", "rfc") and doc.group is not None:
|
||||||
can_add_comment = bool(has_role(request.user, ("Area Director", "Secretariat", "IRTF Chair", "IANA", "RFC Editor")) or (
|
can_add_comment = bool(
|
||||||
request.user.is_authenticated and
|
has_role(
|
||||||
Role.objects.filter(name__in=("chair", "secr"),
|
request.user,
|
||||||
group__acronym=doc.group.acronym,
|
("Area Director", "Secretariat", "IRTF Chair", "IANA", "RFC Editor"),
|
||||||
person__user=request.user)))
|
)
|
||||||
|
or (
|
||||||
|
request.user.is_authenticated
|
||||||
|
and Role.objects.filter(
|
||||||
|
name__in=("chair", "secr"),
|
||||||
|
group__acronym=doc.group.acronym,
|
||||||
|
person__user=request.user,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
can_add_comment = has_role(request.user, ("Area Director", "Secretariat", "IRTF Chair"))
|
can_add_comment = has_role(
|
||||||
return render(request, "doc/document_history.html",
|
request.user, ("Area Director", "Secretariat", "IRTF Chair")
|
||||||
dict(doc=doc,
|
)
|
||||||
top=top,
|
|
||||||
diff_revisions=diff_revisions,
|
# Get related docs whose history should be linked
|
||||||
events=events,
|
if doc.type_id == "draft":
|
||||||
can_add_comment=can_add_comment,
|
related = doc.related_that_doc("became_rfc")
|
||||||
))
|
elif doc.type_id == "rfc":
|
||||||
|
related = doc.related_that("became_rfc")
|
||||||
|
else:
|
||||||
|
related = []
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"doc/document_history.html",
|
||||||
|
{
|
||||||
|
"doc": doc,
|
||||||
|
"top": top,
|
||||||
|
"diff_revisions": diff_revisions,
|
||||||
|
"events": events,
|
||||||
|
"related": related,
|
||||||
|
"can_add_comment": can_add_comment,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def document_bibtex(request, name, rev=None):
|
def document_bibtex(request, name, rev=None):
|
||||||
|
@ -1133,25 +1278,30 @@ def document_bibtex(request, name, rev=None):
|
||||||
|
|
||||||
doc = get_object_or_404(Document, docalias__name=name)
|
doc = get_object_or_404(Document, docalias__name=name)
|
||||||
|
|
||||||
latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision")
|
doi = None
|
||||||
replaced_by = [d.name for d in doc.related_that("replaces")]
|
draft_became_rfc = None
|
||||||
published = doc.latest_event(type="published_rfc") is not None
|
replaced_by = None
|
||||||
rfc = latest_revision.doc if latest_revision and latest_revision.doc.get_state_slug() == "rfc" else None
|
latest_revision = None
|
||||||
|
if doc.type_id == "draft":
|
||||||
|
latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision")
|
||||||
|
replaced_by = [d.name for d in doc.related_that("replaces")]
|
||||||
|
draft_became_rfc_alias = next(iter(doc.related_that_doc("became_rfc")), None)
|
||||||
|
|
||||||
if rev != None and rev != doc.rev:
|
if rev != None and rev != doc.rev:
|
||||||
# find the entry in the history
|
# find the entry in the history
|
||||||
for h in doc.history_set.order_by("-time"):
|
for h in doc.history_set.order_by("-time"):
|
||||||
if rev == h.rev:
|
if rev == h.rev:
|
||||||
doc = h
|
doc = h
|
||||||
break
|
break
|
||||||
|
|
||||||
if doc.is_rfc():
|
if draft_became_rfc_alias:
|
||||||
|
draft_became_rfc = draft_became_rfc_alias.document
|
||||||
|
|
||||||
|
elif doc.type_id == "rfc":
|
||||||
# This needs to be replaced with a lookup, as the mapping may change
|
# This needs to be replaced with a lookup, as the mapping may change
|
||||||
# over time. Probably by updating ietf/sync/rfceditor.py to add the
|
# over time. Probably by updating ietf/sync/rfceditor.py to add the
|
||||||
# as a DocAlias, and use a method on Document to retrieve it.
|
# as a DocAlias, and use a method on Document to retrieve it.
|
||||||
doi = "10.17487/RFC%04d" % int(doc.rfc_number())
|
doi = f"10.17487/RFC{doc.rfc_number:04d}"
|
||||||
else:
|
|
||||||
doi = None
|
|
||||||
|
|
||||||
if doc.is_dochistory():
|
if doc.is_dochistory():
|
||||||
latest_event = doc.latest_event(type='new_revision', rev=rev)
|
latest_event = doc.latest_event(type='new_revision', rev=rev)
|
||||||
|
@ -1161,8 +1311,7 @@ def document_bibtex(request, name, rev=None):
|
||||||
return render(request, "doc/document_bibtex.bib",
|
return render(request, "doc/document_bibtex.bib",
|
||||||
dict(doc=doc,
|
dict(doc=doc,
|
||||||
replaced_by=replaced_by,
|
replaced_by=replaced_by,
|
||||||
published=published,
|
published_as=draft_became_rfc,
|
||||||
rfc=rfc,
|
|
||||||
latest_revision=latest_revision,
|
latest_revision=latest_revision,
|
||||||
doi=doi,
|
doi=doi,
|
||||||
),
|
),
|
||||||
|
@ -1618,7 +1767,7 @@ def telechat_date(request, name):
|
||||||
|
|
||||||
def doc_titletext(doc):
|
def doc_titletext(doc):
|
||||||
if doc.type.slug=='conflrev':
|
if doc.type.slug=='conflrev':
|
||||||
conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
|
conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target
|
||||||
return 'the conflict review of %s' % conflictdoc.canonical_name()
|
return 'the conflict review of %s' % conflictdoc.canonical_name()
|
||||||
return doc.canonical_name()
|
return doc.canonical_name()
|
||||||
|
|
||||||
|
@ -2025,9 +2174,16 @@ def idnits2_rfc_status(request):
|
||||||
|
|
||||||
def idnits2_state(request, name, rev=None):
|
def idnits2_state(request, name, rev=None):
|
||||||
doc = get_object_or_404(Document, docalias__name=name)
|
doc = get_object_or_404(Document, docalias__name=name)
|
||||||
if doc.type_id!='draft':
|
if doc.type_id not in ["draft", "rfc"]:
|
||||||
raise Http404
|
raise Http404
|
||||||
zero_revision = NewRevisionDocEvent.objects.filter(doc=doc,rev='00').first()
|
zero_revision = None
|
||||||
|
if doc.type_id == "rfc":
|
||||||
|
draft_alias = next(iter(doc.related_that('became_rfc')), None)
|
||||||
|
if draft_alias:
|
||||||
|
draft = draft_alias.document
|
||||||
|
zero_revision = NewRevisionDocEvent.objects.filter(doc=draft,rev='00').first()
|
||||||
|
else:
|
||||||
|
zero_revision = NewRevisionDocEvent.objects.filter(doc=doc,rev='00').first()
|
||||||
if zero_revision:
|
if zero_revision:
|
||||||
doc.created = zero_revision.time
|
doc.created = zero_revision.time
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -19,7 +19,7 @@ def downref_registry(request):
|
||||||
downref_doc_pairs = [ ]
|
downref_doc_pairs = [ ]
|
||||||
downref_relations = RelatedDocument.objects.filter(relationship_id='downref-approval')
|
downref_relations = RelatedDocument.objects.filter(relationship_id='downref-approval')
|
||||||
for rel in downref_relations:
|
for rel in downref_relations:
|
||||||
downref_doc_pairs.append((rel.target.document, rel.source))
|
downref_doc_pairs.append((rel.target, rel.source))
|
||||||
|
|
||||||
return render(request, 'doc/downref.html', {
|
return render(request, 'doc/downref.html', {
|
||||||
"doc_pairs": downref_doc_pairs,
|
"doc_pairs": downref_doc_pairs,
|
||||||
|
@ -38,18 +38,18 @@ def downref_registry_add(request):
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
drafts = form.cleaned_data['drafts']
|
drafts = form.cleaned_data['drafts']
|
||||||
rfc = form.cleaned_data['rfc']
|
rfc = form.cleaned_data['rfc']
|
||||||
for da in drafts:
|
for d in drafts:
|
||||||
RelatedDocument.objects.create(source=da.document,
|
RelatedDocument.objects.create(source=d,
|
||||||
target=rfc, relationship_id='downref-approval')
|
target=rfc, relationship_id='downref-approval')
|
||||||
c = DocEvent(type="downref_approved", doc=da.document,
|
c = DocEvent(type="downref_approved", doc=d,
|
||||||
rev=da.document.rev, by=login)
|
rev=d.rev, by=login)
|
||||||
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
||||||
rfc.document.rfc_number(), da.name, da.document.rev)
|
rfc.rfc_number, d.name, d.rev)
|
||||||
c.save()
|
c.save()
|
||||||
c = DocEvent(type="downref_approved", doc=rfc.document,
|
c = DocEvent(type="downref_approved", doc=rfc,
|
||||||
rev=rfc.document.rev, by=login)
|
rev=rfc.rev, by=login)
|
||||||
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
c.desc = "Downref to RFC %s approved by Last Call for %s-%s" % (
|
||||||
rfc.document.rfc_number(), da.name, da.document.rev)
|
rfc.rfc_number, d.name, d.rev)
|
||||||
c.save()
|
c.save()
|
||||||
|
|
||||||
return HttpResponseRedirect(urlreverse('ietf.doc.views_downref.downref_registry'))
|
return HttpResponseRedirect(urlreverse('ietf.doc.views_downref.downref_registry'))
|
||||||
|
|
|
@ -23,7 +23,7 @@ from django.utils import timezone
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.doc.models import ( Document, DocAlias, RelatedDocument, State,
|
from ietf.doc.models import ( Document, RelatedDocument, State,
|
||||||
StateType, DocEvent, ConsensusDocEvent, TelechatDocEvent, WriteupDocEvent, StateDocEvent,
|
StateType, DocEvent, ConsensusDocEvent, TelechatDocEvent, WriteupDocEvent, StateDocEvent,
|
||||||
IanaExpertDocEvent, IESG_SUBSTATE_TAGS)
|
IanaExpertDocEvent, IESG_SUBSTATE_TAGS)
|
||||||
from ietf.doc.mails import ( email_pulled_from_rfc_queue, email_resurrect_requested,
|
from ietf.doc.mails import ( email_pulled_from_rfc_queue, email_resurrect_requested,
|
||||||
|
@ -38,7 +38,7 @@ from ietf.doc.utils import ( add_state_change_event, can_adopt_draft, can_unadop
|
||||||
set_replaces_for_document, default_consensus, tags_suffix, can_edit_docextresources,
|
set_replaces_for_document, default_consensus, tags_suffix, can_edit_docextresources,
|
||||||
update_doc_extresources )
|
update_doc_extresources )
|
||||||
from ietf.doc.lastcall import request_last_call
|
from ietf.doc.lastcall import request_last_call
|
||||||
from ietf.doc.fields import SearchableDocAliasesField
|
from ietf.doc.fields import SearchableDocumentsField
|
||||||
from ietf.doc.forms import ExtResourceForm
|
from ietf.doc.forms import ExtResourceForm
|
||||||
from ietf.group.models import Group, Role, GroupFeatures
|
from ietf.group.models import Group, Role, GroupFeatures
|
||||||
from ietf.iesg.models import TelechatDate
|
from ietf.iesg.models import TelechatDate
|
||||||
|
@ -333,7 +333,7 @@ def change_stream(request, name):
|
||||||
))
|
))
|
||||||
|
|
||||||
class ReplacesForm(forms.Form):
|
class ReplacesForm(forms.Form):
|
||||||
replaces = SearchableDocAliasesField(required=False)
|
replaces = SearchableDocumentsField(required=False)
|
||||||
comment = forms.CharField(widget=forms.Textarea, required=False, strip=False)
|
comment = forms.CharField(widget=forms.Textarea, required=False, strip=False)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -343,9 +343,9 @@ class ReplacesForm(forms.Form):
|
||||||
|
|
||||||
def clean_replaces(self):
|
def clean_replaces(self):
|
||||||
for d in self.cleaned_data['replaces']:
|
for d in self.cleaned_data['replaces']:
|
||||||
if d.document == self.doc:
|
if d == self.doc:
|
||||||
raise forms.ValidationError("An Internet-Draft can't replace itself")
|
raise forms.ValidationError("An Internet-Draft can't replace itself")
|
||||||
if d.document.type_id == "draft" and d.document.get_state_slug() == "rfc":
|
if d.type_id == "draft" and d.get_state_slug() == "rfc":
|
||||||
raise forms.ValidationError("An Internet-Draft can't replace an RFC")
|
raise forms.ValidationError("An Internet-Draft can't replace an RFC")
|
||||||
return self.cleaned_data['replaces']
|
return self.cleaned_data['replaces']
|
||||||
|
|
||||||
|
@ -383,7 +383,7 @@ def replaces(request, name):
|
||||||
))
|
))
|
||||||
|
|
||||||
class SuggestedReplacesForm(forms.Form):
|
class SuggestedReplacesForm(forms.Form):
|
||||||
replaces = forms.ModelMultipleChoiceField(queryset=DocAlias.objects.all(),
|
replaces = forms.ModelMultipleChoiceField(queryset=Document.objects.all(),
|
||||||
label="Suggestions", required=False, widget=forms.CheckboxSelectMultiple,
|
label="Suggestions", required=False, widget=forms.CheckboxSelectMultiple,
|
||||||
help_text="Select only the documents that are replaced by this document")
|
help_text="Select only the documents that are replaced by this document")
|
||||||
comment = forms.CharField(label="Optional comment", widget=forms.Textarea, required=False, strip=False)
|
comment = forms.CharField(label="Optional comment", widget=forms.Textarea, required=False, strip=False)
|
||||||
|
@ -673,7 +673,7 @@ def edit_info(request, name):
|
||||||
e.save()
|
e.save()
|
||||||
events.append(e)
|
events.append(e)
|
||||||
|
|
||||||
replaces = Document.objects.filter(docalias__relateddocument__source=doc, docalias__relateddocument__relationship="replaces")
|
replaces = Document.objects.filter(targets_related__source=doc, targets_related__relationship="replaces")
|
||||||
if replaces:
|
if replaces:
|
||||||
# this should perhaps be somewhere else, e.g. the
|
# this should perhaps be somewhere else, e.g. the
|
||||||
# place where the replace relationship is established?
|
# place where the replace relationship is established?
|
||||||
|
|
|
@ -117,7 +117,7 @@ class RequestReviewForm(forms.ModelForm):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def request_review(request, name):
|
def request_review(request, name):
|
||||||
doc = get_object_or_404(Document, name=name)
|
doc = get_object_or_404(Document, type_id="draft", name=name)
|
||||||
|
|
||||||
if not can_request_review_of_doc(request.user, doc):
|
if not can_request_review_of_doc(request.user, doc):
|
||||||
permission_denied(request, "You do not have permission to perform this action")
|
permission_denied(request, "You do not have permission to perform this action")
|
||||||
|
|
|
@ -409,8 +409,8 @@ def shorten_group_name(name):
|
||||||
|
|
||||||
def ad_dashboard_sort_key(doc):
|
def ad_dashboard_sort_key(doc):
|
||||||
|
|
||||||
if doc.type.slug=='draft' and doc.get_state_slug('draft') == 'rfc':
|
if doc.type.slug=='rfc':
|
||||||
return "21%04d" % int(doc.rfc_number())
|
return "21%04d" % int(doc.rfc_number)
|
||||||
if doc.type.slug=='statchg' and doc.get_state_slug('statchg') == 'appr-sent':
|
if doc.type.slug=='statchg' and doc.get_state_slug('statchg') == 'appr-sent':
|
||||||
return "22%d" % 0 # TODO - get the date of the transition into this state here
|
return "22%d" % 0 # TODO - get the date of the transition into this state here
|
||||||
if doc.type.slug=='conflrev' and doc.get_state_slug('conflrev') in ('appr-reqnopub-sent','appr-noprob-sent'):
|
if doc.type.slug=='conflrev' and doc.get_state_slug('conflrev') in ('appr-reqnopub-sent','appr-noprob-sent'):
|
||||||
|
@ -805,21 +805,20 @@ def recent_drafts(request, days=7):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def index_all_drafts(request):
|
def index_all_drafts(request): # Should we rename this
|
||||||
# try to be efficient since this view returns a lot of data
|
# try to be efficient since this view returns a lot of data
|
||||||
categories = []
|
categories = []
|
||||||
|
|
||||||
for s in ("active", "rfc", "expired", "repl", "auth-rm", "ietf-rm"):
|
# Gather drafts
|
||||||
|
for s in ("active", "expired", "repl", "auth-rm", "ietf-rm"):
|
||||||
state = State.objects.get(type="draft", slug=s)
|
state = State.objects.get(type="draft", slug=s)
|
||||||
|
|
||||||
if state.slug == "rfc":
|
if state.slug in ("ietf-rm", "auth-rm"):
|
||||||
heading = "RFCs"
|
|
||||||
elif state.slug in ("ietf-rm", "auth-rm"):
|
|
||||||
heading = "Internet-Drafts %s" % state.name
|
heading = "Internet-Drafts %s" % state.name
|
||||||
else:
|
else:
|
||||||
heading = "%s Internet-Drafts" % state.name
|
heading = "%s Internet-Drafts" % state.name
|
||||||
|
|
||||||
draft_names = DocAlias.objects.filter(docs__states=state).values_list("name", "docs__name")
|
draft_names = DocAlias.objects.filter(docs__type_id="draft", docs__states=state).values_list("name", "docs__name")
|
||||||
|
|
||||||
names = []
|
names = []
|
||||||
names_to_skip = set()
|
names_to_skip = set()
|
||||||
|
@ -828,24 +827,52 @@ def index_all_drafts(request):
|
||||||
if name != doc:
|
if name != doc:
|
||||||
if not name.startswith("rfc"):
|
if not name.startswith("rfc"):
|
||||||
name, doc = doc, name
|
name, doc = doc, name
|
||||||
names_to_skip.add(doc)
|
names_to_skip.add(doc) # this is filtering out subseries docaliases (which we will delete, so TODO clean this out after doing so)
|
||||||
|
|
||||||
if name.startswith("rfc"):
|
|
||||||
name = name.upper()
|
|
||||||
sort_key = '%09d' % (100000000-int(name[3:]))
|
|
||||||
|
|
||||||
names.append((name, sort_key))
|
names.append((name, sort_key))
|
||||||
|
|
||||||
names.sort(key=lambda t: t[1])
|
names.sort(key=lambda t: t[1])
|
||||||
|
|
||||||
names = [f'<a href=\"{urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=n))}\">{n}</a>'
|
names = [f'<a href=\"{urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=n))}\">{n}</a>'
|
||||||
for n, __ in names if n not in names_to_skip]
|
for n, __ in names if n not in names_to_skip]
|
||||||
|
|
||||||
categories.append((state,
|
categories.append((state,
|
||||||
heading,
|
heading,
|
||||||
len(names),
|
len(names),
|
||||||
"<br>".join(names)
|
"<br>".join(names)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# gather RFCs
|
||||||
|
rfc_names = DocAlias.objects.filter(docs__type_id="rfc").values_list("name", "docs__name")
|
||||||
|
names = []
|
||||||
|
names_to_skip = set()
|
||||||
|
for name, doc in rfc_names:
|
||||||
|
sort_key = name
|
||||||
|
if name != doc: # There are some std docalias that pointed to rfc names pre-migration.
|
||||||
|
if not name.startswith("rfc"):
|
||||||
|
name, doc = doc, name
|
||||||
|
names_to_skip.add(doc) # this is filtering out those std docaliases (which we will delete, so TODO clean this out after doing so)
|
||||||
|
name = name.upper()
|
||||||
|
sort_key = '%09d' % (100000000-int(name[3:]))
|
||||||
|
|
||||||
|
names.append((name, sort_key))
|
||||||
|
|
||||||
|
names.sort(key=lambda t: t[1])
|
||||||
|
|
||||||
|
names = [f'<a href=\"{urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=n))}\">{n}</a>'
|
||||||
|
for n, __ in names if n not in names_to_skip]
|
||||||
|
|
||||||
|
state = State.objects.get(type_id="rfc", slug="published")
|
||||||
|
|
||||||
|
categories.append((state,
|
||||||
|
"RFCs",
|
||||||
|
len(names),
|
||||||
|
"<br>".join(names)
|
||||||
|
))
|
||||||
|
|
||||||
|
# Return to the previous section ordering
|
||||||
|
categories = categories[0:1]+categories[5:]+categories[1:5]
|
||||||
|
|
||||||
return render(request, 'doc/index_all_drafts.html', { "categories": categories })
|
return render(request, 'doc/index_all_drafts.html', { "categories": categories })
|
||||||
|
|
||||||
def index_active_drafts(request):
|
def index_active_drafts(request):
|
||||||
|
|
|
@ -98,8 +98,8 @@ def change_state(request, name, option=None):
|
||||||
relationship__slug__in=STATUSCHANGE_RELATIONS
|
relationship__slug__in=STATUSCHANGE_RELATIONS
|
||||||
)
|
)
|
||||||
related_doc_info = [
|
related_doc_info = [
|
||||||
dict(title=rel_doc.target.document.title,
|
dict(title=rel_doc.target.title,
|
||||||
canonical_name=rel_doc.target.document.canonical_name(),
|
canonical_name=rel_doc.target.canonical_name(),
|
||||||
newstatus=newstatus(rel_doc))
|
newstatus=newstatus(rel_doc))
|
||||||
for rel_doc in related_docs
|
for rel_doc in related_docs
|
||||||
]
|
]
|
||||||
|
@ -309,7 +309,7 @@ def default_approval_text(status_change,relateddoc):
|
||||||
|
|
||||||
current_text = status_change.text_or_error() # pyflakes:ignore
|
current_text = status_change.text_or_error() # pyflakes:ignore
|
||||||
|
|
||||||
if relateddoc.target.document.std_level_id in ('std','ps','ds','bcp',):
|
if relateddoc.target.std_level_id in ('std','ps','ds','bcp',):
|
||||||
action = "Protocol Action"
|
action = "Protocol Action"
|
||||||
else:
|
else:
|
||||||
action = "Document Action"
|
action = "Document Action"
|
||||||
|
@ -320,7 +320,7 @@ def default_approval_text(status_change,relateddoc):
|
||||||
dict(status_change=status_change,
|
dict(status_change=status_change,
|
||||||
status_change_url = settings.IDTRACKER_BASE_URL+status_change.get_absolute_url(),
|
status_change_url = settings.IDTRACKER_BASE_URL+status_change.get_absolute_url(),
|
||||||
relateddoc= relateddoc,
|
relateddoc= relateddoc,
|
||||||
relateddoc_url = settings.IDTRACKER_BASE_URL+relateddoc.target.document.get_absolute_url(),
|
relateddoc_url = settings.IDTRACKER_BASE_URL+relateddoc.target.get_absolute_url(),
|
||||||
approved_text = current_text,
|
approved_text = current_text,
|
||||||
action=action,
|
action=action,
|
||||||
newstatus=newstatus(relateddoc),
|
newstatus=newstatus(relateddoc),
|
||||||
|
@ -388,7 +388,7 @@ def approve(request, name):
|
||||||
|
|
||||||
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
||||||
# Add a document event to each target
|
# Add a document event to each target
|
||||||
c = DocEvent(type="added_comment", doc=rel.target.document, rev=rel.target.document.rev, by=login)
|
c = DocEvent(type="added_comment", doc=rel.target, rev=rel.target.rev, by=login)
|
||||||
c.desc = "New status of %s approved by the IESG\n%s%s" % (newstatus(rel), settings.IDTRACKER_BASE_URL,reverse('ietf.doc.views_doc.document_main', kwargs={'name': status_change.name}))
|
c.desc = "New status of %s approved by the IESG\n%s%s" % (newstatus(rel), settings.IDTRACKER_BASE_URL,reverse('ietf.doc.views_doc.document_main', kwargs={'name': status_change.name}))
|
||||||
c.save()
|
c.save()
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ def approve(request, name):
|
||||||
init = []
|
init = []
|
||||||
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
||||||
init.append({"announcement_text" : escape(default_approval_text(status_change,rel)),
|
init.append({"announcement_text" : escape(default_approval_text(status_change,rel)),
|
||||||
"label": "Announcement text for %s to %s"%(rel.target.document.canonical_name(),newstatus(rel)),
|
"label": "Announcement text for %s to %s"%(rel.target.canonical_name(),newstatus(rel)),
|
||||||
})
|
})
|
||||||
formset = AnnouncementFormSet(initial=init)
|
formset = AnnouncementFormSet(initial=init)
|
||||||
for form in formset.forms:
|
for form in formset.forms:
|
||||||
|
@ -439,7 +439,7 @@ def clean_helper(form, formtype):
|
||||||
|
|
||||||
if not re.match(r'(?i)rfc\d{1,4}',key):
|
if not re.match(r'(?i)rfc\d{1,4}',key):
|
||||||
errors.append(key+" is not a valid RFC - please use the form RFCn\n")
|
errors.append(key+" is not a valid RFC - please use the form RFCn\n")
|
||||||
elif not DocAlias.objects.filter(name=key):
|
elif not Document.objects.filter(name=key):
|
||||||
errors.append(key+" does not exist\n")
|
errors.append(key+" does not exist\n")
|
||||||
|
|
||||||
if new_relations[key] not in STATUSCHANGE_RELATIONS:
|
if new_relations[key] not in STATUSCHANGE_RELATIONS:
|
||||||
|
@ -564,10 +564,9 @@ def start_rfc_status_change(request, name=None):
|
||||||
DocAlias.objects.create( name= 'status-change-'+form.cleaned_data['document_name']).docs.add(status_change)
|
DocAlias.objects.create( name= 'status-change-'+form.cleaned_data['document_name']).docs.add(status_change)
|
||||||
|
|
||||||
for key in form.cleaned_data['relations']:
|
for key in form.cleaned_data['relations']:
|
||||||
status_change.relateddocument_set.create(target=DocAlias.objects.get(name=key),
|
status_change.relateddocument_set.create(target=Document.objects.get(name=key),
|
||||||
relationship_id=form.cleaned_data['relations'][key])
|
relationship_id=form.cleaned_data['relations'][key])
|
||||||
|
|
||||||
|
|
||||||
tc_date = form.cleaned_data['telechat_date']
|
tc_date = form.cleaned_data['telechat_date']
|
||||||
if tc_date:
|
if tc_date:
|
||||||
update_telechat(request, status_change, login, tc_date)
|
update_telechat(request, status_change, login, tc_date)
|
||||||
|
@ -605,11 +604,11 @@ def edit_relations(request, name):
|
||||||
|
|
||||||
old_relations={}
|
old_relations={}
|
||||||
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
||||||
old_relations[rel.target.document.canonical_name()]=rel.relationship.slug
|
old_relations[rel.target.canonical_name()]=rel.relationship.slug
|
||||||
new_relations=form.cleaned_data['relations']
|
new_relations=form.cleaned_data['relations']
|
||||||
status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS).delete()
|
status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS).delete()
|
||||||
for key in new_relations:
|
for key in new_relations:
|
||||||
status_change.relateddocument_set.create(target=DocAlias.objects.get(name=key),
|
status_change.relateddocument_set.create(target=Document.objects.get(name=key),
|
||||||
relationship_id=new_relations[key])
|
relationship_id=new_relations[key])
|
||||||
c = DocEvent(type="added_comment", doc=status_change, rev=status_change.rev, by=login)
|
c = DocEvent(type="added_comment", doc=status_change, rev=status_change.rev, by=login)
|
||||||
c.desc = "Affected RFC list changed.\nOLD:"
|
c.desc = "Affected RFC list changed.\nOLD:"
|
||||||
|
@ -626,7 +625,7 @@ def edit_relations(request, name):
|
||||||
else:
|
else:
|
||||||
relations={}
|
relations={}
|
||||||
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
for rel in status_change.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS):
|
||||||
relations[rel.target.document.canonical_name()]=rel.relationship.slug
|
relations[rel.target.canonical_name()]=rel.relationship.slug
|
||||||
init = { "relations":relations,
|
init = { "relations":relations,
|
||||||
}
|
}
|
||||||
form = EditStatusChangeForm(initial=init)
|
form = EditStatusChangeForm(initial=init)
|
||||||
|
@ -653,8 +652,8 @@ def generate_last_call_text(request, doc):
|
||||||
settings=settings,
|
settings=settings,
|
||||||
requester=requester,
|
requester=requester,
|
||||||
expiration_date=expiration_date.strftime("%Y-%m-%d"),
|
expiration_date=expiration_date.strftime("%Y-%m-%d"),
|
||||||
changes=['%s from %s to %s\n (%s)'%(rel.target.name.upper(),rel.target.document.std_level.name,newstatus(rel),rel.target.document.title) for rel in doc.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS)],
|
changes=['%s from %s to %s\n (%s)'%(rel.target.name.upper(),rel.target.std_level.name,newstatus(rel),rel.target.title) for rel in doc.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS)],
|
||||||
urls=[rel.target.document.get_absolute_url() for rel in doc.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS)],
|
urls=[rel.target.get_absolute_url() for rel in doc.relateddocument_set.filter(relationship__slug__in=STATUSCHANGE_RELATIONS)],
|
||||||
cc=cc
|
cc=cc
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -369,7 +369,7 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"):
|
||||||
email_milestones_changed(request, group, changes, states)
|
email_milestones_changed(request, group, changes, states)
|
||||||
|
|
||||||
if milestone_set == "charter":
|
if milestone_set == "charter":
|
||||||
return redirect('ietf.doc.views_doc.document_main', name=group.charter.canonical_name())
|
return redirect('ietf.doc.views_doc.document_main', name=group.charter.name)
|
||||||
else:
|
else:
|
||||||
return HttpResponseRedirect(group.about_url())
|
return HttpResponseRedirect(group.about_url())
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -69,7 +69,7 @@ class GroupStatsTests(TestCase):
|
||||||
a = WgDraftFactory()
|
a = WgDraftFactory()
|
||||||
b = WgDraftFactory()
|
b = WgDraftFactory()
|
||||||
RelatedDocument.objects.create(
|
RelatedDocument.objects.create(
|
||||||
source=a, target=b.docalias.first(), relationship_id="refnorm"
|
source=a, target=b, relationship_id="refnorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_group_stats(self):
|
def test_group_stats(self):
|
||||||
|
@ -95,7 +95,7 @@ class GroupDocDependencyTests(TestCase):
|
||||||
a = WgDraftFactory()
|
a = WgDraftFactory()
|
||||||
b = WgDraftFactory()
|
b = WgDraftFactory()
|
||||||
RelatedDocument.objects.create(
|
RelatedDocument.objects.create(
|
||||||
source=a, target=b.docalias.first(), relationship_id="refnorm"
|
source=a, target=b, relationship_id="refnorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_group_document_dependencies(self):
|
def test_group_document_dependencies(self):
|
||||||
|
|
|
@ -117,8 +117,9 @@ class GroupPagesTests(TestCase):
|
||||||
|
|
||||||
chair = Email.objects.filter(role__group=group, role__name="chair")[0]
|
chair = Email.objects.filter(role__group=group, role__name="chair")[0]
|
||||||
|
|
||||||
with (Path(settings.CHARTER_PATH) / ("%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))).open("w") as f:
|
(
|
||||||
f.write("This is a charter.")
|
Path(settings.CHARTER_PATH) / f"{group.charter.name}-{group.charter.rev}.txt"
|
||||||
|
).write_text("This is a charter.")
|
||||||
|
|
||||||
url = urlreverse('ietf.group.views.wg_summary_area', kwargs=dict(group_type="wg"))
|
url = urlreverse('ietf.group.views.wg_summary_area', kwargs=dict(group_type="wg"))
|
||||||
r = self.client.get(url)
|
r = self.client.get(url)
|
||||||
|
@ -264,8 +265,9 @@ class GroupPagesTests(TestCase):
|
||||||
group = CharterFactory().group
|
group = CharterFactory().group
|
||||||
draft = WgDraftFactory(group=group)
|
draft = WgDraftFactory(group=group)
|
||||||
|
|
||||||
with (Path(settings.CHARTER_PATH) / ("%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))).open("w") as f:
|
(
|
||||||
f.write("This is a charter.")
|
Path(settings.CHARTER_PATH) / f"{group.charter.name}-{group.charter.rev}.txt"
|
||||||
|
).write_text("This is a charter.")
|
||||||
|
|
||||||
milestone = GroupMilestone.objects.create(
|
milestone = GroupMilestone.objects.create(
|
||||||
group=group,
|
group=group,
|
||||||
|
@ -668,8 +670,9 @@ class GroupEditTests(TestCase):
|
||||||
self.assertTrue(len(q('form .is-invalid')) > 0)
|
self.assertTrue(len(q('form .is-invalid')) > 0)
|
||||||
|
|
||||||
# edit info
|
# edit info
|
||||||
with (Path(settings.CHARTER_PATH) / ("%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))).open("w") as f:
|
(
|
||||||
f.write("This is a charter.")
|
Path(settings.CHARTER_PATH) / f"{group.charter.name}-{group.charter.rev}.txt"
|
||||||
|
).write_text("This is a charter.")
|
||||||
area = group.parent
|
area = group.parent
|
||||||
ad = Person.objects.get(name="Areað Irector")
|
ad = Person.objects.get(name="Areað Irector")
|
||||||
state = GroupStateName.objects.get(slug="bof")
|
state = GroupStateName.objects.get(slug="bof")
|
||||||
|
@ -711,7 +714,9 @@ class GroupEditTests(TestCase):
|
||||||
self.assertEqual(group.list_archive, "archive.mars")
|
self.assertEqual(group.list_archive, "archive.mars")
|
||||||
self.assertEqual(group.description, '')
|
self.assertEqual(group.description, '')
|
||||||
|
|
||||||
self.assertTrue((Path(settings.CHARTER_PATH) / ("%s-%s.txt" % (group.charter.canonical_name(), group.charter.rev))).exists())
|
self.assertTrue(
|
||||||
|
(Path(settings.CHARTER_PATH) / f"{group.charter.name}-{group.charter.rev}.txt").exists()
|
||||||
|
)
|
||||||
self.assertEqual(len(outbox), 2)
|
self.assertEqual(len(outbox), 2)
|
||||||
self.assertTrue('Personnel change' in outbox[0]['Subject'])
|
self.assertTrue('Personnel change' in outbox[0]['Subject'])
|
||||||
for prefix in ['ad1','ad2','aread','marschairman','marsdelegate']:
|
for prefix in ['ad1','ad2','aread','marschairman','marsdelegate']:
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
import io
|
from pathlib import Path
|
||||||
import os
|
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
@ -55,15 +54,14 @@ def get_charter_text(group):
|
||||||
if (h.rev > c.rev and not (c_appr and not h_appr)) or (h_appr and not c_appr):
|
if (h.rev > c.rev and not (c_appr and not h_appr)) or (h_appr and not c_appr):
|
||||||
c = h
|
c = h
|
||||||
|
|
||||||
filename = os.path.join(c.get_file_path(), "%s-%s.txt" % (c.canonical_name(), c.rev))
|
filename = Path(c.get_file_path()) / f"{c.name}-{c.rev}.txt"
|
||||||
try:
|
try:
|
||||||
with io.open(filename, 'rb') as f:
|
text = filename.read_bytes()
|
||||||
text = f.read()
|
try:
|
||||||
try:
|
text = text.decode('utf8')
|
||||||
text = text.decode('utf8')
|
except UnicodeDecodeError:
|
||||||
except UnicodeDecodeError:
|
text = text.decode('latin1')
|
||||||
text = text.decode('latin1')
|
return text
|
||||||
return text
|
|
||||||
except IOError:
|
except IOError:
|
||||||
return 'Error Loading Group Charter'
|
return 'Error Loading Group Charter'
|
||||||
|
|
||||||
|
|
|
@ -534,9 +534,8 @@ def group_documents_txt(request, acronym, group_type=None):
|
||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
for d in itertools.chain(docs, docs_related):
|
for d in itertools.chain(docs, docs_related):
|
||||||
rfc_number = d.rfc_number()
|
if d.type_id == "rfc":
|
||||||
if rfc_number != None:
|
name = str(d.rfc_number)
|
||||||
name = rfc_number
|
|
||||||
else:
|
else:
|
||||||
name = "%s-%s" % (d.name, d.rev)
|
name = "%s-%s" % (d.name, d.rev)
|
||||||
|
|
||||||
|
@ -746,7 +745,7 @@ def dependencies(request, acronym, group_type=None):
|
||||||
relationship__slug__startswith="ref",
|
relationship__slug__startswith="ref",
|
||||||
)
|
)
|
||||||
|
|
||||||
both_rfcs = Q(source__states__slug="rfc", target__docs__states__slug="rfc")
|
both_rfcs = Q(source__states__slug="rfc", target__states__slug="rfc")
|
||||||
inactive = Q(source__states__slug__in=["expired", "repl"])
|
inactive = Q(source__states__slug__in=["expired", "repl"])
|
||||||
attractor = Q(target__name__in=["rfc5000", "rfc5741"])
|
attractor = Q(target__name__in=["rfc5000", "rfc5741"])
|
||||||
removed = Q(source__states__slug__in=["auth-rm", "ietf-rm"])
|
removed = Q(source__states__slug__in=["auth-rm", "ietf-rm"])
|
||||||
|
@ -760,19 +759,19 @@ def dependencies(request, acronym, group_type=None):
|
||||||
|
|
||||||
links = set()
|
links = set()
|
||||||
for x in relations:
|
for x in relations:
|
||||||
target_state = x.target.document.get_state_slug("draft")
|
target_state = x.target.get_state_slug("draft")
|
||||||
if target_state != "rfc" or x.is_downref():
|
if target_state != "rfc" or x.is_downref():
|
||||||
links.add(x)
|
links.add(x)
|
||||||
|
|
||||||
replacements = RelatedDocument.objects.filter(
|
replacements = RelatedDocument.objects.filter(
|
||||||
relationship__slug="replaces",
|
relationship__slug="replaces",
|
||||||
target__docs__in=[x.target.document for x in links],
|
target__in=[x.target for x in links],
|
||||||
)
|
)
|
||||||
|
|
||||||
for x in replacements:
|
for x in replacements:
|
||||||
links.add(x)
|
links.add(x)
|
||||||
|
|
||||||
nodes = set([x.source for x in links]).union([x.target.document for x in links])
|
nodes = set([x.source for x in links]).union([x.target for x in links])
|
||||||
graph = {
|
graph = {
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
|
@ -795,7 +794,7 @@ def dependencies(request, acronym, group_type=None):
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"source": x.source.canonical_name(),
|
"source": x.source.canonical_name(),
|
||||||
"target": x.target.document.canonical_name(),
|
"target": x.target.canonical_name(),
|
||||||
"rel": "downref" if x.is_downref() else x.relationship.slug,
|
"rel": "downref" if x.is_downref() else x.relationship.slug,
|
||||||
}
|
}
|
||||||
for x in links
|
for x in links
|
||||||
|
|
|
@ -34,7 +34,7 @@ def all_id_txt():
|
||||||
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc",
|
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc",
|
||||||
docs__states=State.objects.get(type="draft", slug="rfc")).values_list("docs__name", "name"))
|
docs__states=State.objects.get(type="draft", slug="rfc")).values_list("docs__name", "name"))
|
||||||
|
|
||||||
replacements = dict(RelatedDocument.objects.filter(target__docs__states=State.objects.get(type="draft", slug="repl"),
|
replacements = dict(RelatedDocument.objects.filter(target__states=State.objects.get(type="draft", slug="repl"),
|
||||||
relationship="replaces").values_list("target__name", "source__name"))
|
relationship="replaces").values_list("target__name", "source__name"))
|
||||||
|
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ def all_id2_txt():
|
||||||
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc",
|
rfc_aliases = dict(DocAlias.objects.filter(name__startswith="rfc",
|
||||||
docs__states=State.objects.get(type="draft", slug="rfc")).values_list("docs__name", "name"))
|
docs__states=State.objects.get(type="draft", slug="rfc")).values_list("docs__name", "name"))
|
||||||
|
|
||||||
replacements = dict(RelatedDocument.objects.filter(target__docs__states=State.objects.get(type="draft", slug="repl"),
|
replacements = dict(RelatedDocument.objects.filter(target__states=State.objects.get(type="draft", slug="repl"),
|
||||||
relationship="replaces").values_list("target__name", "source__name"))
|
relationship="replaces").values_list("target__name", "source__name"))
|
||||||
|
|
||||||
revision_time = dict(DocEvent.objects.filter(type="new_revision", doc__name__startswith="draft-").order_by('time').values_list("doc__name", "time"))
|
revision_time = dict(DocEvent.objects.filter(type="new_revision", doc__name__startswith="draft-").order_by('time').values_list("doc__name", "time"))
|
||||||
|
|
|
@ -52,8 +52,13 @@ class IndexTests(TestCase):
|
||||||
|
|
||||||
RelatedDocument.objects.create(
|
RelatedDocument.objects.create(
|
||||||
relationship=DocRelationshipName.objects.get(slug="replaces"),
|
relationship=DocRelationshipName.objects.get(slug="replaces"),
|
||||||
source=Document.objects.create(type_id="draft", rev="00", name="draft-test-replacement"),
|
source=Document.objects.create(
|
||||||
target=draft.docalias.get(name__startswith="draft"))
|
type_id="draft",
|
||||||
|
rev="00",
|
||||||
|
name="draft-test-replacement"
|
||||||
|
),
|
||||||
|
target=draft
|
||||||
|
)
|
||||||
|
|
||||||
txt = all_id_txt()
|
txt = all_id_txt()
|
||||||
self.assertTrue(draft.name + "-" + draft.rev in txt)
|
self.assertTrue(draft.name + "-" + draft.rev in txt)
|
||||||
|
@ -111,8 +116,12 @@ class IndexTests(TestCase):
|
||||||
draft.set_state(State.objects.get(type="draft", slug="repl"))
|
draft.set_state(State.objects.get(type="draft", slug="repl"))
|
||||||
RelatedDocument.objects.create(
|
RelatedDocument.objects.create(
|
||||||
relationship=DocRelationshipName.objects.get(slug="replaces"),
|
relationship=DocRelationshipName.objects.get(slug="replaces"),
|
||||||
source=Document.objects.create(type_id="draft", rev="00", name="draft-test-replacement"),
|
source=Document.objects.create(
|
||||||
target=draft.docalias.get(name__startswith="draft"))
|
type_id="draft",
|
||||||
|
rev="00",
|
||||||
|
name="draft-test-replacement"
|
||||||
|
),
|
||||||
|
target=draft)
|
||||||
|
|
||||||
t = get_fields(all_id2_txt())
|
t = get_fields(all_id2_txt())
|
||||||
self.assertEqual(t[5], "draft-test-replacement")
|
self.assertEqual(t[5], "draft-test-replacement")
|
||||||
|
|
|
@ -66,7 +66,7 @@ def get_doc_section(doc):
|
||||||
elif doc.type_id == 'statchg':
|
elif doc.type_id == 'statchg':
|
||||||
protocol_action = False
|
protocol_action = False
|
||||||
for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')):
|
for relation in doc.relateddocument_set.filter(relationship__slug__in=('tops','tois','tohist','toinf','tobcp','toexp')):
|
||||||
if relation.relationship_id in ('tops','tois') or relation.target.document.std_level_id in ('std','ds','ps'):
|
if relation.relationship_id in ('tops','tois') or relation.target.std_level_id in ('std','ds','ps'):
|
||||||
protocol_action = True
|
protocol_action = True
|
||||||
if protocol_action:
|
if protocol_action:
|
||||||
s = "2.3"
|
s = "2.3"
|
||||||
|
@ -186,7 +186,7 @@ def fill_in_agenda_docs(date, sections, docs=None):
|
||||||
|
|
||||||
doc.review_assignments = review_assignments_for_docs.get(doc.name, [])
|
doc.review_assignments = review_assignments_for_docs.get(doc.name, [])
|
||||||
elif doc.type_id == "conflrev":
|
elif doc.type_id == "conflrev":
|
||||||
doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
|
doc.conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target
|
||||||
elif doc.type_id == "charter":
|
elif doc.type_id == "charter":
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -219,4 +219,4 @@ def agenda_data(date=None):
|
||||||
fill_in_agenda_docs(date, sections)
|
fill_in_agenda_docs(date, sections)
|
||||||
fill_in_agenda_management_issues(date, sections)
|
fill_in_agenda_management_issues(date, sections)
|
||||||
|
|
||||||
return { 'date': date.isoformat(), 'sections': sections }
|
return { 'date': date.isoformat(), 'sections': sections }
|
||||||
|
|
|
@ -17,7 +17,7 @@ from django.utils.html import escape
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.doc.models import DocEvent, BallotPositionDocEvent, TelechatDocEvent
|
from ietf.doc.models import DocEvent, BallotPositionDocEvent, TelechatDocEvent
|
||||||
from ietf.doc.models import Document, DocAlias, State, RelatedDocument
|
from ietf.doc.models import Document, State, RelatedDocument
|
||||||
from ietf.doc.factories import WgDraftFactory, IndividualDraftFactory, ConflictReviewFactory, BaseDocumentFactory, CharterFactory, WgRfcFactory, IndividualRfcFactory
|
from ietf.doc.factories import WgDraftFactory, IndividualDraftFactory, ConflictReviewFactory, BaseDocumentFactory, CharterFactory, WgRfcFactory, IndividualRfcFactory
|
||||||
from ietf.doc.utils import create_ballot_if_not_open
|
from ietf.doc.utils import create_ballot_if_not_open
|
||||||
from ietf.group.factories import RoleFactory, GroupFactory
|
from ietf.group.factories import RoleFactory, GroupFactory
|
||||||
|
@ -107,8 +107,8 @@ class IESGAgendaTests(TestCase):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
mars = GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut'))
|
mars = GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut'))
|
||||||
wgdraft = WgDraftFactory(name='draft-ietf-mars-test', group=mars, intended_std_level_id='ps')
|
wgdraft = WgDraftFactory(name='draft-ietf-mars-test', group=mars, intended_std_level_id='ps')
|
||||||
rfc = IndividualRfcFactory.create(stream_id='irtf', other_aliases=['rfc6666',], states=[('draft','rfc'),('draft-iesg','pub')], std_level_id='inf', )
|
rfc = IndividualRfcFactory.create(stream_id='irtf', rfc_number=6666, std_level_id='inf', )
|
||||||
wgdraft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'), relationship_id='refnorm')
|
wgdraft.relateddocument_set.create(target=rfc, relationship_id='refnorm')
|
||||||
ise_draft = IndividualDraftFactory(name='draft-imaginary-independent-submission')
|
ise_draft = IndividualDraftFactory(name='draft-imaginary-independent-submission')
|
||||||
ise_draft.stream = StreamName.objects.get(slug="ise")
|
ise_draft.stream = StreamName.objects.get(slug="ise")
|
||||||
ise_draft.save_with_history([DocEvent(doc=ise_draft, rev=ise_draft.rev, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")])
|
ise_draft.save_with_history([DocEvent(doc=ise_draft, rev=ise_draft.rev, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")])
|
||||||
|
@ -238,7 +238,7 @@ class IESGAgendaTests(TestCase):
|
||||||
|
|
||||||
relation = RelatedDocument.objects.create(
|
relation = RelatedDocument.objects.create(
|
||||||
source=statchg,
|
source=statchg,
|
||||||
target=DocAlias.objects.filter(name__startswith='rfc', docs__std_level="ps")[0],
|
target=Document.objects.filter(type_id="rfc", std_level="ps").first(),
|
||||||
relationship_id="tohist")
|
relationship_id="tohist")
|
||||||
|
|
||||||
statchg.group = Group.objects.get(acronym="mars")
|
statchg.group = Group.objects.get(acronym="mars")
|
||||||
|
@ -256,7 +256,7 @@ class IESGAgendaTests(TestCase):
|
||||||
self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.3"]["docs"])
|
self.assertTrue(statchg in agenda_data(date_str)["sections"]["2.3.3"]["docs"])
|
||||||
|
|
||||||
# 3.3 document status changes
|
# 3.3 document status changes
|
||||||
relation.target = DocAlias.objects.filter(name__startswith='rfc', docs__std_level="inf")[0]
|
relation.target = Document.objects.filter(type_id="rfc", std_level="inf").first()
|
||||||
relation.save()
|
relation.save()
|
||||||
|
|
||||||
statchg.group = Group.objects.get(acronym="mars")
|
statchg.group = Group.objects.get(acronym="mars")
|
||||||
|
|
|
@ -32,10 +32,10 @@ def telechat_page_count(date=None, docs=None):
|
||||||
pages_for_action += d.pages or 0
|
pages_for_action += d.pages or 0
|
||||||
elif d.type_id == 'statchg':
|
elif d.type_id == 'statchg':
|
||||||
for rel in d.related_that_doc(STATUSCHANGE_RELATIONS):
|
for rel in d.related_that_doc(STATUSCHANGE_RELATIONS):
|
||||||
pages_for_action += rel.document.pages or 0
|
pages_for_action += rel.pages or 0
|
||||||
elif d.type_id == 'conflrev':
|
elif d.type_id == 'conflrev':
|
||||||
for rel in d.related_that_doc('conflrev'):
|
for rel in d.related_that_doc('conflrev'):
|
||||||
pages_for_action += rel.document.pages or 0
|
pages_for_action += rel.pages or 0
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ def telechat_page_count(date=None, docs=None):
|
||||||
for d in for_approval-set(drafts):
|
for d in for_approval-set(drafts):
|
||||||
if d.type_id == 'statchg':
|
if d.type_id == 'statchg':
|
||||||
for rel in d.related_that_doc(STATUSCHANGE_RELATIONS):
|
for rel in d.related_that_doc(STATUSCHANGE_RELATIONS):
|
||||||
related_pages += rel.document.pages or 0
|
related_pages += rel.pages or 0
|
||||||
elif d.type_id == 'conflrev':
|
elif d.type_id == 'conflrev':
|
||||||
for rel in d.related_that_doc('conflrev'):
|
for rel in d.related_that_doc('conflrev'):
|
||||||
related_pages += rel.document.pages or 0
|
related_pages += rel.pages or 0
|
||||||
else:
|
else:
|
||||||
# There's really nothing to rely on to give a reading load estimate for charters
|
# There's really nothing to rely on to give a reading load estimate for charters
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -151,8 +151,8 @@ def agenda_json(request, date=None):
|
||||||
if doc.type_id == "draft":
|
if doc.type_id == "draft":
|
||||||
docinfo['rev'] = doc.rev
|
docinfo['rev'] = doc.rev
|
||||||
docinfo['intended-std-level'] = str(doc.intended_std_level)
|
docinfo['intended-std-level'] = str(doc.intended_std_level)
|
||||||
if doc.rfc_number():
|
if doc.type_id == "rfc":
|
||||||
docinfo['rfc-number'] = doc.rfc_number()
|
docinfo['rfc-number'] = doc.rfc_number
|
||||||
|
|
||||||
iana_state = doc.get_state("draft-iana-review")
|
iana_state = doc.get_state("draft-iana-review")
|
||||||
if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"):
|
if iana_state and iana_state.slug in ("not-ok", "changed", "need-rev"):
|
||||||
|
@ -172,7 +172,7 @@ def agenda_json(request, date=None):
|
||||||
|
|
||||||
elif doc.type_id == 'conflrev':
|
elif doc.type_id == 'conflrev':
|
||||||
docinfo['rev'] = doc.rev
|
docinfo['rev'] = doc.rev
|
||||||
td = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
|
td = doc.relateddocument_set.get(relationship__slug='conflrev').target
|
||||||
docinfo['target-docname'] = td.canonical_name()
|
docinfo['target-docname'] = td.canonical_name()
|
||||||
docinfo['target-title'] = td.title
|
docinfo['target-title'] = td.title
|
||||||
docinfo['target-rev'] = td.rev
|
docinfo['target-rev'] = td.rev
|
||||||
|
|
|
@ -94,7 +94,7 @@ admin.site.register(IprDocRel, IprDocRelAdmin)
|
||||||
|
|
||||||
class RelatedIprAdmin(admin.ModelAdmin):
|
class RelatedIprAdmin(admin.ModelAdmin):
|
||||||
list_display = ['source', 'target', 'relationship', ]
|
list_display = ['source', 'target', 'relationship', ]
|
||||||
search_fields = ['source__name', 'target__name', 'target__docs__name', ]
|
search_fields = ['source__name', 'target__name', ]
|
||||||
raw_id_fields = ['source', 'target', ]
|
raw_id_fields = ['source', 'target', ]
|
||||||
admin.site.register(RelatedIpr, RelatedIprAdmin)
|
admin.site.register(RelatedIpr, RelatedIprAdmin)
|
||||||
|
|
||||||
|
|
|
@ -41,24 +41,17 @@ def iprs_from_docs(aliases,**kwargs):
|
||||||
iprdocrels += document.ipr(**kwargs)
|
iprdocrels += document.ipr(**kwargs)
|
||||||
return list(set([i.disclosure for i in iprdocrels]))
|
return list(set([i.disclosure for i in iprdocrels]))
|
||||||
|
|
||||||
def related_docs(alias, relationship=('replaces', 'obs')):
|
def related_docs(doc, relationship=('replaces', 'obs')):
|
||||||
"""Returns list of related documents"""
|
"""Returns list of related documents"""
|
||||||
|
|
||||||
results = []
|
results = [doc]
|
||||||
for doc in alias.docs.all():
|
|
||||||
results += list(doc.docalias.all())
|
rels = list(doc.document.all_relations_that_doc(relationship))
|
||||||
|
|
||||||
rels = []
|
|
||||||
for doc in alias.docs.all():
|
|
||||||
rels += list(doc.all_relations_that_doc(relationship))
|
|
||||||
|
|
||||||
for rel in rels:
|
for rel in rels:
|
||||||
rel_aliases = list(rel.target.document.docalias.all())
|
rel.target.related = rel
|
||||||
|
rel.target.relation = rel.relationship.revname
|
||||||
for x in rel_aliases:
|
results += [x.target for x in rels]
|
||||||
x.related = rel
|
|
||||||
x.relation = rel.relationship.revname
|
|
||||||
results += rel_aliases
|
|
||||||
|
|
||||||
return list(set(results))
|
return list(set(results))
|
||||||
|
|
||||||
|
|
|
@ -96,35 +96,35 @@ class Recipient(models.Model):
|
||||||
addrs = []
|
addrs = []
|
||||||
if 'doc' in kwargs:
|
if 'doc' in kwargs:
|
||||||
for reldoc in kwargs['doc'].related_that_doc(('conflrev','tohist','tois','tops')):
|
for reldoc in kwargs['doc'].related_that_doc(('conflrev','tohist','tois','tops')):
|
||||||
addrs.extend(Recipient.objects.get(slug='doc_authors').gather(**{'doc':reldoc.document}))
|
addrs.extend(Recipient.objects.get(slug='doc_authors').gather(**{'doc':reldoc}))
|
||||||
return addrs
|
return addrs
|
||||||
|
|
||||||
def gather_doc_affecteddoc_group_chairs(self, **kwargs):
|
def gather_doc_affecteddoc_group_chairs(self, **kwargs):
|
||||||
addrs = []
|
addrs = []
|
||||||
if 'doc' in kwargs:
|
if 'doc' in kwargs:
|
||||||
for reldoc in kwargs['doc'].related_that_doc(('conflrev','tohist','tois','tops')):
|
for reldoc in kwargs['doc'].related_that_doc(('conflrev','tohist','tois','tops')):
|
||||||
addrs.extend(Recipient.objects.get(slug='doc_group_chairs').gather(**{'doc':reldoc.document}))
|
addrs.extend(Recipient.objects.get(slug='doc_group_chairs').gather(**{'doc':reldoc}))
|
||||||
return addrs
|
return addrs
|
||||||
|
|
||||||
def gather_doc_affecteddoc_notify(self, **kwargs):
|
def gather_doc_affecteddoc_notify(self, **kwargs):
|
||||||
addrs = []
|
addrs = []
|
||||||
if 'doc' in kwargs:
|
if 'doc' in kwargs:
|
||||||
for reldoc in kwargs['doc'].related_that_doc(('conflrev','tohist','tois','tops')):
|
for reldoc in kwargs['doc'].related_that_doc(('conflrev','tohist','tois','tops')):
|
||||||
addrs.extend(Recipient.objects.get(slug='doc_notify').gather(**{'doc':reldoc.document}))
|
addrs.extend(Recipient.objects.get(slug='doc_notify').gather(**{'doc':reldoc}))
|
||||||
return addrs
|
return addrs
|
||||||
|
|
||||||
def gather_conflict_review_stream_manager(self, **kwargs):
|
def gather_conflict_review_stream_manager(self, **kwargs):
|
||||||
addrs = []
|
addrs = []
|
||||||
if 'doc' in kwargs:
|
if 'doc' in kwargs:
|
||||||
for reldoc in kwargs['doc'].related_that_doc(('conflrev',)):
|
for reldoc in kwargs['doc'].related_that_doc(('conflrev',)):
|
||||||
addrs.extend(Recipient.objects.get(slug='doc_stream_manager').gather(**{'doc':reldoc.document}))
|
addrs.extend(Recipient.objects.get(slug='doc_stream_manager').gather(**{'doc':reldoc}))
|
||||||
return addrs
|
return addrs
|
||||||
|
|
||||||
def gather_conflict_review_steering_group(self,**kwargs):
|
def gather_conflict_review_steering_group(self,**kwargs):
|
||||||
addrs = []
|
addrs = []
|
||||||
if 'doc' in kwargs:
|
if 'doc' in kwargs:
|
||||||
for reldoc in kwargs['doc'].related_that_doc(('conflrev',)):
|
for reldoc in kwargs['doc'].related_that_doc(('conflrev',)):
|
||||||
if reldoc.document.stream_id=='irtf':
|
if reldoc.stream_id=='irtf':
|
||||||
addrs.append('"Internet Research Steering Group" <irsg@irtf.org>')
|
addrs.append('"Internet Research Steering Group" <irsg@irtf.org>')
|
||||||
return addrs
|
return addrs
|
||||||
|
|
||||||
|
|
|
@ -2565,6 +2565,19 @@
|
||||||
"model": "doc.state",
|
"model": "doc.state",
|
||||||
"pk": 176
|
"pk": 176
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"desc": "",
|
||||||
|
"name": "Published",
|
||||||
|
"next_states": [],
|
||||||
|
"order": 1,
|
||||||
|
"slug": "published",
|
||||||
|
"type": "rfc",
|
||||||
|
"used": true
|
||||||
|
},
|
||||||
|
"model": "doc.state",
|
||||||
|
"pk": 177
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"label": "State"
|
"label": "State"
|
||||||
|
@ -2747,6 +2760,13 @@
|
||||||
"model": "doc.statetype",
|
"model": "doc.statetype",
|
||||||
"pk": "review"
|
"pk": "review"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"label": "State"
|
||||||
|
},
|
||||||
|
"model": "doc.statetype",
|
||||||
|
"pk": "rfc"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"label": "Shepherd's Writeup State"
|
"label": "Shepherd's Writeup State"
|
||||||
|
@ -9933,6 +9953,17 @@
|
||||||
"model": "name.dbtemplatetypename",
|
"model": "name.dbtemplatetypename",
|
||||||
"pk": "rst"
|
"pk": "rst"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"desc": "",
|
||||||
|
"name": "Became RFC",
|
||||||
|
"order": 0,
|
||||||
|
"revname": "Became RFC as",
|
||||||
|
"used": true
|
||||||
|
},
|
||||||
|
"model": "name.docrelationshipname",
|
||||||
|
"pk": "became_rfc"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"desc": "",
|
"desc": "",
|
||||||
|
@ -10603,6 +10634,17 @@
|
||||||
"model": "name.doctypename",
|
"model": "name.doctypename",
|
||||||
"pk": "review"
|
"pk": "review"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"desc": "",
|
||||||
|
"name": "RFC",
|
||||||
|
"order": 0,
|
||||||
|
"prefix": "rfc",
|
||||||
|
"used": true
|
||||||
|
},
|
||||||
|
"model": "name.doctypename",
|
||||||
|
"pk": "rfc"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"desc": "",
|
"desc": "",
|
||||||
|
|
30
ietf/name/migrations/0004_rfc_doctype_names.py
Normal file
30
ietf/name/migrations/0004_rfc_doctype_names.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Generated by Django 4.2.2 on 2023-06-14 20:39
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
DocTypeName = apps.get_model("name", "DocTypeName")
|
||||||
|
DocTypeName.objects.get_or_create(
|
||||||
|
slug="rfc",
|
||||||
|
name="RFC",
|
||||||
|
used=True,
|
||||||
|
prefix="rfc",
|
||||||
|
)
|
||||||
|
|
||||||
|
DocRelationshipName = apps.get_model("name", "DocRelationshipName")
|
||||||
|
DocRelationshipName.objects.get_or_create(
|
||||||
|
slug="became_rfc",
|
||||||
|
name="became RFC",
|
||||||
|
used=True,
|
||||||
|
revname="came from draft",
|
||||||
|
)
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("name", "0003_populate_telechatagendasectionname"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward),
|
||||||
|
]
|
|
@ -42,7 +42,7 @@ class DocRelationshipName(NameModel):
|
||||||
|
|
||||||
class DocTypeName(NameModel):
|
class DocTypeName(NameModel):
|
||||||
"""Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email,
|
"""Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email,
|
||||||
Review, Issue, Wiki"""
|
Review, Issue, Wiki, RFC"""
|
||||||
prefix = models.CharField(max_length=16, default="")
|
prefix = models.CharField(max_length=16, default="")
|
||||||
class DocTagName(NameModel):
|
class DocTagName(NameModel):
|
||||||
"""Waiting for Reference, IANA Coordination, Revised ID Needed,
|
"""Waiting for Reference, IANA Coordination, Revised ID Needed,
|
||||||
|
|
|
@ -50,6 +50,8 @@ def can_request_review_of_doc(user, doc):
|
||||||
if not user.is_authenticated:
|
if not user.is_authenticated:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# This is in a strange place as it has nothing to do with the user
|
||||||
|
# but this utility is used in too many places to move this quickly.
|
||||||
if doc.type_id == 'draft' and doc.get_state_slug() != 'active':
|
if doc.type_id == 'draft' and doc.get_state_slug() != 'active':
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
@ -67,10 +67,8 @@ class SecrTelechatTestCase(TestCase):
|
||||||
def test_doc_detail_draft_with_downref(self):
|
def test_doc_detail_draft_with_downref(self):
|
||||||
ad = Person.objects.get(user__username="ad")
|
ad = Person.objects.get(user__username="ad")
|
||||||
draft = WgDraftFactory(ad=ad, intended_std_level_id='ps', states=[('draft-iesg','pub-req'),])
|
draft = WgDraftFactory(ad=ad, intended_std_level_id='ps', states=[('draft-iesg','pub-req'),])
|
||||||
rfc = IndividualRfcFactory.create(stream_id='irtf', other_aliases=['rfc6666',],
|
rfc = IndividualRfcFactory.create(stream_id='irtf', rfc_number=6666, std_level_id='inf')
|
||||||
states=[('draft','rfc'),('draft-iesg','pub')], std_level_id='inf', )
|
draft.relateddocument_set.create(target=rfc, relationship_id='refnorm')
|
||||||
draft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'),
|
|
||||||
relationship_id='refnorm')
|
|
||||||
create_ballot_if_not_open(None, draft, ad, 'approve')
|
create_ballot_if_not_open(None, draft, ad, 'approve')
|
||||||
d = get_next_telechat_date()
|
d = get_next_telechat_date()
|
||||||
date = d.strftime('%Y-%m-%d')
|
date = d.strftime('%Y-%m-%d')
|
||||||
|
|
|
@ -313,7 +313,7 @@ def doc_detail(request, date, name):
|
||||||
|
|
||||||
# if this is a conflict review document add referenced document
|
# if this is a conflict review document add referenced document
|
||||||
if doc.type_id == 'conflrev':
|
if doc.type_id == 'conflrev':
|
||||||
conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target.document
|
conflictdoc = doc.relateddocument_set.get(relationship__slug='conflrev').target
|
||||||
else:
|
else:
|
||||||
conflictdoc = None
|
conflictdoc = None
|
||||||
|
|
||||||
|
|
|
@ -85,13 +85,13 @@
|
||||||
{% if downrefs %}
|
{% if downrefs %}
|
||||||
<h2 id="downrefs">Downward References</h2>
|
<h2 id="downrefs">Downward References</h2>
|
||||||
{% for ref in downrefs %}
|
{% for ref in downrefs %}
|
||||||
<p>Add {{ref.target.document.canonical_name}}
|
<p>Add {{ref.target.canonical_name}}
|
||||||
({{ref.target.document.std_level}} - {{ref.target.document.stream.desc}})
|
({{ref.target.std_level}} - {{ref.target.stream.desc}})
|
||||||
to downref registry.<br>
|
to downref registry.<br>
|
||||||
{% if not ref.target.document.std_level %}
|
{% if not ref.target.std_level %}
|
||||||
+++ Warning: The standards level has not been set yet!!!<br>
|
+++ Warning: The standards level has not been set yet!!!<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not ref.target.document.stream %}
|
{% if not ref.target.stream %}
|
||||||
+++ Warning: document stream has not been set yet!!!<br>
|
+++ Warning: document stream has not been set yet!!!<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}</p>
|
{% endfor %}</p>
|
||||||
|
|
|
@ -3,11 +3,8 @@
|
||||||
|
|
||||||
|
|
||||||
# Python imports
|
# Python imports
|
||||||
import io
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Django imports
|
# Django imports
|
||||||
from django.conf import settings
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
# Datatracker imports
|
# Datatracker imports
|
||||||
|
@ -15,27 +12,6 @@ from ietf.group.models import Group
|
||||||
from ietf.ietfauth.utils import has_role
|
from ietf.ietfauth.utils import has_role
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def current_nomcom():
|
|
||||||
qs = Group.objects.filter(acronym__startswith='nomcom',state__slug="active").order_by('-time')
|
|
||||||
if qs.count():
|
|
||||||
return qs[0]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_charter_text(group):
|
|
||||||
'''
|
|
||||||
Takes a group object and returns the text or the group's charter as a string
|
|
||||||
'''
|
|
||||||
charter = group.charter
|
|
||||||
path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter.canonical_name(), charter.rev))
|
|
||||||
f = io.open(path,'r')
|
|
||||||
text = f.read()
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
return text
|
|
||||||
|
|
||||||
def get_my_groups(user,conclude=False):
|
def get_my_groups(user,conclude=False):
|
||||||
'''
|
'''
|
||||||
Takes a Django user object (from request)
|
Takes a Django user object (from request)
|
||||||
|
|
|
@ -705,7 +705,7 @@ LIAISON_ATTACH_URL = 'https://www.ietf.org/lib/dt/documents/LIAISON/' # should e
|
||||||
DOC_HREFS = {
|
DOC_HREFS = {
|
||||||
"charter": "https://www.ietf.org/charter/{doc.name}-{doc.rev}.txt",
|
"charter": "https://www.ietf.org/charter/{doc.name}-{doc.rev}.txt",
|
||||||
"draft": "https://www.ietf.org/archive/id/{doc.name}-{doc.rev}.txt",
|
"draft": "https://www.ietf.org/archive/id/{doc.name}-{doc.rev}.txt",
|
||||||
"rfc": "https://www.rfc-editor.org/rfc/rfc{doc.rfcnum}.txt",
|
"rfc": "https://www.rfc-editor.org/rfc/rfc{doc.rfc_number}.txt",
|
||||||
"slides": "https://www.ietf.org/slides/{doc.name}-{doc.rev}",
|
"slides": "https://www.ietf.org/slides/{doc.name}-{doc.rev}",
|
||||||
"procmaterials": "https://www.ietf.org/procmaterials/{doc.name}-{doc.rev}",
|
"procmaterials": "https://www.ietf.org/procmaterials/{doc.name}-{doc.rev}",
|
||||||
"conflrev": "https://www.ietf.org/cr/{doc.name}-{doc.rev}.txt",
|
"conflrev": "https://www.ietf.org/cr/{doc.name}-{doc.rev}.txt",
|
||||||
|
|
|
@ -82,7 +82,7 @@ class StatisticsTests(TestCase):
|
||||||
DocAlias.objects.create(name=referencing_draft.name).docs.add(referencing_draft)
|
DocAlias.objects.create(name=referencing_draft.name).docs.add(referencing_draft)
|
||||||
RelatedDocument.objects.create(
|
RelatedDocument.objects.create(
|
||||||
source=referencing_draft,
|
source=referencing_draft,
|
||||||
target=draft.docalias.first(),
|
target=draft,
|
||||||
relationship=DocRelationshipName.objects.get(slug="refinfo")
|
relationship=DocRelationshipName.objects.get(slug="refinfo")
|
||||||
)
|
)
|
||||||
NewRevisionDocEvent.objects.create(
|
NewRevisionDocEvent.objects.create(
|
||||||
|
|
|
@ -563,11 +563,11 @@ def document_stats(request, stats_type=None):
|
||||||
bins = defaultdict(set)
|
bins = defaultdict(set)
|
||||||
|
|
||||||
cite_relationships = list(DocRelationshipName.objects.filter(slug__in=['refnorm', 'refinfo', 'refunk', 'refold']))
|
cite_relationships = list(DocRelationshipName.objects.filter(slug__in=['refnorm', 'refinfo', 'refunk', 'refold']))
|
||||||
person_filters &= Q(documentauthor__document__docalias__relateddocument__relationship__in=cite_relationships)
|
person_filters &= Q(documentauthor__document__relateddocument__relationship__in=cite_relationships)
|
||||||
|
|
||||||
person_qs = Person.objects.filter(person_filters)
|
person_qs = Person.objects.filter(person_filters)
|
||||||
|
|
||||||
for name, citations in person_qs.values_list("name").annotate(Count("documentauthor__document__docalias__relateddocument")):
|
for name, citations in person_qs.values_list("name").annotate(Count("documentauthor__document__relateddocument")):
|
||||||
bins[citations or 0].add(name)
|
bins[citations or 0].add(name)
|
||||||
|
|
||||||
total_persons = count_bins(bins)
|
total_persons = count_bins(bins)
|
||||||
|
@ -587,11 +587,11 @@ def document_stats(request, stats_type=None):
|
||||||
bins = defaultdict(set)
|
bins = defaultdict(set)
|
||||||
|
|
||||||
cite_relationships = list(DocRelationshipName.objects.filter(slug__in=['refnorm', 'refinfo', 'refunk', 'refold']))
|
cite_relationships = list(DocRelationshipName.objects.filter(slug__in=['refnorm', 'refinfo', 'refunk', 'refold']))
|
||||||
person_filters &= Q(documentauthor__document__docalias__relateddocument__relationship__in=cite_relationships)
|
person_filters &= Q(documentauthor__document__relateddocument__relationship__in=cite_relationships)
|
||||||
|
|
||||||
person_qs = Person.objects.filter(person_filters)
|
person_qs = Person.objects.filter(person_filters)
|
||||||
|
|
||||||
values = person_qs.values_list("name", "documentauthor__document").annotate(Count("documentauthor__document__docalias__relateddocument"))
|
values = person_qs.values_list("name", "documentauthor__document").annotate(Count("documentauthor__document__relateddocument"))
|
||||||
for name, ts in itertools.groupby(values.order_by("name"), key=lambda t: t[0]):
|
for name, ts in itertools.groupby(values.order_by("name"), key=lambda t: t[0]):
|
||||||
h_index = compute_hirsch_index([citations for _, document, citations in ts])
|
h_index = compute_hirsch_index([citations for _, document, citations in ts])
|
||||||
bins[h_index or 0].add(name)
|
bins[h_index or 0].add(name)
|
||||||
|
|
|
@ -28,8 +28,7 @@ import debug # pyflakes:ignore
|
||||||
from ietf.doc.models import Document
|
from ietf.doc.models import Document
|
||||||
from ietf.group.models import Group
|
from ietf.group.models import Group
|
||||||
from ietf.ietfauth.utils import has_role
|
from ietf.ietfauth.utils import has_role
|
||||||
from ietf.doc.fields import SearchableDocAliasesField
|
from ietf.doc.fields import SearchableDocumentsField
|
||||||
from ietf.doc.models import DocAlias
|
|
||||||
from ietf.ipr.mail import utc_from_string
|
from ietf.ipr.mail import utc_from_string
|
||||||
from ietf.meeting.models import Meeting
|
from ietf.meeting.models import Meeting
|
||||||
from ietf.message.models import Message
|
from ietf.message.models import Message
|
||||||
|
@ -684,9 +683,9 @@ class SubmissionAutoUploadForm(SubmissionBaseUploadForm):
|
||||||
if self.cleaned_data['replaces']:
|
if self.cleaned_data['replaces']:
|
||||||
names_replaced = [s.strip() for s in self.cleaned_data['replaces'].split(',')]
|
names_replaced = [s.strip() for s in self.cleaned_data['replaces'].split(',')]
|
||||||
self.cleaned_data['replaces'] = ','.join(names_replaced)
|
self.cleaned_data['replaces'] = ','.join(names_replaced)
|
||||||
aliases_replaced = DocAlias.objects.filter(name__in=names_replaced)
|
documents_replaced = Document.objects.filter(name__in=names_replaced)
|
||||||
if len(names_replaced) != len(aliases_replaced):
|
if len(names_replaced) != len(documents_replaced):
|
||||||
known_names = aliases_replaced.values_list('name', flat=True)
|
known_names = documents_replaced.values_list('name', flat=True)
|
||||||
unknown_names = [n for n in names_replaced if n not in known_names]
|
unknown_names = [n for n in names_replaced if n not in known_names]
|
||||||
self.add_error(
|
self.add_error(
|
||||||
'replaces',
|
'replaces',
|
||||||
|
@ -694,27 +693,27 @@ class SubmissionAutoUploadForm(SubmissionBaseUploadForm):
|
||||||
'Unknown Internet-Draft name(s): ' + ', '.join(unknown_names)
|
'Unknown Internet-Draft name(s): ' + ', '.join(unknown_names)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for alias in aliases_replaced:
|
for doc in documents_replaced:
|
||||||
if alias.document.name == self.filename:
|
if doc.name == self.filename:
|
||||||
self.add_error(
|
self.add_error(
|
||||||
'replaces',
|
'replaces',
|
||||||
forms.ValidationError("An Internet-Draft cannot replace itself"),
|
forms.ValidationError("An Internet-Draft cannot replace itself"),
|
||||||
)
|
)
|
||||||
elif alias.document.type_id != "draft":
|
elif doc.type_id != "draft":
|
||||||
self.add_error(
|
self.add_error(
|
||||||
'replaces',
|
'replaces',
|
||||||
forms.ValidationError("An Internet-Draft can only replace another Internet-Draft"),
|
forms.ValidationError("An Internet-Draft can only replace another Internet-Draft"),
|
||||||
)
|
)
|
||||||
elif alias.document.get_state_slug() == "rfc":
|
elif doc.get_state_slug() == "rfc":
|
||||||
self.add_error(
|
self.add_error(
|
||||||
'replaces',
|
'replaces',
|
||||||
forms.ValidationError("An Internet-Draft cannot replace an RFC"),
|
forms.ValidationError("An Internet-Draft cannot replace another Internet-Draft that has become an RFC"),
|
||||||
)
|
)
|
||||||
elif alias.document.get_state_slug('draft-iesg') in ('approved', 'ann', 'rfcqueue'):
|
elif doc.get_state_slug('draft-iesg') in ('approved', 'ann', 'rfcqueue'):
|
||||||
self.add_error(
|
self.add_error(
|
||||||
'replaces',
|
'replaces',
|
||||||
forms.ValidationError(
|
forms.ValidationError(
|
||||||
alias.name + " is approved by the IESG and cannot be replaced"
|
doc.name + " is approved by the IESG and cannot be replaced"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return cleaned_data
|
return cleaned_data
|
||||||
|
@ -755,22 +754,20 @@ class SubmitterForm(NameEmailForm):
|
||||||
return line
|
return line
|
||||||
|
|
||||||
class ReplacesForm(forms.Form):
|
class ReplacesForm(forms.Form):
|
||||||
replaces = SearchableDocAliasesField(required=False, help_text="Any Internet-Drafts that this document replaces (approval required for replacing an Internet-Draft you are not the author of)")
|
replaces = SearchableDocumentsField(required=False, help_text="Any Internet-Drafts that this document replaces (approval required for replacing an Internet-Draft you are not the author of)")
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.name = kwargs.pop("name")
|
self.name = kwargs.pop("name")
|
||||||
super(ReplacesForm, self).__init__(*args, **kwargs)
|
super(ReplacesForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def clean_replaces(self):
|
def clean_replaces(self):
|
||||||
for alias in self.cleaned_data['replaces']:
|
for doc in self.cleaned_data['replaces']:
|
||||||
if alias.document.name == self.name:
|
if doc.name == self.name:
|
||||||
raise forms.ValidationError("An Internet-Draft cannot replace itself.")
|
raise forms.ValidationError("An Internet-Draft cannot replace itself.")
|
||||||
if alias.document.type_id != "draft":
|
if doc.type_id != "draft":
|
||||||
raise forms.ValidationError("An Internet-Draft can only replace another Internet-Draft")
|
raise forms.ValidationError("An Internet-Draft can only replace another Internet-Draft")
|
||||||
if alias.document.get_state_slug() == "rfc":
|
if doc.get_state_slug('draft-iesg') in ('approved','ann','rfcqueue'):
|
||||||
raise forms.ValidationError("An Internet-Draft cannot replace an RFC")
|
raise forms.ValidationError(doc.name+" is approved by the IESG and cannot be replaced")
|
||||||
if alias.document.get_state_slug('draft-iesg') in ('approved','ann','rfcqueue'):
|
|
||||||
raise forms.ValidationError(alias.name+" is approved by the IESG and cannot be replaced")
|
|
||||||
return self.cleaned_data['replaces']
|
return self.cleaned_data['replaces']
|
||||||
|
|
||||||
class EditSubmissionForm(forms.ModelForm):
|
class EditSubmissionForm(forms.ModelForm):
|
||||||
|
|
|
@ -32,7 +32,7 @@ from ietf.submit.utils import (expirable_submissions, expire_submission, find_su
|
||||||
process_and_accept_uploaded_submission, SubmissionError, process_submission_text,
|
process_and_accept_uploaded_submission, SubmissionError, process_submission_text,
|
||||||
process_submission_xml, process_uploaded_submission,
|
process_submission_xml, process_uploaded_submission,
|
||||||
process_and_validate_submission)
|
process_and_validate_submission)
|
||||||
from ietf.doc.factories import (DocumentFactory, WgDraftFactory, IndividualDraftFactory, IndividualRfcFactory,
|
from ietf.doc.factories import (DocumentFactory, WgDraftFactory, IndividualDraftFactory,
|
||||||
ReviewFactory, WgRfcFactory)
|
ReviewFactory, WgRfcFactory)
|
||||||
from ietf.doc.models import ( Document, DocAlias, DocEvent, State,
|
from ietf.doc.models import ( Document, DocAlias, DocEvent, State,
|
||||||
BallotPositionDocEvent, DocumentAuthor, SubmissionDocEvent )
|
BallotPositionDocEvent, DocumentAuthor, SubmissionDocEvent )
|
||||||
|
@ -302,7 +302,7 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
submission = Submission.objects.get(name=name)
|
submission = Submission.objects.get(name=name)
|
||||||
self.assertEqual(submission.submitter, email.utils.formataddr((submitter_name, submitter_email)))
|
self.assertEqual(submission.submitter, email.utils.formataddr((submitter_name, submitter_email)))
|
||||||
self.assertEqual([] if submission.replaces == "" else submission.replaces.split(','),
|
self.assertEqual([] if submission.replaces == "" else submission.replaces.split(','),
|
||||||
[ d.name for d in DocAlias.objects.filter(pk__in=replaces) ])
|
[ d.name for d in Document.objects.filter(pk__in=replaces) ])
|
||||||
self.assertCountEqual(
|
self.assertCountEqual(
|
||||||
[str(r) for r in submission.external_resources.all()],
|
[str(r) for r in submission.external_resources.all()],
|
||||||
[str(r) for r in extresources] if extresources else [],
|
[str(r) for r in extresources] if extresources else [],
|
||||||
|
@ -369,9 +369,8 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
|
|
||||||
# supply submitter info, then draft should be in and ready for approval
|
# supply submitter info, then draft should be in and ready for approval
|
||||||
mailbox_before = len(outbox)
|
mailbox_before = len(outbox)
|
||||||
replaced_alias = draft.docalias.first()
|
|
||||||
r = self.supply_extra_metadata(name, status_url, author.ascii, author.email().address.lower(),
|
r = self.supply_extra_metadata(name, status_url, author.ascii, author.email().address.lower(),
|
||||||
replaces=[str(replaced_alias.pk), str(sug_replaced_alias.pk)])
|
replaces=[str(draft.pk), str(sug_replaced_draft.pk)])
|
||||||
|
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
status_url = r["Location"]
|
status_url = r["Location"]
|
||||||
|
@ -419,7 +418,7 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
self.assertEqual(authors[0].person, author)
|
self.assertEqual(authors[0].person, author)
|
||||||
self.assertEqual(set(draft.formal_languages.all()), set(FormalLanguageName.objects.filter(slug="json")))
|
self.assertEqual(set(draft.formal_languages.all()), set(FormalLanguageName.objects.filter(slug="json")))
|
||||||
self.assertEqual(draft.relations_that_doc("replaces").count(), 1)
|
self.assertEqual(draft.relations_that_doc("replaces").count(), 1)
|
||||||
self.assertTrue(draft.relations_that_doc("replaces").first().target, replaced_alias)
|
self.assertTrue(draft.relations_that_doc("replaces").first().target, draft)
|
||||||
self.assertEqual(draft.relations_that_doc("possibly-replaces").count(), 1)
|
self.assertEqual(draft.relations_that_doc("possibly-replaces").count(), 1)
|
||||||
self.assertTrue(draft.relations_that_doc("possibly-replaces").first().target, sug_replaced_alias)
|
self.assertTrue(draft.relations_that_doc("possibly-replaces").first().target, sug_replaced_alias)
|
||||||
self.assertEqual(len(outbox), mailbox_before + 5)
|
self.assertEqual(len(outbox), mailbox_before + 5)
|
||||||
|
@ -1142,7 +1141,7 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
self.verify_bibxml_ids_creation(draft)
|
self.verify_bibxml_ids_creation(draft)
|
||||||
|
|
||||||
def test_submit_update_individual(self):
|
def test_submit_update_individual(self):
|
||||||
IndividualDraftFactory(name='draft-ietf-random-thing', states=[('draft','rfc')], other_aliases=['rfc9999',], pages=5)
|
IndividualDraftFactory(name='draft-ietf-random-thing', states=[('draft','active'),('draft-iesg','approved')], pages=5)
|
||||||
ad=Person.objects.get(user__username='ad')
|
ad=Person.objects.get(user__username='ad')
|
||||||
# Group of None here does not reflect real individual submissions
|
# Group of None here does not reflect real individual submissions
|
||||||
draft = IndividualDraftFactory(group=None, ad = ad, authors=[ad,], notify='aliens@example.mars', pages=5)
|
draft = IndividualDraftFactory(group=None, ad = ad, authors=[ad,], notify='aliens@example.mars', pages=5)
|
||||||
|
@ -1152,23 +1151,14 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
status_url, author = self.do_submission(name,rev)
|
status_url, author = self.do_submission(name,rev)
|
||||||
mailbox_before = len(outbox)
|
mailbox_before = len(outbox)
|
||||||
|
|
||||||
replaced_alias = draft.docalias.first()
|
r = self.supply_extra_metadata(name, status_url, "Submitter Name", "author@example.com", replaces=[str(draft.pk)])
|
||||||
r = self.supply_extra_metadata(name, status_url, "Submitter Name", "author@example.com", replaces=[str(replaced_alias.pk)])
|
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, 'cannot replace itself')
|
self.assertContains(r, 'cannot replace itself')
|
||||||
self._assert_extresources_in_table(r, [])
|
self._assert_extresources_in_table(r, [])
|
||||||
self._assert_extresources_form(r, [])
|
self._assert_extresources_form(r, [])
|
||||||
|
|
||||||
replaced_alias = DocAlias.objects.get(name='draft-ietf-random-thing')
|
replaced = Document.objects.get(name='draft-ietf-random-thing')
|
||||||
r = self.supply_extra_metadata(name, status_url, "Submitter Name", "author@example.com", replaces=[str(replaced_alias.pk)])
|
r = self.supply_extra_metadata(name, status_url, "Submitter Name", "author@example.com", replaces=[str(replaced.pk)])
|
||||||
self.assertEqual(r.status_code, 200)
|
|
||||||
self.assertContains(r, 'cannot replace an RFC')
|
|
||||||
self._assert_extresources_in_table(r, [])
|
|
||||||
self._assert_extresources_form(r, [])
|
|
||||||
|
|
||||||
replaced_alias.document.set_state(State.objects.get(type='draft-iesg',slug='approved'))
|
|
||||||
replaced_alias.document.set_state(State.objects.get(type='draft',slug='active'))
|
|
||||||
r = self.supply_extra_metadata(name, status_url, "Submitter Name", "author@example.com", replaces=[str(replaced_alias.pk)])
|
|
||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, 'approved by the IESG and cannot')
|
self.assertContains(r, 'approved by the IESG and cannot')
|
||||||
self._assert_extresources_in_table(r, [])
|
self._assert_extresources_in_table(r, [])
|
||||||
|
@ -1262,7 +1252,7 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
status_url,
|
status_url,
|
||||||
"Submitter Name",
|
"Submitter Name",
|
||||||
"submitter@example.com",
|
"submitter@example.com",
|
||||||
replaces=[str(replaced_draft.docalias.first().pk)],
|
replaces=[str(replaced_draft.pk)],
|
||||||
)
|
)
|
||||||
|
|
||||||
submission = Submission.objects.get(name=name, rev=rev)
|
submission = Submission.objects.get(name=name, rev=rev)
|
||||||
|
@ -1412,7 +1402,7 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
"edit-pages": "123",
|
"edit-pages": "123",
|
||||||
"submitter-name": "Some Random Test Person",
|
"submitter-name": "Some Random Test Person",
|
||||||
"submitter-email": "random@example.com",
|
"submitter-email": "random@example.com",
|
||||||
"replaces": [str(draft.docalias.first().pk)],
|
"replaces": [str(draft.pk)],
|
||||||
"edit-note": "no comments",
|
"edit-note": "no comments",
|
||||||
"authors-0-name": "Person 1",
|
"authors-0-name": "Person 1",
|
||||||
"authors-0-email": "person1@example.com",
|
"authors-0-email": "person1@example.com",
|
||||||
|
@ -1431,7 +1421,7 @@ class SubmitTests(BaseSubmitTestCase):
|
||||||
self.assertEqual(submission.pages, 123)
|
self.assertEqual(submission.pages, 123)
|
||||||
self.assertEqual(submission.note, "no comments")
|
self.assertEqual(submission.note, "no comments")
|
||||||
self.assertEqual(submission.submitter, "Some Random Test Person <random@example.com>")
|
self.assertEqual(submission.submitter, "Some Random Test Person <random@example.com>")
|
||||||
self.assertEqual(submission.replaces, draft.docalias.first().name)
|
self.assertEqual(submission.replaces, draft.name)
|
||||||
self.assertEqual(submission.state_id, "manual")
|
self.assertEqual(submission.state_id, "manual")
|
||||||
|
|
||||||
authors = submission.authors
|
authors = submission.authors
|
||||||
|
@ -3099,13 +3089,15 @@ class SubmissionUploadFormTests(BaseSubmitTestCase):
|
||||||
|
|
||||||
# can't replace RFC
|
# can't replace RFC
|
||||||
rfc = WgRfcFactory()
|
rfc = WgRfcFactory()
|
||||||
|
draft = WgDraftFactory(states=[("draft", "rfc")])
|
||||||
|
draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc)
|
||||||
form = SubmissionAutoUploadForm(
|
form = SubmissionAutoUploadForm(
|
||||||
request_factory.get('/some/url'),
|
request_factory.get('/some/url'),
|
||||||
data={'user': auth.user.username, 'replaces': rfc.name},
|
data={'user': auth.user.username, 'replaces': draft.name},
|
||||||
files=files_dict,
|
files=files_dict,
|
||||||
)
|
)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertIn('An Internet-Draft cannot replace an RFC', form.errors['replaces'])
|
self.assertIn('An Internet-Draft cannot replace another Internet-Draft that has become an RFC', form.errors['replaces'])
|
||||||
|
|
||||||
# can't replace draft approved by iesg
|
# can't replace draft approved by iesg
|
||||||
existing_drafts[0].set_state(State.objects.get(type='draft-iesg', slug='approved'))
|
existing_drafts[0].set_state(State.objects.get(type='draft-iesg', slug='approved'))
|
||||||
|
@ -3697,25 +3689,9 @@ class RefsTests(BaseSubmitTestCase):
|
||||||
|
|
||||||
|
|
||||||
class PostSubmissionTests(BaseSubmitTestCase):
|
class PostSubmissionTests(BaseSubmitTestCase):
|
||||||
@override_settings(RFC_FILE_TYPES=('txt', 'xml'), IDSUBMIT_FILE_TYPES=('pdf', 'md'))
|
|
||||||
def test_find_submission_filenames_rfc(self):
|
|
||||||
"""Posting an RFC submission should use RFC_FILE_TYPES"""
|
|
||||||
rfc = IndividualRfcFactory()
|
|
||||||
path = Path(self.staging_dir)
|
|
||||||
for ext in ['txt', 'xml', 'pdf', 'md']:
|
|
||||||
(path / f'{rfc.name}-{rfc.rev}.{ext}').touch()
|
|
||||||
files = find_submission_filenames(rfc)
|
|
||||||
self.assertCountEqual(
|
|
||||||
files,
|
|
||||||
{
|
|
||||||
'txt': f'{path}/{rfc.name}-{rfc.rev}.txt',
|
|
||||||
'xml': f'{path}/{rfc.name}-{rfc.rev}.xml',
|
|
||||||
# should NOT find the pdf or md
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
@override_settings(RFC_FILE_TYPES=('txt', 'xml'), IDSUBMIT_FILE_TYPES=('pdf', 'md'))
|
@override_settings(RFC_FILE_TYPES=('txt', 'xml'), IDSUBMIT_FILE_TYPES=('pdf', 'md'))
|
||||||
def test_find_submission_filenames_draft(self):
|
def test_find_submission_filenames(self):
|
||||||
"""Posting an I-D submission should use IDSUBMIT_FILE_TYPES"""
|
"""Posting an I-D submission should use IDSUBMIT_FILE_TYPES"""
|
||||||
draft = WgDraftFactory()
|
draft = WgDraftFactory()
|
||||||
path = Path(self.staging_dir)
|
path = Path(self.staging_dir)
|
||||||
|
|
|
@ -287,7 +287,7 @@ def find_submission_filenames(draft):
|
||||||
"""
|
"""
|
||||||
path = pathlib.Path(settings.IDSUBMIT_STAGING_PATH)
|
path = pathlib.Path(settings.IDSUBMIT_STAGING_PATH)
|
||||||
stem = f'{draft.name}-{draft.rev}'
|
stem = f'{draft.name}-{draft.rev}'
|
||||||
allowed_types = settings.RFC_FILE_TYPES if draft.get_state_slug() == 'rfc' else settings.IDSUBMIT_FILE_TYPES
|
allowed_types = settings.IDSUBMIT_FILE_TYPES
|
||||||
candidates = {ext: path / f'{stem}.{ext}' for ext in allowed_types}
|
candidates = {ext: path / f'{stem}.{ext}' for ext in allowed_types}
|
||||||
return {ext: str(filename) for ext, filename in candidates.items() if filename.exists()}
|
return {ext: str(filename) for ext, filename in candidates.items() if filename.exists()}
|
||||||
|
|
||||||
|
@ -504,7 +504,7 @@ def update_replaces_from_submission(request, submission, draft):
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
is_chair_of = list(Group.objects.filter(role__person__user=request.user, role__name="chair"))
|
is_chair_of = list(Group.objects.filter(role__person__user=request.user, role__name="chair"))
|
||||||
|
|
||||||
replaces = DocAlias.objects.filter(name__in=submission.replaces.split(",")).prefetch_related("docs", "docs__group")
|
replaces = Document.objects.filter(name__in=submission.replaces.split(",")).prefetch_related("group")
|
||||||
existing_replaces = list(draft.related_that_doc("replaces"))
|
existing_replaces = list(draft.related_that_doc("replaces"))
|
||||||
existing_suggested = set(draft.related_that_doc("possibly-replaces"))
|
existing_suggested = set(draft.related_that_doc("possibly-replaces"))
|
||||||
|
|
||||||
|
@ -516,14 +516,12 @@ def update_replaces_from_submission(request, submission, draft):
|
||||||
if r in existing_replaces:
|
if r in existing_replaces:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
rdoc = r.document
|
if r == draft:
|
||||||
|
|
||||||
if rdoc == draft:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (is_secretariat
|
if (is_secretariat
|
||||||
or (draft.group in is_chair_of and (rdoc.group.type_id == "individ" or rdoc.group in is_chair_of))
|
or (draft.group in is_chair_of and (r.group.type_id == "individ" or r.group in is_chair_of))
|
||||||
or (submitter_email and rdoc.documentauthor_set.filter(email__address__iexact=submitter_email).exists())):
|
or (submitter_email and r.documentauthor_set.filter(email__address__iexact=submitter_email).exists())):
|
||||||
approved.append(r)
|
approved.append(r)
|
||||||
else:
|
else:
|
||||||
if r not in existing_suggested:
|
if r not in existing_suggested:
|
||||||
|
|
|
@ -488,12 +488,15 @@ def update_docs_from_rfc_index(index_data, errata_data, skip_older_than_date=Non
|
||||||
def parse_relation_list(l):
|
def parse_relation_list(l):
|
||||||
res = []
|
res = []
|
||||||
for x in l:
|
for x in l:
|
||||||
if x[:3] in ("NIC", "IEN", "STD", "RTR"):
|
# This lookup wasn't finding anything but maybe some STD and we know
|
||||||
# try translating this to RFCs that we can handle
|
# if the STD had more than one RFC the wrong thing happens
|
||||||
# sensibly; otherwise we'll have to ignore them
|
#
|
||||||
l = DocAlias.objects.filter(name__startswith="rfc", docs__docalias__name=x.lower())
|
#if x[:3] in ("NIC", "IEN", "STD", "RTR"):
|
||||||
else:
|
# # try translating this to RFCs that we can handle
|
||||||
l = DocAlias.objects.filter(name=x.lower())
|
# # sensibly; otherwise we'll have to ignore them
|
||||||
|
# l = DocAlias.objects.filter(name__startswith="rfc", docs__docalias__name=x.lower())
|
||||||
|
#else:
|
||||||
|
l = Document.objects.filter(name=x.lower())
|
||||||
|
|
||||||
for a in l:
|
for a in l:
|
||||||
if a not in res:
|
if a not in res:
|
||||||
|
|
|
@ -14,7 +14,7 @@ from django.utils import timezone
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.doc.factories import WgDraftFactory
|
from ietf.doc.factories import WgDraftFactory, RfcFactory
|
||||||
from ietf.doc.models import Document, DocAlias, DocEvent, DeletedEvent, DocTagName, RelatedDocument, State, StateDocEvent
|
from ietf.doc.models import Document, DocAlias, DocEvent, DeletedEvent, DocTagName, RelatedDocument, State, StateDocEvent
|
||||||
from ietf.doc.utils import add_state_change_event
|
from ietf.doc.utils import add_state_change_event
|
||||||
from ietf.group.factories import GroupFactory
|
from ietf.group.factories import GroupFactory
|
||||||
|
@ -235,9 +235,7 @@ class RFCSyncTests(TestCase):
|
||||||
# too, but for testing purposes ...
|
# too, but for testing purposes ...
|
||||||
doc.action_holders.add(doc.ad) # not normally set, but add to be sure it's cleared
|
doc.action_holders.add(doc.ad) # not normally set, but add to be sure it's cleared
|
||||||
|
|
||||||
updated_doc = Document.objects.create(name="draft-ietf-something")
|
RfcFactory(rfc_number=123)
|
||||||
DocAlias.objects.create(name=updated_doc.name).docs.add(updated_doc)
|
|
||||||
DocAlias.objects.create(name="rfc123").docs.add(updated_doc)
|
|
||||||
|
|
||||||
today = date_today()
|
today = date_today()
|
||||||
|
|
||||||
|
@ -360,7 +358,7 @@ class RFCSyncTests(TestCase):
|
||||||
self.assertTrue(DocAlias.objects.filter(name="bcp1", docs=doc))
|
self.assertTrue(DocAlias.objects.filter(name="bcp1", docs=doc))
|
||||||
self.assertTrue(DocAlias.objects.filter(name="fyi1", docs=doc))
|
self.assertTrue(DocAlias.objects.filter(name="fyi1", docs=doc))
|
||||||
self.assertTrue(DocAlias.objects.filter(name="std1", docs=doc))
|
self.assertTrue(DocAlias.objects.filter(name="std1", docs=doc))
|
||||||
self.assertTrue(RelatedDocument.objects.filter(source=doc, target__name="rfc123", relationship="updates"))
|
self.assertTrue(RelatedDocument.objects.filter(source=doc, target__name="rfc123", relationship="updates").exists())
|
||||||
self.assertEqual(doc.title, "A Testing RFC")
|
self.assertEqual(doc.title, "A Testing RFC")
|
||||||
self.assertEqual(doc.abstract, "This is some interesting text.")
|
self.assertEqual(doc.abstract, "This is some interesting text.")
|
||||||
self.assertEqual(doc.get_state_slug(), "rfc")
|
self.assertEqual(doc.get_state_slug(), "rfc")
|
||||||
|
@ -602,4 +600,4 @@ class RFCEditorUndoTests(TestCase):
|
||||||
e = DeletedEvent.objects.all().order_by("-time", "-id")[0]
|
e = DeletedEvent.objects.all().order_by("-time", "-id")[0]
|
||||||
|
|
||||||
e.content_type.model_class().objects.create(**json.loads(e.json))
|
e.content_type.model_class().objects.create(**json.loads(e.json))
|
||||||
self.assertTrue(StateDocEvent.objects.filter(desc="First", doc=draft))
|
self.assertTrue(StateDocEvent.objects.filter(desc="First", doc=draft))
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
</button>
|
</button>
|
||||||
{% if user|has_role:"Secretariat" %}
|
{% if user|has_role:"Secretariat" %}
|
||||||
<a class="btn btn-primary"
|
<a class="btn btn-primary"
|
||||||
href="{% url 'ietf.doc.views_charter.approve' name=charter.canonical_name %}">
|
href="{% url 'ietf.doc.views_charter.approve' name=charter.name %}">
|
||||||
Charter approval page
|
Charter approval page
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||||
{% load origin %}
|
{% load origin %}
|
||||||
{% load django_bootstrap5 %}
|
{% load django_bootstrap5 %}
|
||||||
{% block title %}Approve {{ charter.canonical_name }}{% endblock %}
|
{% block title %}Approve {{ charter.name }}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% origin %}
|
{% origin %}
|
||||||
<h1>Approve {{ charter.canonical_name }}-{{ charter.rev }}</h1>
|
<h1>Approve {{ charter.name }}-{{ charter.rev }}</h1>
|
||||||
<form class="mt-3" method="post">
|
<form class="mt-3" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<pre class="border p-3">{{ announcement }}</pre>
|
<pre class="border p-3">{{ announcement }}</pre>
|
||||||
<button type="submit" class="btn btn-primary">Send announcement, close ballot & update revision</button>
|
<button type="submit" class="btn btn-primary">Send announcement, close ballot & update revision</button>
|
||||||
<a class="btn btn-warning"
|
<a class="btn btn-warning"
|
||||||
href="{% url "ietf.doc.views_charter.action_announcement_text" name=charter.canonical_name %}?next=approve">
|
href="{% url "ietf.doc.views_charter.action_announcement_text" name=charter.name %}?next=approve">
|
||||||
Edit/regenerate announcement
|
Edit/regenerate announcement
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-secondary float-end"
|
<a class="btn btn-secondary float-end"
|
||||||
|
|
|
@ -2,20 +2,20 @@
|
||||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||||
{% load origin %}
|
{% load origin %}
|
||||||
{% load django_bootstrap5 %}
|
{% load django_bootstrap5 %}
|
||||||
{% block title %}Change responsible AD for {{ charter.canonical_name }}-{{ charter.rev }}{% endblock %}
|
{% block title %}Change responsible AD for {{ charter.name }}-{{ charter.rev }}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% origin %}
|
{% origin %}
|
||||||
<h1>
|
<h1>
|
||||||
Change responsible AD
|
Change responsible AD
|
||||||
<br>
|
<br>
|
||||||
<small class="text-body-secondary">{{ charter.canonical_name }}-{{ charter.rev }}</small>
|
<small class="text-body-secondary">{{ charter.name }}-{{ charter.rev }}</small>
|
||||||
</h1>
|
</h1>
|
||||||
<form class="mt-3" enctype="multipart/form-data" method="post">
|
<form class="mt-3" enctype="multipart/form-data" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_form form %}
|
{% bootstrap_form form %}
|
||||||
<button type="submit" class="btn btn-primary">Submit</button>
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
<a class="btn btn-secondary float-end"
|
<a class="btn btn-secondary float-end"
|
||||||
href="{% url "ietf.doc.views_doc.document_main" name=charter.canonical_name %}">
|
href="{% url "ietf.doc.views_doc.document_main" name=charter.name %}">
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{% load ietf_filters %}
|
{% load ietf_filters %}
|
||||||
{% load textfilters %}
|
{% load textfilters %}
|
||||||
|
|
||||||
{% if doc.get_state_slug == "rfc" %}
|
{% if doc.type_id == "rfc" %}
|
||||||
{% if doc.stream|slugify == "legacy" %}
|
{% if doc.stream|slugify == "legacy" %}
|
||||||
% Datatracker information for RFCs on the Legacy Stream is unfortunately often
|
% Datatracker information for RFCs on the Legacy Stream is unfortunately often
|
||||||
% incorrect. Please correct the bibtex below based on the information in the
|
% incorrect. Please correct the bibtex below based on the information in the
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
publisher = {RFC Editor},
|
publisher = {RFC Editor},
|
||||||
doi = {% templatetag openbrace %}{{ doi }}{% templatetag closebrace %},
|
doi = {% templatetag openbrace %}{{ doi }}{% templatetag closebrace %},
|
||||||
url = {% templatetag openbrace %}{{ doc.rfc_number|rfceditor_info_url }}{% templatetag closebrace %},{% else %}
|
url = {% templatetag openbrace %}{{ doc.rfc_number|rfceditor_info_url }}{% templatetag closebrace %},{% else %}
|
||||||
{% if published %}%% You should probably cite rfc{{ latest_revision.doc.rfc_number }} instead of this I-D.{% else %}{% if replaced_by %}%% You should probably cite {{replaced_by|join:" or "}} instead of this I-D.{% else %}
|
{% if published_as %}%% You should probably cite rfc{{ published_as.rfc_number }} instead of this I-D.{% else %}{% if replaced_by %}%% You should probably cite {{replaced_by|join:" or "}} instead of this I-D.{% else %}
|
||||||
{% if doc.rev != latest_revision.rev %}%% You should probably cite {{latest_revision.doc.name}}-{{latest_revision.rev}} instead of this revision.{%endif%}{% endif %}{% endif %}
|
{% if doc.rev != latest_revision.rev %}%% You should probably cite {{latest_revision.doc.name}}-{{latest_revision.rev}} instead of this revision.{%endif%}{% endif %}{% endif %}
|
||||||
@techreport{% templatetag openbrace %}{{doc.name|slice:"6:"}}-{{doc.rev}},
|
@techreport{% templatetag openbrace %}{{doc.name|slice:"6:"}}-{{doc.rev}},
|
||||||
number = {% templatetag openbrace %}{{doc.name}}-{{doc.rev}}{% templatetag closebrace %},
|
number = {% templatetag openbrace %}{{doc.name}}-{{doc.rev}}{% templatetag closebrace %},
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
title = {% templatetag openbrace %}{% templatetag openbrace %}{{doc.title|texescape}}{% templatetag closebrace %}{% templatetag closebrace %},
|
title = {% templatetag openbrace %}{% templatetag openbrace %}{{doc.title|texescape}}{% templatetag closebrace %}{% templatetag closebrace %},
|
||||||
pagetotal = {{ doc.pages }},
|
pagetotal = {{ doc.pages }},
|
||||||
year = {{ doc.pub_date.year }},
|
year = {{ doc.pub_date.year }},
|
||||||
month = {{ doc.pub_date|date:"b" }},{% if not doc.rfc_number or doc.pub_date.day == 1 and doc.pub_date.month == 4 %}
|
month = {{ doc.pub_date|date:"b" }},{% if not doc.type_id == "rfc" or doc.pub_date.day == 1 and doc.pub_date.month == 4 %}
|
||||||
day = {{ doc.pub_date.day }},{% endif %}
|
day = {{ doc.pub_date.day }},{% endif %}
|
||||||
abstract = {% templatetag openbrace %}{{ doc.abstract|clean_whitespace|texescape }}{% templatetag closebrace %},
|
abstract = {% templatetag openbrace %}{{ doc.abstract|clean_whitespace|texescape }}{% templatetag closebrace %},
|
||||||
{% templatetag closebrace %}
|
{% templatetag closebrace %}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<link rel="alternate"
|
<link rel="alternate"
|
||||||
type="application/atom+xml"
|
type="application/atom+xml"
|
||||||
href="/feed/document-changes/{{ doc.name }}/">
|
href="/feed/document-changes/{{ doc.name }}/">
|
||||||
<link rel="stylesheet" href="{% static "ietf/css/list.css" %}">
|
<link rel="stylesheet" href="{% static 'ietf/css/list.css' %}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% origin %}
|
{% origin %}
|
||||||
|
@ -20,7 +20,17 @@
|
||||||
<h2 class="my-3">Revision differences</h2>
|
<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 %}
|
{% include "doc/document_history_form.html" with doc=doc diff_revisions=diff_revisions action=rfcdiff_base_url snapshot=snapshot only %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2 class="my-3">Document history</h2>
|
<h2 class="my-3">Document history
|
||||||
|
{% if related %}
|
||||||
|
<div class="float-end">
|
||||||
|
{% for related_docalias in related %}
|
||||||
|
<a class="btn btn-outline-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_doc.document_history' name=related_docalias.name %}">
|
||||||
|
Related history for {{ related_docalias.name }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}</h2>
|
||||||
{% if can_add_comment %}
|
{% if can_add_comment %}
|
||||||
<div class="buttonlist">
|
<div class="buttonlist">
|
||||||
<a class="btn btn-primary"
|
<a class="btn btn-primary"
|
||||||
|
@ -34,7 +44,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" data-sort="date">Date</th>
|
<th scope="col" data-sort="date">Date</th>
|
||||||
<th scope="col" data-sort="rev">Rev.</th>
|
{% if doc.type_id != "rfc" %}<th scope="col" data-sort="rev">Rev.</th>{% endif %}
|
||||||
<th scope="col" data-sort="by">By</th>
|
<th scope="col" data-sort="by">By</th>
|
||||||
<th scope="col" data-sort="action">Action</th>
|
<th scope="col" data-sort="action">Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -45,7 +55,7 @@
|
||||||
<td>
|
<td>
|
||||||
<div title="{{ e.time|date:'Y-m-d H:i:s O' }}">{{ e.time|date:"Y-m-d" }}</div>
|
<div title="{{ e.time|date:'Y-m-d H:i:s O' }}">{{ e.time|date:"Y-m-d" }}</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">{{ e.rev }}</td>
|
{% if doc.type_id != "rfc" %}<td class="text-end">{{ e.rev }}</td>{% endif %}
|
||||||
<td>{{ e.by|escape }}</td>
|
<td>{{ e.by|escape }}</td>
|
||||||
<td>{{ e.desc|format_history_text }}</td>
|
<td>{{ e.desc|format_history_text }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<title>
|
<title>
|
||||||
{% if not snapshot and doc.get_state_slug == "rfc" %}
|
{% if doc.type_id == "rfc" %}
|
||||||
RFC {{ doc.rfc_number }} - {{ doc.title }}
|
RFC {{ doc.rfc_number }} - {{ doc.title }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ doc.name }}-{{ doc.rev }}
|
{{ doc.name }}-{{ doc.rev }}
|
||||||
|
|
|
@ -11,15 +11,15 @@
|
||||||
<th scope="row">{% if document_html %}Document type{% else %}Type{% endif %}</th>
|
<th scope="row">{% if document_html %}Document type{% else %}Type{% endif %}</th>
|
||||||
<td class="edit"></td>
|
<td class="edit"></td>
|
||||||
<td>
|
<td>
|
||||||
{% if doc.get_state_slug == "rfc" and not snapshot %}
|
{% if doc.type_id == "rfc" %}
|
||||||
<span class="text-success">RFC
|
<span class="text-success">RFC
|
||||||
{% if not document_html %}
|
{% if not document_html %}
|
||||||
- {{ doc.std_level }}
|
- {{ doc.std_level }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="badge rounded-pill badge-{% if not snapshot %}{{ doc|std_level_to_label_format }}{% else %}draft{% endif %}">{{ doc.std_level }}</span>
|
<span class="badge rounded-pill badge-{% if not snapshot %}{{ doc|std_level_to_label_format }}{% else %}draft{% endif %}">{{ doc.std_level }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% if published %}
|
{% if doc.pub_date %}
|
||||||
{% if document_html %}<br>{% else %}({% endif %}{{ doc.pub_date|date:"F Y" }}{% if not document_html %}){% endif %}
|
{% if document_html %}<br>{% else %}({% endif %}{{ doc.pub_date|date:"F Y" }}{% if not document_html %}){% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="text-body-secondary">(Publication date unknown)</span>
|
<span class="text-body-secondary">(Publication date unknown)</span>
|
||||||
|
@ -52,7 +52,6 @@
|
||||||
{% if proposed_status_changes %}
|
{% if proposed_status_changes %}
|
||||||
<div>Proposed status changed by {{ proposed_status_changes|urlize_related_source_list|join:", " }}</div>
|
<div>Proposed status changed by {{ proposed_status_changes|urlize_related_source_list|join:", " }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if rfc_aliases %}<div>Also known as {{ rfc_aliases|join:", "|urlize_ietf_docs }}</div>{% endif %}
|
|
||||||
{% if draft_name %}
|
{% if draft_name %}
|
||||||
<div>
|
<div>
|
||||||
Was
|
Was
|
||||||
|
@ -132,91 +131,93 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if replaces or not document_html and can_edit_stream_info %}
|
{% if doc.type_id != "rfc" %}
|
||||||
<tr>
|
{% if replaces or not document_html and can_edit_stream_info %}
|
||||||
<td></td>
|
|
||||||
<th scope="row">Replaces</th>
|
|
||||||
<td class="edit">
|
|
||||||
{% if can_edit_stream_info and not snapshot %}
|
|
||||||
<a class="btn btn-primary btn-sm"
|
|
||||||
href="{% url 'ietf.doc.views_draft.replaces' name=doc.name %}">Edit</a>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if replaces %}
|
|
||||||
{% if document_html %}
|
|
||||||
{{ replaces|urlize_related_target_list:document_html|join:"<br>" }}
|
|
||||||
{% else %}
|
|
||||||
{{ replaces|urlize_related_target_list:document_html|join:", " }}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<span class="text-body-secondary">(None)</span>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% if replaced_by %}
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<th scope="row">
|
|
||||||
Replaced by
|
|
||||||
</th>
|
|
||||||
<td class="edit">
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if document_html %}
|
|
||||||
{{ replaced_by|urlize_related_source_list:document_html|join:"<br>" }}
|
|
||||||
{% else %}
|
|
||||||
{{ replaced_by|urlize_related_source_list:document_html|join:", " }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% if can_view_possibly_replaces %}
|
|
||||||
{% if possibly_replaces %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<th scope="row">
|
<th scope="row">Replaces</th>
|
||||||
Possibly Replaces
|
|
||||||
</th>
|
|
||||||
<td class="edit">
|
<td class="edit">
|
||||||
{% if can_edit_replaces and not snapshot %}
|
{% if can_edit_stream_info and not snapshot %}
|
||||||
<a class="btn btn-primary btn-sm"
|
<a class="btn btn-primary btn-sm"
|
||||||
href="{% url 'ietf.doc.views_draft.review_possibly_replaces' name=doc.name %}">
|
href="{% url 'ietf.doc.views_draft.replaces' name=doc.name %}">Edit</a>
|
||||||
Edit
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if document_html %}
|
{% if replaces %}
|
||||||
{{ possibly_replaces|urlize_related_target_list:document_html|join:"<br>" }}
|
{% if document_html %}
|
||||||
|
{{ replaces|urlize_related_target_list:document_html|join:"<br>" }}
|
||||||
|
{% else %}
|
||||||
|
{{ replaces|urlize_related_target_list:document_html|join:", " }}
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ possibly_replaces|urlize_related_target_list:document_html|join:", " }}
|
<span class="text-body-secondary">(None)</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if possibly_replaced_by %}
|
{% if replaced_by %}
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
Possibly Replaced By
|
Replaced by
|
||||||
</th>
|
</th>
|
||||||
<td class="edit">
|
<td class="edit">
|
||||||
{% if can_edit_replaces and not snapshot %}
|
|
||||||
{% comment %}<a class="btn btn-primary btn-sm"
|
|
||||||
href="{% url 'ietf.doc.views_draft.review_possibly_replaces' name=doc.name %}">Edit</a>{% endcomment %}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if document_html %}
|
{% if document_html %}
|
||||||
{{ possibly_replaced_by|urlize_related_source_list:document_html|join:"<br>" }}
|
{{ replaced_by|urlize_related_source_list:document_html|join:"<br>" }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ possibly_replaced_by|urlize_related_source_list:document_html|join:", " }}
|
{{ replaced_by|urlize_related_source_list:document_html|join:", " }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if can_view_possibly_replaces %}
|
||||||
|
{% if possibly_replaces %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<th scope="row">
|
||||||
|
Possibly Replaces
|
||||||
|
</th>
|
||||||
|
<td class="edit">
|
||||||
|
{% if can_edit_replaces and not snapshot %}
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_draft.review_possibly_replaces' name=doc.name %}">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if document_html %}
|
||||||
|
{{ possibly_replaces|urlize_related_target_list:document_html|join:"<br>" }}
|
||||||
|
{% else %}
|
||||||
|
{{ possibly_replaces|urlize_related_target_list:document_html|join:", " }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if possibly_replaced_by %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<th scope="row">
|
||||||
|
Possibly Replaced By
|
||||||
|
</th>
|
||||||
|
<td class="edit">
|
||||||
|
{% if can_edit_replaces and not snapshot %}
|
||||||
|
{% comment %}<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_draft.review_possibly_replaces' name=doc.name %}">Edit</a>{% endcomment %}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if document_html %}
|
||||||
|
{{ possibly_replaced_by|urlize_related_source_list:document_html|join:"<br>" }}
|
||||||
|
{% else %}
|
||||||
|
{{ possibly_replaced_by|urlize_related_source_list:document_html|join:", " }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
@ -231,15 +232,15 @@ href="{% url 'ietf.doc.views_draft.review_possibly_replaces' name=doc.name %}">E
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td {% if stream_desc == "(None)" %}class="text-body-secondary"{%endif%}>
|
<td {% if doc.stream is None %}class="text-body-secondary"{%endif%}>
|
||||||
{% if stream_desc != "(None)" %}
|
{% if doc.stream is not None %}
|
||||||
{% if doc.stream.name|lower in 'iab,irtf,ise,editorial' %}
|
{% if doc.stream.name|lower in 'iab,irtf,ise,editorial' %}
|
||||||
<a href="{% url 'ietf.group.views.stream_documents' acronym=doc.stream.name|lower %}">
|
<a href="{% url 'ietf.group.views.stream_documents' acronym=doc.stream.name|lower %}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if document_html %}
|
{% if document_html %}
|
||||||
{% if doc.stream.name|lower in 'iab,ietf,irtf' %}
|
{% if doc.stream.name|lower in 'iab,ietf,irtf' %}
|
||||||
<img alt="{{ doc.stream.name|upper }} Logo"
|
<img alt="{{ doc.stream.name|upper }} Logo"
|
||||||
title="{{ stream_desc }}"
|
title="{{ doc.stream.desc }}"
|
||||||
class="w-25 mt-1"
|
class="w-25 mt-1"
|
||||||
{% if doc.stream.name|lower == 'iab' %}
|
{% if doc.stream.name|lower == 'iab' %}
|
||||||
src="{% static 'ietf/images/iab-logo.svg' %}"
|
src="{% static 'ietf/images/iab-logo.svg' %}"
|
||||||
|
@ -250,20 +251,20 @@ href="{% url 'ietf.doc.views_draft.review_possibly_replaces' name=doc.name %}">E
|
||||||
{% endif %}
|
{% endif %}
|
||||||
>
|
>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ stream_desc }}
|
{{ doc.stream.desc }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ stream_desc }}
|
{{ doc.stream.desc }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if doc.stream.name|lower in 'iab,irtf,ise,editorial' %}
|
{% if doc.stream.name|lower in 'iab,irtf,ise,editorial' %}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ stream_desc }}
|
(None)
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% if doc.get_state_slug != "rfc" and not snapshot %}
|
{% if doc.type_id != "rfc" and not snapshot %}
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
|
@ -339,70 +340,72 @@ href="{% url 'ietf.doc.views_draft.review_possibly_replaces' name=doc.name %}">E
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if review_assignments or can_request_review %}
|
{% if doc.type_id != "rfc" %}{# do not show reviews or conflict_reviews for RFCs, even if present #}
|
||||||
<tr>
|
{% if review_assignments or can_request_review %}
|
||||||
<td></td>
|
<tr>
|
||||||
<th scope="row">
|
<td></td>
|
||||||
Reviews
|
<th scope="row">
|
||||||
</th>
|
Reviews
|
||||||
<td class="edit">
|
</th>
|
||||||
</td>
|
<td class="edit">
|
||||||
<td>
|
</td>
|
||||||
{% for review_assignment in review_assignments %}
|
<td>
|
||||||
|
{% for review_assignment in review_assignments %}
|
||||||
{% include "doc/review_assignment_summary.html" with current_doc_name=doc.name current_rev=doc.rev review_assignment=review_assignment only %}
|
{% include "doc/review_assignment_summary.html" with current_doc_name=doc.name current_rev=doc.rev review_assignment=review_assignment only %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for review_request in review_requests %}
|
{% for review_request in review_requests %}
|
||||||
{% include "doc/review_request_summary.html" with review_request=review_request only %}
|
{% include "doc/review_request_summary.html" with review_request=review_request only %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if no_review_from_teams %}
|
{% if no_review_from_teams %}
|
||||||
{% for team in no_review_from_teams %}
|
{% for team in no_review_from_teams %}
|
||||||
{{ team.acronym.upper }}{% if not forloop.last %},{% endif %}
|
{{ team.acronym.upper }}{% if not forloop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
will not review this version
|
will not review this version
|
||||||
{% endif %}
|
|
||||||
{% if can_request_review or can_submit_unsolicited_review_for_teams %}
|
|
||||||
<div {% if review_assignments or no_review_from_teams %}class="mt-3"{% endif %}>
|
|
||||||
{% if can_request_review %}
|
|
||||||
<a class="btn btn-primary btn-sm"
|
|
||||||
href="{% url "ietf.doc.views_review.request_review" doc.name %}">
|
|
||||||
<i class="bi bi-check-circle">
|
|
||||||
</i>
|
|
||||||
Request review
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if can_submit_unsolicited_review_for_teams|length == 1 %}
|
{% if can_request_review or can_submit_unsolicited_review_for_teams %}
|
||||||
<a class="btn btn-primary btn-sm"
|
<div {% if review_assignments or no_review_from_teams %}class="mt-3"{% endif %}>
|
||||||
href="{% url "ietf.doc.views_review.complete_review" doc.name can_submit_unsolicited_review_for_teams.0.acronym %}">
|
{% if can_request_review %}
|
||||||
<i class="bi bi-pencil-square">
|
<a class="btn btn-primary btn-sm"
|
||||||
</i>
|
href="{% url "ietf.doc.views_review.request_review" doc.name %}">
|
||||||
Submit unsolicited review
|
<i class="bi bi-check-circle">
|
||||||
</a>
|
</i>
|
||||||
{% elif can_submit_unsolicited_review_for_teams %}
|
Request review
|
||||||
<a class="btn btn-primary btn-sm"
|
</a>
|
||||||
href="{% url "ietf.doc.views_review.submit_unsolicited_review_choose_team" doc.name %}">
|
{% endif %}
|
||||||
<i class="bi bi-pencil-square">
|
{% if can_submit_unsolicited_review_for_teams|length == 1 %}
|
||||||
</i>
|
<a class="btn btn-primary btn-sm"
|
||||||
Submit unsolicited review
|
href="{% url "ietf.doc.views_review.complete_review" doc.name can_submit_unsolicited_review_for_teams.0.acronym %}">
|
||||||
</a>
|
<i class="bi bi-pencil-square">
|
||||||
|
</i>
|
||||||
|
Submit unsolicited review
|
||||||
|
</a>
|
||||||
|
{% elif can_submit_unsolicited_review_for_teams %}
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url "ietf.doc.views_review.submit_unsolicited_review_choose_team" doc.name %}">
|
||||||
|
<i class="bi bi-pencil-square">
|
||||||
|
</i>
|
||||||
|
Submit unsolicited review
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</td>
|
||||||
{% endif %}
|
</tr>
|
||||||
</td>
|
{% endif %}
|
||||||
</tr>
|
{% if conflict_reviews %}
|
||||||
{% endif %}
|
<tr>
|
||||||
{% if conflict_reviews %}
|
<td></td>
|
||||||
<tr>
|
<th scope="row">
|
||||||
<td></td>
|
IETF conflict review
|
||||||
<th scope="row">
|
</th>
|
||||||
IETF conflict review
|
<td class="edit">
|
||||||
</th>
|
</td>
|
||||||
<td class="edit">
|
<td>
|
||||||
</td>
|
{{ conflict_reviews|join:", "|urlize_ietf_docs }}
|
||||||
<td>
|
</td>
|
||||||
{{ conflict_reviews|join:", "|urlize_ietf_docs }}
|
</tr>
|
||||||
</td>
|
{% endif %}
|
||||||
</tr>
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% with doc.docextresource_set.all as resources %}
|
{% with doc.docextresource_set.all as resources %}
|
||||||
{% if resources or doc.group and doc.group.list_archive or can_edit_stream_info or can_edit_individual %}
|
{% if resources or doc.group and doc.group.list_archive or can_edit_stream_info or can_edit_individual %}
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if ref.source.get_state.slug == 'rfc' %}
|
{% if ref.source.type_id == "rfc" %}
|
||||||
{% with ref.source.std_level as lvl %}
|
{% with ref.source.std_level as lvl %}
|
||||||
{% if lvl %}{{ lvl }}{% endif %}
|
{% if lvl %}{{ lvl }}{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
<a href="{% url 'ietf.doc.views_doc.document_main' name=name %}">{{ name|prettystdname }}</a>
|
<a href="{% url 'ietf.doc.views_doc.document_main' name=name %}">{{ name|prettystdname }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<b>{{ ref.target.document.title }}</b>
|
<b>{{ ref.target.title }}</b>
|
||||||
<br>
|
<br>
|
||||||
<a class="btn btn-primary btn-sm"
|
<a class="btn btn-primary btn-sm"
|
||||||
href="{% url 'ietf.doc.views_doc.document_references' name %}"
|
href="{% url 'ietf.doc.views_doc.document_references' name %}"
|
||||||
|
@ -51,12 +51,12 @@
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if ref.target.document.get_state.slug == 'rfc' %}
|
{% if ref.target.type_id == "rfc" %}
|
||||||
{% with ref.target.document.std_level as lvl %}
|
{% with ref.target.std_level as lvl %}
|
||||||
{% if lvl %}{{ lvl }}{% endif %}
|
{% if lvl %}{{ lvl }}{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% with ref.target.document.intended_std_level as lvl %}
|
{% with ref.target.intended_std_level as lvl %}
|
||||||
{% if lvl %}{{ lvl }}{% endif %}
|
{% if lvl %}{{ lvl }}{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
339
ietf/templates/doc/document_rfc.html
Normal file
339
ietf/templates/doc/document_rfc.html
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{# Copyright The IETF Trust 2016-2020, All Rights Reserved #}
|
||||||
|
{% load origin %}
|
||||||
|
{% load static %}
|
||||||
|
{% load ietf_filters %}
|
||||||
|
{% load person_filters %}
|
||||||
|
{% load textfilters %}
|
||||||
|
{% block html_attrs %}prefix="og: http://ogp.me/ns# article: http://ogp.me/ns/article#"{% endblock %}
|
||||||
|
{% block pagehead %}
|
||||||
|
{% include "doc/opengraph.html" %}
|
||||||
|
<link rel="alternate"
|
||||||
|
type="application/atom+xml"
|
||||||
|
title="Document changes"
|
||||||
|
href="/feed/document-changes/{{ name }}/">
|
||||||
|
<meta name="description"
|
||||||
|
content="{{ doc.title }} {% if doc.get_state_slug == 'rfc' and not snapshot %}(RFC {{ rfc_number }}{% if published %}, {{ doc.pub_date|date:'F Y' }}{% endif %}{% if obsoleted_by %}; obsoleted by {% for rel in obsoleted_by %}{{ rel.source.canonical_name|prettystdname}}{% if not forloop.last%}, {% endif %}{% endfor %}{% endif %}){% endif %}">
|
||||||
|
{% endblock %}
|
||||||
|
{% block morecss %}.inline { display: inline; }{% endblock %}
|
||||||
|
{% block title %}
|
||||||
|
RFC {{ rfc_number }} - {{ doc.title }}
|
||||||
|
{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% origin %}
|
||||||
|
{{ top|safe }}
|
||||||
|
<div id="timeline"></div>
|
||||||
|
{% if doc.rev != latest_rev %}
|
||||||
|
<div class="alert alert-warning my-3">The information below is for an old version of the document.</div>
|
||||||
|
{% endif %}
|
||||||
|
<table class="table table-sm table-borderless">
|
||||||
|
{% include "doc/document_info.html" %}
|
||||||
|
<tbody class="meta border-top">
|
||||||
|
{% if milestones %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<th scope="row">
|
||||||
|
Associated
|
||||||
|
{% if doc.stream_id == 'ietf' %}
|
||||||
|
WG
|
||||||
|
{% else %}
|
||||||
|
{{ doc.stream }}
|
||||||
|
{% endif %} milestone{{ milestones|pluralize }}</th>
|
||||||
|
<td class="edit"></td>
|
||||||
|
<td>
|
||||||
|
<dl class="row">
|
||||||
|
{% for m in milestones %}
|
||||||
|
<dt class="col-sm-2 my-0">{{ m.due|date:"M Y" }}</dt>
|
||||||
|
<dd class="col-sm-10 my-0">{{ m.desc }}</dd>
|
||||||
|
{% endfor %}
|
||||||
|
</dl>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
{% if doc.stream_id != 'iab' %}
|
||||||
|
<tbody class="meta border-top">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
IESG
|
||||||
|
</th>
|
||||||
|
<th scope="row">
|
||||||
|
Responsible AD
|
||||||
|
</th>
|
||||||
|
<td class="edit">
|
||||||
|
{% if can_edit %}
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_draft.edit_ad' name=doc.name %}">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if doc.ad %}
|
||||||
|
{% person_link doc.ad %}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-body-secondary">
|
||||||
|
(None)
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<th scope="row">
|
||||||
|
Send notices to
|
||||||
|
</th>
|
||||||
|
<td class="edit">
|
||||||
|
{% if can_edit_notify and not snapshot %}
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_doc.edit_notify' name=doc.name %}">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if doc.notify %}
|
||||||
|
{{ doc.notify|linkify }}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-body-secondary">
|
||||||
|
(None)
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{% endif %}
|
||||||
|
{% if rfc_editor_state %}
|
||||||
|
<tbody class="meta border-top">
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
RFC Editor
|
||||||
|
</th>
|
||||||
|
<th scope="row">
|
||||||
|
<a href="{% url "ietf.help.views.state" doc=doc.type.slug type="rfceditor" %}">
|
||||||
|
RFC Editor state
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<td class="edit">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ rfc_editor_state }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<th scope="row">
|
||||||
|
Details
|
||||||
|
</th>
|
||||||
|
<td class="edit">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div>
|
||||||
|
<a href="https://www.rfc-editor.org/queue2.html#{{ doc.name }}">
|
||||||
|
Publication queue entry
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% if rfc_editor_auth48_url %}
|
||||||
|
<div>
|
||||||
|
<a href="{{ rfc_editor_auth48_url }}">
|
||||||
|
Auth48 status
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
<div class="buttonlist">
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="mailto:{{ doc.name }}@ietf.org?subject=Mail%20regarding%20{{ doc.name }}">
|
||||||
|
<i class="bi bi-envelope">
|
||||||
|
</i>
|
||||||
|
Email authors
|
||||||
|
</a>
|
||||||
|
{% if doc.group.type.slug == 'wg' or doc.group.type.slug == 'rg' %}
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="mailto:{{ doc.group.list_email }}?subject=Mail%20regarding%20{{ doc.name }}">
|
||||||
|
<i class="bi bi-envelope">
|
||||||
|
</i>
|
||||||
|
Email {{ doc.group.type }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url "ietf.ipr.views.search" %}?submit=draft&id={{ doc.name }}"
|
||||||
|
rel="nofollow">
|
||||||
|
<i class="bi bi-lightning">
|
||||||
|
</i>
|
||||||
|
IPR
|
||||||
|
{% if doc.related_ipr %}
|
||||||
|
<span class="badge rounded-pill">
|
||||||
|
{{ doc.related_ipr|length }}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_doc.document_references' doc.canonical_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' doc.canonical_name %}"
|
||||||
|
rel="nofollow">
|
||||||
|
<i class="bi bi-arrow-right">
|
||||||
|
</i>
|
||||||
|
Referenced by
|
||||||
|
</a>
|
||||||
|
{# document_draft shows Nits here, excluded for RFCs #}
|
||||||
|
<div class="dropdown inline">
|
||||||
|
<button class="btn btn-primary btn-sm dropdown-toggle"
|
||||||
|
type="button"
|
||||||
|
id="ddSearchMenu"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="true">
|
||||||
|
<i class="bi bi-search">
|
||||||
|
</i>
|
||||||
|
Search lists
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" role="menu">
|
||||||
|
<li role="presentation">
|
||||||
|
<a class="dropdown-item"
|
||||||
|
href="https://mailarchive.ietf.org/arch/search?q=%22{{ doc.name }}%22"
|
||||||
|
rel="nofollow"
|
||||||
|
target="_blank">
|
||||||
|
IETF Mail Archive
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li role="presentation">
|
||||||
|
<a class="dropdown-item"
|
||||||
|
href="https://www.google.com/search?as_q={{ doc.name }}&as_sitesearch={{ search_archive }}"
|
||||||
|
rel="nofollow"
|
||||||
|
target="_blank">
|
||||||
|
Google
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% if user|has_role:"Area Director" %}
|
||||||
|
<li role="presentation">
|
||||||
|
<a class="dropdown-item"
|
||||||
|
href="https://www.iesg.org/bin/c5i?mid=6&rid=77&target={{ doc.name }}"
|
||||||
|
rel="nofollow"
|
||||||
|
target="_blank">
|
||||||
|
ARO
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<a class="btn btn-primary btn-sm track-untrack-doc {% if not doc.tracked_in_personal_community_list %}hide{% endif %}"
|
||||||
|
href="{% url "ietf.community.views.untrack_document" username=user.username name=doc.name %}"
|
||||||
|
title="Remove from your personal I-D list">
|
||||||
|
<i class="bi bi-bookmark-check-fill">
|
||||||
|
</i>
|
||||||
|
Untrack
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-primary btn-sm track-untrack-doc {% if doc.tracked_in_personal_community_list %}hide{% endif %}"
|
||||||
|
href="{% url "ietf.community.views.track_document" username=user.username name=doc.name %}"
|
||||||
|
title="Add to your personal I-D list">
|
||||||
|
<i class="bi bi-bookmark">
|
||||||
|
</i>
|
||||||
|
Track
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if user.review_teams %}
|
||||||
|
<a class="btn btn-primary btn-sm review-wish-add-remove-doc ajax {% if not doc.has_review_wish %}hide{% endif %}"
|
||||||
|
href="{% url "ietf.doc.views_review.review_wishes_remove" name=doc.name %}?next={{ request.get_full_path|urlencode }}"
|
||||||
|
title="Remove from your review wishes for all teams">
|
||||||
|
<i class="bi bi-chat-left-heart-fill">
|
||||||
|
</i>
|
||||||
|
Remove review wishes
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-primary btn-sm review-wish-add-remove-doc {% if user.review_teams|length_is:"1" %}ajax {% endif %}{% if doc.has_review_wish %}hide{% endif %}"
|
||||||
|
href="{% url "ietf.doc.views_review.review_wish_add" name=doc.name %}?next={{ request.get_full_path|urlencode }}"
|
||||||
|
title="Add to your review wishes">
|
||||||
|
<i class="bi bi-chat-left-heart">
|
||||||
|
</i>
|
||||||
|
Add review wish
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if can_edit and iesg_state.slug != 'idexists' %}
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_ballot.lastcalltext' name=doc.name %}">
|
||||||
|
Last call text
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_ballot.ballot_writeupnotes' name=doc.name %}">
|
||||||
|
Ballot text
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-primary btn-sm"
|
||||||
|
href="{% url 'ietf.doc.views_ballot.ballot_approvaltext' name=doc.name %}">
|
||||||
|
Announcement text
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if actions %}
|
||||||
|
{% for label, url in actions %}
|
||||||
|
<a class="btn btn-primary btn-sm" href="{{ url }}">
|
||||||
|
{{ label|capfirst_allcaps }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if doc.get_state_slug == "active" or doc.get_state_slug == "rfc" %}
|
||||||
|
<div class="card mt-5">
|
||||||
|
<div class="card-header">
|
||||||
|
{% if doc.get_state_slug == "rfc" and not snapshot %}
|
||||||
|
RFC {{ rfc_number }}
|
||||||
|
{% else %}
|
||||||
|
{{ name }}-{{ doc.rev }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<pre>{{ content|sanitize|safe|default:"(Unavailable)" }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if split_content %}
|
||||||
|
<a class="btn btn-primary my-3" href="?include_text=1">
|
||||||
|
<i class="bi bi-caret-down">
|
||||||
|
</i>
|
||||||
|
Show full document
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<div class="card border-warning mt-5">
|
||||||
|
<div class="card-header bg-warning">
|
||||||
|
<p><b>This Internet-Draft is no longer active. A copy of
|
||||||
|
the expired Internet-Draft is available in these formats:</b></p>
|
||||||
|
|
||||||
|
{% include "doc/document_format_buttons.html" %}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="h5 card-title">
|
||||||
|
Abstract
|
||||||
|
</p>
|
||||||
|
<p class="card-text">
|
||||||
|
{{ doc.abstract }}
|
||||||
|
</p>
|
||||||
|
<p class="h5 card-title">
|
||||||
|
Authors
|
||||||
|
</p>
|
||||||
|
<p class="card-text">
|
||||||
|
{% for author in doc.documentauthor_set.all %}
|
||||||
|
{% person_link author.person %}
|
||||||
|
{% if not forloop.last %}<br>{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
<p class="text-body-secondary card-text">
|
||||||
|
(Note: The e-mail addresses provided for the authors of this Internet-Draft may no longer be valid.)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block js %}
|
||||||
|
<script src="{% static 'ietf/js/d3.js' %}">
|
||||||
|
</script>
|
||||||
|
<script src="{% static 'ietf/js/document_timeline.js' %}">
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -52,7 +52,7 @@
|
||||||
<td class="edit"></td>
|
<td class="edit"></td>
|
||||||
<td>
|
<td>
|
||||||
{% for rel in relation_group.list %}
|
{% for rel in relation_group.list %}
|
||||||
{{ rel.target.document.canonical_name|upper|urlize_ietf_docs }}{% if not forloop.last %},{% endif %}
|
{{ rel.target.canonical_name|upper|urlize_ietf_docs }}{% if not forloop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% load ietf_filters %}{% filter linebreaks_lf %}{% comment %}
|
{% load ietf_filters %}{% filter linebreaks_lf %}{% comment %}
|
||||||
{% endcomment %}Doc-tag: {{doc.name}};datatracker{% if doc.rfcnum %}
|
{% endcomment %}Doc-tag: {{doc.name}};datatracker{% if doc.type_id == "rfc" %}
|
||||||
Doc-rfcnum: {{doc.rfcnum}}{% endif %}
|
Doc-rfcnum: {{doc.rfc_number}}{% endif %}
|
||||||
Doc-created: {{doc.created|date:"Y-m-d"}};datatracker{% if doc.deststatus %}
|
Doc-created: {{doc.created|date:"Y-m-d"}};datatracker{% if doc.deststatus %}
|
||||||
Doc-deststatus: {{doc.deststatus}};datatracker{% endif %}
|
Doc-deststatus: {{doc.deststatus}};datatracker{% endif %}
|
||||||
Doc-rev: {{doc.rev}};datatracker
|
Doc-rev: {{doc.rev}};datatracker
|
||||||
{% endfilter %}
|
{% endfilter %}
|
||||||
|
|
|
@ -33,7 +33,7 @@ No IPR declarations have been submitted directly on this I-D.
|
||||||
{% if downrefs %}
|
{% if downrefs %}
|
||||||
The document contains these normative downward references.
|
The document contains these normative downward references.
|
||||||
See RFC 3967 for additional information:
|
See RFC 3967 for additional information:
|
||||||
{% for ref in downrefs %} {{ref.target.document.canonical_name}}: {{ref.target.document.title}} ({{ref.target.document.std_level}} - {{ref.target.document.stream.desc}})
|
{% for ref in downrefs %} {{ref.target.canonical_name}}: {{ref.target.title}} ({{ref.target.std_level}} - {{ref.target.stream.desc}})
|
||||||
{% endfor %}{%endif%}
|
{% endfor %}{%endif%}
|
||||||
|
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
|
|
@ -49,13 +49,13 @@
|
||||||
{% if doc.pages %}<small class="float-end text-body-secondary d-none d-sm-block">{{ doc.pages }} page{{ doc.pages|pluralize }}</small>{% endif %}
|
{% if doc.pages %}<small class="float-end text-body-secondary d-none d-sm-block">{{ doc.pages }} page{{ doc.pages|pluralize }}</small>{% endif %}
|
||||||
<div>
|
<div>
|
||||||
<a href="{{ doc.get_absolute_url }}">
|
<a href="{{ doc.get_absolute_url }}">
|
||||||
{% if doc.get_state_slug == "rfc" %}
|
{% if doc.type_id == "rfc" %}
|
||||||
RFC {{ doc.rfc_number }}
|
RFC {{ doc.rfc_number }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ doc.name }}-{{ doc.rev }}
|
{{ doc.name }}-{{ doc.rev }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
{% if doc.get_state_slug == "rfc" and "draft" in doc.name %}<i class="text-body-secondary">(was {{ doc.name }})</i>{% endif %}
|
{# {% if doc.type_id == "rfc" and "draft" in doc.name %}<i class="text-body-secondary">(was {{ doc.name }})</i>{% endif %} TODO drop this or look up the corresponding draft name#}
|
||||||
<br>
|
<br>
|
||||||
{% comment %}
|
{% comment %}
|
||||||
<div class="float-end">
|
<div class="float-end">
|
||||||
|
@ -106,19 +106,19 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if doc.latest_revision_date|timesince_days|new_enough:request and doc.get_state_slug != "rfc" %}
|
{% if doc.latest_revision_date|timesince_days|new_enough:request and doc.type_id != "rfc" %}
|
||||||
{% if doc.rev != "00" %}
|
{% if doc.rev != "00" %}
|
||||||
<a href="{{ rfcdiff_base_url }}?url2={{ doc.name }}-{{ doc.rev }}">
|
<a href="{{ rfcdiff_base_url }}?url2={{ doc.name }}-{{ doc.rev }}">
|
||||||
{% elif doc.replaces %}
|
{% elif doc.replaces %}
|
||||||
<a href="{{ rfcdiff_base_url }}?url1={{ doc.replaces_canonical_name }}&url2={{ doc.name }}-{{ doc.rev }}">
|
<a href="{{ rfcdiff_base_url }}?url1={{ doc.replaces_canonical_name }}&url2={{ doc.name }}-{{ doc.rev }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if doc.get_state_slug == "rfc" %}
|
{% if doc.type_id == "rfc" %}
|
||||||
{{ doc.latest_revision_date|date:"Y-m" }}
|
{{ doc.latest_revision_date|date:"Y-m" }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ doc.latest_revision_date|date:"Y-m-d" }}
|
{{ doc.latest_revision_date|date:"Y-m-d" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if doc.latest_revision_date|timesince_days|new_enough:request and doc.get_state_slug != "rfc" %}
|
{% if doc.latest_revision_date|timesince_days|new_enough:request and doc.type_id != "rfc" %}
|
||||||
{% if doc.rev != "00" or doc.replaces %}</a>{% endif %}
|
{% if doc.rev != "00" or doc.replaces %}</a>{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if doc.latest_revision_date|timesince_days|new_enough:request %}
|
{% if doc.latest_revision_date|timesince_days|new_enough:request %}
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
<span class="badge rounded-pill bg-success">New</span>
|
<span class="badge rounded-pill bg-success">New</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if doc.get_state_slug == "active" and doc.expirable and doc.expires|timesince_days|expires_soon:request %}
|
{% if doc.type_id == "draft" and doc.get_state_slug == "active" and doc.expirable and doc.expires|timesince_days|expires_soon:request %}
|
||||||
<br>
|
<br>
|
||||||
<span class="badge rounded-pill bg-warning">Expires soon</span>
|
<span class="badge rounded-pill bg-warning">Expires soon</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
{% load ietf_filters %}{% load mail_filters %}{% autoescape off %}From: The IESG <iesg-secretary@ietf.org>
|
{% load ietf_filters %}{% load mail_filters %}{% autoescape off %}From: The IESG <iesg-secretary@ietf.org>
|
||||||
To: {{ to }}{% if cc %}
|
To: {{ to }}{% if cc %}
|
||||||
Cc: {{ cc }}{% endif %}
|
Cc: {{ cc }}{% endif %}
|
||||||
Subject: {{action}}: {{relateddoc.target.document.title}} to {{newstatus}}
|
Subject: {{action}}: {{relateddoc.target.title}} to {{newstatus}}
|
||||||
|
|
||||||
{% filter wordwrap:78 %}The IESG has approved changing the status of the following document:
|
{% filter wordwrap:78 %}The IESG has approved changing the status of the following document:
|
||||||
- {{relateddoc.target.document.title }}
|
- {{relateddoc.target.title }}
|
||||||
({{relateddoc.target.document.canonical_name }}) to {{ newstatus }}
|
({{relateddoc.target.canonical_name }}) to {{ newstatus }}
|
||||||
|
|
||||||
This {{action|lower}} is documented at:
|
This {{action|lower}} is documented at:
|
||||||
{{status_change_url}}
|
{{status_change_url}}
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
<a class="btn btn-primary" href="{{ group.about_url }}">{{ group.acronym }} {{ group.type.name }}</a>
|
<a class="btn btn-primary" href="{{ group.about_url }}">{{ group.acronym }} {{ group.type.name }}</a>
|
||||||
{% if group.charter %}
|
{% if group.charter %}
|
||||||
<a class="btn btn-primary"
|
<a class="btn btn-primary"
|
||||||
href="{% url "ietf.doc.views_doc.document_main" name=group.charter.canonical_name %}">
|
href="{% url "ietf.doc.views_doc.document_main" name=group.charter.name %}">
|
||||||
{{ group.charter.canonical_name }}
|
{{ group.charter.name }}
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if can_change_uses_milestone_dates %}
|
{% if can_change_uses_milestone_dates %}
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-secondary float-end"
|
<a class="btn btn-secondary float-end"
|
||||||
href="{% if milestone_set == "charter" %}{% url "ietf.doc.views_doc.document_main" name=group.charter.canonical_name %}{% else %}{{ group.about_url }}{% endif %}">
|
href="{% if milestone_set == "charter" %}{% url "ietf.doc.views_doc.document_main" name=group.charter.name %}{% else %}{{ group.about_url }}{% endif %}">
|
||||||
Cancel
|
Cancel
|
||||||
</a>
|
</a>
|
||||||
<button class="btn btn-primary hidden"
|
<button class="btn btn-primary hidden"
|
||||||
|
|
|
@ -165,13 +165,13 @@ Parts Copyright (c) 2009 The IETF Trust, all rights reserved.
|
||||||
{% if downrefs %}
|
{% if downrefs %}
|
||||||
<p>If APPROVED - The Secretariat will add to the downref registry:<br>
|
<p>If APPROVED - The Secretariat will add to the downref registry:<br>
|
||||||
{% for ref in downrefs %}
|
{% for ref in downrefs %}
|
||||||
+ Add {{ref.target.document.canonical_name}}
|
+ Add {{ref.target.canonical_name}}
|
||||||
({{ref.target.document.std_level}} - {{ref.target.document.stream.desc}})
|
({{ref.target.std_level}} - {{ref.target.stream.desc}})
|
||||||
to downref registry.<br>
|
to downref registry.<br>
|
||||||
{% if not ref.target.document.std_level %}
|
{% if not ref.target.std_level %}
|
||||||
+++ Warning: The standards level has not been set yet!!!<br>
|
+++ Warning: The standards level has not been set yet!!!<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not ref.target.document.stream %}
|
{% if not ref.target.stream %}
|
||||||
+++ Warning: document stream has not been set yet!!!<br>
|
+++ Warning: document stream has not been set yet!!!<br>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}</p>
|
{% endfor %}</p>
|
||||||
|
|
|
@ -54,7 +54,7 @@ class Command(BaseCommand):
|
||||||
doc = getattr(obj, docattr)
|
doc = getattr(obj, docattr)
|
||||||
time = getattr(obj, timeattr)
|
time = getattr(obj, timeattr)
|
||||||
if not obj.rev:
|
if not obj.rev:
|
||||||
if not doc.is_rfc():
|
if doc.type_id != "rfc":
|
||||||
self.stdout.write("Bad revision number: %-52s: '%s'" % (doc.name, obj.rev))
|
self.stdout.write("Bad revision number: %-52s: '%s'" % (doc.name, obj.rev))
|
||||||
continue
|
continue
|
||||||
rev = int(obj.rev.lstrip('0') or '0')
|
rev = int(obj.rev.lstrip('0') or '0')
|
||||||
|
|
|
@ -12,11 +12,12 @@ from django.utils.encoding import smart_str
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.doc.models import Document, DocAlias, State, DocumentAuthor, DocEvent, RelatedDocument, NewRevisionDocEvent
|
from ietf.doc.models import Document, DocAlias, State, DocumentAuthor, DocEvent, RelatedDocument, NewRevisionDocEvent
|
||||||
|
from ietf.doc.factories import IndividualDraftFactory, ConflictReviewFactory, StatusChangeFactory, WgDraftFactory, WgRfcFactory
|
||||||
from ietf.group.models import Group, GroupHistory, Role, RoleHistory
|
from ietf.group.models import Group, GroupHistory, Role, RoleHistory
|
||||||
from ietf.iesg.models import TelechatDate
|
from ietf.iesg.models import TelechatDate
|
||||||
from ietf.ipr.models import HolderIprDisclosure, IprDocRel, IprDisclosureStateName, IprLicenseTypeName
|
from ietf.ipr.models import HolderIprDisclosure, IprDocRel, IprDisclosureStateName, IprLicenseTypeName
|
||||||
from ietf.meeting.models import Meeting, ResourceAssociation
|
from ietf.meeting.models import Meeting, ResourceAssociation
|
||||||
from ietf.name.models import StreamName, DocRelationshipName, RoomResourceName, ConstraintName
|
from ietf.name.models import DocRelationshipName, RoomResourceName, ConstraintName
|
||||||
from ietf.person.models import Person, Email
|
from ietf.person.models import Person, Email
|
||||||
from ietf.group.utils import setup_default_community_list_for_group
|
from ietf.group.utils import setup_default_community_list_for_group
|
||||||
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName, ReviewTypeName, ReviewTeamSettings )
|
from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName, ReviewTypeName, ReviewTeamSettings )
|
||||||
|
@ -316,7 +317,7 @@ def make_test_data():
|
||||||
doc_alias = DocAlias.objects.create(name=draft.name)
|
doc_alias = DocAlias.objects.create(name=draft.name)
|
||||||
doc_alias.docs.add(draft)
|
doc_alias.docs.add(draft)
|
||||||
|
|
||||||
RelatedDocument.objects.create(source=draft, target=old_alias, relationship=DocRelationshipName.objects.get(slug='replaces'))
|
RelatedDocument.objects.create(source=draft, target=old_draft, relationship=DocRelationshipName.objects.get(slug='replaces'))
|
||||||
old_draft.set_state(State.objects.get(type='draft', slug='repl'))
|
old_draft.set_state(State.objects.get(type='draft', slug='repl'))
|
||||||
|
|
||||||
DocumentAuthor.objects.create(
|
DocumentAuthor.objects.create(
|
||||||
|
@ -391,37 +392,27 @@ def make_test_data():
|
||||||
)
|
)
|
||||||
|
|
||||||
# an independent submission before review
|
# an independent submission before review
|
||||||
doc = Document.objects.create(name='draft-imaginary-independent-submission',type_id='draft',rev='00',
|
IndividualDraftFactory(title="Some Independent Notes on Imagination")
|
||||||
title="Some Independent Notes on Imagination")
|
|
||||||
doc.set_state(State.objects.get(used=True, type="draft", slug="active"))
|
|
||||||
DocAlias.objects.create(name=doc.name).docs.add(doc)
|
|
||||||
|
|
||||||
# an irtf submission mid review
|
# an irtf submission mid review
|
||||||
doc = Document.objects.create(name='draft-imaginary-irtf-submission', type_id='draft',rev='00',
|
doc = IndividualDraftFactory(name="draft-imaginary-irtf-submission", stream_id="irtf", title="The Importance of Research Imagination")
|
||||||
stream=StreamName.objects.get(slug='irtf'), title="The Importance of Research Imagination")
|
ConflictReviewFactory(name="conflict-review-imaginary-irtf-submission", review_of=doc, notify="fsm@ietf.org", title="Conflict Review of IRTF Imagination Document")
|
||||||
docalias = DocAlias.objects.create(name=doc.name)
|
|
||||||
docalias.docs.add(doc)
|
|
||||||
doc.set_state(State.objects.get(type="draft", slug="active"))
|
|
||||||
crdoc = Document.objects.create(name='conflict-review-imaginary-irtf-submission', type_id='conflrev',
|
|
||||||
rev='00', notify="fsm@ietf.org", title="Conflict Review of IRTF Imagination Document")
|
|
||||||
DocAlias.objects.create(name=crdoc.name).docs.add(crdoc)
|
|
||||||
crdoc.set_state(State.objects.get(name='Needs Shepherd', type__slug='conflrev'))
|
|
||||||
crdoc.relateddocument_set.create(target=docalias,relationship_id='conflrev')
|
|
||||||
|
|
||||||
# A status change mid review
|
# A status change mid review
|
||||||
iesg = Group.objects.get(acronym='iesg')
|
iesg = Group.objects.get(acronym='iesg')
|
||||||
doc = Document.objects.create(name='status-change-imaginary-mid-review',type_id='statchg', rev='00',
|
doc = StatusChangeFactory(
|
||||||
notify="fsm@ietf.org", group=iesg, title="Status Change Review without Imagination")
|
name='status-change-imaginary-mid-review',
|
||||||
doc.set_state(State.objects.get(slug='needshep',type__slug='statchg'))
|
notify="fsm@ietf.org",
|
||||||
docalias = DocAlias.objects.create(name='status-change-imaginary-mid-review')
|
group=iesg,
|
||||||
docalias.docs.add(doc)
|
title="Status Change Review without Imagination",
|
||||||
|
states= [State.objects.get(type_id="statchg",slug="needshep")]
|
||||||
|
)
|
||||||
|
|
||||||
# Some things for a status change to affect
|
# Some things for a status change to affect
|
||||||
def rfc_for_status_change_test_factory(name,rfc_num,std_level_id):
|
def rfc_for_status_change_test_factory(name,rfc_num,std_level_id):
|
||||||
target_rfc = Document.objects.create(name=name, type_id='draft', std_level_id=std_level_id, notify="%s@ietf.org"%name)
|
target_rfc = WgRfcFactory(rfc_number=rfc_num, std_level_id=std_level_id)
|
||||||
target_rfc.set_state(State.objects.get(slug='rfc',type__slug='draft'))
|
source_draft = WgDraftFactory(name=name, states=[("draft","rfc")], notify=f"{name}@ietf.org")
|
||||||
DocAlias.objects.create(name=name).docs.add(target_rfc)
|
source_draft.relateddocument_set.create(relationship_id="became_rfc", target=target_rfc)
|
||||||
DocAlias.objects.create(name='rfc%d'%rfc_num).docs.add(target_rfc)
|
|
||||||
return target_rfc
|
return target_rfc
|
||||||
rfc_for_status_change_test_factory('draft-ietf-random-thing',9999,'ps')
|
rfc_for_status_change_test_factory('draft-ietf-random-thing',9999,'ps')
|
||||||
rfc_for_status_change_test_factory('draft-ietf-random-otherthing',9998,'inf')
|
rfc_for_status_change_test_factory('draft-ietf-random-otherthing',9998,'inf')
|
||||||
|
|
Loading…
Reference in a new issue