Ported last call text and ballot writeup views with friends and tests to new schema

- Legacy-Id: 2860
This commit is contained in:
Ole Laursen 2011-02-18 18:04:20 +00:00
parent 280080e55a
commit bf29f1a5f9
9 changed files with 479 additions and 87 deletions

View file

@ -9,7 +9,7 @@ from django.conf import settings
from ietf.utils.mail import send_mail, send_mail_text
from ietf.idtracker.models import *
from doc.models import Text
from doc.models import Text, BallotPosition, Expiration
from person.models import Email
def email_state_changed(request, doc, text):
@ -94,6 +94,8 @@ def generate_ballot_writeup(request, doc):
e.content = unicode(render_to_string("idrfc/ballot_writeup.txt"))
e.save()
return e
def generate_last_call_announcement(request, doc):
status = full_intended_status(doc.intended_status).replace("a ", "").replace("an ", "")
@ -156,7 +158,6 @@ def generate_last_call_announcementREDESIGN(request, doc):
)
)
from doc.models import Text
e = Text()
e.type = "changed_last_call_text"
e.by = request.user.get_profile().email()
@ -164,6 +165,8 @@ def generate_last_call_announcementREDESIGN(request, doc):
e.desc = u"Last call announcement was generated by %s" % e.by.get_name()
e.content = unicode(mail)
e.save()
return e
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
@ -250,7 +253,6 @@ def generate_approval_mailREDESIGN(request, doc):
else:
mail = generate_approval_mail_approved(request, doc)
from doc.models import Text
e = Text()
e.type = "changed_ballot_approval_text"
e.by = request.user.get_profile().email()
@ -259,6 +261,8 @@ def generate_approval_mailREDESIGN(request, doc):
e.content = unicode(mail)
e.save()
return e
def generate_approval_mail_approved(request, doc):
doc.full_status = full_intended_status(doc.intended_std_level.name)
status = doc.full_status.replace("a ", "").replace("an ", "")
@ -467,7 +471,79 @@ def generate_issue_ballot_mail(request, doc):
ad_feedback=ad_feedback
)
)
def generate_issue_ballot_mailREDESIGN(request, doc):
full_status = full_intended_status(doc.intended_std_level.name)
status = full_status.replace("a ", "").replace("an ", "")
active_ads = Email.objects.filter(role__name="ad", role__group__state="active")
e = doc.latest_event(type="started_iesg_process")
positions = BallotPosition.objects.filter(doc=doc, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad')
# format positions and setup discusses and comments
ad_feedback = []
seen = set()
active_ad_positions = []
inactive_ad_positions = []
for p in positions:
if p.ad in seen:
continue
seen.add(p.ad)
def formatted(val):
if val:
return "[ X ]"
else:
return "[ ]"
fmt = u"%-21s%-10s%-11s%-9s%-10s" % (
p.ad.get_name()[:21],
formatted(p.pos_id == "yes"),
formatted(p.pos_id == "noobj"),
formatted(p.pos_id == "discuss"),
"[ R ]" if p.pos_id == "recuse" else formatted(p.pos_id == "abstain"),
)
if p.ad in active_ads:
active_ad_positions.append(fmt)
if not p.pos_id == "discuss":
p.discuss = ""
if p.comment or p.discuss:
ad_feedback.append(p)
else:
inactive_ad_positions.append(fmt)
active_ad_positions.sort()
inactive_ad_positions.sort()
ad_feedback.sort(key=lambda p: p.ad.get_name())
e = doc.latest_event(Expiration, type="sent_last_call")
last_call_expires = e.expires if e else None
e = doc.latest_event(Text, type="changed_ballot_approval_text")
approval_text = e.content if e else ""
e = doc.latest_event(Text, type="changed_ballot_writeup_text")
ballot_writeup = e.content if e else ""
return render_to_string("idrfc/issue_ballot_mailREDESIGN.txt",
dict(doc=doc,
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
status=status,
active_ad_positions=active_ad_positions,
inactive_ad_positions=inactive_ad_positions,
ad_feedback=ad_feedback,
last_call_expires=last_call_expires,
approval_text=approval_text,
ballot_writeup=ballot_writeup,
)
)
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
generate_issue_ballot_mail = generate_issue_ballot_mailREDESIGN
def email_iana(request, doc, to, msg):
# fix up message and send message to IANA for each in ballot set
import email
@ -485,6 +561,25 @@ def email_iana(request, doc, to, msg):
extra=extra,
bcc="fenner@research.att.com")
def email_ianaREDESIGN(request, doc, to, msg):
# fix up message and send it with extra info on doc in headers
import email
parsed_msg = email.message_from_string(msg.encode("utf-8"))
extra = {}
extra["Reply-To"] = "noreply@ietf.org"
extra["X-IETF-Draft-string"] = doc.name
extra["X-IETF-Draft-revision"] = doc.rev
send_mail_text(request, "IANA <%s>" % to,
parsed_msg["From"], parsed_msg["Subject"],
parsed_msg.get_payload(),
extra=extra,
bcc="fenner@research.att.com")
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
email_iana = email_ianaREDESIGN
def email_last_call_expired(doc):
text = "IETF Last Call has ended, and the state has been changed to\n%s." % doc.idinternal.cur_state.state

