feat: remove "AD is watching" state (#7960)

* feat: remove "AD is watching" state

* chore: update names.json

* refactor: use idexists state, not dead

* fix: remove guidance to use watching state

* chore: remove references to 'watching' state

* feat: remove create_in_state from edit_info view

* test: update test

* style: Black + move class closer to use

* refactor: remove lint

* fix: restore missing admonition

---------

Co-authored-by: Robert Sparks <rjsparks@nostrum.com>
This commit is contained in:
Jennifer Richards 2024-09-24 14:13:12 -03:00 committed by GitHub
parent 5ea4cc13bb
commit fbcfa19add
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 311 additions and 146 deletions

View file

@ -34,8 +34,8 @@ def expirable_drafts(queryset=None):
# Populate this first time through (but after django has been set up) # Populate this first time through (but after django has been set up)
if nonexpirable_states is None: if nonexpirable_states is None:
# all IESG states except I-D Exists, AD Watching, and Dead block expiry # all IESG states except I-D Exists and Dead block expiry
nonexpirable_states = list(State.objects.filter(used=True, type="draft-iesg").exclude(slug__in=("idexists","watching", "dead"))) nonexpirable_states = list(State.objects.filter(used=True, type="draft-iesg").exclude(slug__in=("idexists", "dead")))
# sent to RFC Editor and RFC Published block expiry (the latter # sent to RFC Editor and RFC Published block expiry (the latter
# shouldn't be possible for an active draft, though) # shouldn't be possible for an active draft, though)
nonexpirable_states += list(State.objects.filter(used=True, type__in=("draft-stream-iab", "draft-stream-irtf", "draft-stream-ise"), slug__in=("rfc-edit", "pub"))) nonexpirable_states += list(State.objects.filter(used=True, type__in=("draft-stream-iab", "draft-stream-irtf", "draft-stream-ise"), slug__in=("rfc-edit", "pub")))

View file

@ -0,0 +1,121 @@
# Copyright The IETF Trust 2024, All Rights Reserved
from django.db import migrations
def get_helper(DocHistory, RelatedDocument, RelatedDocHistory, DocumentAuthor, DocHistoryAuthor):
"""Dependency injection wrapper"""
def save_document_in_history(doc):
"""Save a snapshot of document and related objects in the database.
Local copy of ietf.doc.utils.save_document_in_history() to avoid depending on the
code base in a migration.
"""
def get_model_fields_as_dict(obj):
return dict((field.name, getattr(obj, field.name))
for field in obj._meta.fields
if field is not obj._meta.pk)
# copy fields
fields = get_model_fields_as_dict(doc)
fields["doc"] = doc
fields["name"] = doc.name
dochist = DocHistory(**fields)
dochist.save()
# copy many to many
for field in doc._meta.many_to_many:
if field.remote_field.through and field.remote_field.through._meta.auto_created:
hist_field = getattr(dochist, field.name)
hist_field.clear()
hist_field.set(getattr(doc, field.name).all())
# copy remaining tricky many to many
def transfer_fields(obj, HistModel):
mfields = get_model_fields_as_dict(item)
# map doc -> dochist
for k, v in mfields.items():
if v == doc:
mfields[k] = dochist
HistModel.objects.create(**mfields)
for item in RelatedDocument.objects.filter(source=doc):
transfer_fields(item, RelatedDocHistory)
for item in DocumentAuthor.objects.filter(document=doc):
transfer_fields(item, DocHistoryAuthor)
return dochist
return save_document_in_history
def forward(apps, schema_editor):
"""Mark watching draft-iesg state unused after removing it from Documents"""
StateDocEvent = apps.get_model("doc", "StateDocEvent")
Document = apps.get_model("doc", "Document")
State = apps.get_model("doc", "State")
StateType = apps.get_model("doc", "StateType")
Person = apps.get_model("person", "Person")
save_document_in_history = get_helper(
DocHistory=apps.get_model("doc", "DocHistory"),
RelatedDocument=apps.get_model("doc", "RelatedDocument"),
RelatedDocHistory=apps.get_model("doc", "RelatedDocHistory"),
DocumentAuthor=apps.get_model("doc", "DocumentAuthor"),
DocHistoryAuthor=apps.get_model("doc", "DocHistoryAuthor"),
)
draft_iesg_state_type = StateType.objects.get(slug="draft-iesg")
idexists_state = State.objects.get(type=draft_iesg_state_type, slug="idexists")
watching_state = State.objects.get(type=draft_iesg_state_type, slug="watching")
system_person = Person.objects.get(name="(System)")
# Remove state from documents that currently have it
for doc in Document.objects.filter(states=watching_state):
assert doc.type_id == "draft"
doc.states.remove(watching_state)
doc.states.add(idexists_state)
e = StateDocEvent.objects.create(
type="changed_state",
by=system_person,
doc=doc,
rev=doc.rev,
desc=f"{draft_iesg_state_type.label} changed to <b>{idexists_state.name}</b> from {watching_state.name}",
state_type=draft_iesg_state_type,
state=idexists_state,
)
doc.time = e.time
doc.save()
save_document_in_history(doc)
assert not Document.objects.filter(states=watching_state).exists()
# Mark state as unused
watching_state.used = False
watching_state.save()
def reverse(apps, schema_editor):
"""Mark watching draft-iesg state as used
Does not try to re-apply the state to Documents modified by the forward migration. This
could be done in theory, but would either require dangerous history rewriting or add a
lot of history junk.
"""
State = apps.get_model("doc", "State")
StateType = apps.get_model("doc", "StateType")
State.objects.filter(
type=StateType.objects.get(slug="draft-iesg"), slug="watching"
).update(used=True)
class Migration(migrations.Migration):
dependencies = [
("doc", "0023_bofreqspamstate"),
]
operations = [migrations.RunPython(forward, reverse)]

View file

@ -184,7 +184,7 @@ def state_age_colored(doc):
if not iesg_state: if not iesg_state:
return "" return ""
if iesg_state in ["dead", "watching", "pub", "idexists"]: if iesg_state in ["dead", "pub", "idexists"]:
return "" return ""
try: try:
state_datetime = ( state_datetime = (

View file

@ -559,7 +559,7 @@ class BallotWriteupsTests(TestCase):
q = PyQuery(r.content) q = PyQuery(r.content)
self.assertFalse(q('[class=text-danger]:contains("not completed IETF Last Call")')) self.assertFalse(q('[class=text-danger]:contains("not completed IETF Last Call")'))
for state_slug in ["lc", "watching", "ad-eval"]: for state_slug in ["lc", "ad-eval"]:
draft.set_state(State.objects.get(type="draft-iesg",slug=state_slug)) draft.set_state(State.objects.get(type="draft-iesg",slug=state_slug))
r = self.client.get(url) r = self.client.get(url)
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)

View file

@ -26,7 +26,7 @@ from ietf.doc.models import ( Document, DocReminder, DocEvent,
WriteupDocEvent, DocRelationshipName, IanaExpertDocEvent ) WriteupDocEvent, DocRelationshipName, IanaExpertDocEvent )
from ietf.doc.utils import get_tags_for_stream_id, create_ballot_if_not_open from ietf.doc.utils import get_tags_for_stream_id, create_ballot_if_not_open
from ietf.doc.views_draft import AdoptDraftForm from ietf.doc.views_draft import AdoptDraftForm
from ietf.name.models import StreamName, DocTagName, RoleName from ietf.name.models import DocTagName, RoleName
from ietf.group.factories import GroupFactory, RoleFactory from ietf.group.factories import GroupFactory, RoleFactory
from ietf.group.models import Group, Role from ietf.group.models import Group, Role
from ietf.person.factories import PersonFactory, EmailFactory from ietf.person.factories import PersonFactory, EmailFactory
@ -471,69 +471,61 @@ class EditInfoTests(TestCase):
self.assertIn("may not leave enough time", get_payload_text(outbox[-1])) self.assertIn("may not leave enough time", get_payload_text(outbox[-1]))
def test_start_iesg_process_on_draft(self): def test_start_iesg_process_on_draft(self):
draft = WgDraftFactory( draft = WgDraftFactory(
name="draft-ietf-mars-test2", name="draft-ietf-mars-test2",
group__acronym='mars', group__acronym="mars",
intended_std_level_id="ps", intended_std_level_id="ps",
authors=[Person.objects.get(user__username='ad')], authors=[Person.objects.get(user__username="ad")],
) )
url = urlreverse('ietf.doc.views_draft.edit_info', kwargs=dict(name=draft.name)) url = urlreverse("ietf.doc.views_draft.edit_info", kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url) login_testing_unauthorized(self, "secretary", url)
# normal get # normal get
r = self.client.get(url) r = self.client.get(url)
self.assertEqual(r.status_code, 200) self.assertEqual(r.status_code, 200)
q = PyQuery(r.content) q = PyQuery(r.content)
self.assertEqual(len(q('form select[name=intended_std_level]')), 1) self.assertEqual(len(q("form select[name=intended_std_level]")), 1)
self.assertEqual("", q('form textarea[name=notify]')[0].value.strip()) self.assertEqual("", q("form textarea[name=notify]")[0].value.strip())
# add events_before = list(draft.docevent_set.values_list("id", flat=True))
events_before = draft.docevent_set.count()
mailbox_before = len(outbox) mailbox_before = len(outbox)
ad = Person.objects.get(name="Areað Irector") ad = Person.objects.get(name="Areað Irector")
r = self.client.post(url, r = self.client.post(
dict(intended_std_level=str(draft.intended_std_level_id), url,
ad=ad.pk, dict(
create_in_state=State.objects.get(used=True, type="draft-iesg", slug="watching").pk, intended_std_level=str(draft.intended_std_level_id),
notify="test@example.com", ad=ad.pk,
telechat_date="", notify="test@example.com",
)) telechat_date="",
),
)
self.assertEqual(r.status_code, 302) self.assertEqual(r.status_code, 302)
draft = Document.objects.get(name=draft.name) draft = Document.objects.get(name=draft.name)
self.assertEqual(draft.get_state_slug("draft-iesg"), "watching") self.assertEqual(draft.get_state_slug("draft-iesg"), "pub-req")
self.assertEqual(draft.get_state_slug("draft-stream-ietf"), "sub-pub")
self.assertEqual(draft.ad, ad) self.assertEqual(draft.ad, ad)
self.assertTrue(not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat")) self.assertTrue(
self.assertEqual(draft.docevent_set.count(), events_before + 4) not draft.latest_event(TelechatDocEvent, type="scheduled_for_telechat")
self.assertCountEqual(draft.action_holders.all(), [draft.ad]) )
events = list(draft.docevent_set.order_by('time', 'id')) # check that the expected events were created (don't insist on ordering)
self.assertEqual(events[-4].type, "started_iesg_process") self.assertCountEqual(
self.assertEqual(len(outbox), mailbox_before+1) draft.docevent_set.exclude(id__in=events_before).values_list("type", flat=True),
self.assertTrue('IESG processing' in outbox[-1]['Subject']) [
self.assertTrue('draft-ietf-mars-test2@' in outbox[-1]['To']) "changed_action_holders", # action holders set to AD
"changed_document", # WG state set to sub-pub
# Redo, starting in publication requested to make sure WG state is also set "changed_document", # AD set
draft.set_state(State.objects.get(type_id='draft-iesg', slug='idexists')) "changed_document", # state change notice email set
draft.set_state(State.objects.get(type='draft-stream-ietf',slug='writeupw')) "started_iesg_process", # IESG state is now pub-req
draft.stream = StreamName.objects.get(slug='ietf') ],
draft.action_holders.clear() )
draft.save_with_history([DocEvent.objects.create(doc=draft, rev=draft.rev, type="changed_stream", by=Person.objects.get(user__username="secretary"), desc="Test")])
r = self.client.post(url,
dict(intended_std_level=str(draft.intended_std_level_id),
ad=ad.pk,
create_in_state=State.objects.get(used=True, type="draft-iesg", slug="pub-req").pk,
notify="test@example.com",
telechat_date="",
))
self.assertEqual(r.status_code, 302)
draft = Document.objects.get(name=draft.name)
self.assertEqual(draft.get_state_slug('draft-iesg'),'pub-req')
self.assertEqual(draft.get_state_slug('draft-stream-ietf'),'sub-pub')
self.assertCountEqual(draft.action_holders.all(), [draft.ad]) self.assertCountEqual(draft.action_holders.all(), [draft.ad])
self.assertEqual(len(outbox), mailbox_before + 1)
self.assertTrue("IESG processing" in outbox[-1]["Subject"])
self.assertTrue("draft-ietf-mars-test2@" in outbox[-1]["To"])
def test_edit_consensus(self): def test_edit_consensus(self):
draft = WgDraftFactory() draft = WgDraftFactory()
@ -750,10 +742,6 @@ class ExpireIDsTests(DraftFileMixin, TestCase):
self.assertEqual(len(list(get_expired_drafts())), 1) self.assertEqual(len(list(get_expired_drafts())), 1)
draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="watching"))
self.assertEqual(len(list(get_expired_drafts())), 1)
draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva")) draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="iesg-eva"))
self.assertEqual(len(list(get_expired_drafts())), 0) self.assertEqual(len(list(get_expired_drafts())), 0)

