Indicate on the IESG Telechat Agenda if a document has an RFC Editor Note. To accomplish this, the RFC Editor Note needed to be separated from the rest of the ballot write up. Fixes #1230.

- Legacy-Id: 10770
This commit is contained in:
Russ Housley 2016-02-04 13:32:24 +00:00
parent 8ae439ae0f
commit 4b5ac9e09e
13 changed files with 201 additions and 11 deletions

View file

@ -92,6 +92,17 @@ def generate_ballot_writeup(request, doc):
return e
def generate_ballot_rfceditornote(request, doc):
e = WriteupDocEvent()
e.type = "changed_ballot_rfceditornote_text"
e.by = request.user.person
e.doc = doc
e.desc = u"RFC Editor Note for ballot was generated"
e.text = unicode(render_to_string("doc/mail/ballot_rfceditornote.txt"))
e.save()
return e
def generate_last_call_announcement(request, doc):
expiration_date = datetime.date.today() + datetime.timedelta(days=14)
if doc.group.type_id in ("individ", "area"):

View file

@ -232,6 +232,10 @@ class DocumentInfo(models.Model):
else:
return None
def has_rfc_editor_note(self):
e = self.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
return bool(e and (e.text != ""))
def meeting_related(self):
answer = False
if self.type_id in ("agenda","minutes","bluesheets","slides"):
@ -675,6 +679,7 @@ EVENT_TYPES = [
("changed_ballot_approval_text", "Changed ballot approval text"),
("changed_ballot_writeup_text", "Changed ballot writeup text"),
("changed_rfc_editor_note_text", "Changed RFC Editor Note text"),
("changed_last_call_text", "Changed last call text"),
("requested_last_call", "Requested last call"),

View file

@ -707,11 +707,19 @@ class DocTestCase(TestCase):
text="This is ballot writeup notes.",
by=Person.objects.get(name="(System)"))
rfced_note = WriteupDocEvent.objects.create(
doc=doc,
desc="Changed text",
type="changed_rfc_editor_note_text",
text="This is a note for the RFC Editor.",
by=Person.objects.get(name="(System)"))
url = urlreverse('doc_writeup', kwargs=dict(name=doc.name))
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
self.assertTrue(appr.text in r.content)
self.assertTrue(notes.text in r.content)
self.assertTrue(rfced_note.text in r.content)
def test_history(self):
doc = make_test_data()

View file