View file

@ -97,40 +97,42 @@ def make_test_data():
login_name="ad",
password="foo",
user_level=1,
first_name="Aread",
last_name="Irector",
first_name=porg.first_name,
last_name=porg.last_name,
person=porg,
)
p = Person.objects.create(
name="Ano Therdir",
ascii="Ano Therdir",
)
email = Email.objects.create(
address="ano@ietf.org",
person=p)
Role.objects.create(
name_id="ad",
group=area,
email=email)
porg = PersonOrOrgInfo.objects.create(
first_name="Ano",
last_name="Therdir",
middle_initial="",
)
EmailAddress.objects.create(
person_or_org=porg,
priority=1,
address=ad.address,
)
IESGLogin.objects.create(
login_name="ad2",
password="foo",
user_level=1,
first_name="Ano",
last_name="Therdir",
person=porg,
)
# create a bunch of ads for swarm tests
for i in range(1, 10):
p = Person.objects.create(
name="Ad No%s" % i,
ascii="Ad No%s" % i,
)
email = Email.objects.create(
address="ad%s@ietf.org" % i,
person=p)
Role.objects.create(
name_id="ad" if i <= 5 else "ex-ad",
group=area,
email=email)
porg = PersonOrOrgInfo.objects.create(
first_name="Ad",
last_name="No%s" % i,
middle_initial="",
)
EmailAddress.objects.create(
person_or_org=porg,
priority=1,
address=ad.address,
)
IESGLogin.objects.create(
login_name="ad%s" % i,
password="foo",
user_level=1,
first_name=porg.first_name,
last_name=porg.last_name,
person=porg,
)
p = Person.objects.create(
name="Sec Retary",
@ -153,8 +155,8 @@ def make_test_data():
login_name="secretary",
password="foo",
user_level=0,
first_name="Sec",
last_name="Retary",
first_name=porg.first_name,
last_name=porg.last_name,
person=porg,
)
@ -295,8 +297,6 @@ class ChangeStateTestCase(django.test.TestCase):
# comment
self.assertTrue("Last call was requested" in draft.event_set.all()[0].desc)
# FIXME: test regeneration of announcement when it's not approved/via rfc editor
class EditInfoTestCase(django.test.TestCase):
@ -327,7 +327,7 @@ class EditInfoTestCase(django.test.TestCase):
events_before = draft.event_set.count()
mailbox_before = len(mail_outbox)
new_ad = Email.objects.get(address="ano@ietf.org")
new_ad = Email.objects.get(address="ad1@ietf.org")
r = self.client.post(url,
dict(intended_std_level=str(draft.intended_std_level.pk),
@ -702,10 +702,10 @@ class DeferBallotTestCase(django.test.TestCase):
self.assertEquals(draft.iesg_state_id, "iesg-eva")
class BallotWriteupsTestCase(django.test.TestCase):
fixtures = ['base', 'draft', 'ballot']
fixtures = ['names']
def test_edit_last_call_text(self):
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft = make_test_data()
url = urlreverse('doc_ballot_lastcall', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
@ -715,7 +715,9 @@ class BallotWriteupsTestCase(django.test.TestCase):
q = PyQuery(r.content)
self.assertEquals(len(q('textarea[name=last_call_text]')), 1)
self.assertEquals(len(q('input[type=submit][value*="Save Last Call"]')), 1)
# we're secretariat, so we got The Link
self.assertEquals(len(q('a:contains("Make Last Call")')), 1)
# subject error
r = self.client.post(url, dict(
last_call_text="Subject: test\r\nhello\r\n\r\n",
@ -729,8 +731,8 @@ class BallotWriteupsTestCase(django.test.TestCase):
last_call_text="This is a simple test.",
save_last_call_text="1"))
self.assertEquals(r.status_code, 200)
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
self.assertTrue("This is a simple test" in draft.idinternal.ballot.last_call_text)
draft = Document.objects.get(name=draft.name)
self.assertTrue("This is a simple test" in draft.latest_event(Text, type="changed_last_call_text").content)
# test regenerate
r = self.client.post(url, dict(
@ -738,28 +740,33 @@ class BallotWriteupsTestCase(django.test.TestCase):
regenerate_last_call_text="1"))
self.assertEquals(r.status_code, 200)
q = PyQuery(r.content)
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
self.assertTrue("Subject: Last Call" in draft.idinternal.ballot.last_call_text)
draft = Document.objects.get(name=draft.name)
self.assertTrue("Subject: Last Call" in draft.latest_event(Text, type="changed_last_call_text").content)
def test_request_last_call(self):
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft = make_test_data()
url = urlreverse('doc_ballot_lastcall', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
mailbox_before = len(mail_outbox)
# give us an announcement to send
r = self.client.post(url, dict(regenerate_last_call_text="1"))
self.assertEquals(r.status_code, 200)
mailbox_before = len(mail_outbox)
# send
r = self.client.post(url, dict(
last_call_text=draft.idinternal.ballot.last_call_text,
last_call_text=draft.latest_event(Text, type="changed_last_call_text").content,
send_last_call_request="1"))
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
self.assertEquals(draft.idinternal.cur_state_id, IDState.LAST_CALL_REQUESTED)
draft = Document.objects.get(name=draft.name)
self.assertEquals(draft.iesg_state_id, "lc-req")
self.assertEquals(len(mail_outbox), mailbox_before + 3)
self.assertTrue("Last Call" in mail_outbox[-1]['Subject'])
self.assertTrue(draft.name in mail_outbox[-1]['Subject'])
def test_edit_ballot_writeup(self):
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft = make_test_data()
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "secretary", url)
@ -775,43 +782,66 @@ class BallotWriteupsTestCase(django.test.TestCase):
ballot_writeup="This is a simple test.",
save_ballot_writeup="1"))
self.assertEquals(r.status_code, 200)
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
self.assertTrue("This is a simple test" in draft.idinternal.ballot.ballot_writeup)
draft = Document.objects.get(name=draft.name)
self.assertTrue("This is a simple test" in draft.latest_event(Text, type="changed_ballot_writeup_text").content)
def test_issue_ballot(self):
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft = make_test_data()
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
login_testing_unauthorized(self, "ad", url)
draft.idinternal.ballot.ballot_issued = False
draft.idinternal.ballot.save()
active = IESGLogin.objects.filter(user_level=1)
Position.objects.create(ad=active[0], yes=1, noobj=0, discuss=0, abstain=0, recuse=0, ballot=draft.idinternal.ballot)
Position.objects.create(ad=active[1], yes=0, noobj=1, discuss=0, abstain=0, recuse=0, ballot=draft.idinternal.ballot)
Position.objects.create(ad=active[2], yes=0, noobj=1, discuss=-1, abstain=0, recuse=0, ballot=draft.idinternal.ballot)
Position.objects.create(ad=active[3], yes=0, noobj=0, discuss=1, abstain=0, recuse=0, ballot=draft.idinternal.ballot)
Position.objects.create(ad=active[4], yes=0, noobj=0, discuss=0, abstain=1, recuse=0, ballot=draft.idinternal.ballot)
Position.objects.create(ad=active[5], yes=0, noobj=0, discuss=0, abstain=0, recuse=1, ballot=draft.idinternal.ballot)
inactive = IESGLogin.objects.filter(user_level=2)
Position.objects.create(ad=inactive[0], yes=1, noobj=0, discuss=0, abstain=0, recuse=0, ballot=draft.idinternal.ballot)
IESGDiscuss.objects.create(ad=active[1], active=True, date=datetime.date.today(), text="test " * 20, ballot=draft.idinternal.ballot)
IESGComment.objects.create(ad=active[2], active=True, date=datetime.date.today(), text="test " * 20, ballot=draft.idinternal.ballot)
IESGDiscuss.objects.create(ad=active[3], active=True, date=datetime.date.today(), text="test " * 20, ballot=draft.idinternal.ballot)
IESGComment.objects.create(ad=active[3], active=True, date=datetime.date.today(), text="test " * 20, ballot=draft.idinternal.ballot)
def create_pos(num, vote, comment="", discuss=""):
ad = Email.objects.get(address="ad%s@ietf.org" % num)
e = BallotPosition()
e.doc = draft
e.by = ad
e.ad = ad
e.pos = BallotPositionName.objects.get(slug=vote)
e.type = "changed_ballot_position"
e.comment = comment
if e.comment:
e.comment_time = datetime.datetime.now()
e.discuss = discuss
if e.discuss:
e.discuss_time = datetime.datetime.now()
e.save()
# active
create_pos(1, "yes", discuss="discuss1 " * 20)
create_pos(2, "noobj", comment="comment2 " * 20)
create_pos(3, "discuss", discuss="discuss3 " * 20, comment="comment3 " * 20)
create_pos(4, "abstain")
create_pos(5, "recuse")
# inactive
create_pos(9, "yes")
# we need approval text to be able to submit
e = Text()
e.doc = draft
e.by = Email.objects.get(address="aread@ietf.org")
e.type = "changed_ballot_approval_text"
e.content = "The document has been approved."
e.save()
mailbox_before = len(mail_outbox)
r = self.client.post(url, dict(
ballot_writeup=draft.idinternal.ballot.ballot_writeup,
approval_text=draft.idinternal.ballot.approval_text,
ballot_writeup="This is a test.",
issue_ballot="1"))
self.assertEquals(r.status_code, 200)
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
draft = Document.objects.get(name=draft.name)
self.assertTrue(draft.idinternal.ballot.ballot_issued)
self.assertTrue(draft.latest_event(type="sent_ballot_announcement"))
self.assertEquals(len(mail_outbox), mailbox_before + 2)
self.assertTrue("Evaluation:" in mail_outbox[-2]['Subject'])
issue_email = mail_outbox[-2]
self.assertTrue("Evaluation:" in issue_email['Subject'])
self.assertTrue("comment1" not in str(issue_email))
self.assertTrue("comment2" in str(issue_email))
self.assertTrue("comment3" in str(issue_email))
self.assertTrue("discuss3" in str(issue_email))
self.assertTrue("This is a test" in str(issue_email))
self.assertTrue("The document has been approved" in str(issue_email))
def test_edit_approval_text(self):
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
@ -850,6 +880,8 @@ class BallotWriteupsTestCase(django.test.TestCase):
draft = InternetDraft.objects.get(filename="draft-ietf-mipshop-pfmipv6")
self.assertTrue("Subject: Protocol Action" in draft.idinternal.ballot.approval_text)
# FIXME: test regeneration of announcement when it's not approved/via rfc editor
class ApproveBallotTestCase(django.test.TestCase):
fixtures = ['base', 'draft', 'ballot']

View file

@ -670,6 +670,7 @@ def lastcalltext(request, name):
return render_to_response('idrfc/ballot_lastcalltext.html',
dict(doc=doc,
back_url=doc.idinternal.get_absolute_url(),
ballot=ballot,
last_call_form=last_call_form,
can_request_last_call=can_request_last_call,
@ -678,6 +679,101 @@ def lastcalltext(request, name):
),
context_instance=RequestContext(request))
class LastCallTextFormREDESIGN(forms.Form):
last_call_text = forms.CharField(widget=forms.Textarea, required=True)
def clean_last_call_text(self):
lines = self.cleaned_data["last_call_text"].split("\r\n")
for l, next in zip(lines, lines[1:]):
if l.startswith('Subject:') and next.strip():
raise forms.ValidationError("Subject line appears to have a line break, please make sure there is no line breaks in the subject line and that it is followed by an empty line.")
return self.cleaned_data["last_call_text"].replace("\r", "")
@group_required('Area_Director','Secretariat')
def lastcalltextREDESIGN(request, name):
"""Editing of the last call text"""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile().email()
existing = doc.latest_event(Text, type="changed_last_call_text")
if not existing:
existing = generate_last_call_announcement(request, doc)
form = LastCallTextForm(initial=dict(last_call_text=existing.content))
if request.method == 'POST':
if "save_last_call_text" in request.POST or "send_last_call_request" in request.POST:
form = LastCallTextForm(request.POST)
if form.is_valid():
t = form.cleaned_data['last_call_text']
if t != existing.content:
e = Text(doc=doc, by=login)
e.by = login
e.type = "changed_last_call_text"
e.desc = "Last call announcement was changed by %s" % login.get_name()
e.content = t
e.save()
doc.time = e.time
doc.save()
if "send_last_call_request" in request.POST:
save_document_in_history(doc)
prev = doc.iesg_state
doc.iesg_state = IesgDocStateName.objects.get(slug='lc-req')
e = log_state_changed(request, doc, login, prev)
doc.time = e.time
doc.save()
email_state_changed(request, doc, e.desc)
email_owner(request, doc, doc.ad, login, e.desc)
request_last_call(request, doc)
return render_to_response('idrfc/last_call_requested.html',
dict(doc=doc),
context_instance=RequestContext(request))
if "regenerate_last_call_text" in request.POST:
e = generate_last_call_announcement(request, doc)
doc.time = e.time
doc.save()
# make sure form has the updated text
form = LastCallTextForm(initial=dict(last_call_text=e.content))
can_request_last_call = doc.iesg_state.order < 27
can_make_last_call = doc.iesg_state.order < 20
can_announce = doc.iesg_state.order > 19
need_intended_status = ""
if not doc.intended_std_level:
need_intended_status = doc.file_tag()
return render_to_response('idrfc/ballot_lastcalltext.html',
dict(doc=doc,
back_url=doc.get_absolute_url(),
last_call_form=form,
can_request_last_call=can_request_last_call,
can_make_last_call=can_make_last_call,
need_intended_status=need_intended_status,
),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
LastCallTextForm = LastCallTextFormREDESIGN
lastcalltext = lastcalltextREDESIGN
@group_required('Area_Director','Secretariat')
def ballot_writeupnotes(request, name):
"""Editing of ballot write-up and notes"""
@ -734,7 +830,8 @@ def ballot_writeupnotes(request, name):
doc.idinternal.save()
return render_to_response('idrfc/ballot_issued.html',
dict(doc=doc),
dict(doc=doc,
back_url=doc.idinternal.get_absolute_url()),
context_instance=RequestContext(request))
@ -752,6 +849,93 @@ def ballot_writeupnotes(request, name):
),
context_instance=RequestContext(request))
class BallotWriteupFormREDESIGN(forms.Form):
ballot_writeup = forms.CharField(widget=forms.Textarea, required=True)
def clean_ballot_writeup(self):
return self.cleaned_data["ballot_writeup"].replace("\r", "")
@group_required('Area_Director','Secretariat')
def ballot_writeupnotesREDESIGN(request, name):
"""Editing of ballot write-up and notes"""
doc = get_object_or_404(Document, docalias__name=name)
if not doc.iesg_state:
raise Http404()
login = request.user.get_profile().email()
approval = doc.latest_event(Text, type="changed_ballot_approval_text")
existing = doc.latest_event(Text, type="changed_ballot_writeup_text")
if not existing:
existing = generate_ballot_writeup(request, doc)
form = BallotWriteupForm(initial=dict(ballot_writeup=existing.content))
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.content:
e = Text(doc=doc, by=login)
e.by = login
e.type = "changed_ballot_writeup_text"
e.desc = "Ballot writeup was changed by %s" % login.get_name()
e.content = t
e.save()
doc.time = e.time
doc.save()
if "issue_ballot" in request.POST and approval:
if in_group(request.user, "Area_Director") and not doc.latest_event(BallotPosition, ad=login):
# sending the ballot counts as a yes
pos = BallotPosition(doc=doc, by=login)
pos.type = "changed_ballot_position"
pos.ad = login
pos.pos_id = "yes"
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.get_name())
pos.save()
msg = generate_issue_ballot_mail(request, doc)
send_mail_preformatted(request, msg)
email_iana(request, doc, 'drafts-eval@icann.org', msg)
e = Event(doc=doc, by=login)
e.by = login
e.type = "sent_ballot_announcement"
e.desc = "Ballot has been issued by %s" % login.get_name()
e.save()
doc.time = e.time
doc.save()
return render_to_response('idrfc/ballot_issued.html',
dict(doc=doc,
back_url=doc.get_absolute_url()),
context_instance=RequestContext(request))
need_intended_status = ""
if not doc.intended_std_level:
need_intended_status = doc.file_tag()
return render_to_response('idrfc/ballot_writeupnotesREDESIGN.html',
dict(doc=doc,
back_url=doc.get_absolute_url(),
ballot_issued=bool(doc.latest_event(type="sent_ballot_announcement")),
ballot_writeup_form=form,
need_intended_status=need_intended_status,
approval=approval,
),
context_instance=RequestContext(request))
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
BallotWriteupForm = BallotWriteupFormREDESIGN
ballot_writeupnotes = ballot_writeupnotesREDESIGN
@group_required('Area_Director','Secretariat')
def ballot_approvaltext(request, name):
"""Editing of approval text"""