View file

@ -641,7 +641,7 @@ def ballot_writeupnotes(request, name):
existing.save() existing.save()
if "issue_ballot" in request.POST and not ballot_already_approved: if "issue_ballot" in request.POST and not ballot_already_approved:
if prev_state.slug in ['watching', 'writeupw', 'goaheadw']: if prev_state.slug in ['writeupw', 'goaheadw']:
new_state = State.objects.get(used=True, type="draft-iesg", slug='iesg-eva') new_state = State.objects.get(used=True, type="draft-iesg", slug='iesg-eva')
prev_tags = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS) prev_tags = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
doc.set_state(new_state) doc.set_state(new_state)
@ -708,7 +708,7 @@ def ballot_writeupnotes(request, name):
back_url=doc.get_absolute_url(), back_url=doc.get_absolute_url(),
ballot_issued=bool(doc.latest_event(type="sent_ballot_announcement")), ballot_issued=bool(doc.latest_event(type="sent_ballot_announcement")),
warn_lc = not doc.docevent_set.filter(lastcalldocevent__expires__date__lt=date_today(DEADLINE_TZINFO)).exists(), warn_lc = not doc.docevent_set.filter(lastcalldocevent__expires__date__lt=date_today(DEADLINE_TZINFO)).exists(),
warn_unexpected_state= prev_state if bool(prev_state.slug in ['watching', 'ad-eval', 'lc']) else None, warn_unexpected_state= prev_state if bool(prev_state.slug in ['ad-eval', 'lc']) else None,
ballot_writeup_form=form, ballot_writeup_form=form,
need_intended_status=need_intended_status, need_intended_status=need_intended_status,
)) ))

