Automatically move the IESG document state when a ballot is issued, prevent a writeup change or re-issue of ballot if the document is already approved, and warn about issuing ballots before the IETF Last Call is finished. Fixes #3119.
- Legacy-Id: 18719
This commit is contained in:
parent
0bf56c93c7
commit
89ec802a5b
|
@ -346,7 +346,7 @@ class BallotWriteupsTests(TestCase):
|
|||
self.assertTrue('aread@' in outbox[-1]['Cc'])
|
||||
|
||||
def test_edit_ballot_writeup(self):
|
||||
draft = IndividualDraftFactory()
|
||||
draft = IndividualDraftFactory(states=[('draft','active'),('draft-iesg','iesg-eva')])
|
||||
url = urlreverse('ietf.doc.views_ballot.ballot_writeupnotes', kwargs=dict(name=draft.name))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
|
@ -372,8 +372,32 @@ class BallotWriteupsTests(TestCase):
|
|||
ballot_writeup="This is a simple test.",
|
||||
save_ballot_writeup="1"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
draft = Document.objects.get(name=draft.name)
|
||||
self.assertTrue("This is a simple test" in draft.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text").text)
|
||||
d = Document.objects.get(name=draft.name)
|
||||
self.assertTrue("This is a simple test" in d.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text").text)
|
||||
self.assertTrue('iesg-eva' == d.get_state_slug('draft-iesg'))
|
||||
|
||||
def test_edit_ballot_writeup_already_approved(self):
|
||||
draft = IndividualDraftFactory(states=[('draft','active'),('draft-iesg','approved')])
|
||||
url = urlreverse('ietf.doc.views_ballot.ballot_writeupnotes', kwargs=dict(name=draft.name))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('textarea[name=ballot_writeup]')), 1)
|
||||
self.assertTrue(q('[type=submit]:contains("Save")'))
|
||||
|
||||
# save
|
||||
r = self.client.post(url, dict(
|
||||
ballot_writeup="This is a simple test.",
|
||||
save_ballot_writeup="1"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
msgs = [m for m in r.context['messages']]
|
||||
self.assertTrue(1 == len(msgs))
|
||||
self.assertTrue("Writeup not changed" in msgs[0].message)
|
||||
d = Document.objects.get(name=draft.name)
|
||||
self.assertTrue('approved' == d.get_state_slug('draft-iesg'))
|
||||
|
||||
def test_edit_ballot_rfceditornote(self):
|
||||
draft = IndividualDraftFactory()
|
||||
|
@ -467,6 +491,41 @@ class BallotWriteupsTests(TestCase):
|
|||
self.assertIn('call expires', get_payload_text(outbox[-1]))
|
||||
self.client.logout()
|
||||
|
||||
def test_issue_ballot_auto_state_change(self):
|
||||
ad = Person.objects.get(user__username="ad")
|
||||
draft = IndividualDraftFactory(ad=ad, states=[('draft','active'),('draft-iesg','writeupw')])
|
||||
url = urlreverse('ietf.doc.views_ballot.ballot_writeupnotes', kwargs=dict(name=draft.name))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# normal get
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('textarea[name=ballot_writeup]')), 1)
|
||||
self.assertFalse(q('[class=help-block]:contains("not completed IETF Last Call")'))
|
||||
self.assertTrue(q('[type=submit]:contains("Save")'))
|
||||
|
||||
# save
|
||||
r = self.client.post(url, dict(
|
||||
ballot_writeup="This is a simple test.",
|
||||
issue_ballot="1"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
d = Document.objects.get(name=draft.name)
|
||||
self.assertTrue('iesg-eva' == d.get_state_slug('draft-iesg'))
|
||||
|
||||
def test_issue_ballot_warn_if_early(self):
|
||||
ad = Person.objects.get(user__username="ad")
|
||||
draft = IndividualDraftFactory(ad=ad, states=[('draft','active'),('draft-iesg','lc')])
|
||||
url = urlreverse('ietf.doc.views_ballot.ballot_writeupnotes', kwargs=dict(name=draft.name))
|
||||
login_testing_unauthorized(self, "secretary", url)
|
||||
|
||||
# expect warning about issuing a ballot before IETF Last Call is done
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('textarea[name=ballot_writeup]')), 1)
|
||||
self.assertTrue(q('[class=help-block]:contains("not completed IETF Last Call")'))
|
||||
self.assertTrue(q('[type=submit]:contains("Save")'))
|
||||
|
||||
def test_edit_approval_text(self):
|
||||
ad = Person.objects.get(user__username="ad")
|
||||
|
|
|
@ -8,6 +8,7 @@ import datetime, json
|
|||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.template.defaultfilters import striptags
|
||||
|
@ -592,6 +593,7 @@ class BallotWriteupForm(forms.Form):
|
|||
def ballot_writeupnotes(request, name):
|
||||
"""Editing of ballot write-up and notes"""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
prev_state = doc.get_state("draft-iesg")
|
||||
|
||||
login = request.user.person
|
||||
|
||||
|
@ -604,61 +606,76 @@ def ballot_writeupnotes(request, name):
|
|||
if request.method == 'POST' and "save_ballot_writeup" in request.POST or "issue_ballot" in request.POST:
|
||||
form = BallotWriteupForm(request.POST)
|
||||
if form.is_valid():
|
||||
t = form.cleaned_data["ballot_writeup"]
|
||||
if t != existing.text:
|
||||
e = WriteupDocEvent(doc=doc, rev=doc.rev, by=login)
|
||||
e.by = login
|
||||
e.type = "changed_ballot_writeup_text"
|
||||
e.desc = "Ballot writeup was changed"
|
||||
e.text = t
|
||||
e.save()
|
||||
elif existing.pk == None:
|
||||
existing.save()
|
||||
if prev_state.slug in ['ann', 'approved', 'rfcqueue', 'pub']:
|
||||
ballot_already_approved = True
|
||||
messages.warning(request, "There is an approved ballot for %s. Writeup not changed." % doc.name)
|
||||
else:
|
||||
ballot_already_approved = False
|
||||
t = form.cleaned_data["ballot_writeup"]
|
||||
if t != existing.text:
|
||||
e = WriteupDocEvent(doc=doc, rev=doc.rev, by=login)
|
||||
e.by = login
|
||||
e.type = "changed_ballot_writeup_text"
|
||||
e.desc = "Ballot writeup was changed"
|
||||
e.text = t
|
||||
e.save()
|
||||
elif existing.pk == None:
|
||||
existing.save()
|
||||
|
||||
if "issue_ballot" in request.POST:
|
||||
e = create_ballot_if_not_open(request, doc, login, "approve") # pyflakes:ignore
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, balloter=login, ballot=ballot):
|
||||
# sending the ballot counts as a yes
|
||||
pos = BallotPositionDocEvent(doc=doc, rev=doc.rev, by=login)
|
||||
pos.ballot = ballot
|
||||
pos.type = "changed_ballot_position"
|
||||
pos.balloter = login
|
||||
pos.pos_id = "yes"
|
||||
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.balloter.plain_name())
|
||||
pos.save()
|
||||
if "issue_ballot" in request.POST and not ballot_already_approved:
|
||||
if prev_state.slug in ['watching', 'writeupw', 'goaheadw']:
|
||||
new_state = State.objects.get(used=True, type="draft-iesg", slug='iesg-eva')
|
||||
prev_tags = doc.tags.filter(slug__in=IESG_SUBSTATE_TAGS)
|
||||
doc.set_state(new_state)
|
||||
doc.tags.remove(*prev_tags)
|
||||
|
||||
# Consider mailing this position to 'iesg_ballot_saved'
|
||||
sce = add_state_change_event(doc, login, prev_state, new_state, prev_tags=prev_tags, new_tags=[])
|
||||
if sce:
|
||||
doc.save_with_history([sce])
|
||||
|
||||
approval = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
|
||||
if not approval:
|
||||
approval = generate_approval_mail(request, doc)
|
||||
approval.save()
|
||||
if not ballot_already_approved:
|
||||
e = create_ballot_if_not_open(request, doc, login, "approve") # pyflakes:ignore
|
||||
ballot = doc.latest_event(BallotDocEvent, type="created_ballot")
|
||||
if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionDocEvent, balloter=login, ballot=ballot):
|
||||
# sending the ballot counts as a yes
|
||||
pos = BallotPositionDocEvent(doc=doc, rev=doc.rev, by=login)
|
||||
pos.ballot = ballot
|
||||
pos.type = "changed_ballot_position"
|
||||
pos.balloter = login
|
||||
pos.pos_id = "yes"
|
||||
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.balloter.plain_name())
|
||||
pos.save()
|
||||
|
||||
msg = generate_issue_ballot_mail(request, doc, ballot)
|
||||
# Consider mailing this position to 'iesg_ballot_saved'
|
||||
|
||||
addrs = gather_address_lists('iesg_ballot_issued',doc=doc).as_strings()
|
||||
override = {'To':addrs.to}
|
||||
if addrs.cc:
|
||||
override['CC'] = addrs.cc
|
||||
send_mail_preformatted(request, msg, override=override)
|
||||
approval = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
|
||||
if not approval:
|
||||
approval = generate_approval_mail(request, doc)
|
||||
approval.save()
|
||||
|
||||
addrs = gather_address_lists('ballot_issued_iana',doc=doc).as_strings()
|
||||
override={ "To": "IANA <%s>"%settings.IANA_EVAL_EMAIL, "Bcc": None , "Reply-To": []}
|
||||
if addrs.cc:
|
||||
override['CC'] = addrs.cc
|
||||
send_mail_preformatted(request, msg, extra=extra_automation_headers(doc), override=override)
|
||||
msg = generate_issue_ballot_mail(request, doc, ballot)
|
||||
|
||||
e = DocEvent(doc=doc, rev=doc.rev, by=login)
|
||||
e.by = login
|
||||
e.type = "sent_ballot_announcement"
|
||||
e.desc = "Ballot has been issued"
|
||||
e.save()
|
||||
addrs = gather_address_lists('iesg_ballot_issued',doc=doc).as_strings()
|
||||
override = {'To':addrs.to}
|
||||
if addrs.cc:
|
||||
override['CC'] = addrs.cc
|
||||
send_mail_preformatted(request, msg, override=override)
|
||||
|
||||
return render(request, 'doc/ballot/ballot_issued.html',
|
||||
dict(doc=doc,
|
||||
back_url=doc.get_absolute_url()))
|
||||
|
||||
addrs = gather_address_lists('ballot_issued_iana',doc=doc).as_strings()
|
||||
override={ "To": "IANA <%s>"%settings.IANA_EVAL_EMAIL, "Bcc": None , "Reply-To": []}
|
||||
if addrs.cc:
|
||||
override['CC'] = addrs.cc
|
||||
send_mail_preformatted(request, msg, extra=extra_automation_headers(doc), override=override)
|
||||
|
||||
e = DocEvent(doc=doc, rev=doc.rev, by=login)
|
||||
e.by = login
|
||||
e.type = "sent_ballot_announcement"
|
||||
e.desc = "Ballot has been issued"
|
||||
e.save()
|
||||
|
||||
return render(request, 'doc/ballot/ballot_issued.html',
|
||||
dict(doc=doc,
|
||||
back_url=doc.get_absolute_url()))
|
||||
|
||||
need_intended_status = ""
|
||||
if not doc.intended_std_level:
|
||||
|
@ -668,6 +685,7 @@ def ballot_writeupnotes(request, name):
|
|||
dict(doc=doc,
|
||||
back_url=doc.get_absolute_url(),
|
||||
ballot_issued=bool(doc.latest_event(type="sent_ballot_announcement")),
|
||||
ballot_issue_danger=bool(prev_state.slug in ['ad-eval', 'lc']),
|
||||
ballot_writeup_form=form,
|
||||
need_intended_status=need_intended_status,
|
||||
))
|
||||
|
|
|
@ -17,11 +17,15 @@
|
|||
|
||||
<div class="help-block">
|
||||
Technical summary, Working Group summary, document quality, personnel, IRTF note, IESG note, IANA note. This text will be appended to all announcements and messages to the IRTF or RFC Editor.
|
||||
|
||||
{% if ballot_issue_danger %}
|
||||
<p class="text-danger">This document has not completed IETF Last Call. Please do not issue the ballot early without good reason.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-primary" name="save_ballot_writeup" value="Save Ballot Writeup">Save</button>
|
||||
<button type="submit" class="btn btn-warning" name="issue_ballot" value="Save and Issue Ballot">Save & {% if ballot_issued %}re-{% endif %}issue ballot</button>
|
||||
<button type="submit" class={% if ballot_issue_danger %}"btn btn-danger"{% else %}"btn btn-warning"{% endif %} name="issue_ballot" value="Save and Issue Ballot">Save & {% if ballot_issued %}re-{% endif %}issue ballot</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
|
|
Loading…
Reference in a new issue