View file

@ -179,6 +179,9 @@ class InternetDraft(models.Model):
return self.filename
def file_tag(self):
return "<%s-%s.txt>" % (self.filename, self.revision_display())
def name(self):
# small hack to make model forward-compatible with new schema
return self.filename
def group_acronym(self):
return self.group.acronym
def idstate(self):
@ -397,6 +400,9 @@ class Rfc(models.Model):
return "%s.txt" % ( self.filename() )
def filename(self):
return "rfc%d" % ( self.rfc_number )
def name(self):
# small hack to make model forward-compatible with new schema
return self.filename()
def revision(self):
return "RFC"
def revision_display(self):

View file

@ -8,6 +8,6 @@
<p>Ballot has been sent out.</p>
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back to document</a>
<a href="{{ back_url }}">Back to document</a>
</div>
{% endblock %}

View file

@ -25,7 +25,7 @@ form #id_last_call_text {
{% endif %}
<div class="actions">
<a href="{{ doc.idinternal.get_absolute_url }}">Back</a>
<a href="{{ back_url }}">Back</a>
<input type="submit" name="save_last_call_text" value="Save Last Call Text" />
<input type="submit" name="regenerate_last_call_text" value="Regenerate Last Call Text" />
{% if can_request_last_call and not need_intended_status %}
@ -38,7 +38,7 @@ form #id_last_call_text {
{% if user|in_group:"Secretariat" %}
<p>
{% if can_make_last_call %}
<a href="{% url doc_make_last_call name=doc.filename %}">Make Last Call</a>
<a href="{% url doc_make_last_call name=doc.name %}">Make Last Call</a>
{% endif %}
</p>