View file

@ -587,7 +587,7 @@ def document_main(request, name, rev=None, document_html=False):
if doc.get_state_slug() not in ["rfc", "expired"] and doc.stream_id in ("ietf",) and not snapshot: if doc.get_state_slug() not in ["rfc", "expired"] and doc.stream_id in ("ietf",) and not snapshot:
if iesg_state_slug == 'idexists' and can_edit: if iesg_state_slug == 'idexists' and can_edit:
actions.append(("Begin IESG Processing", urlreverse('ietf.doc.views_draft.edit_info', kwargs=dict(name=doc.name)) + "?new=1")) actions.append(("Begin IESG Processing", urlreverse('ietf.doc.views_draft.edit_info', kwargs=dict(name=doc.name)) + "?new=1"))
elif can_edit_stream_info and (iesg_state_slug in ('idexists','watching')): elif can_edit_stream_info and (iesg_state_slug == 'idexists'):
actions.append(("Submit to IESG for Publication", urlreverse('ietf.doc.views_draft.to_iesg', kwargs=dict(name=doc.name)))) actions.append(("Submit to IESG for Publication", urlreverse('ietf.doc.views_draft.to_iesg', kwargs=dict(name=doc.name))))
if request.user.is_authenticated and hasattr(request.user, "person"): if request.user.is_authenticated and hasattr(request.user, "person"):

