From e45a32e49fdc3e3e201c8d3eb12ed26a16881591 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Thu, 15 Aug 2019 20:36:07 +0000 Subject: [PATCH] Add IANA expert review tracking. Commit ready for merge. - Legacy-Id: 16656 --- ietf/doc/admin.py | 4 +- ietf/doc/migrations/0024_iana_experts.py | 59 ++++++++++++ .../doc/migrations/0025_ianaexpertdocevent.py | 24 +++++ ietf/doc/models.py | 3 + ietf/doc/resources.py | 27 +++++- ietf/doc/tests_draft.py | 13 ++- ietf/doc/urls.py | 3 +- ietf/doc/views_doc.py | 7 +- ietf/doc/views_draft.py | 20 ++++- ietf/templates/doc/document_draft.html | 89 +++++++++++++------ .../doc/draft/add_iana_experts_comment.html | 24 +++++ 11 files changed, 239 insertions(+), 34 deletions(-) create mode 100644 ietf/doc/migrations/0024_iana_experts.py create mode 100644 ietf/doc/migrations/0025_ianaexpertdocevent.py create mode 100644 ietf/templates/doc/draft/add_iana_experts_comment.html diff --git a/ietf/doc/admin.py b/ietf/doc/admin.py index 73dfb22e3..56fb59afc 100644 --- a/ietf/doc/admin.py +++ b/ietf/doc/admin.py @@ -12,7 +12,7 @@ from .models import (StateType, State, RelatedDocument, DocumentAuthor, Document StateDocEvent, ConsensusDocEvent, BallotType, BallotDocEvent, WriteupDocEvent, LastCallDocEvent, TelechatDocEvent, BallotPositionDocEvent, ReviewRequestDocEvent, InitialReviewDocEvent, AddedMessageEvent, SubmissionDocEvent, DeletedEvent, EditedAuthorsDocEvent, DocumentURL, - ReviewAssignmentDocEvent ) + ReviewAssignmentDocEvent, IanaExpertDocEvent ) class StateTypeAdmin(admin.ModelAdmin): @@ -155,7 +155,7 @@ admin.site.register(InitialReviewDocEvent, DocEventAdmin) admin.site.register(AddedMessageEvent, DocEventAdmin) admin.site.register(SubmissionDocEvent, DocEventAdmin) admin.site.register(EditedAuthorsDocEvent, DocEventAdmin) - +admin.site.register(IanaExpertDocEvent, DocEventAdmin) class DeletedEventAdmin(admin.ModelAdmin): list_display = ['id', 'content_type', 'json', 'by', 'time'] diff --git a/ietf/doc/migrations/0024_iana_experts.py b/ietf/doc/migrations/0024_iana_experts.py new file mode 100644 index 000000000..14faf15e1 --- /dev/null +++ b/ietf/doc/migrations/0024_iana_experts.py @@ -0,0 +1,59 @@ +# Copyright The IETF Trust 2019, All Rights Reserved +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-08-07 12:07 +from __future__ import unicode_literals + +from django.db import migrations + +def forward(apps, schema_editor): + StateType = apps.get_model('doc','StateType') + State = apps.get_model('doc','State') + + StateType.objects.create(slug='draft-iana-experts',label='IANA Experts State') + State.objects.create(type_id='draft-iana-experts', + slug='need-experts', + name='Need IANA Expert(s)', + used=True, + desc='One or more registries need experts assigned', + order=0 + ) + State.objects.create(type_id='draft-iana-experts', + slug='reviews-assigned', + name='Reviews assigned', + used=True, + desc='One or more expert reviews have been assigned', + order=1 + ) + State.objects.create(type_id='draft-iana-experts', + slug='expert-issues', + name='Issues identified', + used=True, + desc='Some expert reviewers have identified issues', + order=2 + ) + State.objects.create(type_id='draft-iana-experts', + slug='reviewers-ok', + name='Expert Reviews OK', + used=True, + desc='All expert reviews have been completed with no blocking issues', + order=2 + ) + +def reverse(apps, schema_editor): + StateType = apps.get_model('doc','StateType') + State = apps.get_model('doc','State') + + State.objects.filter(type_id='draft-iana-experts', slug__in=('need-experts','reviews-assigned','reviews-complete')).delete() + StateType.objects.filter(slug='draft-iana-experts').delete() + + + +class Migration(migrations.Migration): + + dependencies = [ + ('doc', '0023_one_to_many_docalias'), + ] + + operations = [ + migrations.RunPython(forward, reverse) + ] diff --git a/ietf/doc/migrations/0025_ianaexpertdocevent.py b/ietf/doc/migrations/0025_ianaexpertdocevent.py new file mode 100644 index 000000000..e6bbe0fa9 --- /dev/null +++ b/ietf/doc/migrations/0025_ianaexpertdocevent.py @@ -0,0 +1,24 @@ +# Copyright The IETF Trust 2019, All Rights Reserved +# -*- coding: utf-8 -*- +# Generated by Django 1.11.23 on 2019-08-07 12:27 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('doc', '0024_iana_experts'), + ] + + operations = [ + migrations.CreateModel( + name='IanaExpertDocEvent', + fields=[ + ('docevent_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='doc.DocEvent')), + ], + bases=('doc.docevent',), + ), + ] diff --git a/ietf/doc/models.py b/ietf/doc/models.py index eaa5d9c91..02409018d 100644 --- a/ietf/doc/models.py +++ b/ietf/doc/models.py @@ -1052,6 +1052,9 @@ class DocEvent(models.Model): class NewRevisionDocEvent(DocEvent): pass +class IanaExpertDocEvent(DocEvent): + pass + class StateDocEvent(DocEvent): state_type = ForeignKey(StateType) state = ForeignKey(State, blank=True, null=True) diff --git a/ietf/doc/resources.py b/ietf/doc/resources.py index b80d2b409..2559f7f94 100644 --- a/ietf/doc/resources.py +++ b/ietf/doc/resources.py @@ -16,7 +16,8 @@ from ietf.doc.models import (BallotType, DeletedEvent, StateType, State, Documen TelechatDocEvent, DocReminder, LastCallDocEvent, NewRevisionDocEvent, WriteupDocEvent, InitialReviewDocEvent, DocHistoryAuthor, BallotDocEvent, RelatedDocument, RelatedDocHistory, BallotPositionDocEvent, AddedMessageEvent, SubmissionDocEvent, - ReviewRequestDocEvent, ReviewAssignmentDocEvent, EditedAuthorsDocEvent, DocumentURL) + ReviewRequestDocEvent, ReviewAssignmentDocEvent, EditedAuthorsDocEvent, DocumentURL, + IanaExpertDocEvent ) from ietf.name.resources import BallotPositionNameResource, DocTypeNameResource class BallotTypeResource(ModelResource): @@ -713,3 +714,27 @@ class ReviewAssignmentDocEventResource(ModelResource): "state": ALL_WITH_RELATIONS, } api.doc.register(ReviewAssignmentDocEventResource()) + + +from ietf.person.resources import PersonResource +class IanaExpertDocEventResource(ModelResource): + by = ToOneField(PersonResource, 'by') + doc = ToOneField(DocumentResource, 'doc') + docevent_ptr = ToOneField(DocEventResource, 'docevent_ptr') + class Meta: + queryset = IanaExpertDocEvent.objects.all() + serializer = api.Serializer() + cache = SimpleCache() + #resource_name = 'ianaexpertdocevent' + ordering = ['docevent_ptr', ] + filtering = { + "id": ALL, + "time": ALL, + "type": ALL, + "rev": ALL, + "desc": ALL, + "by": ALL_WITH_RELATIONS, + "doc": ALL_WITH_RELATIONS, + "docevent_ptr": ALL_WITH_RELATIONS, + } +api.doc.register(IanaExpertDocEventResource()) diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py index a6952ac0d..9de63c819 100644 --- a/ietf/doc/tests_draft.py +++ b/ietf/doc/tests_draft.py @@ -19,7 +19,7 @@ import debug # pyflakes:ignore from ietf.doc.factories import IndividualDraftFactory, WgDraftFactory, RgDraftFactory, DocEventFactory from ietf.doc.models import ( Document, DocReminder, DocEvent, ConsensusDocEvent, LastCallDocEvent, RelatedDocument, State, TelechatDocEvent, - WriteupDocEvent, DocRelationshipName) + WriteupDocEvent, DocRelationshipName, IanaExpertDocEvent ) from ietf.doc.utils import get_tags_for_stream_id, create_ballot_if_not_open from ietf.name.models import StreamName, DocTagName from ietf.group.factories import GroupFactory, RoleFactory @@ -200,6 +200,17 @@ class ChangeStateTests(TestCase): draft = Document.objects.get(name=draft.name) self.assertEqual(draft.get_state("draft-iana-review"), next_state) + def test_add_expert_review_comment(self): + draft = WgDraftFactory() + url = urlreverse('ietf.doc.views_draft.add_iana_experts_comment',kwargs=dict(name=draft.name)) + login_testing_unauthorized(self, 'iana', url) + r = self.client.get(url) + self.assertEqual(r.status_code, 200) + r = self.client.post(url,dict(comment='!2ab3x#1')) + self.assertEqual(r.status_code, 302) + self.assertEqual(draft.latest_event(IanaExpertDocEvent,type='comment').desc,'!2ab3x#1') + + def test_request_last_call(self): ad = Person.objects.get(user__username="ad") draft = WgDraftFactory(ad=ad,states=[('draft-iesg','ad-eval')]) diff --git a/ietf/doc/urls.py b/ietf/doc/urls.py index 2de7d2806..83ab67a2d 100644 --- a/ietf/doc/urls.py +++ b/ietf/doc/urls.py @@ -91,7 +91,8 @@ urlpatterns = [ url(r'^%(name)s/email-aliases/$' % settings.URL_REGEXPS, RedirectView.as_view(pattern_name='ietf.doc.views_doc.document_email', permanent=False),name='ietf.doc.urls.redirect.document_email'), url(r'^%(name)s/edit/state/$' % settings.URL_REGEXPS, views_draft.change_state), # IESG state - url(r'^%(name)s/edit/state/(?Piana-action|iana-review)/$' % settings.URL_REGEXPS, views_draft.change_iana_state), + url(r'^%(name)s/edit/state/(?Piana-action|iana-review|iana-experts)/$' % settings.URL_REGEXPS, views_draft.change_iana_state), + url(r'^%(name)s/edit/ianaexpertcomment/$' % settings.URL_REGEXPS, views_draft.add_iana_experts_comment), url(r'^%(name)s/edit/info/$' % settings.URL_REGEXPS, views_draft.edit_info), url(r'^%(name)s/edit/requestresurrect/$' % settings.URL_REGEXPS, views_draft.request_resurrect), url(r'^%(name)s/edit/submit-to-iesg/$' % settings.URL_REGEXPS, views_draft.to_iesg), diff --git a/ietf/doc/views_doc.py b/ietf/doc/views_doc.py index 3f1ea00a5..bacf75ea2 100644 --- a/ietf/doc/views_doc.py +++ b/ietf/doc/views_doc.py @@ -56,7 +56,7 @@ from django import forms import debug # pyflakes:ignore from ietf.doc.models import ( Document, DocAlias, DocHistory, DocEvent, BallotDocEvent, - ConsensusDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent, + ConsensusDocEvent, NewRevisionDocEvent, TelechatDocEvent, WriteupDocEvent, IanaExpertDocEvent, IESG_BALLOT_ACTIVE_STATES, STATUSCHANGE_RELATIONS ) from ietf.doc.utils import ( add_links_in_new_revision_events, augment_events_with_revision, can_adopt_draft, can_unadopt_draft, get_chartering_type, get_tags_for_stream_id, @@ -396,6 +396,9 @@ def document_main(request, name, rev=None): review_assignments = review_assignments_to_list_for_docs([doc]).get(doc.name, []) no_review_from_teams = no_review_from_teams_on_doc(doc, rev or doc.rev) + exp_comment = doc.latest_event(IanaExpertDocEvent,type="comment") + iana_experts_comment = exp_comment and exp_comment.desc + return render(request, "doc/document_draft.html", dict(doc=doc, group=group, @@ -452,6 +455,8 @@ def document_main(request, name, rev=None): rfc_editor_state=doc.get_state("draft-rfceditor"), iana_review_state=doc.get_state("draft-iana-review"), iana_action_state=doc.get_state("draft-iana-action"), + iana_experts_state=doc.get_state("draft-iana-experts"), + iana_experts_comment=iana_experts_comment, started_iesg_process=started_iesg_process, shepherd_writeup=shepherd_writeup, search_archive=search_archive, diff --git a/ietf/doc/views_draft.py b/ietf/doc/views_draft.py index 3a4be72c5..ba1e1e73d 100644 --- a/ietf/doc/views_draft.py +++ b/ietf/doc/views_draft.py @@ -25,7 +25,7 @@ import debug # pyflakes:ignore from ietf.doc.models import ( Document, DocAlias, RelatedDocument, State, StateType, DocEvent, ConsensusDocEvent, TelechatDocEvent, WriteupDocEvent, StateDocEvent, - IESG_SUBSTATE_TAGS) + IanaExpertDocEvent, IESG_SUBSTATE_TAGS) from ietf.doc.mails import ( email_pulled_from_rfc_queue, email_resurrect_requested, email_resurrection_completed, email_state_changed, email_stream_changed, email_stream_state_changed, email_stream_tags_changed, extra_automation_headers, @@ -188,6 +188,23 @@ def change_state(request, name): next_states=next_states, to_iesg_eval=to_iesg_eval)) +class AddIanaExpertsCommentForm(forms.Form): + comment = forms.CharField(required=True, widget=forms.Textarea, strip=False) + +@role_required('Secretariat', 'IANA') +def add_iana_experts_comment(request, name): + doc = get_object_or_404(Document, docalias__name = name) + if request.method == 'POST': + form = AddIanaExpertsCommentForm(request.POST) + if form.is_valid(): + IanaExpertDocEvent.objects.create(doc=doc, rev=doc.rev, by=request.user.person, type="comment", desc=form.cleaned_data['comment']) + return HttpResponseRedirect(doc.get_absolute_url()) + else: + form = AddIanaExpertsCommentForm() + + return render(request, 'doc/draft/add_iana_experts_comment.html', dict(form=form, doc=doc)) + + class ChangeIanaStateForm(forms.Form): state = forms.ModelChoiceField(State.objects.all(), required=False) @@ -197,6 +214,7 @@ class ChangeIanaStateForm(forms.Form): choices = State.objects.filter(used=True, type=state_type).order_by("order").values_list("pk", "name") self.fields['state'].choices = [("", "-------")] + list(choices) + @role_required('Secretariat', 'IANA') def change_iana_state(request, name, state_type): """Change IANA review state of Internet Draft. Normally, this is done via diff --git a/ietf/templates/doc/document_draft.html b/ietf/templates/doc/document_draft.html index d6fc15904..5bd3a1258 100644 --- a/ietf/templates/doc/document_draft.html +++ b/ietf/templates/doc/document_draft.html @@ -514,34 +514,69 @@ {% endif %} - {% if iana_review_state %} - - - IANA - IANA review state - - {% if can_edit_iana_state and not snapshot %} - Edit - {% endif %} - - - {{ iana_review_state }} - - + {% if can_edit_iana_state or iana_review_state or iana_experts_state or iana_experts_comment %} + + {% if iana_review_state or can_edit_iana_state %} + + IANA + IANA review state + + {% if can_edit_iana_state and not snapshot %} + Edit + {% endif %} + + + {{ iana_review_state }} + + + {% endif %} - - - IANA action state - - {% if can_edit_iana_state and not snapshot %} - Edit - {% endif %} - - - {{ iana_action_state }} - - - + {% if iana_action_state or can_edit_iana_state %} + + {% if not can_edit_iana_state and not iana_review_state %}IANA{% endif %} + IANA action state + + {% if can_edit_iana_state and not snapshot %} + Edit + {% endif %} + + + {{ iana_action_state }} + + + {% endif %} + + {% if iana_experts_state or can_edit_iana_state %} + + {% if not can_edit_iana_state and not iana_review_state and not iana_action_state %}IANA{% endif %} + IANA expert review state + + {% if can_edit_iana_state and not snapshot %} + Edit + {% endif %} + + + {{ iana_experts_state }} + + + {% endif %} + + {% if iana_experts_comment or can_edit_iana_state %} + + {% if not can_edit_iana_state and not iana_review_state and not iana_action_state and not iana_experts_state %}IANA{% endif %} + IANA expert review comments + + {% if can_edit_iana_state and not snapshot %} + Edit + {% endif %} + + + {{ iana_experts_comment }} + + + {% endif %} + + {% endif %} diff --git a/ietf/templates/doc/draft/add_iana_experts_comment.html b/ietf/templates/doc/draft/add_iana_experts_comment.html new file mode 100644 index 000000000..3bc0a4deb --- /dev/null +++ b/ietf/templates/doc/draft/add_iana_experts_comment.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{# Copyright The IETF Trust 2019, All Rights Reserved #} +{% load origin %} + +{% load bootstrap3 %} + +{% block title %}Add IANA Experts Review State comment for {{ doc }}{% endblock %} + +{% block content %} + {% origin %} +

Add IANA Experts Review State comment
{{ doc }}

+ +
+ {% csrf_token %} + {% bootstrap_form form %} +

The comment will be added to the history trail.

+ + {% buttons %} + + Back + {% endbuttons %} +
+ +{% endblock %} \ No newline at end of file