View file

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% block title %}Ballot writeup and notes for {{ doc }}{% endblock %}
{% block morecss %}
form #id_ballot_writeup {
width: 700px;
height: 600px;
}
{% endblock %}
{% block content %}
<h1>Ballot writeup and notes for {{ doc }}</h1>
<form action="" method="POST">
<p>(Technical Summary, Working Group Summary, Document Quality,
Personnel, RFC Editor Note, IRTF Note, IESG Note, IANA Note)</p>
<p>This text will be appended to all announcements and messages to
the IRTF or RFC Editor.</p>
{{ ballot_writeup_form.ballot_writeup }}
{% if not approval %}<p style="font-style:italic">Ballot cannot be issued before <a href="{% url doc_ballot_approvaltext name=doc.name %}">announcement text</a> is added.</p>{% endif %}
<div class="actions">
<a href="{{ back_url }}">Back</a>
<input type="submit" name="save_ballot_writeup" value="Save Ballot Writeup" />
<input style="margin-left: 8px" type="submit" {% if not approval %}disabled="disabled"{% endif %} name="issue_ballot" value="Save and {% if ballot_issued %}Re-{% endif %}Issue Ballot" />
</div>
</form>
{% endblock%}

View file

@ -0,0 +1,39 @@
To: Internet Engineering Steering Group <iesg@ietf.org>
From: IESG Secretary <iesg-secretary@ietf.org>
Reply-To: IESG Secretary <iesg-secretary@ietf.org>
Subject: Evaluation: {{ doc.file_tag|safe }} to {{ status }}
{% filter wordwrap:73 %}Evaluation for {{ doc.file_tag|safe }} can be found at {{ doc_url }}
{% if last_call_expires %}Last call to expire on: {{ last_call_expires }}
{% endif %}{% endfilter %}
Please return the full line with your position.
Yes No-Objection Discuss Abstain
{% for fmt in active_ad_positions %}{{ fmt|safe }}
{% endfor %}{% if inactive_ad_positions %}
{% for fmt in inactive_ad_positions %}{{ fmt|safe }}
{% endfor %}{% endif %}
"Yes" or "No-Objection" positions from 2/3 of non-recused ADs,
with no "Discuss" positions, are needed for approval.
DISCUSSES AND COMMENTS
======================
{% filter wordwrap:79 %}{% for pos in ad_feedback %}{{ pos.ad.get_name }}:
{% if pos.discuss %}Discuss [{{ pos.discuss_time|date:"Y-m-d" }}]:
{{ pos.discuss }}
{% endif %}{% if pos.comment %}Comment [{{ pos.comment_time|date:"Y-m-d" }}]:
{{ pos.comment }}
{% endif %}
{% endfor %}{% endfilter %}
---- following is a DRAFT of message to be sent AFTER approval ---
{{ approval_text|safe }}{% if ballot_writeup %}
{{ ballot_writeup|safe }}
{% endif %}

View file

@ -106,8 +106,8 @@ class InternetDraft(Document):
#lc_expiration_date = models.DateField(null=True, blank=True)
@property
def lc_expiration_date(self):
e = self.latest_event(type="sent_last_call")
return e.expiration.expires if e else None
e = self.latest_event(Expiration, type="sent_last_call")
return e.expires if e else None
#b_sent_date = models.DateField(null=True, blank=True)
@property
@ -171,11 +171,11 @@ class InternetDraft(Document):
def calc_process_start_end(self):
import datetime
start, end = datetime.datetime.min, datetime.datetime.max
e = self.ballot.latest_event(type="started_iesg_process")
e = self.latest_event(type="started_iesg_process")
if e:
start = e.time
if self.ballot.state_id == "rfc" and self.ballot.name.startswith("draft") and not hasattr(self.ballot, "viewing_as_rfc"):
previous_process = self.ballot.latest_event(type="started_iesg_process", time__lt=e.time)
if self.state_id == "rfc" and self.name.startswith("draft") and not hasattr(self, "viewing_as_rfc"):
previous_process = self.latest_event(type="started_iesg_process", time__lt=e.time)
if previous_process:
start = previous_process.time
end = e.time