View file

@ -487,40 +487,6 @@ def change_intention(request, name):
doc=doc, doc=doc,
)) ))
class EditInfoForm(forms.Form):
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.filter(used=True), empty_label="(None)", required=True, label="Intended RFC status")
area = forms.ModelChoiceField(Group.objects.filter(type="area", state="active"), empty_label="(None - individual submission)", required=False, label="Assigned to area")
ad = forms.ModelChoiceField(Person.objects.filter(role__name="ad", role__group__state="active",role__group__type='area').order_by('name'), label="Responsible AD", empty_label="(None)", required=True)
create_in_state = forms.ModelChoiceField(State.objects.filter(used=True, type="draft-iesg", slug__in=("pub-req", "watching")), empty_label=None, required=False)
notify = forms.CharField(
widget=forms.Textarea,
max_length=1023,
label="Notice emails",
help_text="Separate email addresses with commas.",
required=False,
)
telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False, widget=forms.Select(attrs={'onchange':'make_bold()'}))
returning_item = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
# if previous AD is now ex-AD, append that person to the list
ad_pk = self.initial.get('ad')
choices = self.fields['ad'].choices
if ad_pk and ad_pk not in [pk for pk, name in choices]:
self.fields['ad'].choices = list(choices) + [("", "-------"), (ad_pk, Person.objects.get(pk=ad_pk).plain_name())]
# telechat choices
dates = [d.date for d in TelechatDate.objects.active().order_by('date')]
init = kwargs['initial']['telechat_date']
if init and init not in dates:
dates.insert(0, init)
self.fields['telechat_date'].choices = [("", "(not on agenda)")] + [(d, d.strftime("%Y-%m-%d")) for d in dates]
# returning item is rendered non-standard
self.standard_fields = [x for x in self.visible_fields() if x.name not in ('returning_item',)]
def to_iesg(request,name): def to_iesg(request,name):
""" Submit an IETF stream document to the IESG for publication """ """ Submit an IETF stream document to the IESG for publication """
@ -619,7 +585,71 @@ def to_iesg(request,name):
notify=notify, notify=notify,
)) ))
@role_required('Area Director','Secretariat') class EditInfoForm(forms.Form):
intended_std_level = forms.ModelChoiceField(
IntendedStdLevelName.objects.filter(used=True),
empty_label="(None)",
required=True,
label="Intended RFC status",
)
area = forms.ModelChoiceField(
Group.objects.filter(type="area", state="active"),
empty_label="(None - individual submission)",
required=False,
label="Assigned to area",
)
ad = forms.ModelChoiceField(
Person.objects.filter(
role__name="ad", role__group__state="active", role__group__type="area"
).order_by("name"),
label="Responsible AD",
empty_label="(None)",
required=True,
)
notify = forms.CharField(
widget=forms.Textarea,
max_length=1023,
label="Notice emails",
help_text="Separate email addresses with commas.",
required=False,
)
telechat_date = forms.TypedChoiceField(
coerce=lambda x: datetime.datetime.strptime(x, "%Y-%m-%d").date(),
empty_value=None,
required=False,
widget=forms.Select(attrs={"onchange": "make_bold()"}),
)
returning_item = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
# if previous AD is now ex-AD, append that person to the list
ad_pk = self.initial.get("ad")
choices = self.fields["ad"].choices
if ad_pk and ad_pk not in [pk for pk, name in choices]:
self.fields["ad"].choices = list(choices) + [
("", "-------"),
(ad_pk, Person.objects.get(pk=ad_pk).plain_name()),
]
# telechat choices
dates = [d.date for d in TelechatDate.objects.active().order_by("date")]
init = kwargs["initial"]["telechat_date"]
if init and init not in dates:
dates.insert(0, init)
self.fields["telechat_date"].choices = [("", "(not on agenda)")] + [
(d, d.strftime("%Y-%m-%d")) for d in dates
]
# returning item is rendered non-standard
self.standard_fields = [
x for x in self.visible_fields() if x.name not in ("returning_item",)
]
@role_required("Area Director", "Secretariat")
def edit_info(request, name): def edit_info(request, name):
"""Edit various Internet-Draft attributes, notifying parties as """Edit various Internet-Draft attributes, notifying parties as
necessary and logging changes as document events.""" necessary and logging changes as document events."""
@ -628,7 +658,8 @@ def edit_info(request, name):
raise Http404 raise Http404
new_document = False new_document = False
if doc.get_state_slug("draft-iesg") == "idexists": # FIXME: should probably receive "new document" as argument to view instead of this # FIXME: should probably receive "new document" as argument to view instead of this
if doc.get_state_slug("draft-iesg") == "idexists":
new_document = True new_document = True
doc.notify = get_initial_notify(doc) doc.notify = get_initial_notify(doc)
@ -636,34 +667,45 @@ def edit_info(request, name):
initial_telechat_date = e.telechat_date if e else None initial_telechat_date = e.telechat_date if e else None
initial_returning_item = bool(e and e.returning_item) initial_returning_item = bool(e and e.returning_item)
if request.method == 'POST': if request.method == "POST":
form = EditInfoForm(request.POST, form = EditInfoForm(
initial=dict(ad=doc.ad_id, request.POST,
telechat_date=initial_telechat_date)) initial=dict(ad=doc.ad_id, telechat_date=initial_telechat_date),
)
if form.is_valid(): if form.is_valid():
by = request.user.person by = request.user.person
pubreq_state = State.objects.get(type="draft-iesg", slug="pub-req")
r = form.cleaned_data r = form.cleaned_data
events = [] events = []
if new_document: if new_document:
doc.set_state(r['create_in_state']) doc.set_state(pubreq_state)
# Is setting the WG state here too much of a hidden side-effect? # Is setting the WG state here too much of a hidden side-effect?
if r['create_in_state'].slug=='pub-req': if (
if doc.stream and doc.stream.slug=='ietf' and doc.group and doc.group.type_id == 'wg': doc.stream
submitted_state = State.objects.get(type='draft-stream-ietf',slug='sub-pub') and doc.stream.slug == "ietf"
doc.set_state(submitted_state) and doc.group
e = DocEvent() and doc.group.type_id == "wg"
e.type = "changed_document" ):
e.by = by submitted_state = State.objects.get(
e.doc = doc type="draft-stream-ietf", slug="sub-pub"
e.rev = doc.rev )
e.desc = "Working group state set to %s" % submitted_state.name doc.set_state(submitted_state)
e.save() e = DocEvent()
events.append(e) e.type = "changed_document"
e.by = by
e.doc = doc
e.rev = doc.rev
e.desc = "Working group state set to %s" % submitted_state.name
e.save()
events.append(e)
replaces = Document.objects.filter(targets_related__source=doc, targets_related__relationship="replaces") replaces = Document.objects.filter(
targets_related__source=doc,
targets_related__relationship="replaces",
)
if replaces: if replaces:
# this should perhaps be somewhere else, e.g. the # this should perhaps be somewhere else, e.g. the
# place where the replace relationship is established? # place where the replace relationship is established?
@ -672,7 +714,10 @@ def edit_info(request, name):
e.by = Person.objects.get(name="(System)") e.by = Person.objects.get(name="(System)")
e.doc = doc e.doc = doc
e.rev = doc.rev e.rev = doc.rev
e.desc = "Earlier history may be found in the Comment Log for <a href=\"%s\">%s</a>" % (replaces[0], replaces[0].get_absolute_url()) e.desc = (
'Earlier history may be found in the Comment Log for <a href="%s">%s</a>'
% (replaces[0], replaces[0].get_absolute_url())
)
e.save() e.save()
events.append(e) events.append(e)
@ -681,7 +726,10 @@ def edit_info(request, name):
e.by = by e.by = by
e.doc = doc e.doc = doc
e.rev = doc.rev e.rev = doc.rev
e.desc = "Document is now in IESG state <b>%s</b>" % doc.get_state("draft-iesg").name e.desc = (
"Document is now in IESG state <b>%s</b>"
% doc.get_state("draft-iesg").name
)
e.save() e.save()
events.append(e) events.append(e)
@ -691,9 +739,9 @@ def edit_info(request, name):
entry = "%(attr)s changed to <b>%(new)s</b> from <b>%(old)s</b>" entry = "%(attr)s changed to <b>%(new)s</b> from <b>%(old)s</b>"
if new_document: if new_document:
entry = "%(attr)s changed to <b>%(new)s</b>" entry = "%(attr)s changed to <b>%(new)s</b>"
return entry % dict(attr=attr, new=new, old=old) return entry % dict(attr=attr, new=new, old=old)
def diff(attr, name): def diff(attr, name):
v = getattr(doc, attr) v = getattr(doc, attr)
if r[attr] != v: if r[attr] != v:
@ -701,9 +749,9 @@ def edit_info(request, name):
setattr(doc, attr, r[attr]) setattr(doc, attr, r[attr])
# update the attributes, keeping track of what we're doing # update the attributes, keeping track of what we're doing
diff('intended_std_level', "Intended Status") diff("intended_std_level", "Intended Status")
diff('ad', "Responsible AD") diff("ad", "Responsible AD")
diff('notify', "State Change Notice email list") diff("notify", "State Change Notice email list")
if doc.group.type_id in ("individ", "area"): if doc.group.type_id in ("individ", "area"):
if not r["area"]: if not r["area"]:
@ -717,12 +765,16 @@ def edit_info(request, name):
doc.group = r["area"] doc.group = r["area"]
for c in changes: for c in changes:
events.append(DocEvent.objects.create(doc=doc, rev=doc.rev, by=by, desc=c, type="changed_document")) events.append(
DocEvent.objects.create(
doc=doc, rev=doc.rev, by=by, desc=c, type="changed_document"
)
)
# Todo - chase this # Todo - chase this
e = update_telechat(request, doc, by, e = update_telechat(
r['telechat_date'], r['returning_item']) request, doc, by, r["telechat_date"], r["returning_item"]
)
if e: if e:
events.append(e) events.append(e)
@ -730,40 +782,44 @@ def edit_info(request, name):
if new_document: if new_document:
# If we created a new doc, update the action holders as though it # If we created a new doc, update the action holders as though it
# started in idexists and moved to its create_in_state. Do this # started in idexists and moved to pub-req. Do this
# after the doc has been updated so, e.g., doc.ad is set. # after the doc has been updated so, e.g., doc.ad is set.
update_action_holders( update_action_holders(
doc, doc,
State.objects.get(type='draft-iesg', slug='idexists'), State.objects.get(type="draft-iesg", slug="idexists"),
r['create_in_state'] pubreq_state,
) )
if changes: if changes:
email_iesg_processing_document(request, doc, changes) email_iesg_processing_document(request, doc, changes)
return HttpResponseRedirect(doc.get_absolute_url()) return HttpResponseRedirect(doc.get_absolute_url())
else: else:
init = dict(intended_std_level=doc.intended_std_level_id, init = dict(
area=doc.group_id, intended_std_level=doc.intended_std_level_id,
ad=doc.ad_id, area=doc.group_id,
notify=doc.notify, ad=doc.ad_id,
telechat_date=initial_telechat_date, notify=doc.notify,
returning_item=initial_returning_item, telechat_date=initial_telechat_date,
) returning_item=initial_returning_item,
)
form = EditInfoForm(initial=init) form = EditInfoForm(initial=init)
# optionally filter out some fields # optionally filter out some fields
if not new_document:
form.standard_fields = [x for x in form.standard_fields if x.name != "create_in_state"]
if doc.group.type_id not in ("individ", "area"): if doc.group.type_id not in ("individ", "area"):
form.standard_fields = [x for x in form.standard_fields if x.name != "area"] form.standard_fields = [x for x in form.standard_fields if x.name != "area"]
return render(request, 'doc/draft/edit_info.html', return render(
dict(doc=doc, request,
form=form, "doc/draft/edit_info.html",
user=request.user, dict(
ballot_issued=doc.latest_event(type="sent_ballot_announcement"))) doc=doc,
form=form,
user=request.user,
ballot_issued=doc.latest_event(type="sent_ballot_announcement"),
),
)
@role_required('Area Director','Secretariat') @role_required('Area Director','Secretariat')
def request_resurrect(request, name): def request_resurrect(request, name):