@ -273,6 +273,45 @@ class BallotWriteupsTests(TestCase):
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)
def test_edit_ballot_rfceditornote(self):
draft = make_test_data()
url = urlreverse('doc_ballot_rfceditornote', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
# add a note to the RFC Editor
rfced_note = WriteupDocEvent.objects.create(
doc=draft,
desc="Changed text",
type="changed_rfc_editor_note_text",
text="This is a note for the RFC Editor.",
by=Person.objects.get(name="(System)"))
# normal get
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
q = PyQuery(r.content)
self.assertEqual(len(q('textarea[name=rfc_editor_note]')), 1)
self.assertTrue(q('[type=submit]:contains("Save")'))
self.assertTrue("<label class=\"control-label\">RFC Editor Note</label>" in r.content)
self.assertTrue("This is a note for the RFC Editor" in r.content)
# save with a note
r = self.client.post(url, dict(
rfc_editor_note="This is a simple test.",
save_ballot_rfceditornote="1"))
self.assertEqual(r.status_code, 200)
draft = Document.objects.get(name=draft.name)
self.assertTrue(draft.has_rfc_editor_note())
self.assertTrue("This is a simple test" in draft.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text").text)
# clear the existing note
r = self.client.post(url, dict(
rfc_editor_note=" ",
clear_ballot_rfceditornote="1"))
self.assertEqual(r.status_code, 200)
draft = Document.objects.get(name=draft.name)
self.assertFalse(draft.has_rfc_editor_note())
def test_issue_ballot(self):
draft = make_test_data()
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
@ -340,6 +379,55 @@ class BallotWriteupsTests(TestCase):
draft = Document.objects.get(name=draft.name)
self.assertTrue("Subject: Results of IETF-conflict review" in draft.latest_event(WriteupDocEvent, type="changed_ballot_approval_text").text)
def test_edit_verify_permissions(self):
def verify_fail(username, url):
if username:
self.client.login(username=username, password=username+"+password")
r = self.client.get(url)
self.assertEqual(r.status_code,403)
def verify_can_see(username, url):
self.client.login(username=username, password=username+"+password")
r = self.client.get(url)
self.assertEqual(r.status_code,200)
q = PyQuery(r.content)
self.assertEqual(len(q("<textarea class=\"form-control\"")),1)
draft = make_test_data()
e = WriteupDocEvent()
e.type = "changed_ballot_approval_text"
e.by = Person.objects.get(name="(System)")
e.doc = draft
e.desc = u"Ballot approval text was generated"
e.text = u"Test approval text."
e.save()
e = WriteupDocEvent()
e.type = "changed_ballot_writeup_text"
e.by = Person.objects.get(name="(System)")
e.doc = draft
e.desc = u"Ballot writeup was generated"
e.text = u"Test ballot writeup text."
e.save()
e = WriteupDocEvent()
e.type = "changed_ballot_rfceditornote_text"
e.by = Person.objects.get(name="(System)")
e.doc = draft
e.desc = u"RFC Editor Note for ballot was generated"
e.text = u"Test note to the RFC Editor text."
e.save()
for p in ['doc_ballot_approvaltext','doc_ballot_writeupnotes','doc_ballot_rfceditornote']:
url = urlreverse(p, kwargs=dict(name=draft.name))
for username in ['plain','marschairman','iana','iab chair']:
verify_fail(username, url)
for username in ['secretary','ad']:
verify_can_see(username, url)
class ApproveBallotTests(TestCase):
def test_approve_ballot(self):

View file

@ -99,6 +99,7 @@ urlpatterns = patterns('',
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/undeferballot/$', views_ballot.undefer_ballot, name='doc_undefer_ballot'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/lastcalltext/$', views_ballot.lastcalltext, name='doc_ballot_lastcall'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ballotwriteupnotes/$', views_ballot.ballot_writeupnotes, name='doc_ballot_writeupnotes'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/ballotrfceditornote/$', views_ballot.ballot_rfceditornote, name='doc_ballot_rfceditornote'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approvaltext/$', views_ballot.ballot_approvaltext, name='doc_ballot_approvaltext'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/approveballot/$', views_ballot.approve_ballot, name='doc_approve_ballot'),
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/makelastcall/$', views_ballot.make_last_call, name='doc_make_last_call'),

View file

@ -19,7 +19,8 @@ from ietf.doc.utils import ( add_state_change_event, close_ballot, close_open_ba
create_ballot_if_not_open, update_telechat )
from ietf.doc.mails import ( email_ballot_deferred, email_ballot_undeferred,
extra_automation_headers, generate_last_call_announcement,
generate_issue_ballot_mail, generate_ballot_writeup, generate_approval_mail )
generate_issue_ballot_mail, generate_ballot_writeup, generate_ballot_rfceditornote,
generate_approval_mail )
from ietf.doc.lastcall import request_last_call
from ietf.iesg.models import TelechatDate
from ietf.ietfauth.utils import has_role, role_required
@ -585,6 +586,57 @@ def ballot_writeupnotes(request, name):
),
context_instance=RequestContext(request))
class BallotRfcEditorNoteForm(forms.Form):
rfc_editor_note = forms.CharField(widget=forms.Textarea, label="RFC Editor Note", required=True)
def clean_rfc_editor_note(self):
return self.cleaned_data["rfc_editor_note"].replace("\r", "")
@role_required('Area Director','Secretariat')
def ballot_rfceditornote(request, name):
"""Editing of RFC Editor Note in the ballot"""
doc = get_object_or_404(Document, docalias__name=name)
login = request.user.person
existing = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
if not existing or (existing.text == ""):
existing = generate_ballot_rfceditornote(request, doc)
form = BallotRfcEditorNoteForm(auto_id=False, initial=dict(rfc_editor_note=existing.text))
if request.method == 'POST' and "save_ballot_rfceditornote" in request.POST:
form = BallotRfcEditorNoteForm(request.POST)
if form.is_valid():
t = form.cleaned_data["rfc_editor_note"]
if t != existing.text:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_rfc_editor_note_text"
e.desc = "RFC Editor Note was changed"
e.text = t.rstrip()
e.save()
if request.method == 'POST' and "clear_ballot_rfceditornote" in request.POST:
e = WriteupDocEvent(doc=doc, by=login)
e.by = login
e.type = "changed_rfc_editor_note_text"
e.desc = "RFC Editor Note was cleared"
e.text = ""
e.save()
# make sure form shows a blank RFC Editor Note
form = BallotRfcEditorNoteForm(initial=dict(rfc_editor_note=" "))
return render_to_response('doc/ballot/rfceditornote.html',
dict(doc=doc,
back_url=doc.get_absolute_url(),
ballot_rfceditornote_form=form,
),
context_instance=RequestContext(request))
class ApprovalTextForm(forms.Form):
approval_text = forms.CharField(widget=forms.Textarea, required=True)
@ -657,7 +709,19 @@ def approve_ballot(request, name):
if not e:
e = generate_ballot_writeup(request, doc)
ballot_writeup = e.text
error_duplicate_rfc_editor_note = False
e = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
if e and (e.text != ""):
if "RFC Editor Note" in ballot_writeup:
error_duplicate_rfc_editor_note = True
ballot_writeup += "\n\n" + e.text
if error_duplicate_rfc_editor_note:
return render_to_response('doc/draft/rfceditor_note_duplicate_error.html',
dict(doc=doc),
context_instance=RequestContext(request))
if "NOT be published" in approval_text:
action = "do_not_publish"
elif "To: RFC Editor" in approval_text:

View file

@ -691,6 +691,10 @@ def document_writeup(request, name):
text_from_writeup("changed_ballot_writeup_text"),
urlreverse("doc_ballot_writeupnotes", kwargs=dict(name=doc.name))))
writeups.append(("RFC Editor Note",
text_from_writeup("changed_rfc_editor_note_text"),
urlreverse("doc_ballot_rfceditornote", kwargs=dict(name=doc.name))))
elif doc.type_id == "charter":
sections.append(("WG Review Announcement",
"",

View file

@ -156,6 +156,9 @@ def agenda_json(request, date=None):
e = doc.latest_event(ConsensusDocEvent, type="changed_consensus")
if e:
docinfo['consensus'] = e.consensus
docinfo['rfc-ed-note'] = doc.has_rfc_editor_note()
elif doc.type_id == 'conflrev':
docinfo['rev'] = doc.rev
td = doc.relateddocument_set.get(relationship__slug='conflrev').target.document

View file

@ -57,7 +57,14 @@ def get_doc_writeup(doc):
want to display the contents of the document
'''
writeup = 'This document has no writeup'
if doc.type_id in ('draft','charter'):
if doc.type_id == 'draft':
latest = doc.latest_event(WriteupDocEvent, type='changed_ballot_writeup_text')
if latest and doc.has_rfc_editor_note:
rfced_note = doc.latest_event(WriteupDocEvent, type="changed_rfc_editor_note_text")
writeup = latest.text + "\n\n" + rfced_note.text
else:
writeup = latest.text
if doc.type_id == 'charter':
latest = doc.latest_event(WriteupDocEvent, type='changed_ballot_writeup_text')
if latest:
writeup = latest.text

View file

@ -1,5 +1,5 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{# Copyright The IETF Trust 2016, All Rights Reserved #}
{% load origin %}
{% load bootstrap3 %}
@ -18,7 +18,7 @@
{% bootstrap_form ballot_writeup_form %}
<div class="help-block">
Technical summary, Working Group summary, document quality, personnel, RFC Editor note, IRTF note, IESG note, IANA note. This text will be appended to all announcements and messages to the IRTF or RFC Editor.
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.
</div>
{% buttons %}

View file

@ -32,10 +32,6 @@ Personnel
experts(s), insert 'The IANA Expert(s) for the registries
in this document are <TO BE ADDED BY THE AD>.'
RFC Editor Note
(Insert RFC Editor Note here or remove section)
IRTF Note
(Insert IRTF Note here or remove section)

View file

@ -1,4 +1,4 @@
{# Copyright The IETF Trust 2015, All Rights Reserved #}{% load origin %}{% origin %}
{# Copyright The IETF Trust 2016, All Rights Reserved #}{% load origin %}{% origin %}
{% load ietf_filters ballot_icon %}
<div class="pull-right">{% ballot_icon doc %}</div>
@ -20,6 +20,9 @@
<span class="fa fa-file"></span></a>
{% endwith %}
<a href="{% url "doc_view" name=doc.canonical_name %}">{{ doc.canonical_name }}</a>
{% if doc.has_rfc_editor_note %}
<a href="{% url "doc_view" name=doc.canonical_name %}writeup/"> <em>(Has RFC Editor Note)</em></a>
{% endif %}
</dd>
<dt>{{ doc.intended_std_level }}</dt><dd><b>{{ doc.title }}</b></dd>

View file

@ -1,5 +1,5 @@
{% load ietf_filters %}{% with doc.rfc_number as rfc_number %}
o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %}
o {{doc.canonical_name}}{% if not rfc_number %}-{{doc.rev}}{% endif %}{% endwith %}{%if doc.has_rfc_editor_note %} (Has RFC Editor Note){% endif %}{% if doc.stream %} - {{ doc.stream }} stream{% endif %}
{% filter wordwrap:"68"|indent|indent %}{{ doc.title }} ({{ doc.intended_std_level }}){% endfilter %}
{% if doc.note %}{# note: note is not escaped #} {% filter wordwrap:"68"|indent|indent %}Note: {{ doc.note|striptags }}{% endfilter %}
{% endif %} Token: {{ doc.ad }}{% if doc.iana_review_state %}