From 18d32c2304a61471ff6df72efee51126da7d0867 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Tue, 13 Jun 2023 10:35:19 -0300 Subject: [PATCH 01/79] ci: Enable CI tests for feat/rfc branch --- .github/workflows/ci-run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-run-tests.yml b/.github/workflows/ci-run-tests.yml index 8a7ee696b..7339c7299 100644 --- a/.github/workflows/ci-run-tests.yml +++ b/.github/workflows/ci-run-tests.yml @@ -4,7 +4,7 @@ on: pull_request: branches: - 'main' - - 'feat/django4' + - 'feat/rfc' paths: - 'client/**' - 'ietf/**' From c8dcda0fae7b7142fbc1abf4cad472c2e4870b80 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 14 Jun 2023 10:32:21 -0300 Subject: [PATCH 02/79] refactor: Do not use canonical_name() for charters (#5818) * fix: Enforce naming of charter docs in submit() * style: Reformat submit() with Black * refactor: Remove redundant check of charter name * style: Reformat charter_with_milestones_txt with Black * refactor: Drop canonical_name, use Path in charter_with_milestones_txt * style: Reformat review_announcement_text() with Black * style: Reformat action_announcement_text() with Black * refactor: Change uses of charter.canonical_name() to charter.name * refactor: Skip docialias when retrieving charter * refactor: Change canonical_name() to name in utils_charter.py * refactor: Use Path in read_charter_text() * refactor: Drop canonical_name, minor refactor of tests_charter.py * refactor: charter.name instead of canonical_name in milestones.py * refactor: charter.name instead of canonical_name in tests_info.py * refactor: Remove unused functions in ietf/secr/utils/groups.py * refactor: charter.canonical_name -> charter.name in templates * refactor: Remove charter handling from canonical_name Temporarily raise an exception for testing * refactor: Refactor get_charter_text() without canonical_name * refactor: Remove raise when canonical_name called on a charter * fix: Add back missing ".txt" extension * test: Test rejection of invalid charter names --- ietf/doc/models.py | 6 - ietf/doc/tests_charter.py | 32 +- ietf/doc/utils_charter.py | 14 +- ietf/doc/views_charter.py | 289 ++++++++++++------ ietf/group/milestones.py | 2 +- ietf/group/tests_info.py | 19 +- ietf/group/utils.py | 18 +- ietf/secr/utils/group.py | 24 -- .../doc/charter/action_announcement_text.html | 2 +- ietf/templates/doc/charter/approve.html | 6 +- ietf/templates/doc/charter/change_ad.html | 6 +- ietf/templates/group/edit_milestones.html | 6 +- 12 files changed, 256 insertions(+), 168 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 86d814d73..547e40f4f 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -856,12 +856,6 @@ class Document(DocumentInfo): a = self.docalias.filter(name__startswith="rfc").order_by('-name').first() if a: 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 return self._canonical_name diff --git a/ietf/doc/tests_charter.py b/ietf/doc/tests_charter.py index f65cf14e0..c5c5ce5f9 100644 --- a/ietf/doc/tests_charter.py +++ b/ietf/doc/tests_charter.py @@ -88,10 +88,7 @@ class EditCharterTests(TestCase): settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['CHARTER_PATH'] def write_charter_file(self, charter): - with (Path(settings.CHARTER_PATH) / - ("%s-%s.txt" % (charter.canonical_name(), charter.rev)) - ).open("w") as f: - f.write("This is a charter.") + (Path(settings.CHARTER_PATH) / f"{charter.name}-{charter.rev}.txt").write_text("This is a charter.") def test_startstop_process(self): CharterFactory(group__acronym='mars') @@ -509,8 +506,13 @@ class EditCharterTests(TestCase): self.assertEqual(charter.rev, next_revision(prev_rev)) 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: - self.assertEqual(f.read(), "Windows line\nMac line\nUnix line\n" + utf_8_snippet.decode('utf-8')) + file_contents = ( + 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): 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) 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): area = GroupFactory(type_id='area') RoleFactory(name_id='ad',group=area,person=Person.objects.get(user__username='ad')) diff --git a/ietf/doc/utils_charter.py b/ietf/doc/utils_charter.py index 2e85b3cc1..7d2001e4d 100644 --- a/ietf/doc/utils_charter.py +++ b/ietf/doc/utils_charter.py @@ -3,11 +3,12 @@ import datetime -import io import os import re import shutil +from pathlib import Path + from django.conf import settings from django.urls import reverse as urlreverse from django.template.loader import render_to_string @@ -62,10 +63,9 @@ def next_approved_revision(rev): return "%#02d" % (int(m.group('major')) + 1) 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: - with io.open(filename, 'r') as f: - return f.read() + return filename.read_text() except IOError: 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): # according to spec, 00-02 becomes 01, so copy file and record new revision try: - old = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), charter.rev)) - new = os.path.join(charter.get_file_path(), '%s-%s.txt' % (charter.canonical_name(), next_approved_revision(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.name, next_approved_revision(charter.rev))) shutil.copy(old, new) except IOError: log("There was an error copying %s to %s" % (old, new)) @@ -101,7 +101,7 @@ def fix_charter_revision_after_approval(charter, by): events = [] e = NewRevisionDocEvent(doc=charter, by=by, type="new_revision") e.rev = next_approved_revision(charter.rev) - e.desc = "New version available: %s-%s.txt" % (charter.canonical_name(), e.rev) + e.desc = "New version available: %s-%s.txt" % (charter.name, e.rev) e.save() events.append(e) diff --git a/ietf/doc/views_charter.py b/ietf/doc/views_charter.py index d3173291d..b3d9d06c3 100644 --- a/ietf/doc/views_charter.py +++ b/ietf/doc/views_charter.py @@ -3,11 +3,11 @@ import datetime -import io import json -import os import textwrap +from pathlib import Path + from django.http import HttpResponseRedirect, HttpResponseNotFound, Http404 from django.shortcuts import get_object_or_404, redirect, render 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, derive_new_work_text, 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.group.mails import email_admin_re_charter 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.person.models import Person 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.textupload import get_cleaned_text_file_content from ietf.utils.response import permission_denied @@ -362,38 +363,41 @@ class UploadForm(forms.Form): @login_required def submit(request, name, option=None): - if not name.startswith('charter-'): - raise Http404 - + # Charters are named "charter--" charter = Document.objects.filter(type="charter", name=name).first() if charter: group = charter.group - charter_canonical_name = charter.canonical_name() + assertion("charter.name == charter_name_for_group(group)") charter_rev = charter.rev else: top_org, group_acronym = split_charter_name(name) 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" - 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.") - - path = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter_canonical_name, charter_rev)) - not_uploaded_yet = charter_rev.endswith("-00") and not os.path.exists(path) + charter_filename = Path(settings.CHARTER_PATH) / f"{name}-{charter_rev}.txt" + not_uploaded_yet = charter_rev.endswith("-00") and not charter_filename.exists() if not_uploaded_yet or not charter: # this case is special - we recently chartered or rechartered and have no file yet next_rev = charter_rev else: # 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) while next_rev in prev_revs: next_rev = next_revision(next_rev) - if request.method == 'POST': + if request.method == "POST": form = UploadForm(request.POST, request.FILES) if form.is_valid(): # Also save group history so we can search for it @@ -408,9 +412,11 @@ def submit(request, name, option=None): abstract=group.name, 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.save() @@ -418,56 +424,74 @@ def submit(request, name, option=None): charter.rev = next_rev events = [] - e = NewRevisionDocEvent(doc=charter, by=request.user.person, type="new_revision") - e.desc = "New version available: %s-%s.txt" % (charter.canonical_name(), charter.rev) + e = NewRevisionDocEvent( + doc=charter, by=request.user.person, type="new_revision" + ) + e.desc = "New version available: %s-%s.txt" % ( + charter.name, + charter.rev, + ) e.rev = charter.rev e.save() events.append(e) # Save file on disk - filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter.canonical_name(), charter.rev)) - with io.open(filename, 'w', encoding='utf-8') as destination: - if form.cleaned_data['txt']: - destination.write(form.cleaned_data['txt']) + charter_filename = charter_filename.with_name( + f"{name}-{charter.rev}.txt" + ) # update rev + with charter_filename.open("w", encoding="utf-8") as destination: + if form.cleaned_data["txt"]: + destination.write(form.cleaned_data["txt"]) else: - destination.write(form.cleaned_data['content']) + destination.write(form.cleaned_data["content"]) - if option in ['initcharter','recharter'] and charter.ad == None: - charter.ad = getattr(group.ad_role(),'person',None) + if option in ["initcharter", "recharter"] and charter.ad == None: + charter.ad = getattr(group.ad_role(), "person", None) charter.save_with_history(events) 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: return redirect("ietf.doc.views_doc.document_main", name=charter.name) else: - init = { "content": "" } + init = {"content": ""} if not_uploaded_yet and charter: # use text from last approved revision 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: - charter_canonical_name = h.canonical_name() - charter_rev = h.rev - - filename = os.path.join(settings.CHARTER_PATH, '%s-%s.txt' % (charter_canonical_name, charter_rev)) + assertion("h.name == charter_name_for_group(group)") + charter_filename = charter_filename.with_name( + f"{name}-{h.rev}.txt" + ) # update rev try: - with io.open(filename, 'r') as f: - init["content"] = f.read() + init["content"] = charter_filename.read_text() except IOError: pass form = UploadForm(initial=init) fill_in_charter_info(group) - return render(request, 'doc/charter/submit.html', { - 'form': form, - 'next_rev': next_rev, - 'group': group, - 'name': name, - }) + return render( + request, + "doc/charter/submit.html", + { + "form": form, + "next_rev": next_rev, + "group": group, + "name": name, + }, + ) class ActionAnnouncementTextForm(forms.Form): 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", "") -@role_required('Area Director','Secretariat') +@role_required("Area Director", "Secretariat") def review_announcement_text(request, name): """Editing of review announcement text""" charter = get_object_or_404(Document, type="charter", name=name) @@ -493,7 +517,9 @@ def review_announcement_text(request, name): by = request.user.person 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: (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.type = "changed_new_work_text" 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() - 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) if "save_text" in request.POST and form.is_valid(): - now = timezone.now() events = [] - t = form.cleaned_data['announcement_text'] + t = form.cleaned_data["announcement_text"] if t != existing.text: e = WriteupDocEvent(doc=charter, rev=charter.rev) e.by = by @@ -532,11 +562,11 @@ def review_announcement_text(request, name): existing.save() events.append(existing) - t = form.cleaned_data['new_work_text'] + t = form.cleaned_data["new_work_text"] if t != existing_new_work.text: e = WriteupDocEvent(doc=charter, rev=charter.rev) 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.text = t e.time = now @@ -549,33 +579,71 @@ def review_announcement_text(request, name): charter.save_with_history(events) 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: (existing, existing_new_work) = default_review_text(group, charter, by) existing.save() existing_new_work.save() - 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 any(x in request.POST for x in ['send_annc_only','send_nw_only','send_both']) and form.is_valid(): - 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) + if ( + any( + x in request.POST + for x in ["send_annc_only", "send_nw_only", "send_both"] + ) + and form.is_valid() + ): + 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', - dict(charter=charter, - back_url=urlreverse('ietf.doc.views_doc.document_writeup', kwargs=dict(name=charter.name)), - announcement_text_form=form, - )) + return render( + request, + "doc/charter/review_announcement_text.html", + 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): """Editing of action announcement text""" charter = get_object_or_404(Document, type="charter", name=name) @@ -590,16 +658,18 @@ def action_announcement_text(request, name): if not existing: 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) 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: e = WriteupDocEvent(doc=charter, rev=charter.rev) e.by = by - e.type = "changed_action_announcement" + e.type = "changed_action_announcement" e.desc = "%s action text was changed" % group.type.name e.text = t e.save() @@ -607,25 +677,46 @@ def action_announcement_text(request, name): existing.save() 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: e = default_action_text(group, charter, by) 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(): - 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"],)) - return redirect('ietf.doc.views_doc.document_writeup', name=charter.name) + 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"], + ), + ) + return redirect("ietf.doc.views_doc.document_writeup", name=charter.name) - return render(request, 'doc/charter/action_announcement_text.html', - dict(charter=charter, - back_url=urlreverse('ietf.doc.views_doc.document_writeup', kwargs=dict(name=charter.name)), - announcement_text_form=form, - )) + return render( + request, + "doc/charter/action_announcement_text.html", + 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): ballot_writeup = forms.CharField(widget=forms.Textarea, required=True, strip=False) @@ -806,33 +897,37 @@ def approve(request, name): dict(charter=charter, 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: return HttpResponseNotFound("Revision %s not found in database" % rev) # read charter text c = find_history_active_at(charter, revision_event.time) or charter - filename = '%s-%s.txt' % (c.canonical_name(), rev) - - charter_text = "" - + filename = Path(settings.CHARTER_PATH) / f"{c.name}-{rev}.txt" try: - with io.open(os.path.join(settings.CHARTER_PATH, filename), 'r') as f: - charter_text = force_str(f.read(), errors='ignore') + with filename.open() as f: + charter_text = force_str(f.read(), errors="ignore") 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) # 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: m.desc_filled = wrapper.fill(m.desc) - return render(request, 'doc/charter/charter_with_milestones.txt', - dict(charter_text=charter_text, - milestones=milestones), - content_type="text/plain; charset=%s"%settings.DEFAULT_CHARSET) + return render( + request, + "doc/charter/charter_with_milestones.txt", + dict(charter_text=charter_text, milestones=milestones), + content_type="text/plain; charset=%s" % settings.DEFAULT_CHARSET, + ) diff --git a/ietf/group/milestones.py b/ietf/group/milestones.py index 64ebb389e..039fdb44c 100644 --- a/ietf/group/milestones.py +++ b/ietf/group/milestones.py @@ -369,7 +369,7 @@ def edit_milestones(request, acronym, group_type=None, milestone_set="current"): email_milestones_changed(request, group, changes, states) 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: return HttpResponseRedirect(group.about_url()) else: diff --git a/ietf/group/tests_info.py b/ietf/group/tests_info.py index 672d18c8f..72b632bcc 100644 --- a/ietf/group/tests_info.py +++ b/ietf/group/tests_info.py @@ -117,8 +117,9 @@ class GroupPagesTests(TestCase): 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")) r = self.client.get(url) @@ -264,8 +265,9 @@ class GroupPagesTests(TestCase): group = CharterFactory().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( group=group, @@ -674,8 +676,9 @@ class GroupEditTests(TestCase): self.assertTrue(len(q('form .is-invalid')) > 0) # 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 ad = Person.objects.get(name="AreaĆ° Irector") state = GroupStateName.objects.get(slug="bof") @@ -717,7 +720,9 @@ class GroupEditTests(TestCase): self.assertEqual(group.list_archive, "archive.mars") 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.assertTrue('Personnel change' in outbox[0]['Subject']) for prefix in ['ad1','ad2','aread','marschairman','marsdelegate']: diff --git a/ietf/group/utils.py b/ietf/group/utils.py index b701d6a7c..9cf093044 100644 --- a/ietf/group/utils.py +++ b/ietf/group/utils.py @@ -2,8 +2,7 @@ # -*- coding: utf-8 -*- -import io -import os +from pathlib import Path from django.db.models import Q 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): 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: - with io.open(filename, 'rb') as f: - text = f.read() - try: - text = text.decode('utf8') - except UnicodeDecodeError: - text = text.decode('latin1') - return text + text = filename.read_bytes() + try: + text = text.decode('utf8') + except UnicodeDecodeError: + text = text.decode('latin1') + return text except IOError: return 'Error Loading Group Charter' diff --git a/ietf/secr/utils/group.py b/ietf/secr/utils/group.py index a4c1c0f98..40a9065ac 100644 --- a/ietf/secr/utils/group.py +++ b/ietf/secr/utils/group.py @@ -3,11 +3,8 @@ # Python imports -import io -import os # Django imports -from django.conf import settings from django.core.exceptions import ObjectDoesNotExist # Datatracker imports @@ -15,27 +12,6 @@ from ietf.group.models import Group 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): ''' Takes a Django user object (from request) diff --git a/ietf/templates/doc/charter/action_announcement_text.html b/ietf/templates/doc/charter/action_announcement_text.html index 5722b342a..87af2510a 100644 --- a/ietf/templates/doc/charter/action_announcement_text.html +++ b/ietf/templates/doc/charter/action_announcement_text.html @@ -21,7 +21,7 @@ {% if user|has_role:"Secretariat" %} + href="{% url 'ietf.doc.views_charter.approve' name=charter.name %}"> Charter approval page {% endif %} diff --git a/ietf/templates/doc/charter/approve.html b/ietf/templates/doc/charter/approve.html index f109da687..2a8654482 100644 --- a/ietf/templates/doc/charter/approve.html +++ b/ietf/templates/doc/charter/approve.html @@ -2,16 +2,16 @@ {# Copyright The IETF Trust 2015, All Rights Reserved #} {% load origin %} {% load django_bootstrap5 %} -{% block title %}Approve {{ charter.canonical_name }}{% endblock %} +{% block title %}Approve {{ charter.name }}{% endblock %} {% block content %} {% origin %} -

Approve {{ charter.canonical_name }}-{{ charter.rev }}

+

Approve {{ charter.name }}-{{ charter.rev }}

{% csrf_token %}
{{ announcement }}
+ href="{% url "ietf.doc.views_charter.action_announcement_text" name=charter.name %}?next=approve"> Edit/regenerate announcement Change responsible AD
- {{ charter.canonical_name }}-{{ charter.rev }} + {{ charter.name }}-{{ charter.rev }} {% csrf_token %} {% bootstrap_form form %}
+ href="{% url "ietf.doc.views_doc.document_main" name=charter.name %}"> Back
diff --git a/ietf/templates/group/edit_milestones.html b/ietf/templates/group/edit_milestones.html index 36a4a714c..1ae1c57a5 100644 --- a/ietf/templates/group/edit_milestones.html +++ b/ietf/templates/group/edit_milestones.html @@ -14,8 +14,8 @@ {{ group.acronym }} {{ group.type.name }} {% if group.charter %} - {{ group.charter.canonical_name }} + href="{% url "ietf.doc.views_doc.document_main" name=group.charter.name %}"> + {{ group.charter.name }} {% endif %} {% if can_change_uses_milestone_dates %} @@ -106,7 +106,7 @@ + href="{% if milestone_set == "charter" %}{% url "ietf.doc.views_doc.document_main" name=group.charter.name %}{% else %}{{ group.about_url }}{% endif %}"> Cancel + + + {% if user.is_authenticated %} + + + + Untrack + + + + + Track + + {% endif %} + {% if user.review_teams %} + + + + Remove review wishes + + + + + Add review wish + + {% endif %} + {% if can_edit and iesg_state.slug != 'idexists' %} + + Last call text + + + Ballot text + + + Announcement text + + {% endif %} + {% if actions %} + {% for label, url in actions %} + + {{ label|capfirst_allcaps }} + + {% endfor %} + {% endif %} + + {% if doc.get_state_slug == "active" or doc.get_state_slug == "rfc" %} +
+
+ {% if doc.get_state_slug == "rfc" and not snapshot %} + RFC {{ rfc_number }} + {% else %} + {{ name }}-{{ doc.rev }} + {% endif %} +
+
+
{{ content|sanitize|safe|default:"(Unavailable)" }}
+
+
+ {% if split_content %} + + + + Show full document + + {% endif %} + {% else %} +
+
+

This Internet-Draft is no longer active. A copy of + the expired Internet-Draft is available in these formats:

+ + {% include "doc/document_format_buttons.html" %} +
+
+

+ Abstract +

+

+ {{ doc.abstract }} +

+

+ Authors +

+

+ {% for author in doc.documentauthor_set.all %} + {% person_link author.person %} + {% if not forloop.last %}
{% endif %} + {% endfor %} +

+

+ (Note: The e-mail addresses provided for the authors of this Internet-Draft may no longer be valid.) +

+
+
+ {% endif %} + {% endblock %} + {% block js %} + + + {% endblock %} \ No newline at end of file From 8f0b4590502b64f552d86ff165de37a7b243c12a Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Thu, 6 Jul 2023 12:07:53 -0300 Subject: [PATCH 18/79] refactor: Replace is_rfc() tests (#5925) * refactor: Remove is_rfc() - test type_id instead * fix: Guard against unknown pub_date This should not ever come up - we have a published_rfc event for every rfc. Should investigate fixing pub_date() to always return a val. --- ietf/api/views.py | 7 +------ ietf/doc/forms.py | 2 +- ietf/doc/models.py | 7 ++----- ietf/doc/templatetags/ietf_filters.py | 2 +- ietf/doc/utils_search.py | 2 +- ietf/doc/views_ballot.py | 8 +++++++- ietf/doc/views_doc.py | 13 +++++++++---- ietf/group/views.py | 2 +- ietf/iesg/views.py | 2 +- ietf/templates/doc/document_info.html | 10 +++++----- ietf/templates/doc/idnits2-state.txt | 2 +- .../check_draft_event_revision_integrity.py | 2 +- 12 files changed, 31 insertions(+), 28 deletions(-) diff --git a/ietf/api/views.py b/ietf/api/views.py index a6372be17..387924976 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -317,12 +317,9 @@ def get_previous_url(name, rev=None): previous_url = '' if condition in ('historic version', 'current version'): doc = history if history else document - if found_rev: - doc.is_rfc = lambda: False previous_url = doc.get_href() elif condition == 'version dochistory not found': document.rev = found_rev - document.is_rfc = lambda: False previous_url = document.get_href() return previous_url @@ -335,7 +332,7 @@ def rfcdiff_latest_json(request, name, rev=None): raise Http404 elif condition in ('historic version', 'current version'): doc = history if history else document - if not found_rev and doc.is_rfc(): + if not found_rev and doc.type_id == "rfc": response['content_url'] = doc.get_href() response['name']=doc.canonical_name() if doc.name != doc.canonical_name(): @@ -345,7 +342,6 @@ def rfcdiff_latest_json(request, name, rev=None): response['previous'] = f'{doc.name}-{prev_rev}' response['previous_url'] = get_previous_url(doc.name, prev_rev) else: - doc.is_rfc = lambda: False response['content_url'] = doc.get_href() response['rev'] = doc.rev response['name'] = doc.name @@ -371,7 +367,6 @@ def rfcdiff_latest_json(request, name, rev=None): response['name'] = document.name response['rev'] = found_rev document.rev = found_rev - document.is_rfc = lambda: False response['content_url'] = document.get_href() # not sure what to do if non-numeric values come back, so at least log it log.assertion('found_rev.isdigit()') diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index cac22e410..fc32db8f0 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -148,7 +148,7 @@ class AddDownrefForm(forms.Form): raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft") 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) return rfc diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 6fed8d3ca..518088414 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -245,7 +245,7 @@ class DocumentInfo(models.Model): format = settings.DOC_HREFS[self.type_id] elif self.type_id in settings.DOC_HREFS: self.is_meeting_related = False - if self.is_rfc(): + if self.type_id == "rfc": format = settings.DOC_HREFS['rfc'] else: format = settings.DOC_HREFS[self.type_id] @@ -383,9 +383,6 @@ class DocumentInfo(models.Model): else: return state.name - def is_rfc(self): - return self.type_id == "rfc" - def author_list(self): best_addresses = [] for author in self.documentauthor_set.all(): @@ -994,7 +991,7 @@ class Document(DocumentInfo): This is the rfc publication date for RFCs, and the new-revision date for other documents. """ - if self.is_rfc(): + if self.type_id == "rfc": # 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 # assigned by the RFC editor. diff --git a/ietf/doc/templatetags/ietf_filters.py b/ietf/doc/templatetags/ietf_filters.py index 1c5836328..9f1e5d122 100644 --- a/ietf/doc/templatetags/ietf_filters.py +++ b/ietf/doc/templatetags/ietf_filters.py @@ -556,7 +556,7 @@ def consensus(doc): @register.filter def std_level_to_label_format(doc): """Returns valid Bootstrap classes to label a status level badge.""" - if doc.is_rfc(): + if doc.type_id == "rfc": if doc.related_that("obs"): return "obs" else: diff --git a/ietf/doc/utils_search.py b/ietf/doc/utils_search.py index 093bf74a3..c2554bf2c 100644 --- a/ietf/doc/utils_search.py +++ b/ietf/doc/utils_search.py @@ -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 # d.canonical_name = wrap_value(rfc_aliases[d.pk] if d.pk in rfc_aliases else d.name) - if d.is_rfc() 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 elif d.latest_event_cache["new_revision"]: d.latest_revision_date = d.latest_event_cache["new_revision"].time diff --git a/ietf/doc/views_ballot.py b/ietf/doc/views_ballot.py index cc4420e56..89b3c1b2d 100644 --- a/ietf/doc/views_ballot.py +++ b/ietf/doc/views_ballot.py @@ -953,7 +953,13 @@ def approve_downrefs(request, name): 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.document.type_id == "rfc" + ] downrefs_to_rfc_qs = RelatedDocument.objects.filter(pk__in=[r.pk for r in downrefs_to_rfc]) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 0a0603e43..aaf541553 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -240,7 +240,7 @@ def document_main(request, name, rev=None, document_html=False): # specific document types - if doc.is_rfc(): + 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 @@ -988,7 +988,7 @@ def document_html(request, name, rev=None): doc = found.documents.get() 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'): return redirect('ietf.doc.views_doc.document_html', name=doc.canonical_name()) @@ -998,7 +998,12 @@ def document_html(request, name, rev=None): if not os.path.exists(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): @@ -1221,7 +1226,7 @@ def document_bibtex(request, name, rev=None): doc = h break - if doc.is_rfc(): + if doc.type_id == "rfc": # 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 # as a DocAlias, and use a method on Document to retrieve it. diff --git a/ietf/group/views.py b/ietf/group/views.py index b6ec6cd04..3fce18d22 100644 --- a/ietf/group/views.py +++ b/ietf/group/views.py @@ -534,7 +534,7 @@ def group_documents_txt(request, acronym, group_type=None): rows = [] for d in itertools.chain(docs, docs_related): - if d.is_rfc(): + if d.type_id == "rfc": name = str(d.rfc_number) else: name = "%s-%s" % (d.name, d.rev) diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index 2d83efbbd..c8b718c27 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -151,7 +151,7 @@ def agenda_json(request, date=None): if doc.type_id == "draft": docinfo['rev'] = doc.rev docinfo['intended-std-level'] = str(doc.intended_std_level) - if doc.is_rfc(): + if doc.type_id == "rfc": docinfo['rfc-number'] = doc.rfc_number iana_state = doc.get_state("draft-iana-review") diff --git a/ietf/templates/doc/document_info.html b/ietf/templates/doc/document_info.html index d189c8b75..69851f33e 100644 --- a/ietf/templates/doc/document_info.html +++ b/ietf/templates/doc/document_info.html @@ -11,7 +11,7 @@ {% if document_html %}Document type{% else %}Type{% endif %} - {% if doc.is_rfc %} + {% if doc.type_id == "rfc" %} RFC {% if not document_html %} - {{ doc.std_level }} @@ -19,7 +19,7 @@ {{ doc.std_level }} {% endif %} - {% if doc.is_rfc %} + {% if doc.pub_date %} {% if document_html %}
{% else %}({% endif %}{{ doc.pub_date|date:"F Y" }}{% if not document_html %}){% endif %} {% else %} (Publication date unknown) @@ -132,7 +132,7 @@ {% endif %} - {% if not doc.is_rfc %} + {% if doc.type_id != "rfc" %} {% if replaces or not document_html and can_edit_stream_info %} @@ -265,7 +265,7 @@ {% endif %} - {% if not doc.is_rfc and not snapshot %} + {% if doc.type_id != "rfc" and not snapshot %} @@ -341,7 +341,7 @@ {% endif %} {% endfor %} - {% if not doc.is_rfc %}{# do not show reviews or conflict_reviews for RFCs, even if present #} + {% if doc.type_id != "rfc" %}{# do not show reviews or conflict_reviews for RFCs, even if present #} {% if review_assignments or can_request_review %} diff --git a/ietf/templates/doc/idnits2-state.txt b/ietf/templates/doc/idnits2-state.txt index 8b2cefdf0..55fc78927 100644 --- a/ietf/templates/doc/idnits2-state.txt +++ b/ietf/templates/doc/idnits2-state.txt @@ -1,5 +1,5 @@ {% load ietf_filters %}{% filter linebreaks_lf %}{% comment %} -{% endcomment %}Doc-tag: {{doc.name}};datatracker{% if doc.is_rfc %} +{% endcomment %}Doc-tag: {{doc.name}};datatracker{% if doc.type_id == "rfc" %} Doc-rfcnum: {{doc.rfc_number}}{% endif %} Doc-created: {{doc.created|date:"Y-m-d"}};datatracker{% if doc.deststatus %} Doc-deststatus: {{doc.deststatus}};datatracker{% endif %} diff --git a/ietf/utils/management/commands/check_draft_event_revision_integrity.py b/ietf/utils/management/commands/check_draft_event_revision_integrity.py index c8d2cbd21..c2d427278 100644 --- a/ietf/utils/management/commands/check_draft_event_revision_integrity.py +++ b/ietf/utils/management/commands/check_draft_event_revision_integrity.py @@ -54,7 +54,7 @@ class Command(BaseCommand): doc = getattr(obj, docattr) time = getattr(obj, timeattr) 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)) continue rev = int(obj.rev.lstrip('0') or '0') From 63a99206859f21d5ec882c6dc68c0f0ec1968432 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Thu, 6 Jul 2023 13:11:26 -0500 Subject: [PATCH 19/79] fix: restored where an rfc came from to the main rfc view (#5930) --- ietf/doc/tests.py | 9 +++------ ietf/doc/views_doc.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 827f004c3..eb45e985f 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1490,11 +1490,9 @@ Man Expires September 22, 2015 [Page 3] self.assertEqual(r.status_code, 200) self.assert_correct_wg_group_link(r, group) - rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group) + rfc = WgRfcFactory(group=group) DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime) - # get the rfc name to avoid a redirect - 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))) + r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))) self.assertEqual(r.status_code, 200) self.assert_correct_wg_group_link(r, group) @@ -1508,8 +1506,7 @@ Man Expires September 22, 2015 [Page 3] rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group) DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime) # get the rfc name to avoid a redirect - 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))) + r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))) self.assertEqual(r.status_code, 200) self.assert_correct_non_wg_group_link(r, group) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index aaf541553..caa996dc3 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -329,6 +329,32 @@ def document_main(request, name, rev=None, document_html=False): 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() + # 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 = '%s' % (group.about_url(), submission) + draft_alias = next(iter(doc.related_that("became_rfc")), None) + # Should be unreachable? + if ( + draft_alias + and draft_alias.document.stream_id + and draft_alias.document.get_state_slug( + "draft-stream-%s" % draft_alias.document.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", @@ -367,7 +393,8 @@ def document_main(request, name, rev=None, document_html=False): iana_experts_comment=iana_experts_comment, search_archive=search_archive, presentations=presentations, - diff_revisions=diff_revisions + diff_revisions=diff_revisions, + submission=submission )) elif doc.type_id == "draft": From f53a849660f7c47e6aa1dc1e68b45bfb426ccb91 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Thu, 6 Jul 2023 15:24:35 -0500 Subject: [PATCH 20/79] fix: various corrections to rfc main document view and tests (#5931) --- ietf/doc/models.py | 22 +++--- ietf/doc/tests.py | 99 ++++----------------------- ietf/doc/utils.py | 8 ++- ietf/doc/views_doc.py | 29 ++++---- ietf/templates/doc/document_html.html | 2 +- ietf/templates/doc/document_info.html | 1 - ietf/templates/doc/document_rfc.html | 10 --- 7 files changed, 45 insertions(+), 126 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 518088414..958fde12a 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -137,18 +137,17 @@ class DocumentInfo(models.Model): def get_file_path(self): 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(): self._cached_file_path = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR else: - if self.get_state_slug() == "rfc": - self._cached_file_path = settings.RFC_PATH + draft_state = self.get_state('draft') + if draft_state and draft_state.slug == 'active': + self._cached_file_path = settings.INTERNET_DRAFT_PATH else: - draft_state = self.get_state('draft') - 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 + self._cached_file_path = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR elif self.meeting_related() and self.type_id in ( "agenda", "minutes", "slides", "bluesheets", "procmaterials", "chatlog", "polls" ): @@ -173,14 +172,13 @@ class DocumentInfo(models.Model): if not hasattr(self, '_cached_base_name'): if self.uploaded_filename: self._cached_base_name = self.uploaded_filename + if self.type_id == 'rfc': + self._cached_base_name = "%s.txt" % self.canonical_name() elif self.type_id == 'draft': if self.is_dochistory(): self._cached_base_name = "%s-%s.txt" % (self.doc.name, self.rev) else: - if self.get_state_slug() == 'rfc': - self._cached_base_name = "%s.txt" % self.canonical_name() - else: - self._cached_base_name = "%s-%s.txt" % (self.name, self.rev) + 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(): ext = 'pdf' if self.type_id == 'procmaterials' else 'txt' self._cached_base_name = f'{self.canonical_name()}-{self.rev}.{ext}' diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index eb45e985f..0cffc46a1 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -619,21 +619,12 @@ Man Expires September 22, 2015 [Page 3] def test_document_draft(self): draft = WgDraftFactory(name='draft-ietf-mars-test',rev='01', create_revisions=range(0,2)) - HolderIprDisclosureFactory(docs=[draft]) # Docs for testing relationships. Does not test 'possibly-replaces'. The 'replaced_by' direction # is tested separately below. replaced = IndividualDraftFactory() draft.relateddocument_set.create(relationship_id='replaces',source=draft,target=replaced.docalias.first()) - obsoleted = IndividualDraftFactory() - draft.relateddocument_set.create(relationship_id='obs',source=draft,target=obsoleted.docalias.first()) - obsoleted_by = IndividualDraftFactory() - obsoleted_by.relateddocument_set.create(relationship_id='obs',source=obsoleted_by,target=draft.docalias.first()) - updated = IndividualDraftFactory() - draft.relateddocument_set.create(relationship_id='updates',source=draft,target=updated.docalias.first()) - updated_by = IndividualDraftFactory() - updated_by.relateddocument_set.create(relationship_id='updates',source=obsoleted_by,target=draft.docalias.first()) external_resource = DocExtResourceFactory(doc=draft) @@ -648,16 +639,6 @@ Man Expires September 22, 2015 [Page 3] self.assertNotContains(r, "Deimos street") self.assertContains(r, replaced.canonical_name()) 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") self.assertEqual(r.status_code, 200) @@ -666,15 +647,6 @@ Man Expires September 22, 2015 [Page 3] self.assertNotContains(r, "Deimos street") self.assertContains(r, replaced.canonical_name()) 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") self.assertEqual(r.status_code, 200) @@ -683,15 +655,6 @@ Man Expires September 22, 2015 [Page 3] self.assertContains(r, "Deimos street") self.assertContains(r, replaced.canonical_name()) 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") self.assertEqual(r.status_code, 200) @@ -700,15 +663,6 @@ Man Expires September 22, 2015 [Page 3] self.assertContains(r, "Deimos street") self.assertContains(r, replaced.canonical_name()) 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')}) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name))) @@ -718,15 +672,6 @@ Man Expires September 22, 2015 [Page 3] self.assertContains(r, "Deimos street") self.assertContains(r, replaced.canonical_name()) 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')}) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name))) @@ -736,15 +681,6 @@ Man Expires September 22, 2015 [Page 3] self.assertNotContains(r, "Deimos street") self.assertContains(r, replaced.canonical_name()) 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')}) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name))) @@ -755,15 +691,6 @@ Man Expires September 22, 2015 [Page 3] self.assertNotContains(r, "Deimos street") self.assertContains(r, replaced.canonical_name()) 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))) self.assertEqual(r.status_code, 200) @@ -831,26 +758,29 @@ Man Expires September 22, 2015 [Page 3] # draft published as RFC draft.set_state(State.objects.get(type="draft", slug="rfc")) - draft.std_level_id = "bcp" - draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="published_rfc", by=Person.objects.get(name="(System)"))]) + draft.std_level_id = "ps" + 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") - rfc_alias.docs.add(draft) - bcp_alias = DocAlias.objects.create(name="bcp123456") - bcp_alias.docs.add(draft) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + + obsoleted = IndividualRfcFactory() + rfc.relateddocument_set.create(relationship_id='obs',target=obsoleted.docalias.first()) + obsoleted_by = IndividualRfcFactory() + obsoleted_by.relateddocument_set.create(relationship_id='obs',target=rfc.docalias.first()) + updated = IndividualRfcFactory() + rfc.relateddocument_set.create(relationship_id='updates',target=updated.docalias.first()) + updated_by = IndividualRfcFactory() + updated_by.relateddocument_set.create(relationship_id='updates',target=rfc.docalias.first()) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=draft.name))) 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.assertContains(r, "RFC 123456") self.assertContains(r, draft.name) - self.assertContains(r, replaced.canonical_name()) - self.assertContains(r, replaced.title) # obs/updates included with RFC self.assertContains(r, obsoleted.canonical_name()) self.assertContains(r, obsoleted.title) @@ -1505,7 +1435,6 @@ Man Expires September 22, 2015 [Page 3] rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group) 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))) self.assertEqual(r.status_code, 200) self.assert_correct_non_wg_group_link(r, group) diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index 78baa808b..aa250af5f 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -1169,8 +1169,14 @@ def fuzzy_find_documents(name, rev=None): if re.match("^[0-9]+$", 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 - 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(): # No document found, see if the name/rev split has been misidentified. # Handles some special cases, like draft-ietf-tsvwg-ieee-802-11. diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index caa996dc3..91ddf7657 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -194,11 +194,10 @@ def document_main(request, name, rev=None, document_html=False): doc = get_object_or_404(Document.objects.select_related(), docalias__name=name) # take care of possible redirections - aliases = DocAlias.objects.filter(docs=doc).values_list("name", flat=True) - if document_html is False and rev==None and doc.type_id == "draft" and not name.startswith("rfc"): - for a in aliases: - if a.startswith("rfc"): - return redirect("ietf.doc.views_doc.document_main", name=a) + if document_html is False and rev is None: + became_rfc = next(iter(doc.related_that_doc("became_rfc")), None) + if became_rfc: + return redirect("ietf.doc.views_doc.document_main", name=became_rfc.name) revisions = [] for h in doc.history_set.order_by("time", "id"): @@ -265,9 +264,6 @@ def document_main(request, name, rev=None, document_html=False): can_change_stream = bool(can_edit or roles) - rfc_aliases = [prettify_std_name(a) for a in aliases - if a.startswith("fyi") or a.startswith("std") or a.startswith("bcp")] - 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)) @@ -329,6 +325,10 @@ def document_main(request, name, rev=None, document_html=False): 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: @@ -343,13 +343,12 @@ def document_main(request, name, rev=None, document_html=False): else: submission = group.acronym submission = '%s' % (group.about_url(), submission) - draft_alias = next(iter(doc.related_that("became_rfc")), None) # Should be unreachable? if ( - draft_alias - and draft_alias.document.stream_id - and draft_alias.document.get_state_slug( - "draft-stream-%s" % draft_alias.document.stream_id + 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" ): @@ -375,14 +374,13 @@ def document_main(request, name, rev=None, document_html=False): can_edit_authors=can_edit_authors, can_change_stream=can_change_stream, rfc_number=doc.rfc_number, - draft_name=doc.name, + 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, - rfc_aliases=rfc_aliases, 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, @@ -708,7 +706,6 @@ def document_main(request, name, rev=None, document_html=False): conflict_reviews=conflict_reviews, status_changes=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 published=published, file_urls=file_urls, diff --git a/ietf/templates/doc/document_html.html b/ietf/templates/doc/document_html.html index e5cc45719..c6b680e40 100644 --- a/ietf/templates/doc/document_html.html +++ b/ietf/templates/doc/document_html.html @@ -11,7 +11,7 @@ - {% if not snapshot and doc.get_state_slug == "rfc" %} + {% if doc.type_id == "rfc" %} RFC {{ doc.rfc_number }} - {{ doc.title }} {% else %} {{ doc.name }}-{{ doc.rev }} diff --git a/ietf/templates/doc/document_info.html b/ietf/templates/doc/document_info.html index 69851f33e..a725c681d 100644 --- a/ietf/templates/doc/document_info.html +++ b/ietf/templates/doc/document_info.html @@ -52,7 +52,6 @@ {% if proposed_status_changes %} <div>Proposed status changed by {{ proposed_status_changes|urlize_related_source_list|join:", " }}</div> {% endif %} - {% if rfc_aliases %}<div>Also known as {{ rfc_aliases|join:", "|urlize_ietf_docs }}</div>{% endif %} {% if draft_name %} <div> Was diff --git a/ietf/templates/doc/document_rfc.html b/ietf/templates/doc/document_rfc.html index f9c425686..3d6ffc1f7 100644 --- a/ietf/templates/doc/document_rfc.html +++ b/ietf/templates/doc/document_rfc.html @@ -17,11 +17,7 @@ {% endblock %} {% block morecss %}.inline { display: inline; }{% endblock %} {% block title %} - {% if doc.get_state_slug == "rfc" and not snapshot %} RFC {{ rfc_number }} - {{ doc.title }} - {% else %} - {{ name }}-{{ doc.rev }} - {{ doc.title }} - {% endif %} {% endblock %} {% block content %} {% origin %} @@ -29,12 +25,6 @@ <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> - {% else %} - {% if doc.get_state_slug == "rfc" and snapshot %} - <div class="alert alert-warning my-3"> - The information below is for an old version of the document that is already published as an RFC. - </div> - {% endif %} {% endif %} <table class="table table-sm table-borderless"> {% include "doc/document_info.html" %} From 563b5a9fe27701bf2acfe8e1879b8396268fe2f5 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Thu, 6 Jul 2023 15:51:49 -0300 Subject: [PATCH 21/79] chore: Migrate RelatedDocuments for RFCs --- .../0009_move_rfc_relateddocuments.py | 31 +++++++++++++++++++ ...aliases.py => 0010_move_rfc_docaliases.py} | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 ietf/doc/migrations/0009_move_rfc_relateddocuments.py rename ietf/doc/migrations/{0009_move_rfc_docaliases.py => 0010_move_rfc_docaliases.py} (96%) diff --git a/ietf/doc/migrations/0009_move_rfc_relateddocuments.py b/ietf/doc/migrations/0009_move_rfc_relateddocuments.py new file mode 100644 index 000000000..2b3dcf23c --- /dev/null +++ b/ietf/doc/migrations/0009_move_rfc_relateddocuments.py @@ -0,0 +1,31 @@ +# 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"): + 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)) + + +class Migration(migrations.Migration): + dependencies = [ + ("doc", "0008_move_rfc_docevents"), + ] + + operations = [migrations.RunPython(forward)] diff --git a/ietf/doc/migrations/0009_move_rfc_docaliases.py b/ietf/doc/migrations/0010_move_rfc_docaliases.py similarity index 96% rename from ietf/doc/migrations/0009_move_rfc_docaliases.py rename to ietf/doc/migrations/0010_move_rfc_docaliases.py index 2d38ff34d..f7be94412 100644 --- a/ietf/doc/migrations/0009_move_rfc_docaliases.py +++ b/ietf/doc/migrations/0010_move_rfc_docaliases.py @@ -30,7 +30,7 @@ def forward(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("doc", "0008_move_rfc_docevents"), + ("doc", "0009_move_rfc_relateddocuments"), ] operations = [ From 9f1b05a51846c93d6c50ede8dc405128a201d991 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Thu, 6 Jul 2023 17:27:38 -0300 Subject: [PATCH 22/79] chore: Move/copy RelatedDocuments to rfcs --- ...documents.py => 0009_rfc_relateddocuments.py} | 16 +++++++++++++++- ietf/doc/migrations/0010_move_rfc_docaliases.py | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) rename ietf/doc/migrations/{0009_move_rfc_relateddocuments.py => 0009_rfc_relateddocuments.py} (61%) diff --git a/ietf/doc/migrations/0009_move_rfc_relateddocuments.py b/ietf/doc/migrations/0009_rfc_relateddocuments.py similarity index 61% rename from ietf/doc/migrations/0009_move_rfc_relateddocuments.py rename to ietf/doc/migrations/0009_rfc_relateddocuments.py index 2b3dcf23c..a00de56d6 100644 --- a/ietf/doc/migrations/0009_move_rfc_relateddocuments.py +++ b/ietf/doc/migrations/0009_rfc_relateddocuments.py @@ -7,7 +7,10 @@ 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"): + 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", @@ -21,6 +24,17 @@ def forward(apps, schema_editor): ), 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): diff --git a/ietf/doc/migrations/0010_move_rfc_docaliases.py b/ietf/doc/migrations/0010_move_rfc_docaliases.py index f7be94412..af12c26a1 100644 --- a/ietf/doc/migrations/0010_move_rfc_docaliases.py +++ b/ietf/doc/migrations/0010_move_rfc_docaliases.py @@ -30,7 +30,7 @@ def forward(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("doc", "0009_move_rfc_relateddocuments"), + ("doc", "0009_rfc_relateddocuments"), ] operations = [ From fc284be6d8f520915a91ea5d140513dd4a90e819 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Thu, 6 Jul 2023 17:33:04 -0300 Subject: [PATCH 23/79] fix: Deal with rfc doc types for references/referenced_by views --- ietf/doc/models.py | 16 +++++++++++++--- ietf/templates/doc/document_referenced_by.html | 2 +- ietf/templates/doc/document_references.html | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 958fde12a..76c30738e 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -641,10 +641,20 @@ class DocumentInfo(models.Model): return self.relations_that_doc(('refnorm','refinfo','refunk','refold')) 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): - 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: abstract = True diff --git a/ietf/templates/doc/document_referenced_by.html b/ietf/templates/doc/document_referenced_by.html index 958108bdd..7f7fe13c3 100644 --- a/ietf/templates/doc/document_referenced_by.html +++ b/ietf/templates/doc/document_referenced_by.html @@ -64,7 +64,7 @@ </a> </td> <td> - {% if ref.source.get_state.slug == 'rfc' %} + {% if ref.source.type_id == "rfc" %} {% with ref.source.std_level as lvl %} {% if lvl %}{{ lvl }}{% endif %} {% endwith %} diff --git a/ietf/templates/doc/document_references.html b/ietf/templates/doc/document_references.html index d9134be6f..83358774f 100644 --- a/ietf/templates/doc/document_references.html +++ b/ietf/templates/doc/document_references.html @@ -51,7 +51,7 @@ </a> </td> <td> - {% if ref.target.document.get_state.slug == 'rfc' %} + {% if ref.target.document.type_id == "rfc" %} {% with ref.target.document.std_level as lvl %} {% if lvl %}{{ lvl }}{% endif %} {% endwith %} From 400280e50cc01862e547902aa1fd222d194807ba Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 6 Jul 2023 16:05:10 -0500 Subject: [PATCH 24/79] fix: repair test_history_bis_00 --- ietf/doc/tests.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index eb45e985f..158d2d789 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1850,15 +1850,14 @@ class DocTestCase(TestCase): self.assertContains(r, e.desc) def test_history_bis_00(self): - rfcname='rfc9090' - rfc = WgRfcFactory(alias2=rfcname) - bis_draft = WgDraftFactory(name='draft-ietf-{}-{}bis'.format(rfc.group.acronym,rfcname)) + rfc = WgRfcFactory(name='rfc9090') + bis_draft = WgDraftFactory(name='draft-ietf-{}-{}bis'.format(rfc.group.acronym,rfc.name)) url = urlreverse('ietf.doc.views_doc.document_history', kwargs=dict(name=bis_draft.name)) r = self.client.get(url) self.assertEqual(r.status_code, 200) q = PyQuery(unicontent(r)) - attr1='value="{}"'.format(rfcname) + attr1='value="{}"'.format(rfc.name) self.assertEqual(len(q('option['+attr1+'][selected="selected"]')), 1) From 799d3a1a3e90204923c0a74237478cca7f980180 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 6 Jul 2023 17:24:05 -0500 Subject: [PATCH 25/79] fix: adjusted bibtex view and tests --- ietf/doc/tests.py | 6 ++--- ietf/doc/views_doc.py | 36 ++++++++++++++------------ ietf/templates/doc/document_bibtex.bib | 6 ++--- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 158d2d789..923d6fd95 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1981,7 +1981,7 @@ class DocTestCase(TestCase): r = self.client.get(url) entry = self._parse_bibtex_response(r)["rfc%s"%num] 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['year'], '2010') self.assertEqual(entry['month'].lower()[0:3], 'oct') @@ -1995,7 +1995,7 @@ class DocTestCase(TestCase): std_level_id = 'inf', time = datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo(settings.TIME_ZONE)), ) - num = april1.rfc_number() + num = april1.rfc_number DocEventFactory.create( doc=april1, type='published_rfc', @@ -2007,7 +2007,7 @@ class DocTestCase(TestCase): self.assertEqual(r.get('Content-Type'), 'text/plain; charset=utf-8') entry = self._parse_bibtex_response(r)["rfc%s"%num] 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['year'], '1990') self.assertEqual(entry['month'].lower()[0:3], 'apr') diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index caa996dc3..c47f24a4e 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -1241,31 +1241,35 @@ def document_bibtex(request, name, rev=None): doc = get_object_or_404(Document, docalias__name=name) - latest_revision = doc.latest_event(NewRevisionDocEvent, type="new_revision") - replaced_by = [d.name for d in doc.related_that("replaces")] - published = doc.latest_event(type="published_rfc") is not None - rfc = latest_revision.doc if latest_revision and latest_revision.doc.get_state_slug() == "rfc" else None + doi = None + draft_became_rfc = None + replaced_by = 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: - # find the entry in the history - for h in doc.history_set.order_by("-time"): - if rev == h.rev: - doc = h - break - - if doc.type_id == "rfc": + if rev != None and rev != doc.rev: + # find the entry in the history + for h in doc.history_set.order_by("-time"): + if rev == h.rev: + doc = h + break + + 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 # over time. Probably by updating ietf/sync/rfceditor.py to add the # as a DocAlias, and use a method on Document to retrieve it. doi = f"10.17487/RFC{doc.rfc_number:04d}" - else: - doi = None return render(request, "doc/document_bibtex.bib", dict(doc=doc, replaced_by=replaced_by, - published=published, - rfc=rfc, + published_as=draft_became_rfc, latest_revision=latest_revision, doi=doi, ), diff --git a/ietf/templates/doc/document_bibtex.bib b/ietf/templates/doc/document_bibtex.bib index 5dda4649e..2e4dc9c87 100644 --- a/ietf/templates/doc/document_bibtex.bib +++ b/ietf/templates/doc/document_bibtex.bib @@ -3,7 +3,7 @@ {% load ietf_filters %} {% load textfilters %} -{% if doc.get_state_slug == "rfc" %} +{% if doc.type_id == "rfc" %} {% if doc.stream|slugify == "legacy" %} % Datatracker information for RFCs on the Legacy Stream is unfortunately often % incorrect. Please correct the bibtex below based on the information in the @@ -16,7 +16,7 @@ publisher = {RFC Editor}, doi = {% templatetag openbrace %}{{ doi }}{% templatetag closebrace %}, 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 %} @techreport{% templatetag openbrace %}{{doc.name|slice:"6:"}}-{{doc.rev}}, 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 %}, pagetotal = {{ doc.pages }}, 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 %} abstract = {% templatetag openbrace %}{{ doc.abstract|clean_whitespace|texescape }}{% templatetag closebrace %}, {% templatetag closebrace %} From 3486f38421985da627042f00540c5dac3a83af51 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 6 Jul 2023 17:31:11 -0500 Subject: [PATCH 26/79] fix: adjust (useless) assertion in status_change test to reflect that the relevant relation points to an RFC --- ietf/doc/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 923d6fd95..19a9462f9 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1608,7 +1608,7 @@ class DocTestCase(TestCase): r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=statchg.name))) 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))) - self.assertEqual(r.status_code, 302) + self.assertEqual(r.status_code, 200) # What was this even trying to prove? def test_document_charter(self): CharterFactory(name='charter-ietf-mars') From a3c87a1945e855901448d94d30b4046955a767c4 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 6 Jul 2023 17:43:40 -0500 Subject: [PATCH 27/79] fix: repaired rfc_feed --- ietf/doc/feeds.py | 6 +++--- ietf/doc/tests.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ietf/doc/feeds.py b/ietf/doc/feeds.py index c5bb467e9..4f49aec66 100644 --- a/ietf/doc/feeds.py +++ b/ietf/doc/feeds.py @@ -224,8 +224,8 @@ class RfcFeed(Feed): extra.update({"dcterms_accessRights": "gratis"}) extra.update({"dcterms_format": "text/html"}) media_contents = [] - if int(item.rfc_number()) < 8650: - if int(item.rfc_number()) not in [8, 9, 51, 418, 500, 530, 589]: + if item.rfc_number < 8650: + if item.rfc_number not in [8, 9, 51, 418, 500, 530, 589]: for fmt, media_type in [("txt", "text/plain"), ("html", "text/html")]: media_contents.append( { @@ -234,7 +234,7 @@ class RfcFeed(Feed): "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( { "url": f"https://www.rfc-editor.org/rfc/pdfrfc/{item.canonical_name()}.txt.pdf", diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 19a9462f9..c6fa0648c 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1907,7 +1907,7 @@ class DocTestCase(TestCase): self.assertContains(r, doc.name) def test_rfc_feed(self): - rfc = WgRfcFactory(alias2__name="rfc9000") + rfc = WgRfcFactory(rfc_number=9000) DocEventFactory(doc=rfc, type="published_rfc") r = self.client.get("/feed/rfc/") self.assertTrue(r.status_code, 200) From 285b11a0535cb6f156eed834865546e87e3b9b93 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 09:32:29 -0500 Subject: [PATCH 28/79] fix: better use of factory --- ietf/doc/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index c6fa0648c..5f0f56d0d 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1850,7 +1850,7 @@ class DocTestCase(TestCase): self.assertContains(r, e.desc) def test_history_bis_00(self): - rfc = WgRfcFactory(name='rfc9090') + rfc = WgRfcFactory(rfc_number=9090) bis_draft = WgDraftFactory(name='draft-ietf-{}-{}bis'.format(rfc.group.acronym,rfc.name)) url = urlreverse('ietf.doc.views_doc.document_history', kwargs=dict(name=bis_draft.name)) From f7d9e49731f9775525e88d7273327e66a4861090 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 10:30:57 -0500 Subject: [PATCH 29/79] test: pass a correct type --- ietf/doc/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 6dd0b8895..0754672ff 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1536,8 +1536,8 @@ class DocTestCase(TestCase): statchg = StatusChangeFactory() r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=statchg.name))) 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))) - self.assertEqual(r.status_code, 200) # What was this even trying to prove? + r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=statchg.relateddocument_set.first().target.document.name))) + self.assertEqual(r.status_code, 200) def test_document_charter(self): CharterFactory(name='charter-ietf-mars') From 6eeffb116f70f7d9df5d6c5a5b265deba26fff34 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 10:33:29 -0500 Subject: [PATCH 30/79] fix: become agnostic about RFC states until we care --- ietf/doc/utils.py | 2 +- ietf/doc/views_search.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py index aa250af5f..91907c73f 100644 --- a/ietf/doc/utils.py +++ b/ietf/doc/utils.py @@ -1110,7 +1110,7 @@ def generate_idnits2_rfc_status(): 'unkn': 'U', } - rfcs = Document.objects.filter(type_id='rfc',states__slug='published',states__type='rfc') + rfcs = Document.objects.filter(type_id='rfc') for rfc in rfcs: offset = int(rfc.rfc_number)-1 blob[offset] = symbols[rfc.std_level_id] diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index 7b144051c..9dd8b7fca 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -409,7 +409,7 @@ def shorten_group_name(name): def ad_dashboard_sort_key(doc): - if doc.type.slug=='rfc' and doc.get_state_slug('rfc') == 'published': + if doc.type.slug=='rfc': return "21%04d" % int(doc.rfc_number) 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 From 8b1fcfdbdda8858f655698c21f2aaf027b7f7e52 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 11:22:44 -0500 Subject: [PATCH 31/79] fix: repair views_search.index_all_drafts --- ietf/doc/views_search.py | 51 ++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/ietf/doc/views_search.py b/ietf/doc/views_search.py index 9dd8b7fca..6a9e55b79 100644 --- a/ietf/doc/views_search.py +++ b/ietf/doc/views_search.py @@ -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 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) - if state.slug == "rfc": - heading = "RFCs" - elif state.slug in ("ietf-rm", "auth-rm"): + if state.slug in ("ietf-rm", "auth-rm"): heading = "Internet-Drafts %s" % state.name else: 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_to_skip = set() @@ -828,24 +827,52 @@ def index_all_drafts(request): if name != doc: if not name.startswith("rfc"): name, doc = doc, name - names_to_skip.add(doc) - - if name.startswith("rfc"): - name = name.upper() - sort_key = '%09d' % (100000000-int(name[3:])) + names_to_skip.add(doc) # this is filtering out subseries docaliases (which we will delete, so TODO clean this out after doing so) 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] + for n, __ in names if n not in names_to_skip] categories.append((state, heading, len(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 }) def index_active_drafts(request): From 06e711288adb429a53cfa9b6c642e740784a27fd Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 11:31:11 -0500 Subject: [PATCH 32/79] test: adjusted RawIdTests --- ietf/doc/tests.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 0754672ff..7f71d235e 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2681,16 +2681,12 @@ class RawIdTests(TestCase): self.should_succeed(dict(name=draft.name, rev='00',ext='txt')) self.should_404(dict(name=draft.name, rev='00',ext='html')) - def test_raw_id_rfc(self): - rfc = WgRfcFactory() - 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())) + # test_raw_id_rfc intentionally removed + # an rfc is no longer a pseudo-version of a draft. def test_non_draft(self): - charter = CharterFactory() - self.should_404(dict(name=charter.name)) + for doc in [CharterFactory(), WgRfcFactory()]: + self.should_404(dict(name=doc.name)) class PdfizedTests(TestCase): From f30613973c8c5ba300131eb0d66694363ac4c2b3 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 12:32:48 -0500 Subject: [PATCH 33/79] test: repair pdfized tests --- ietf/doc/tests.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 7f71d235e..fb80500fb 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2705,24 +2705,27 @@ class PdfizedTests(TestCase): r = self.client.get(url) 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): - 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.docalias.first()) 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') dir = settings.INTERNET_ALL_DRAFTS_ARCHIVE_DIR 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') - self.should_succeed(dict(name=rfc.canonical_name())) self.should_succeed(dict(name=rfc.name)) + self.should_succeed(dict(name=draft.name)) 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'): - self.should_succeed(dict(name=rfc.name,rev=f'{r:02d}',ext=ext)) - self.should_404(dict(name=rfc.name,rev='02')) + self.should_succeed(dict(name=draft.name,rev=f'{r:02d}',ext=ext)) + self.should_404(dict(name=draft.name,rev='02')) class NotifyValidationTests(TestCase): def test_notify_validation(self): From e6a1a17973d547610986082fdf6df9872179475e Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 12:56:23 -0500 Subject: [PATCH 34/79] fix: typo/logic error in get_base_name --- ietf/doc/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index 76c30738e..f41c89c82 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -172,7 +172,7 @@ class DocumentInfo(models.Model): if not hasattr(self, '_cached_base_name'): if self.uploaded_filename: self._cached_base_name = self.uploaded_filename - if self.type_id == 'rfc': + elif self.type_id == 'rfc': self._cached_base_name = "%s.txt" % self.canonical_name() elif self.type_id == 'draft': if self.is_dochistory(): From 11ec3685c459fec9356b851a1d031490d5b70e42 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 13:10:52 -0500 Subject: [PATCH 35/79] fix: repaired idnits2_state --- ietf/doc/tests.py | 10 ++++++---- ietf/doc/views_doc.py | 11 +++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index fb80500fb..fa9213da0 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2595,10 +2595,10 @@ class Idnits2SupportTests(TestCase): settings_temp_path_overrides = TestCase.settings_temp_path_overrides + ['DERIVED_DIR'] def test_obsoleted(self): - rfc = WgRfcFactory(alias2__name='rfc1001') - WgRfcFactory(alias2__name='rfc1003',relations=[('obs',rfc)]) - rfc = WgRfcFactory(alias2__name='rfc1005') - WgRfcFactory(alias2__name='rfc1007',relations=[('obs',rfc)]) + rfc = WgRfcFactory(rfc_number=1001) + WgRfcFactory(rfc_number=1003,relations=[('obs',rfc)]) + rfc = WgRfcFactory(rfc_number=1005) + WgRfcFactory(rfc_number=1007,relations=[('obs',rfc)]) url = urlreverse('ietf.doc.views_doc.idnits2_rfcs_obsoleted') r = self.client.get(url) @@ -2623,6 +2623,8 @@ class Idnits2SupportTests(TestCase): def test_idnits2_state(self): rfc = WgRfcFactory() + draft = WgDraftFactory() + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) url = urlreverse('ietf.doc.views_doc.idnits2_state', kwargs=dict(name=rfc.canonical_name())) r = self.client.get(url) self.assertEqual(r.status_code, 200) diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index e299e6155..cc84057d1 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -2129,9 +2129,16 @@ def idnits2_rfc_status(request): def idnits2_state(request, name, rev=None): doc = get_object_or_404(Document, docalias__name=name) - if doc.type_id!='draft': + if doc.type_id not in ["draft", "rfc"]: 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: doc.created = zero_revision.time else: From 78b6f06519e90c3c6b5330ec78747dfb421f43f2 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 13:47:50 -0500 Subject: [PATCH 36/79] fix: repair generate_draft_aliases --- .../commands/generate_draft_aliases.py | 12 +++-- ietf/doc/tests.py | 46 ++++++++++++------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/ietf/doc/management/commands/generate_draft_aliases.py b/ietf/doc/management/commands/generate_draft_aliases.py index 88f4aa98c..9d62cf527 100755 --- a/ietf/doc/management/commands/generate_draft_aliases.py +++ b/ietf/doc/management/commands/generate_draft_aliases.py @@ -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.utils.aliases import dump_sublist from utils.mail import parseaddr +from ietf.utils import log DEFAULT_YEARS = 2 @@ -120,16 +121,19 @@ class Command(BaseCommand): vfile.write("%s anything\n" % settings.DRAFT_VIRTUAL_DOMAIN) # 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') inactive_recent_drafts = drafts.exclude(states__slug='active').filter(expires__gte=show_since) interesting_drafts = active_drafts | inactive_recent_drafts alias_domains = ['ietf.org', ] for draft in interesting_drafts.distinct().iterator(): - # Omit RFCs, unless they were published in the last DEFAULT_YEARS - if draft.docalias.filter(name__startswith='rfc'): - if draft.latest_event(type='published_rfc').time < show_since: + # Omit drafts that became RFCs, unless they were published in the last DEFAULT_YEARS + if draft.get_state_slug()=="rfc": + rfc_alias = next(iter(draft.related_that_doc("became_rfc")), None) + log.assertion("rfc_alias is not None") + rfc = rfc_alias.document + if rfc.latest_event(type='published_rfc').time < show_since: continue alias = draft.name diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index fa9213da0..3db154ec4 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2079,10 +2079,14 @@ class GenerateDraftAliasesTests(TestCase): mars.role_set.create(name_id='chair', person=marschairman, email=marschairman.email()) doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), ad=ad) doc2 = WgDraftFactory(name='draft-ietf-mars-test', group__acronym='mars', authors=[author2], ad=ad) - 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) - 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))) - DocEventFactory.create(doc=doc4, type='published_rfc', time=datetime.datetime(2010, 10, 10, tzinfo=RPC_TZINFO)) + 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.docalias.first()) + 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.docalias.first()) doc5 = IndividualDraftFactory(authors=[author6]) args = [ ] @@ -2093,7 +2097,7 @@ class GenerateDraftAliasesTests(TestCase): with open(settings.DRAFT_ALIASES_PATH) as afile: acontent = afile.read() - self.assertTrue(all([x in acontent for x in [ + for x in [ 'xfilter-' + doc1.name, 'xfilter-' + doc1.name + '.ad', 'xfilter-' + doc1.name + '.authors', @@ -2111,19 +2115,22 @@ class GenerateDraftAliasesTests(TestCase): 'xfilter-' + doc5.name, 'xfilter-' + doc5.name + '.authors', 'xfilter-' + doc5.name + '.all', - ]])) - self.assertFalse(all([x in acontent for x in [ + ]: + self.assertIn(x, acontent) + + 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, acontent) with open(settings.DRAFT_VIRTUAL_PATH) as vfile: vcontent = vfile.read() - self.assertTrue(all([x in vcontent for x in [ + for x in [ ad.email_address(), shepherd.email_address(), marschairman.email_address(), @@ -2131,12 +2138,16 @@ class GenerateDraftAliasesTests(TestCase): author2.email_address(), author3.email_address(), author6.email_address(), - ]])) - self.assertFalse(all([x in vcontent for x in [ + ]: + self.assertIn(x, vcontent) + + for x in [ author4.email_address(), author5.email_address(), - ]])) - self.assertTrue(all([x in vcontent for x in [ + ]: + self.assertNotIn(x, vcontent) + + for x in [ 'xfilter-' + doc1.name, 'xfilter-' + doc1.name + '.ad', 'xfilter-' + doc1.name + '.authors', @@ -2154,15 +2165,18 @@ class GenerateDraftAliasesTests(TestCase): 'xfilter-' + doc5.name, 'xfilter-' + doc5.name + '.authors', 'xfilter-' + doc5.name + '.all', - ]])) - self.assertFalse(all([x in vcontent for x in [ + ]: + 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): From d3f1794fd4c99b457d9c2d8140f3e55062b2243c Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 13:53:37 -0500 Subject: [PATCH 37/79] chore: apply black to GenerateDraftAliasesTests --- ietf/doc/tests.py | 276 +++++++++++++++++++++++++--------------------- 1 file changed, 153 insertions(+), 123 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 3db154ec4..2d541108a 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -2045,138 +2045,168 @@ class ReferencesTest(TestCase): self.assertContains(r, doc1.name) class GenerateDraftAliasesTests(TestCase): - def setUp(self): - super().setUp() - self.doc_aliases_file = NamedTemporaryFile(delete=False, mode='w+') - self.doc_aliases_file.close() - self.doc_virtual_file = NamedTemporaryFile(delete=False, mode='w+') - self.doc_virtual_file.close() - self.saved_draft_aliases_path = settings.DRAFT_ALIASES_PATH - self.saved_draft_virtual_path = settings.DRAFT_VIRTUAL_PATH - settings.DRAFT_ALIASES_PATH = self.doc_aliases_file.name - settings.DRAFT_VIRTUAL_PATH = self.doc_virtual_file.name + def setUp(self): + super().setUp() + self.doc_aliases_file = NamedTemporaryFile(delete=False, mode="w+") + self.doc_aliases_file.close() + self.doc_virtual_file = NamedTemporaryFile(delete=False, mode="w+") + self.doc_virtual_file.close() + self.saved_draft_aliases_path = settings.DRAFT_ALIASES_PATH + self.saved_draft_virtual_path = settings.DRAFT_VIRTUAL_PATH + settings.DRAFT_ALIASES_PATH = self.doc_aliases_file.name + settings.DRAFT_VIRTUAL_PATH = self.doc_virtual_file.name - def tearDown(self): - settings.DRAFT_ALIASES_PATH = self.saved_draft_aliases_path - settings.DRAFT_VIRTUAL_PATH = self.saved_draft_virtual_path - os.unlink(self.doc_aliases_file.name) - os.unlink(self.doc_virtual_file.name) - super().tearDown() + def tearDown(self): + settings.DRAFT_ALIASES_PATH = self.saved_draft_aliases_path + settings.DRAFT_VIRTUAL_PATH = self.saved_draft_virtual_path + os.unlink(self.doc_aliases_file.name) + os.unlink(self.doc_virtual_file.name) + super().tearDown() - def testManagementCommand(self): - 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) - ad = RoleFactory(name_id='ad', group__type_id='area', group__state_id='active').person - shepherd = PersonFactory() - author1 = PersonFactory() - author2 = PersonFactory() - author3 = PersonFactory() - author4 = PersonFactory() - author5 = PersonFactory() - author6 = PersonFactory() - mars = GroupFactory(type_id='wg', acronym='mars') - marschairman = PersonFactory(user__username='marschairman') - mars.role_set.create(name_id='chair', person=marschairman, email=marschairman.email()) - doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), ad=ad) - 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.docalias.first()) - 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.docalias.first()) - doc5 = IndividualDraftFactory(authors=[author6]) + def testManagementCommand(self): + 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) + ad = RoleFactory( + name_id="ad", group__type_id="area", group__state_id="active" + ).person + shepherd = PersonFactory() + author1 = PersonFactory() + author2 = PersonFactory() + author3 = PersonFactory() + author4 = PersonFactory() + author5 = PersonFactory() + author6 = PersonFactory() + mars = GroupFactory(type_id="wg", acronym="mars") + marschairman = PersonFactory(user__username="marschairman") + mars.role_set.create( + name_id="chair", person=marschairman, email=marschairman.email() + ) + doc1 = IndividualDraftFactory( + authors=[author1], shepherd=shepherd.email(), ad=ad + ) + 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.docalias.first() + ) + 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.docalias.first() + ) + doc5 = IndividualDraftFactory(authors=[author6]) - args = [ ] - kwargs = { } - out = io.StringIO() - call_command("generate_draft_aliases", *args, **kwargs, stdout=out, stderr=out) - self.assertFalse(out.getvalue()) + args = [] + kwargs = {} + out = io.StringIO() + call_command("generate_draft_aliases", *args, **kwargs, stdout=out, stderr=out) + self.assertFalse(out.getvalue()) - with open(settings.DRAFT_ALIASES_PATH) as afile: - acontent = afile.read() - for x in [ - 'xfilter-' + doc1.name, - 'xfilter-' + doc1.name + '.ad', - 'xfilter-' + doc1.name + '.authors', - 'xfilter-' + doc1.name + '.shepherd', - 'xfilter-' + doc1.name + '.all', - 'xfilter-' + doc2.name, - 'xfilter-' + doc2.name + '.ad', - 'xfilter-' + doc2.name + '.authors', - 'xfilter-' + doc2.name + '.chairs', - 'xfilter-' + doc2.name + '.all', - '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, acontent) + with open(settings.DRAFT_ALIASES_PATH) as afile: + acontent = afile.read() + for x in [ + "xfilter-" + doc1.name, + "xfilter-" + doc1.name + ".ad", + "xfilter-" + doc1.name + ".authors", + "xfilter-" + doc1.name + ".shepherd", + "xfilter-" + doc1.name + ".all", + "xfilter-" + doc2.name, + "xfilter-" + doc2.name + ".ad", + "xfilter-" + doc2.name + ".authors", + "xfilter-" + doc2.name + ".chairs", + "xfilter-" + doc2.name + ".all", + "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, acontent) - 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, acontent) + 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, acontent) - with open(settings.DRAFT_VIRTUAL_PATH) as vfile: - vcontent = vfile.read() - for x in [ - ad.email_address(), - shepherd.email_address(), - marschairman.email_address(), - author1.email_address(), - author2.email_address(), - author3.email_address(), - author6.email_address(), - ]: - self.assertIn(x, vcontent) + with open(settings.DRAFT_VIRTUAL_PATH) as vfile: + vcontent = vfile.read() + for x in [ + ad.email_address(), + shepherd.email_address(), + marschairman.email_address(), + author1.email_address(), + author2.email_address(), + author3.email_address(), + author6.email_address(), + ]: + self.assertIn(x, vcontent) - for x in [ - author4.email_address(), - author5.email_address(), - ]: - self.assertNotIn(x, vcontent) + for x in [ + author4.email_address(), + author5.email_address(), + ]: + self.assertNotIn(x, vcontent) - for x in [ - 'xfilter-' + doc1.name, - 'xfilter-' + doc1.name + '.ad', - 'xfilter-' + doc1.name + '.authors', - 'xfilter-' + doc1.name + '.shepherd', - 'xfilter-' + doc1.name + '.all', - 'xfilter-' + doc2.name, - 'xfilter-' + doc2.name + '.ad', - 'xfilter-' + doc2.name + '.authors', - 'xfilter-' + doc2.name + '.chairs', - 'xfilter-' + doc2.name + '.all', - '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, + "xfilter-" + doc1.name + ".ad", + "xfilter-" + doc1.name + ".authors", + "xfilter-" + doc1.name + ".shepherd", + "xfilter-" + doc1.name + ".all", + "xfilter-" + doc2.name, + "xfilter-" + doc2.name + ".ad", + "xfilter-" + doc2.name + ".authors", + "xfilter-" + doc2.name + ".chairs", + "xfilter-" + doc2.name + ".all", + "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) + 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): From ff058e3c63a25033e9ceebaac6b14dea507e2b2e Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 14:03:16 -0500 Subject: [PATCH 38/79] test: repair draft_group_link test --- ietf/doc/tests.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 2d541108a..0d143fee2 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -1421,6 +1421,8 @@ Man Expires September 22, 2015 [Page 3] self.assert_correct_wg_group_link(r, group) rfc = WgRfcFactory(group=group) + draft = WgDraftFactory(group=group) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))) self.assertEqual(r.status_code, 200) @@ -1433,7 +1435,9 @@ Man Expires September 22, 2015 [Page 3] self.assertEqual(r.status_code, 200) 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.docalias.first()) DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))) self.assertEqual(r.status_code, 200) From 457b95094aa1d8bf26baf5e2dcccf3cc05f55b9f Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 15:08:41 -0500 Subject: [PATCH 39/79] fix: improvements to submit form validation --- ietf/submit/forms.py | 2 +- ietf/submit/tests.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ietf/submit/forms.py b/ietf/submit/forms.py index b29669a0f..c2f99975b 100644 --- a/ietf/submit/forms.py +++ b/ietf/submit/forms.py @@ -707,7 +707,7 @@ class SubmissionAutoUploadForm(SubmissionBaseUploadForm): elif alias.document.get_state_slug() == "rfc": self.add_error( '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'): self.add_error( diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index 6eeaa47c5..17bf17819 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -3099,13 +3099,15 @@ class SubmissionUploadFormTests(BaseSubmitTestCase): # can't replace RFC rfc = WgRfcFactory() + draft = WgDraftFactory(states=[("draft", "rfc")]) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) form = SubmissionAutoUploadForm( request_factory.get('/some/url'), - data={'user': auth.user.username, 'replaces': rfc.name}, + data={'user': auth.user.username, 'replaces': draft.name}, files=files_dict, ) 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 existing_drafts[0].set_state(State.objects.get(type='draft-iesg', slug='approved')) From bbef89140ed645416643dbe33788bcee26feaf24 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 15:41:38 -0500 Subject: [PATCH 40/79] fix: removed notion of submitting RFCs from find_submission_filenames --- ietf/submit/tests.py | 18 +----------------- ietf/submit/utils.py | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index 17bf17819..ded7a4560 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -3699,25 +3699,9 @@ class RefsTests(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')) - def test_find_submission_filenames_draft(self): + def test_find_submission_filenames(self): """Posting an I-D submission should use IDSUBMIT_FILE_TYPES""" draft = WgDraftFactory() path = Path(self.staging_dir) diff --git a/ietf/submit/utils.py b/ietf/submit/utils.py index ec5af8efe..3447d4124 100644 --- a/ietf/submit/utils.py +++ b/ietf/submit/utils.py @@ -287,7 +287,7 @@ def find_submission_filenames(draft): """ path = pathlib.Path(settings.IDSUBMIT_STAGING_PATH) 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} return {ext: str(filename) for ext, filename in candidates.items() if filename.exists()} From 632293d3696a0f5ef9aaf8c86e1afe1dd136ea63 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 16:14:18 -0500 Subject: [PATCH 41/79] fix: repaired RelatedDocument is_downref and is_approved_downref methods --- ietf/doc/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ietf/doc/models.py b/ietf/doc/models.py index f41c89c82..0b562033e 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -686,7 +686,7 @@ class RelatedDocument(models.Model): if source_lvl not in ['bcp','ps','ds','std']: return None - if self.target.document.get_state().slug == 'rfc': + if self.target.document.type_id == 'rfc': if not self.target.document.std_level: target_lvl = 'unkn' else: @@ -709,8 +709,8 @@ class RelatedDocument(models.Model): def is_approved_downref(self): - if self.target.document.get_state().slug == 'rfc': - if RelatedDocument.objects.filter(relationship_id='downref-approval', target=self.target): + if self.target.document.type_id == "rfc": + if RelatedDocument.objects.filter(relationship_id='downref-approval', target=self.target).exists(): return "Approved Downref" return False From 09f0710e773fe72845b281867b91bcdcb2781783 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 17:02:16 -0500 Subject: [PATCH 42/79] test: correct use of factories for downref tests --- ietf/doc/tests_ballot.py | 2 +- ietf/doc/tests_downref.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ietf/doc/tests_ballot.py b/ietf/doc/tests_ballot.py index 8a4717c74..32b69d77c 100644 --- a/ietf/doc/tests_ballot.py +++ b/ietf/doc/tests_ballot.py @@ -1121,8 +1121,8 @@ class RegenerateLastCallTestCase(TestCase): self.assertFalse("contains these normative down" in lc_text) rfc = IndividualRfcFactory.create( + rfc_number=6666, stream_id='ise', - other_aliases=['rfc6666',], states=[('draft','rfc'),('draft-iesg','pub')], std_level_id='inf', ) diff --git a/ietf/doc/tests_downref.py b/ietf/doc/tests_downref.py index 258494e36..5545bd771 100644 --- a/ietf/doc/tests_downref.py +++ b/ietf/doc/tests_downref.py @@ -22,7 +22,7 @@ class Downref(TestCase): 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.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') RelatedDocument.objects.create(source=self.doc, target=self.rfcalias, relationship_id='downref-approval') @@ -100,7 +100,7 @@ class Downref(TestCase): 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')]) 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') url = urlreverse('ietf.doc.views_ballot.lastcalltext', kwargs=dict(name=draft.name)) login_testing_unauthorized(self, "secretary", url) From ffb11fd0c10e3b78f79cf1cf7d17d269590da3d5 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 17:15:39 -0500 Subject: [PATCH 43/79] fix: downref form validation --- ietf/api/tests.py | 2 ++ ietf/doc/forms.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ietf/api/tests.py b/ietf/api/tests.py index f1333ccf7..3664cc579 100644 --- a/ietf/api/tests.py +++ b/ietf/api/tests.py @@ -868,6 +868,8 @@ class TastypieApiTestCase(ResourceTestCaseMixin, TestCase): "There doesn't seem to be any API resource for model %s.models.%s"%(app.__name__,model.__name__,)) +from unittest import skip +@skip("Holding fixup for later: DO NOT MERGE this line") ##TODO class RfcdiffSupportTests(TestCase): def setUp(self): diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index fc32db8f0..29e03afdd 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -148,7 +148,7 @@ class AddDownrefForm(forms.Form): raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft") rfc = self.cleaned_data['rfc'] - if rfc.type_id != "rfc": + if rfc.document.type_id != "rfc": raise forms.ValidationError("Cannot find the RFC: " + rfc.name) return rfc From 2c6fe0383c1dee943033a74e77de27e27f0f1d2a Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 17:31:24 -0500 Subject: [PATCH 44/79] fix: adjust gating of review requests --- ietf/doc/tests_review.py | 10 +++++++++- ietf/doc/views_review.py | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ietf/doc/tests_review.py b/ietf/doc/tests_review.py index 1f84303a9..2b9dd6a07 100644 --- a/ietf/doc/tests_review.py +++ b/ietf/doc/tests_review.py @@ -137,10 +137,18 @@ class ReviewTests(TestCase): url = urlreverse('ietf.doc.views_review.request_review', kwargs={ "name": doc.name }) 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) self.assertEqual(r.status_code, 403) + + def test_doc_page(self): doc = WgDraftFactory(group__acronym='mars',rev='01') diff --git a/ietf/doc/views_review.py b/ietf/doc/views_review.py index fa6e3a7ff..2ded177f5 100644 --- a/ietf/doc/views_review.py +++ b/ietf/doc/views_review.py @@ -117,7 +117,7 @@ class RequestReviewForm(forms.ModelForm): @login_required 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): permission_denied(request, "You do not have permission to perform this action") From d4f5bc6707cf69ed74477c2a15fa3cc999017ab6 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 17:48:00 -0500 Subject: [PATCH 45/79] fix: make factory name derivation better. Adjust status_change test --- ietf/doc/factories.py | 2 +- ietf/doc/tests_status_change.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ietf/doc/factories.py b/ietf/doc/factories.py index cdd2bc980..cdc8a3413 100644 --- a/ietf/doc/factories.py +++ b/ietf/doc/factories.py @@ -122,7 +122,7 @@ class DocumentFactory(BaseDocumentFactory): class RfcFactory(BaseDocumentFactory): type_id = "rfc" rfc_number = factory.Sequence(lambda n: n + 1000) - name = factory.LazyAttribute(lambda o: f"rfc{o.rfc_number:04d}") + name = factory.LazyAttribute(lambda o: f"rfc{o.rfc_number:d}") expires = None @factory.post_generation diff --git a/ietf/doc/tests_status_change.py b/ietf/doc/tests_status_change.py index 571d9ed1d..b2790aa93 100644 --- a/ietf/doc/tests_status_change.py +++ b/ietf/doc/tests_status_change.py @@ -14,7 +14,7 @@ from textwrap import wrap from django.conf import settings from django.urls import reverse as urlreverse -from ietf.doc.factories import DocumentFactory, IndividualRfcFactory, WgRfcFactory +from ietf.doc.factories import DocumentFactory, IndividualRfcFactory, WgRfcFactory, WgDraftFactory from ietf.doc.models import ( Document, DocAlias, State, DocEvent, BallotPositionDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent ) from ietf.doc.utils import create_ballot_if_not_open @@ -449,9 +449,16 @@ class StatusChangeTests(TestCase): def setUp(self): super().setUp() - IndividualRfcFactory(alias2__name='rfc14',name='draft-was-never-issued',std_level_id='unkn') - 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') + IndividualRfcFactory(rfc_number=14,std_level_id='unkn') # draft was never issued + + 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.docalias.first()) + + 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.docalias.first()) + DocumentFactory(type_id='statchg',name='status-change-imaginary-mid-review',notify='notify@example.org') class StatusChangeSubmitTests(TestCase): From f0a32edec6089a8ff0a5eed9ddac1c2de474b8ea Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 17:48:21 -0500 Subject: [PATCH 46/79] chore: note odd placement of code --- ietf/review/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ietf/review/utils.py b/ietf/review/utils.py index 31b6b401f..979682ab6 100644 --- a/ietf/review/utils.py +++ b/ietf/review/utils.py @@ -50,6 +50,8 @@ def can_request_review_of_doc(user, doc): if not user.is_authenticated: 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': return False From c980649c32eae3f78cc9091e1e24494d1281c518 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 17:49:55 -0500 Subject: [PATCH 47/79] chore: remove unintended commit of temporary skip --- ietf/api/tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ietf/api/tests.py b/ietf/api/tests.py index 3664cc579..f1333ccf7 100644 --- a/ietf/api/tests.py +++ b/ietf/api/tests.py @@ -868,8 +868,6 @@ class TastypieApiTestCase(ResourceTestCaseMixin, TestCase): "There doesn't seem to be any API resource for model %s.models.%s"%(app.__name__,model.__name__,)) -from unittest import skip -@skip("Holding fixup for later: DO NOT MERGE this line") ##TODO class RfcdiffSupportTests(TestCase): def setUp(self): From 0b9d450a3eb42326dce08a043b92c87013796e8e Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Fri, 7 Jul 2023 17:55:50 -0500 Subject: [PATCH 48/79] test: adjust status change test to reflect rfc target --- ietf/doc/tests_status_change.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/doc/tests_status_change.py b/ietf/doc/tests_status_change.py index b2790aa93..399060eb4 100644 --- a/ietf/doc/tests_status_change.py +++ b/ietf/doc/tests_status_change.py @@ -74,7 +74,7 @@ class StatusChangeTests(TestCase): self.assertEqual(status_change.rev,'00') self.assertEqual(status_change.ad.name,'AreaĆ° Irector') 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__docs__name='rfc9999')) # Verify that it's possible to start a status change without a responsible ad. r = self.client.post(url,dict( From b8febcb7a377c7780c92efb567ed68d46e09f598 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Sat, 8 Jul 2023 09:42:52 -0500 Subject: [PATCH 49/79] test: adjusted fuzzy_find_document_rfc test --- ietf/doc/tests_utils.py | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/ietf/doc/tests_utils.py b/ietf/doc/tests_utils.py index 2c224c1db..dbbafe0cf 100644 --- a/ietf/doc/tests_utils.py +++ b/ietf/doc/tests_utils.py @@ -11,7 +11,7 @@ from django.utils import timezone from ietf.group.factories import GroupFactory, RoleFactory from ietf.name.models import DocTagName 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.doc.factories import DocumentFactory, WgRfcFactory, WgDraftFactory from ietf.doc.models import State, DocumentActionHolder, DocumentAuthor, Document @@ -251,40 +251,42 @@ class MiscTests(TestCase): self.assertEqual(docauth.country, '') def do_fuzzy_find_documents_rfc_test(self, name): - rfc = WgRfcFactory(name=name, create_revisions=(0, 1, 2)) - rfc = Document.objects.get(pk=rfc.pk) # clear out any cached values + draft = WgDraftFactory(name=name, create_revisions=(0, 1, 2)) + rfc = WgRfcFactory() + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + draft, rfc = reload_db_objects(draft, rfc) # 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) self.assertCountEqual(found.documents, [rfc]) self.assertEqual(found.matched_rev, None) 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 - found = fuzzy_find_documents(rfc.name, '02') - self.assertCountEqual(found.documents, [rfc]) + found = fuzzy_find_documents(draft.name, '02') + self.assertCountEqual(found.documents, [draft]) 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 - found = fuzzy_find_documents(rfc.name, '01') - self.assertCountEqual(found.documents, [rfc]) + found = fuzzy_find_documents(draft.name, '01') + self.assertCountEqual(found.documents, [draft]) self.assertEqual(found.matched_rev, '01') - self.assertEqual(found.matched_name, rfc.name) + self.assertEqual(found.matched_name, draft.name) # 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') - 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') - found = fuzzy_find_documents(rfc.name, '22') - self.assertCountEqual(found.documents, [rfc], + found = fuzzy_find_documents(draft.name, '22') + self.assertCountEqual(found.documents, [draft], 'Should find document even if rev does not exist') From 16b1583e33cefce4f38469d93a226868b3e2a6e1 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Sat, 8 Jul 2023 09:58:45 -0500 Subject: [PATCH 50/79] test: adjust iesg tests --- ietf/iesg/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 53172e645..ee627187d 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -107,7 +107,7 @@ class IESGAgendaTests(TestCase): super().setUp() mars = GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut')) 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, states=[('draft','rfc'),('draft-iesg','pub')], std_level_id='inf', ) wgdraft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'), relationship_id='refnorm') ise_draft = IndividualDraftFactory(name='draft-imaginary-independent-submission') ise_draft.stream = StreamName.objects.get(slug="ise") From 63c809a20be0583a79b1c566673297b47102b01f Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Sat, 8 Jul 2023 12:42:21 -0500 Subject: [PATCH 51/79] test: adjust secr tests --- ietf/secr/telechat/tests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ietf/secr/telechat/tests.py b/ietf/secr/telechat/tests.py index e4661b767..f12226a4f 100644 --- a/ietf/secr/telechat/tests.py +++ b/ietf/secr/telechat/tests.py @@ -67,8 +67,7 @@ class SecrTelechatTestCase(TestCase): def test_doc_detail_draft_with_downref(self): ad = Person.objects.get(user__username="ad") draft = WgDraftFactory(ad=ad, intended_std_level_id='ps', states=[('draft-iesg','pub-req'),]) - 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') draft.relateddocument_set.create(target=rfc.docalias.get(name='rfc6666'), relationship_id='refnorm') create_ballot_if_not_open(None, draft, ad, 'approve') From 35ba49f5ccce6cc1811cff30d5badb51add6e4ff Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Sat, 8 Jul 2023 13:20:17 -0500 Subject: [PATCH 52/79] test: cleanup use of factory --- ietf/iesg/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index ee627187d..a5482f574 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -107,7 +107,7 @@ class IESGAgendaTests(TestCase): super().setUp() mars = GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut')) wgdraft = WgDraftFactory(name='draft-ietf-mars-test', group=mars, intended_std_level_id='ps') - rfc = IndividualRfcFactory.create(stream_id='irtf', rfc_number=6666, 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') ise_draft = IndividualDraftFactory(name='draft-imaginary-independent-submission') ise_draft.stream = StreamName.objects.get(slug="ise") From b2ebad11d5b82d9d47fe951f8f0a1623fda74ea8 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Mon, 10 Jul 2023 12:56:06 -0500 Subject: [PATCH 53/79] fix: repaired rfcdiff api endpoint --- ietf/api/tests.py | 16 ++++++++-------- ietf/api/views.py | 23 +++++++++++++++++------ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/ietf/api/tests.py b/ietf/api/tests.py index f1333ccf7..5f350d94e 100644 --- a/ietf/api/tests.py +++ b/ietf/api/tests.py @@ -964,11 +964,11 @@ class RfcdiffSupportTests(TestCase): def do_rfc_test(self, draft_name): 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.docalias.first()) draft.set_state(State.objects.get(type_id='draft',slug='rfc')) draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub')) - draft = reload_db_objects(draft) - rfc = WgRfcFactory(group=draft.group) # todo link this with its pre-publication draft + draft, rfc = reload_db_objects(draft, rfc) number = rfc.rfc_number received = self.getJson(dict(name=number)) @@ -976,7 +976,7 @@ class RfcdiffSupportTests(TestCase): received, dict( content_url=rfc.get_href(), - name=rfc.canonical_name(), + name=rfc.name, previous=f'{draft.name}-{draft.rev}', previous_url= draft.history_set.get(rev=draft.rev).get_href(), ), @@ -1016,11 +1016,11 @@ class RfcdiffSupportTests(TestCase): def test_rfc_with_tombstone(self): 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.docalias.first()) draft.set_state(State.objects.get(type_id='draft',slug='rfc')) draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub')) draft = reload_db_objects(draft) - rfc = draft # Some old rfcs had tombstones that shouldn't be used for comparisons received = self.getJson(dict(name=rfc.canonical_name())) @@ -1028,11 +1028,11 @@ class RfcdiffSupportTests(TestCase): def do_rfc_with_broken_history_test(self, 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.docalias.first()) draft.set_state(State.objects.get(type_id='draft',slug='rfc')) draft.set_state(State.objects.get(type_id='draft-iesg', slug='pub')) draft = reload_db_objects(draft) - rfc = draft received = self.getJson(dict(name=draft.name)) self.assertEqual( diff --git a/ietf/api/views.py b/ietf/api/views.py index 387924976..45becbd72 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -327,17 +327,28 @@ def get_previous_url(name, rev=None): def rfcdiff_latest_json(request, name, rev=None): response = dict() 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': raise Http404 elif condition in ('historic version', 'current version'): doc = history if history else document - if not found_rev and doc.type_id == "rfc": - response['content_url'] = doc.get_href() - response['name']=doc.canonical_name() - if doc.name != doc.canonical_name(): + if doc.type_id == "rfc": + response['content_url'] = doc.get_href() + response['name']=doc.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].document + response['content_url'] = rfc.get_href() + response['name']=rfc.name prev_rev = doc.rev - if doc.rfc_number in HAS_TOMBSTONE and prev_rev != '00': + if rfc.rfc_number in HAS_TOMBSTONE and prev_rev != '00': prev_rev = f'{(int(doc.rev)-1):02d}' response['previous'] = f'{doc.name}-{prev_rev}' response['previous_url'] = get_previous_url(doc.name, prev_rev) From 63055554800956f9eab4e4d5f3234a0bdf48f8ca Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Mon, 10 Jul 2023 14:41:45 -0500 Subject: [PATCH 54/79] chore: address pyflake issues --- ietf/doc/tests.py | 2 +- ietf/doc/tests_utils.py | 2 +- ietf/doc/views_doc.py | 4 ++-- ietf/submit/tests.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 0d143fee2..994ec2eb2 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -626,7 +626,7 @@ Man Expires September 22, 2015 [Page 3] replaced = IndividualDraftFactory() draft.relateddocument_set.create(relationship_id='replaces',source=draft,target=replaced.docalias.first()) - external_resource = DocExtResourceFactory(doc=draft) + DocExtResourceFactory(doc=draft) # these tests aren't testing all attributes yet, feel free to # expand them diff --git a/ietf/doc/tests_utils.py b/ietf/doc/tests_utils.py index dbbafe0cf..cfd1b9074 100644 --- a/ietf/doc/tests_utils.py +++ b/ietf/doc/tests_utils.py @@ -14,7 +14,7 @@ from ietf.person.factories import PersonFactory from ietf.utils.test_utils import TestCase, name_of_file_containing, reload_db_objects from ietf.person.models import Person 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, fuzzy_find_documents, rebuild_reference_relations, build_file_urls) from ietf.utils.draft import Draft, PlaintextDraft diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index cc84057d1..ca981dab4 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -54,13 +54,13 @@ from django.contrib.staticfiles import finders 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, IESG_BALLOT_ACTIVE_STATES, STATUSCHANGE_RELATIONS, DocumentActionHolder, DocumentAuthor, RelatedDocument, RelatedDocHistory) from ietf.doc.utils import (augment_events_with_revision, 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, add_events_message_info, get_unicode_document_content, augment_docs_and_user_with_user_info, irsg_needed_ballot_positions, add_action_holder_change_event, diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index ded7a4560..dfb597382 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -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_submission_xml, process_uploaded_submission, process_and_validate_submission) -from ietf.doc.factories import (DocumentFactory, WgDraftFactory, IndividualDraftFactory, IndividualRfcFactory, +from ietf.doc.factories import (DocumentFactory, WgDraftFactory, IndividualDraftFactory, ReviewFactory, WgRfcFactory) from ietf.doc.models import ( Document, DocAlias, DocEvent, State, BallotPositionDocEvent, DocumentAuthor, SubmissionDocEvent ) From 7d51781f4f3c7f14c1b762ddee96af876587fa30 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Tue, 11 Jul 2023 11:49:57 -0300 Subject: [PATCH 55/79] ci: Enable tests on feat/rfc (#5953) --- .github/workflows/ci-run-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-run-tests.yml b/.github/workflows/ci-run-tests.yml index e8e9fe324..7339c7299 100644 --- a/.github/workflows/ci-run-tests.yml +++ b/.github/workflows/ci-run-tests.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - 'main' + - 'feat/rfc' paths: - 'client/**' - 'ietf/**' From 71270621b78e47e31e4135876ec868eed3ec0c2e Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Fri, 7 Jul 2023 17:27:55 -0300 Subject: [PATCH 56/79] chore: Track RFCs if they were tracked as a draft --- ietf/community/migrations/0003_track_rfcs.py | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 ietf/community/migrations/0003_track_rfcs.py diff --git a/ietf/community/migrations/0003_track_rfcs.py b/ietf/community/migrations/0003_track_rfcs.py new file mode 100644 index 000000000..61caabb77 --- /dev/null +++ b/ietf/community/migrations/0003_track_rfcs.py @@ -0,0 +1,27 @@ +# 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") + + 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) + + +class Migration(migrations.Migration): + dependencies = [ + ("community", "0002_auto_20230320_1222"), + ("doc", "0010_move_rfc_docaliases"), + ] + + operations = [migrations.RunPython(forward)] From 5385760f2acf1ea01ac0fea40ff93074465d416d Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Tue, 11 Jul 2023 16:14:49 -0300 Subject: [PATCH 57/79] chore: Migrate SearchRules for the rfc state --- ietf/community/migrations/0003_track_rfcs.py | 24 ++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/ietf/community/migrations/0003_track_rfcs.py b/ietf/community/migrations/0003_track_rfcs.py index 61caabb77..77a553ec1 100644 --- a/ietf/community/migrations/0003_track_rfcs.py +++ b/ietf/community/migrations/0003_track_rfcs.py @@ -8,6 +8,7 @@ def forward(apps, schema_editor): 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( @@ -17,11 +18,30 @@ def forward(apps, schema_editor): ): cl.added_docs.add(rfc) + # Handle rules + SearchRule = apps.get_model("community", "SearchRule") + 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") + SearchRule.objects.filter(state=draft_rfc_state).update(state=rfc_published_state) + + +def reverse(apps, schema_editor): + Document = apps.get_model("doc", "Document") + for rfc in Document.objects.filter(type__slug="rfc"): + rfc.communitylist_set.clear() + + SearchRule = apps.get_model("community", "SearchRule") + 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") + SearchRule.objects.filter(state=rfc_published_state).update(state=draft_rfc_state) + class Migration(migrations.Migration): dependencies = [ ("community", "0002_auto_20230320_1222"), ("doc", "0010_move_rfc_docaliases"), ] - - operations = [migrations.RunPython(forward)] + + operations = [migrations.RunPython(forward, reverse)] From e42ac40ec4902f4ff4b859b3d1d06c746c4a9991 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Tue, 11 Jul 2023 16:16:05 -0300 Subject: [PATCH 58/79] fix: Use (rfc, published) state for RFC SearchRules --- ietf/community/forms.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index 8d72ce0d7..fec53a178 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -42,7 +42,10 @@ class SearchRuleForm(forms.ModelForm): if rule_type == "group_exp": restrict_state("draft", "expired") else: - restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") + if rule_type.endswith("rfc"): + restrict_state("rfc", "published") + else: + restrict_state("draft", "active") if rule_type.startswith("area"): self.fields["group"].label = "Area" @@ -70,7 +73,10 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type in ["author", "author_rfc", "shepherd", "ad"]: - restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") + if rule_type.endswith("rfc"): + restrict_state("rfc", "published") + else: + restrict_state("draft", "active") if rule_type.startswith("author"): self.fields["person"].label = "Author" @@ -84,7 +90,10 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type == "name_contains": - restrict_state("draft", "rfc" if rule_type.endswith("rfc") else "active") + if rule_type.endswith("rfc"): + restrict_state("rfc", "published") + else: + restrict_state("draft", "active") del self.fields["person"] del self.fields["group"] From 7d5b1767f566b6ec00f4ceb6686ecf7001e933cc Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Tue, 11 Jul 2023 16:35:25 -0300 Subject: [PATCH 59/79] fix: Fix display of RFCs in search_result_row.html n.b., dropped the "(was draft...)" suffix for now - need to revive that or commit to removing it. --- ietf/templates/doc/search/search_result_row.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ietf/templates/doc/search/search_result_row.html b/ietf/templates/doc/search/search_result_row.html index d47dd4389..422ef4b0a 100644 --- a/ietf/templates/doc/search/search_result_row.html +++ b/ietf/templates/doc/search/search_result_row.html @@ -49,13 +49,13 @@ {% if doc.pages %}<small class="float-end text-muted d-none d-sm-block">{{ doc.pages }} page{{ doc.pages|pluralize }}</small>{% endif %} <div> <a href="{{ doc.get_absolute_url }}"> - {% if doc.get_state_slug == "rfc" %} + {% if doc.type_id == "rfc" %} RFC {{ doc.rfc_number }} {% else %} {{ doc.name }}-{{ doc.rev }} {% endif %} </a> - {% if doc.get_state_slug == "rfc" and "draft" in doc.name %}<i class="text-muted">(was {{ doc.name }})</i>{% endif %} +{# {% if doc.type_id == "rfc" and "draft" in doc.name %}<i class="text-muted">(was {{ doc.name }})</i>{% endif %} TODO drop this or look up the corresponding draft name#} <br> {% comment %} <div class="float-end"> @@ -106,19 +106,19 @@ {% endif %} </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" %} <a href="{{ rfcdiff_base_url }}?url2={{ doc.name }}-{{ doc.rev }}"> {% elif doc.replaces %} <a href="{{ rfcdiff_base_url }}?url1={{ doc.replaces_canonical_name }}&url2={{ doc.name }}-{{ doc.rev }}"> {% endif %} {% endif %} - {% if doc.get_state_slug == "rfc" %} + {% if doc.type_id == "rfc" %} {{ doc.latest_revision_date|date:"Y-m" }} {% else %} {{ doc.latest_revision_date|date:"Y-m-d" }} {% 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 %} {% endif %} {% if doc.latest_revision_date|timesince_days|new_enough:request %} @@ -127,7 +127,7 @@ <span class="badge rounded-pill bg-success">New</span> </div> {% 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> <span class="badge rounded-pill bg-warning">Expires soon</span> {% endif %} From 9bf0063f11c60ac3f82288026e909f4a19a88b02 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Wed, 12 Jul 2023 13:02:12 -0300 Subject: [PATCH 60/79] fix: Preserve Document.time when migrating rfcs (#5954) --- ietf/doc/migrations/0007_create_rfc_documents.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ietf/doc/migrations/0007_create_rfc_documents.py b/ietf/doc/migrations/0007_create_rfc_documents.py index 44d6e9c5e..c11dcc291 100644 --- a/ietf/doc/migrations/0007_create_rfc_documents.py +++ b/ietf/doc/migrations/0007_create_rfc_documents.py @@ -40,6 +40,7 @@ def forward(apps, schema_editor): 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, From 59c0a1f7f9e862768b25caf63231af555afb79f3 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Wed, 19 Jul 2023 18:09:23 -0500 Subject: [PATCH 61/79] chore: repair merge damage --- ietf/doc/utils_search.py | 4 ++-- ietf/submit/forms.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ietf/doc/utils_search.py b/ietf/doc/utils_search.py index 8e614b3e9..3c5bfeab4 100644 --- a/ietf/doc/utils_search.py +++ b/ietf/doc/utils_search.py @@ -166,9 +166,9 @@ def fill_in_document_table_attributes(docs, have_telechat_date=False): for rel in xed_by: d = doc_dict[rel.target.id] if rel.relationship_id == "obs": - d.obsoleted_by_list.append(s) + d.obsoleted_by_list.append(rel.source) 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): """Augment all documents with related documents information. diff --git a/ietf/submit/forms.py b/ietf/submit/forms.py index 65c0db1c7..36c2a6f1d 100644 --- a/ietf/submit/forms.py +++ b/ietf/submit/forms.py @@ -29,7 +29,6 @@ from ietf.doc.models import Document from ietf.group.models import Group from ietf.ietfauth.utils import has_role from ietf.doc.fields import SearchableDocumentsField -from ietf.doc.models import DocAlias from ietf.ipr.mail import utc_from_string from ietf.meeting.models import Meeting from ietf.message.models import Message From 81734443a552a20dc77bd63bd800e3686b18232c Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 20 Jul 2023 09:25:14 -0500 Subject: [PATCH 62/79] fix: continued repair of code/tests re: RelatedDocument model change --- ietf/api/tests.py | 6 +-- ietf/api/views.py | 2 +- ietf/doc/forms.py | 3 +- .../commands/generate_draft_aliases.py | 5 +-- ietf/doc/tests.py | 22 +++++------ ietf/doc/tests_downref.py | 2 +- ietf/doc/tests_utils.py | 6 +-- ietf/doc/views_ballot.py | 4 +- ietf/submit/tests.py | 9 ++--- ietf/utils/test_data.py | 37 +++++++------------ 10 files changed, 42 insertions(+), 54 deletions(-) diff --git a/ietf/api/tests.py b/ietf/api/tests.py index 6f1b79532..afe47d8d8 100644 --- a/ietf/api/tests.py +++ b/ietf/api/tests.py @@ -965,7 +965,7 @@ class RfcdiffSupportTests(TestCase): def do_rfc_test(self, draft_name): draft = WgDraftFactory(name=draft_name, create_revisions=range(0,2)) rfc = WgRfcFactory(group=draft.group, rfc_number=self.next_rfc_number()) - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + 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-iesg', slug='pub')) draft, rfc = reload_db_objects(draft, rfc) @@ -1017,7 +1017,7 @@ class RfcdiffSupportTests(TestCase): def test_rfc_with_tombstone(self): draft = WgDraftFactory(create_revisions=range(0,2)) rfc = WgRfcFactory(rfc_number=3261,group=draft.group)# See views_doc.HAS_TOMBSTONE - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + 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-iesg', slug='pub')) draft = reload_db_objects(draft) @@ -1029,7 +1029,7 @@ class RfcdiffSupportTests(TestCase): def do_rfc_with_broken_history_test(self, draft_name): draft = WgDraftFactory(rev='10', name=draft_name) rfc = WgRfcFactory(group=draft.group, rfc_number=self.next_rfc_number()) - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + 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-iesg', slug='pub')) draft = reload_db_objects(draft) diff --git a/ietf/api/views.py b/ietf/api/views.py index 1122ff9db..ee85141b4 100644 --- a/ietf/api/views.py +++ b/ietf/api/views.py @@ -344,7 +344,7 @@ def rfcdiff_latest_json(request, name, rev=None): 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].document + rfc = doc.related_that_doc("became_rfc")[0] response['content_url'] = rfc.get_href() response['name']=rfc.name prev_rev = doc.rev diff --git a/ietf/doc/forms.py b/ietf/doc/forms.py index b91e38531..daf02e8ac 100644 --- a/ietf/doc/forms.py +++ b/ietf/doc/forms.py @@ -148,7 +148,7 @@ class AddDownrefForm(forms.Form): raise forms.ValidationError("Please provide a referenced RFC and a referencing Internet-Draft") rfc = self.cleaned_data['rfc'] - if rfc.document.type_id != "rfc": + if rfc.type_id != "rfc": raise forms.ValidationError("Cannot find the RFC: " + rfc.name) return rfc @@ -190,7 +190,6 @@ class AddDownrefForm(forms.Form): v_err_refnorm = d.name if v_err_refnorm: v_err_refnorm_prefix = f"There does not seem to be a normative reference to RFC {rfc.rfc_number} by " - v_err_refnorm_prefix = f"There does not seem to be a normative reference to RFC {rfc.document.rfc_number} by " raise forms.ValidationError(v_err_refnorm_prefix + v_err_refnorm) diff --git a/ietf/doc/management/commands/generate_draft_aliases.py b/ietf/doc/management/commands/generate_draft_aliases.py index 9d62cf527..b377fdda1 100755 --- a/ietf/doc/management/commands/generate_draft_aliases.py +++ b/ietf/doc/management/commands/generate_draft_aliases.py @@ -130,9 +130,8 @@ class Command(BaseCommand): for draft in interesting_drafts.distinct().iterator(): # Omit drafts that became RFCs, unless they were published in the last DEFAULT_YEARS if draft.get_state_slug()=="rfc": - rfc_alias = next(iter(draft.related_that_doc("became_rfc")), None) - log.assertion("rfc_alias is not None") - rfc = rfc_alias.document + 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 diff --git a/ietf/doc/tests.py b/ietf/doc/tests.py index 112803f76..4a88c6276 100644 --- a/ietf/doc/tests.py +++ b/ietf/doc/tests.py @@ -771,16 +771,16 @@ Man Expires September 22, 2015 [Page 3] 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)"))]) - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) obsoleted = IndividualRfcFactory() - rfc.relateddocument_set.create(relationship_id='obs',target=obsoleted.docalias.first()) + rfc.relateddocument_set.create(relationship_id='obs',target=obsoleted) obsoleted_by = IndividualRfcFactory() - obsoleted_by.relateddocument_set.create(relationship_id='obs',target=rfc.docalias.first()) + obsoleted_by.relateddocument_set.create(relationship_id='obs',target=rfc) updated = IndividualRfcFactory() - rfc.relateddocument_set.create(relationship_id='updates',target=updated.docalias.first()) + rfc.relateddocument_set.create(relationship_id='updates',target=updated) updated_by = IndividualRfcFactory() - updated_by.relateddocument_set.create(relationship_id='updates',target=rfc.docalias.first()) + 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))) self.assertEqual(r.status_code, 302) @@ -1430,7 +1430,7 @@ Man Expires September 22, 2015 [Page 3] rfc = WgRfcFactory(group=group) draft = WgDraftFactory(group=group) - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))) self.assertEqual(r.status_code, 200) @@ -1445,7 +1445,7 @@ Man Expires September 22, 2015 [Page 3] 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.docalias.first()) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime) r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc.name))) self.assertEqual(r.status_code, 200) @@ -2111,7 +2111,7 @@ class GenerateDraftAliasesTests(TestCase): rfc3 = WgRfcFactory() DocEventFactory.create(doc=rfc3, type="published_rfc", time=a_month_ago) doc3.relateddocument_set.create( - relationship_id="became_rfc", target=rfc3.docalias.first() + relationship_id="became_rfc", target=rfc3 ) doc4 = WgDraftFactory.create( authors=[author4, author5], @@ -2127,7 +2127,7 @@ class GenerateDraftAliasesTests(TestCase): time=datetime.datetime(2010, 10, 10, tzinfo=RPC_TZINFO), ) doc4.relateddocument_set.create( - relationship_id="became_rfc", target=rfc4.docalias.first() + relationship_id="became_rfc", target=rfc4 ) doc5 = IndividualDraftFactory(authors=[author6]) @@ -2680,7 +2680,7 @@ class Idnits2SupportTests(TestCase): def test_idnits2_state(self): rfc = WgRfcFactory() draft = WgDraftFactory() - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) url = urlreverse('ietf.doc.views_doc.idnits2_state', kwargs=dict(name=rfc.canonical_name())) r = self.client.get(url) self.assertEqual(r.status_code, 200) @@ -2767,7 +2767,7 @@ class PdfizedTests(TestCase): def test_pdfized(self): rfc = WgRfcFactory() draft = WgDraftFactory(create_revisions=range(0,2)) - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) dir = settings.RFC_PATH with (Path(dir) / f'{rfc.name}.txt').open('w') as f: diff --git a/ietf/doc/tests_downref.py b/ietf/doc/tests_downref.py index 6c8fb685f..04bd908a9 100644 --- a/ietf/doc/tests_downref.py +++ b/ietf/doc/tests_downref.py @@ -100,7 +100,7 @@ class Downref(TestCase): 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')]) 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, relationship_id='refnorm') url = urlreverse('ietf.doc.views_ballot.lastcalltext', kwargs=dict(name=draft.name)) login_testing_unauthorized(self, "secretary", url) diff --git a/ietf/doc/tests_utils.py b/ietf/doc/tests_utils.py index dde4a1841..9ae9b6736 100644 --- a/ietf/doc/tests_utils.py +++ b/ietf/doc/tests_utils.py @@ -253,7 +253,7 @@ class MiscTests(TestCase): def do_fuzzy_find_documents_rfc_test(self, name): draft = WgDraftFactory(name=name, create_revisions=(0, 1, 2)) rfc = WgRfcFactory() - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) draft, rfc = reload_db_objects(draft, rfc) # by canonical name @@ -380,7 +380,7 @@ class RebuildReferenceRelationsTests(TestCase): self.assertEqual( result, { - 'warnings': ['There were 1 references with no matching DocAlias'], + 'warnings': ['There were 1 references with no matching Document'], 'unfound': ['draft-not-found'], } ) @@ -443,7 +443,7 @@ class RebuildReferenceRelationsTests(TestCase): self.assertEqual( result, { - 'warnings': ['There were 1 references with no matching DocAlias'], + 'warnings': ['There were 1 references with no matching Document'], 'unfound': ['draft-not-found'], } ) diff --git a/ietf/doc/views_ballot.py b/ietf/doc/views_ballot.py index 564148932..c1afee515 100644 --- a/ietf/doc/views_ballot.py +++ b/ietf/doc/views_ballot.py @@ -974,12 +974,12 @@ def approve_downrefs(request, name): c = DocEvent(type="downref_approved", doc=rel.source, rev=rel.source.rev, by=login) 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 = DocEvent(type="downref_approved", doc=rel.target, rev=rel.target.rev, by=login) 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() return HttpResponseRedirect(doc.get_absolute_url()) diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index 2478bbd99..20a372b85 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -369,7 +369,6 @@ class SubmitTests(BaseSubmitTestCase): # supply submitter info, then draft should be in and ready for approval mailbox_before = len(outbox) - replaced_alias = draft.docalias.first() r = self.supply_extra_metadata(name, status_url, author.ascii, author.email().address.lower(), replaces=[str(draft.pk), str(sug_replaced_draft.pk)]) @@ -1253,7 +1252,7 @@ class SubmitTests(BaseSubmitTestCase): status_url, "Submitter Name", "submitter@example.com", - replaces=[str(replaced_draft.docalias.first().pk)], + replaces=[str(replaced_draft.pk)], ) submission = Submission.objects.get(name=name, rev=rev) @@ -1403,7 +1402,7 @@ class SubmitTests(BaseSubmitTestCase): "edit-pages": "123", "submitter-name": "Some Random Test Person", "submitter-email": "random@example.com", - "replaces": [str(draft.docalias.first().pk)], + "replaces": [str(draft.pk)], "edit-note": "no comments", "authors-0-name": "Person 1", "authors-0-email": "person1@example.com", @@ -1422,7 +1421,7 @@ class SubmitTests(BaseSubmitTestCase): self.assertEqual(submission.pages, 123) self.assertEqual(submission.note, "no comments") 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") authors = submission.authors @@ -3091,7 +3090,7 @@ class SubmissionUploadFormTests(BaseSubmitTestCase): # can't replace RFC rfc = WgRfcFactory() draft = WgDraftFactory(states=[("draft", "rfc")]) - draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc.docalias.first()) + draft.relateddocument_set.create(relationship_id="became_rfc", target=rfc) form = SubmissionAutoUploadForm( request_factory.get('/some/url'), data={'user': auth.user.username, 'replaces': draft.name}, diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index b8694a9e2..a22f17306 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -12,6 +12,7 @@ from django.utils.encoding import smart_str import debug # pyflakes:ignore 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.iesg.models import TelechatDate from ietf.ipr.models import HolderIprDisclosure, IprDocRel, IprDisclosureStateName, IprLicenseTypeName @@ -391,37 +392,27 @@ def make_test_data(): ) # an independent submission before review - doc = Document.objects.create(name='draft-imaginary-independent-submission',type_id='draft',rev='00', - 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) + IndividualDraftFactory(title="Some Independent Notes on Imagination") # an irtf submission mid review - doc = Document.objects.create(name='draft-imaginary-irtf-submission', type_id='draft',rev='00', - stream=StreamName.objects.get(slug='irtf'), title="The Importance of Research Imagination") - 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') + doc = IndividualDraftFactory(name="draft-imaginary-irtf-submission", stream_id="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") # A status change mid review iesg = Group.objects.get(acronym='iesg') - doc = Document.objects.create(name='status-change-imaginary-mid-review',type_id='statchg', rev='00', - notify="fsm@ietf.org", group=iesg, title="Status Change Review without Imagination") - doc.set_state(State.objects.get(slug='needshep',type__slug='statchg')) - docalias = DocAlias.objects.create(name='status-change-imaginary-mid-review') - docalias.docs.add(doc) + doc = StatusChangeFactory( + name='status-change-imaginary-mid-review', + notify="fsm@ietf.org", + group=iesg, + title="Status Change Review without Imagination", + states= [State.objects.get(type_id="statchg",slug="needshep")] + ) # Some things for a status change to affect 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.set_state(State.objects.get(slug='rfc',type__slug='draft')) - DocAlias.objects.create(name=name).docs.add(target_rfc) - DocAlias.objects.create(name='rfc%d'%rfc_num).docs.add(target_rfc) + target_rfc = WgRfcFactory(rfc_number=rfc_num, std_level_id=std_level_id) + source_draft = WgDraftFactory(name=name, states=[("draft","rfc")], notify=f"{name}@ietf.org") + source_draft.relateddocument_set.create(relationship_id="became_rfc", target=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-otherthing',9998,'inf') From ac4ee273874f2f8263544365ddf6631494a0f904 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 20 Jul 2023 09:41:23 -0500 Subject: [PATCH 63/79] fix: track style changes in main --- ietf/templates/doc/document_rfc.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ietf/templates/doc/document_rfc.html b/ietf/templates/doc/document_rfc.html index 3d6ffc1f7..6ceb4427c 100644 --- a/ietf/templates/doc/document_rfc.html +++ b/ietf/templates/doc/document_rfc.html @@ -72,7 +72,7 @@ {% if doc.ad %} {% person_link doc.ad %} {% else %} - <span class="text-muted"> + <span class="text-body-secondary"> (None) </span> {% endif %} @@ -95,7 +95,7 @@ {% if doc.notify %} {{ doc.notify|linkify }} {% else %} - <span class="text-muted"> + <span class="text-body-secondary"> (None) </span> {% endif %} @@ -324,7 +324,7 @@ {% if not forloop.last %}<br>{% endif %} {% endfor %} </p> - <p class="text-muted card-text"> + <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> @@ -336,4 +336,4 @@ </script> <script src="{% static 'ietf/js/document_timeline.js' %}"> </script> - {% endblock %} \ No newline at end of file + {% endblock %} From 51c22d8e6c867b5be23e67e4d0369469b1cd4faa Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 20 Jul 2023 13:03:58 -0500 Subject: [PATCH 64/79] fix: repairs reflecting removal of DocAlias from the RelatedDocument models --- ietf/idindex/tests.py | 17 +++++++++++++---- ietf/iesg/tests.py | 6 +++--- ietf/secr/telechat/tests.py | 3 +-- ietf/stats/views.py | 8 ++++---- ietf/submit/tests.py | 6 +++--- ietf/sync/tests.py | 4 ++-- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/ietf/idindex/tests.py b/ietf/idindex/tests.py index f207fa562..3dabcc642 100644 --- a/ietf/idindex/tests.py +++ b/ietf/idindex/tests.py @@ -52,8 +52,13 @@ class IndexTests(TestCase): RelatedDocument.objects.create( relationship=DocRelationshipName.objects.get(slug="replaces"), - source=Document.objects.create(type_id="draft", rev="00", name="draft-test-replacement"), - target=draft.docalias.get(name__startswith="draft")) + source=Document.objects.create( + type_id="draft", + rev="00", + name="draft-test-replacement" + ), + target=draft + ) txt = all_id_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")) RelatedDocument.objects.create( relationship=DocRelationshipName.objects.get(slug="replaces"), - source=Document.objects.create(type_id="draft", rev="00", name="draft-test-replacement"), - target=draft.docalias.get(name__startswith="draft")) + source=Document.objects.create( + type_id="draft", + rev="00", + name="draft-test-replacement" + ), + target=draft) t = get_fields(all_id2_txt()) self.assertEqual(t[5], "draft-test-replacement") diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index a5482f574..9e8560b51 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -108,7 +108,7 @@ class IESGAgendaTests(TestCase): mars = GroupFactory(acronym='mars',parent=Group.objects.get(acronym='farfut')) wgdraft = WgDraftFactory(name='draft-ietf-mars-test', group=mars, intended_std_level_id='ps') 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.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")]) @@ -238,7 +238,7 @@ class IESGAgendaTests(TestCase): relation = RelatedDocument.objects.create( 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") 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"]) # 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() statchg.group = Group.objects.get(acronym="mars") diff --git a/ietf/secr/telechat/tests.py b/ietf/secr/telechat/tests.py index f12226a4f..39949b83a 100644 --- a/ietf/secr/telechat/tests.py +++ b/ietf/secr/telechat/tests.py @@ -68,8 +68,7 @@ class SecrTelechatTestCase(TestCase): ad = Person.objects.get(user__username="ad") draft = WgDraftFactory(ad=ad, intended_std_level_id='ps', states=[('draft-iesg','pub-req'),]) rfc = IndividualRfcFactory.create(stream_id='irtf', rfc_number=6666, 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') create_ballot_if_not_open(None, draft, ad, 'approve') d = get_next_telechat_date() date = d.strftime('%Y-%m-%d') diff --git a/ietf/stats/views.py b/ietf/stats/views.py index 44fbfb717..3d7d62c06 100644 --- a/ietf/stats/views.py +++ b/ietf/stats/views.py @@ -563,11 +563,11 @@ def document_stats(request, stats_type=None): bins = defaultdict(set) 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) - 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) total_persons = count_bins(bins) @@ -587,11 +587,11 @@ def document_stats(request, stats_type=None): bins = defaultdict(set) 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) - 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]): h_index = compute_hirsch_index([citations for _, document, citations in ts]) bins[h_index or 0].add(name) diff --git a/ietf/submit/tests.py b/ietf/submit/tests.py index 20a372b85..cf5075c6b 100644 --- a/ietf/submit/tests.py +++ b/ietf/submit/tests.py @@ -302,7 +302,7 @@ class SubmitTests(BaseSubmitTestCase): submission = Submission.objects.get(name=name) self.assertEqual(submission.submitter, email.utils.formataddr((submitter_name, submitter_email))) 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( [str(r) for r in submission.external_resources.all()], [str(r) for r in extresources] if extresources else [], @@ -418,7 +418,7 @@ class SubmitTests(BaseSubmitTestCase): self.assertEqual(authors[0].person, author) self.assertEqual(set(draft.formal_languages.all()), set(FormalLanguageName.objects.filter(slug="json"))) 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.assertTrue(draft.relations_that_doc("possibly-replaces").first().target, sug_replaced_alias) self.assertEqual(len(outbox), mailbox_before + 5) @@ -3097,7 +3097,7 @@ class SubmissionUploadFormTests(BaseSubmitTestCase): files=files_dict, ) self.assertFalse(form.is_valid()) - self.assertIn('An Internet-Draft can only replace another Internet-Draft that has become 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 existing_drafts[0].set_state(State.objects.get(type='draft-iesg', slug='approved')) diff --git a/ietf/sync/tests.py b/ietf/sync/tests.py index f245145d2..095783692 100644 --- a/ietf/sync/tests.py +++ b/ietf/sync/tests.py @@ -360,7 +360,7 @@ class RFCSyncTests(TestCase): self.assertTrue(DocAlias.objects.filter(name="bcp1", docs=doc)) self.assertTrue(DocAlias.objects.filter(name="fyi1", 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.abstract, "This is some interesting text.") self.assertEqual(doc.get_state_slug(), "rfc") @@ -602,4 +602,4 @@ class RFCEditorUndoTests(TestCase): e = DeletedEvent.objects.all().order_by("-time", "-id")[0] e.content_type.model_class().objects.create(**json.loads(e.json)) - self.assertTrue(StateDocEvent.objects.filter(desc="First", doc=draft)) \ No newline at end of file + self.assertTrue(StateDocEvent.objects.filter(desc="First", doc=draft)) From 189c32194d68def77f406c229584af2f27a999b0 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 20 Jul 2023 13:25:12 -0500 Subject: [PATCH 65/79] test: construct the right kind of document for the rfc-index tests --- ietf/sync/tests.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ietf/sync/tests.py b/ietf/sync/tests.py index 095783692..34066ebef 100644 --- a/ietf/sync/tests.py +++ b/ietf/sync/tests.py @@ -14,7 +14,7 @@ from django.utils import timezone 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.utils import add_state_change_event from ietf.group.factories import GroupFactory @@ -235,9 +235,7 @@ class RFCSyncTests(TestCase): # too, but for testing purposes ... 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") - DocAlias.objects.create(name=updated_doc.name).docs.add(updated_doc) - DocAlias.objects.create(name="rfc123").docs.add(updated_doc) + updated_doc = RfcFactory(rfc_number=123) today = date_today() From 2593c0396b971c5f8b8411b19f3dc4ce919e8fb6 Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Thu, 20 Jul 2023 13:40:09 -0500 Subject: [PATCH 66/79] chore: style fixes --- ietf/doc/tests_conflict_review.py | 2 +- ietf/templates/doc/document_references.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/doc/tests_conflict_review.py b/ietf/doc/tests_conflict_review.py index 6ee6aa99b..2aa7a161c 100644 --- a/ietf/doc/tests_conflict_review.py +++ b/ietf/doc/tests_conflict_review.py @@ -75,7 +75,7 @@ class ConflictReviewTests(TestCase): 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('Conflict Review requested' in outbox[-1]['Subject']) - + # 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')) self.assertEqual(r.status_code, 404) diff --git a/ietf/templates/doc/document_references.html b/ietf/templates/doc/document_references.html index 47e9871ea..268168a11 100644 --- a/ietf/templates/doc/document_references.html +++ b/ietf/templates/doc/document_references.html @@ -51,7 +51,7 @@ </a> </td> <td> - {% if ref.target.type_id == 'rfc' %} + {% if ref.target.type_id == "rfc" %} {% with ref.target.std_level as lvl %} {% if lvl %}{{ lvl }}{% endif %} {% endwith %} From 1660a1433100c8c49c29ffc573c530b76b11309e Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Fri, 21 Jul 2023 13:33:33 -0300 Subject: [PATCH 67/79] test: Use (rfc, published) state in test --- ietf/community/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/community/tests.py b/ietf/community/tests.py index 3dd86f70e..19530efcd 100644 --- a/ietf/community/tests.py +++ b/ietf/community/tests.py @@ -151,7 +151,7 @@ class CommunityListTests(WebTest): "action": "add_rule", "rule_type": "author_rfc", "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) clist = CommunityList.objects.get(user__username="plain") @@ -408,4 +408,4 @@ class CommunityListTests(WebTest): self.assertEqual(len(outbox), mailbox_before + 1) self.assertTrue(draft.name in outbox[-1]["Subject"]) - \ No newline at end of file + From 0959a2e82e734509b97354ade8d0c45476db8a49 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 11:13:36 -0700 Subject: [PATCH 68/79] refactor: Filter SearchRule matching using rfc doc type --- ietf/community/utils.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ietf/community/utils.py b/ietf/community/utils.py index 8130954b9..71905af93 100644 --- a/ietf/community/utils.py +++ b/ietf/community/utils.py @@ -71,25 +71,33 @@ 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(): r.name_contains_index.add(doc) + def docs_matching_community_list_rule(rule): 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']: - 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']: - return docs.filter(group=rule.group_id, states=rule.state) + return docs.filter(group=rule.group_id) elif rule.rule_type.startswith("state_"): - return docs.filter(states=rule.state) + return docs 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": - return docs.filter(states=rule.state, ad=rule.person) + return docs.filter(ad=rule.person) 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": - return docs.filter(states=rule.state, searchrule=rule) + return docs.filter(searchrule=rule) raise NotImplementedError + def community_list_rules_matching_doc(doc): states = list(doc.states.values_list("pk", flat=True)) From 2f94383fca67e50c3ed942e94a9b6d55da933855 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 11:29:18 -0700 Subject: [PATCH 69/79] refactor: Find SearchRules using rfc doc type --- ietf/community/utils.py | 83 +++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/ietf/community/utils.py b/ietf/community/utils.py index 71905af93..ba4cc67fe 100644 --- a/ietf/community/utils.py +++ b/ietf/community/utils.py @@ -99,50 +99,75 @@ def docs_matching_community_list_rule(rule): 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)) - rules = SearchRule.objects.none() - + # group and area rules if doc.group_id: groups = [doc.group_id] if 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( - 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, - group__in=groups ) - rules |= SearchRule.objects.filter( - rule_type__in=['state_iab', 'state_iana', 'state_iesg', 'state_irtf', 'state_ise', 'state_rfceditor', 'state_ietf'], - 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: + # author rules + if doc.type_id == "rfc": 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, - person=doc.ad_id, + person__in=list(Person.objects.filter(documentauthor__document=doc)), ) - if doc.shepherd_id: - rules |= SearchRule.objects.filter( - rule_type="shepherd", - state__in=states, - person__email=doc.shepherd_id, - ) + # Other draft-only rules rules + if doc.type_id == "draft": + if doc.ad_id: + rules |= SearchRule.objects.filter( + rule_type="ad", + state__in=states, + person=doc.ad_id, + ) - rules |= SearchRule.objects.filter( - rule_type="name_contains", - state__in=states, - name_contains_index=doc, # search our materialized index to avoid full scan - ) + if doc.shepherd_id: + rules |= SearchRule.objects.filter( + rule_type="shepherd", + 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 From a6d5cb69d46c6e9ecf8525f731abbca7a5147f7c Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 11:39:34 -0700 Subject: [PATCH 70/79] refactor: Use empty rule.states for _rfc rules --- ietf/community/forms.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index fec53a178..100f9a08d 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -9,6 +9,7 @@ from django.db.models import Q from ietf.community.models import SearchRule, EmailSubscription from ietf.doc.fields import SearchableDocumentsField +from ietf.doc.models import State from ietf.person.models import Person from ietf.person.fields import SearchablePersonField @@ -38,13 +39,16 @@ class SearchRuleForm(forms.ModelForm): f.initial = f.queryset[0].pk f.widget = forms.HiddenInput() + if rule_type.endswith("_rfc"): + self.fields["state"].queryset = State.objects.none() + self.fields["state"].initial = None + self.fields["state"].widget = forms.HiddenInput() + if rule_type in ["group", "group_rfc", "area", "area_rfc", "group_exp"]: if rule_type == "group_exp": restrict_state("draft", "expired") else: - if rule_type.endswith("rfc"): - restrict_state("rfc", "published") - else: + if not rule_type.endswith("_rfc"): restrict_state("draft", "active") if rule_type.startswith("area"): @@ -73,9 +77,7 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type in ["author", "author_rfc", "shepherd", "ad"]: - if rule_type.endswith("rfc"): - restrict_state("rfc", "published") - else: + if not rule_type.endswith("_rfc"): restrict_state("draft", "active") if rule_type.startswith("author"): @@ -90,9 +92,7 @@ class SearchRuleForm(forms.ModelForm): del self.fields["text"] elif rule_type == "name_contains": - if rule_type.endswith("rfc"): - restrict_state("rfc", "published") - else: + if not rule_type.endswith("_rfc"): restrict_state("draft", "active") del self.fields["person"] From 8f738493437f57e4a45c30a6bd748629da50b14a Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 11:51:27 -0700 Subject: [PATCH 71/79] refactor: Remove "state" field entirely for rfc rules --- ietf/community/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index 100f9a08d..a2bf96dab 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -31,6 +31,8 @@ class SearchRuleForm(forms.ModelForm): super(SearchRuleForm, self).__init__(*args, **kwargs) 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.queryset = f.queryset.filter(used=True).filter(type=state_type) if slug: @@ -40,9 +42,7 @@ class SearchRuleForm(forms.ModelForm): f.widget = forms.HiddenInput() if rule_type.endswith("_rfc"): - self.fields["state"].queryset = State.objects.none() - self.fields["state"].initial = None - self.fields["state"].widget = forms.HiddenInput() + 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 == "group_exp": From 4e670692f8c3e8758860d8a436a90692b241ced2 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 13:29:43 -0700 Subject: [PATCH 72/79] refactor: Clear rule.state when migrating *_rfc rules --- ietf/community/migrations/0003_track_rfcs.py | 23 +++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/ietf/community/migrations/0003_track_rfcs.py b/ietf/community/migrations/0003_track_rfcs.py index 77a553ec1..2bf90fdb0 100644 --- a/ietf/community/migrations/0003_track_rfcs.py +++ b/ietf/community/migrations/0003_track_rfcs.py @@ -18,24 +18,27 @@ def forward(apps, schema_editor): ): cl.added_docs.add(rfc) - # Handle rules + # 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") - 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") - SearchRule.objects.filter(state=draft_rfc_state).update(state=rfc_published_state) - + 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") - draft_rfc_state = State.objects.get(type_id="draft", slug="rfc") - rfc_published_state = State.objects.get(type_id="rfc", slug="published") - SearchRule.objects.filter(state=rfc_published_state).update(state=draft_rfc_state) + SearchRule.objects.filter(rule_type__endswith="_rfc").update( + state=State.objects.get(type_id="draft", slug="rfc") + ) class Migration(migrations.Migration): @@ -43,5 +46,5 @@ class Migration(migrations.Migration): ("community", "0002_auto_20230320_1222"), ("doc", "0010_move_rfc_docaliases"), ] - + operations = [migrations.RunPython(forward, reverse)] From 938f862cc299ef3d20d0cc591a067fc8b4845554 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 14:11:57 -0700 Subject: [PATCH 73/79] refactor: Prevent undefined variable reference --- ietf/community/views.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ietf/community/views.py b/ietf/community/views.py index 054bed302..f1d353741 100644 --- a/ietf/community/views.py +++ b/ietf/community/views.py @@ -79,19 +79,18 @@ def manage_list(request, username=None, acronym=None, group_type=None): rule_type_form = SearchRuleTypeForm(request.POST) if rule_type_form.is_valid(): rule_type = rule_type_form.cleaned_data['rule_type'] - - if rule_type: - rule_form = SearchRuleForm(clist, rule_type, request.POST) - if rule_form.is_valid(): - if clist.pk is None: - clist.save() - - rule = rule_form.save(commit=False) - rule.community_list = clist - rule.rule_type = rule_type - rule.save() - if rule.rule_type == "name_contains": - reset_name_contains_index_for_rule(rule) + if rule_type: + rule_form = SearchRuleForm(clist, rule_type, request.POST) + if rule_form.is_valid(): + if clist.pk is None: + clist.save() + + rule = rule_form.save(commit=False) + rule.community_list = clist + rule.rule_type = rule_type + rule.save() + if rule.rule_type == "name_contains": + reset_name_contains_index_for_rule(rule) return HttpResponseRedirect("") else: From 423514467dccb7d5aed3654a1f8548c6aecc716f Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 14:23:44 -0700 Subject: [PATCH 74/79] fix: Track RFC documents that came from tracked drafts --- ietf/community/utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ietf/community/utils.py b/ietf/community/utils.py index ba4cc67fe..4a6a2fadd 100644 --- a/ietf/community/utils.py +++ b/ietf/community/utils.py @@ -179,7 +179,11 @@ def docs_tracked_by_community_list(clist): # 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 # 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(): doc_ids = doc_ids | set(docs_matching_community_list_rule(rule).values_list("pk", flat=True)) From 2b0f95477eca43b9f91980445a24977b705cf7ff Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Sat, 22 Jul 2023 15:45:25 -0700 Subject: [PATCH 75/79] test: Remove unused import --- ietf/community/forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ietf/community/forms.py b/ietf/community/forms.py index a2bf96dab..ad8570896 100644 --- a/ietf/community/forms.py +++ b/ietf/community/forms.py @@ -9,7 +9,6 @@ from django.db.models import Q from ietf.community.models import SearchRule, EmailSubscription from ietf.doc.fields import SearchableDocumentsField -from ietf.doc.models import State from ietf.person.models import Person from ietf.person.fields import SearchablePersonField From 19141d51e0165af2a9c8287aa65b7accd0b9356e Mon Sep 17 00:00:00 2001 From: Robert Sparks <rjsparks@nostrum.com> Date: Sun, 23 Jul 2023 16:37:21 -0700 Subject: [PATCH 76/79] chore: reorder migrations --- .../{0005_relate_no_aliases.py => 0011_relate_no_aliases.py} | 2 +- ...relate_hist_no_aliases.py => 0012_relate_hist_no_aliases.py} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename ietf/doc/migrations/{0005_relate_no_aliases.py => 0011_relate_no_aliases.py} (96%) rename ietf/doc/migrations/{0006_relate_hist_no_aliases.py => 0012_relate_hist_no_aliases.py} (98%) diff --git a/ietf/doc/migrations/0005_relate_no_aliases.py b/ietf/doc/migrations/0011_relate_no_aliases.py similarity index 96% rename from ietf/doc/migrations/0005_relate_no_aliases.py rename to ietf/doc/migrations/0011_relate_no_aliases.py index 2661ab972..2eedcdf7c 100644 --- a/ietf/doc/migrations/0005_relate_no_aliases.py +++ b/ietf/doc/migrations/0011_relate_no_aliases.py @@ -16,7 +16,7 @@ def reverse(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("doc", "0004_alter_dochistory_ad_alter_dochistory_shepherd_and_more"), + ("doc", "0010_move_rfc_docaliases"), ] operations = [ diff --git a/ietf/doc/migrations/0006_relate_hist_no_aliases.py b/ietf/doc/migrations/0012_relate_hist_no_aliases.py similarity index 98% rename from ietf/doc/migrations/0006_relate_hist_no_aliases.py rename to ietf/doc/migrations/0012_relate_hist_no_aliases.py index 8ded3bd48..e4317795e 100644 --- a/ietf/doc/migrations/0006_relate_hist_no_aliases.py +++ b/ietf/doc/migrations/0012_relate_hist_no_aliases.py @@ -16,7 +16,7 @@ def reverse(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("doc", "0005_relate_no_aliases"), + ("doc", "0011_relate_no_aliases"), ] operations = [ From bb7445575b6c04648b9c564ac562df05a3b9dd94 Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Fri, 28 Jul 2023 10:57:16 -0700 Subject: [PATCH 77/79] test: Remove unused assignment lint --- ietf/sync/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/sync/tests.py b/ietf/sync/tests.py index 34066ebef..4ad26e3e4 100644 --- a/ietf/sync/tests.py +++ b/ietf/sync/tests.py @@ -235,7 +235,7 @@ class RFCSyncTests(TestCase): # too, but for testing purposes ... doc.action_holders.add(doc.ad) # not normally set, but add to be sure it's cleared - updated_doc = RfcFactory(rfc_number=123) + RfcFactory(rfc_number=123) today = date_today() From 34378b9c74a9c7f8a6eb013c54800ab297d1127c Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Fri, 28 Jul 2023 10:57:55 -0700 Subject: [PATCH 78/79] chore: Remove duplicate entry in admin search_fields --- ietf/doc/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ietf/doc/admin.py b/ietf/doc/admin.py index 7beb093d6..3ebce516c 100644 --- a/ietf/doc/admin.py +++ b/ietf/doc/admin.py @@ -126,7 +126,7 @@ admin.site.register(DocReminder, DocReminderAdmin) class RelatedDocumentAdmin(admin.ModelAdmin): list_display = ['source', 'target', 'relationship', ] list_filter = ['relationship', ] - search_fields = ['source__name', 'target__name', 'target__name', ] + search_fields = ['source__name', 'target__name', ] raw_id_fields = ['source', 'target', ] admin.site.register(RelatedDocument, RelatedDocumentAdmin) From f4070ef975c36f8fa799e426ee44715b5f762b3a Mon Sep 17 00:00:00 2001 From: Jennifer Richards <jennifer@staff.ietf.org> Date: Fri, 28 Jul 2023 14:29:58 -0700 Subject: [PATCH 79/79] chore: Remove unused imports --- ietf/iesg/tests.py | 2 +- ietf/utils/test_data.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index 9e8560b51..e58ef1bac 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -17,7 +17,7 @@ from django.utils.html import escape import debug # pyflakes:ignore 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.utils import create_ballot_if_not_open from ietf.group.factories import RoleFactory, GroupFactory diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index a22f17306..2efe4ed1c 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -17,7 +17,7 @@ from ietf.group.models import Group, GroupHistory, Role, RoleHistory from ietf.iesg.models import TelechatDate from ietf.ipr.models import HolderIprDisclosure, IprDocRel, IprDisclosureStateName, IprLicenseTypeName 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.group.utils import setup_default_community_list_for_group from ietf.review.models import (ReviewRequest, ReviewerSettings, ReviewResultName, ReviewTypeName, ReviewTeamSettings )