View file

@ -757,7 +757,7 @@ def drafts_in_last_call(request):
}) })
def drafts_in_iesg_process(request): def drafts_in_iesg_process(request):
states = State.objects.filter(type="draft-iesg").exclude(slug__in=('idexists', 'pub', 'dead', 'watching', 'rfcqueue')) states = State.objects.filter(type="draft-iesg").exclude(slug__in=('idexists', 'pub', 'dead', 'rfcqueue'))
title = "Documents in IESG process" title = "Documents in IESG process"
grouped_docs = [] grouped_docs = []

View file

@ -731,7 +731,7 @@ def dependencies(request, acronym, group_type=None):
{ {
"id": x.became_rfc().name if x.became_rfc() else x.name, "id": x.became_rfc().name if x.became_rfc() else x.name,
"rfc": x.type_id == "rfc" or x.became_rfc() is not None, "rfc": x.type_id == "rfc" or x.became_rfc() is not None,
"post-wg": x.get_state_slug("draft-iesg") not in ["idexists", "watching", "dead"], "post-wg": x.get_state_slug("draft-iesg") not in ["idexists", "dead"],
"expired": x.get_state_slug("draft") == "expired", "expired": x.get_state_slug("draft") == "expired",
"replaced": x.get_state_slug("draft") == "repl", "replaced": x.get_state_slug("draft") == "repl",
"group": x.group.acronym if x.group and x.group.acronym != "none" else "", "group": x.group.acronym if x.group and x.group.acronym != "none" else "",

View file

@ -51,7 +51,7 @@ def all_id_txt():
res.append(f1 + "\t" + f2 + "\t" + f3 + "\t" + f4) res.append(f1 + "\t" + f2 + "\t" + f3 + "\t" + f4)
inactive_states = ["idexists", "pub", "watching", "dead"] inactive_states = ["idexists", "pub", "dead"]
excludes = list(State.objects.filter(type="draft", slug__in=["rfc","repl"])) excludes = list(State.objects.filter(type="draft", slug__in=["rfc","repl"]))
includes = list(State.objects.filter(type="draft-iesg").exclude(slug__in=inactive_states)) includes = list(State.objects.filter(type="draft-iesg").exclude(slug__in=inactive_states))

View file

@ -312,7 +312,7 @@
"order": 42, "order": 42,
"slug": "watching", "slug": "watching",
"type": "draft-iesg", "type": "draft-iesg",
"used": true "used": false
}, },
"model": "doc.state", "model": "doc.state",
"pk": 11 "pk": 11

View file

@ -12,9 +12,9 @@
</h1> </h1>
{% if state.slug == "dead" %} {% if state.slug == "dead" %}
<p class="alert alert-warning my-3"> <p class="alert alert-warning my-3">
This document is in IESG state "Dead". It is unusual to change This document is in IESG state "Dead". It is unusual to change this to
this to anything other than "AD is watching", and this should anything other than "I-D Exists" and this should never be used as a
never be used as a replacement for Begin IESG Processing. replacement for Begin IESG Processing.
</p> </p>
{% endif %} {% endif %}
<a class="btn btn-info my-3" <a class="btn btn-info my-3"