Instrumented issuing ballots. Simplified the ballot-issued email significantly. Deferred adding ballot_saved mail for the automatic yes positions.
- Legacy-Id: 10068
This commit is contained in:
parent
f68b46972e
commit
e5306eda00
|
@ -10,9 +10,8 @@ from django.core.urlresolvers import reverse as urlreverse
|
|||
|
||||
from ietf.utils.mail import send_mail, send_mail_text
|
||||
from ietf.ipr.utils import iprs_from_docs, related_docs
|
||||
from ietf.doc.models import WriteupDocEvent, BallotPositionDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent
|
||||
from ietf.doc.models import WriteupDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent
|
||||
from ietf.doc.utils import needed_ballot_positions, get_document_content
|
||||
from ietf.person.models import Person
|
||||
from ietf.group.models import Role
|
||||
from ietf.doc.models import Document
|
||||
from ietf.mailtoken.utils import gather_address_lists
|
||||
|
@ -308,66 +307,14 @@ def email_ballot_undeferred(request, doc, by, telechat_date):
|
|||
cc=cc)
|
||||
|
||||
def generate_issue_ballot_mail(request, doc, ballot):
|
||||
active_ads = Person.objects.filter(role__name="ad", role__group__state="active", role__group__type="area").distinct()
|
||||
|
||||
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).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.plain_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.plain_name())
|
||||
|
||||
e = doc.latest_event(LastCallDocEvent, type="sent_last_call")
|
||||
last_call_expires = e.expires if e else None
|
||||
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
|
||||
approval_text = e.text if e else ""
|
||||
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
|
||||
ballot_writeup = e.text if e else ""
|
||||
|
||||
return render_to_string("doc/mail/issue_ballot_mail.txt",
|
||||
dict(doc=doc,
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
|
||||
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,
|
||||
needed_ballot_positions=
|
||||
needed_ballot_positions(doc,
|
||||
doc.active_ballot().active_ad_positions().values()
|
||||
|
|
|
@ -12,7 +12,7 @@ from ietf.name.models import BallotPositionName
|
|||
from ietf.iesg.models import TelechatDate
|
||||
from ietf.person.models import Person
|
||||
from ietf.utils.test_utils import TestCase
|
||||
from ietf.utils.mail import outbox
|
||||
from ietf.utils.mail import outbox, empty_outbox
|
||||
from ietf.utils.test_data import make_test_data
|
||||
from ietf.utils.test_utils import login_testing_unauthorized
|
||||
|
||||
|
@ -278,36 +278,8 @@ class BallotWriteupsTests(TestCase):
|
|||
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
|
||||
login_testing_unauthorized(self, "ad", url)
|
||||
|
||||
ballot = draft.latest_event(BallotDocEvent, type="created_ballot")
|
||||
|
||||
def create_pos(num, vote, comment="", discuss=""):
|
||||
ad = Person.objects.get(name="Ad No%s" % num)
|
||||
e = BallotPositionDocEvent()
|
||||
e.doc = draft
|
||||
e.ballot = ballot
|
||||
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")
|
||||
|
||||
mailbox_before = len(outbox)
|
||||
empty_outbox()
|
||||
|
||||
r = self.client.post(url, dict(
|
||||
ballot_writeup="This is a test.",
|
||||
|
@ -316,15 +288,12 @@ class BallotWriteupsTests(TestCase):
|
|||
draft = Document.objects.get(name=draft.name)
|
||||
|
||||
self.assertTrue(draft.latest_event(type="sent_ballot_announcement"))
|
||||
self.assertEqual(len(outbox), mailbox_before + 2)
|
||||
issue_email = 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 IESG has approved" in str(issue_email))
|
||||
self.assertEqual(len(outbox), 2)
|
||||
self.assertTrue('Evaluation:' in outbox[-2]['Subject'])
|
||||
self.assertTrue('iesg@' in outbox[-2]['To'])
|
||||
self.assertTrue('Evaluation:' in outbox[-1]['Subject'])
|
||||
self.assertTrue('drafts-eval@' in outbox[-1]['To'])
|
||||
self.assertTrue('X-IETF-Draft-string' in outbox[-1])
|
||||
|
||||
def test_edit_approval_text(self):
|
||||
draft = make_test_data()
|
||||
|
|
|
@ -3,8 +3,7 @@ import re, datetime, os
|
|||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
|
||||
from ietf.doc.models import NewRevisionDocEvent, WriteupDocEvent, BallotPositionDocEvent
|
||||
from ietf.person.models import Person
|
||||
from ietf.doc.models import NewRevisionDocEvent, WriteupDocEvent
|
||||
from ietf.utils.history import find_history_active_at
|
||||
from ietf.utils.mail import parse_preformatted
|
||||
from ietf.mailtoken.utils import gather_address_lists
|
||||
|
@ -168,67 +167,14 @@ def default_review_text(group, charter, by):
|
|||
return (e1,e2)
|
||||
|
||||
def generate_issue_ballot_mail(request, doc, ballot):
|
||||
active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active", email__role__group__type="area").distinct()
|
||||
|
||||
seen = []
|
||||
positions = []
|
||||
for p in BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad'):
|
||||
if p.ad not in seen:
|
||||
positions.append(p)
|
||||
seen.append(p.ad)
|
||||
|
||||
# format positions and setup blocking and non-blocking 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%-6s%-6s%-8s%-7s" % (
|
||||
p.ad.plain_name(),
|
||||
formatted(p.pos_id == "yes"),
|
||||
formatted(p.pos_id == "no"),
|
||||
formatted(p.pos_id == "block"),
|
||||
formatted(p.pos_id == "abstain"),
|
||||
)
|
||||
|
||||
if p.ad in active_ads:
|
||||
active_ad_positions.append(fmt)
|
||||
if not p.pos or not p.pos.blocking:
|
||||
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.plain_name())
|
||||
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
|
||||
approval_text = e.text if e else ""
|
||||
|
||||
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
|
||||
ballot_writeup = e.text if e else ""
|
||||
addrs=gather_address_lists('ballot_issued',doc=doc).as_strings()
|
||||
|
||||
return render_to_string("doc/charter/issue_ballot_mail.txt",
|
||||
dict(doc=doc,
|
||||
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
|
||||
active_ad_positions=active_ad_positions,
|
||||
inactive_ad_positions=inactive_ad_positions,
|
||||
ad_feedback=ad_feedback,
|
||||
approval_text=approval_text,
|
||||
ballot_writeup=ballot_writeup,
|
||||
to = addrs.to,
|
||||
cc = addrs.cc,
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -539,12 +539,24 @@ def ballot_writeupnotes(request, name):
|
|||
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
|
||||
pos.save()
|
||||
|
||||
# Consider mailing this position to 'ballot_saved'
|
||||
|
||||
approval = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
|
||||
if not approval:
|
||||
approval = generate_approval_mail(request, doc)
|
||||
|
||||
msg = generate_issue_ballot_mail(request, doc, ballot)
|
||||
send_mail_preformatted(request, msg)
|
||||
|
||||
addrs = gather_address_lists('ballot_issued',doc=doc).as_strings()
|
||||
override = {'To':addrs.to}
|
||||
if addrs.cc:
|
||||
override['CC'] = addrs.cc
|
||||
send_mail_preformatted(request, msg, override=override)
|
||||
|
||||
addrs = gather_address_lists('ballot_issued_iana',doc=doc).as_strings()
|
||||
override={ "To": "IANA <%s>"%settings.IANA_EVAL_EMAIL, "Bcc": None , "Reply-To": None}
|
||||
if addrs.cc:
|
||||
override['CC'] = addrs.cc
|
||||
send_mail_preformatted(request, msg, extra=extra_automation_headers(doc),
|
||||
override={ "To": "IANA <%s>"%settings.IANA_EVAL_EMAIL, "CC": None, "Bcc": None , "Reply-To": None})
|
||||
|
||||
|
|
|
@ -622,6 +622,7 @@ def ballot_writeupnotes(request, name):
|
|||
pos.pos_id = "yes"
|
||||
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
|
||||
pos.save()
|
||||
# Consider mailing this position to 'ballot_saved'
|
||||
|
||||
msg = generate_issue_ballot_mail(request, charter, ballot)
|
||||
send_mail_preformatted(request, msg)
|
||||
|
|
|
@ -69,6 +69,7 @@ def change_state(request, name, option=None):
|
|||
pos.pos_id = "yes"
|
||||
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
|
||||
pos.save()
|
||||
# Consider mailing that position to 'ballot_saved'
|
||||
send_conflict_eval_email(request,review)
|
||||
|
||||
|
||||
|
@ -114,11 +115,17 @@ def send_conflict_eval_email(request,review):
|
|||
doc_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
||||
)
|
||||
)
|
||||
send_mail_preformatted(request,msg)
|
||||
addrs = gather_address_lists('ballot_issued',doc=review).as_strings()
|
||||
override = {'To':addrs.to}
|
||||
if addrs.cc:
|
||||
override['Cc']=addrs.cc
|
||||
send_mail_preformatted(request,msg,override=override)
|
||||
addrs = gather_address_lists('ballot_issued_iana',doc=review).as_strings()
|
||||
email_iana(request,
|
||||
review.relateddocument_set.get(relationship__slug='conflrev').target.document,
|
||||
settings.IANA_EVAL_EMAIL,
|
||||
msg)
|
||||
addrs.to,
|
||||
msg,
|
||||
addrs.cc)
|
||||
|
||||
class UploadForm(forms.Form):
|
||||
content = forms.CharField(widget=forms.Textarea, label="Conflict review response", help_text="Edit the conflict review response.", required=False)
|
||||
|
|
|
@ -107,7 +107,11 @@ def send_status_change_eval_email(request,doc):
|
|||
doc_url = settings.IDTRACKER_BASE_URL+doc.get_absolute_url(),
|
||||
)
|
||||
)
|
||||
send_mail_preformatted(request,msg)
|
||||
addrs = gather_address_lists('ballot_issued',doc=doc)
|
||||
override = {'To':addrs.to }
|
||||
if addrs.cc:
|
||||
override['Cc'] = addrs.cc
|
||||
send_mail_preformatted(request,msg,override=override)
|
||||
|
||||
class UploadForm(forms.Form):
|
||||
content = forms.CharField(widget=forms.Textarea, label="Status change text", help_text="Edit the status change text.", required=False)
|
||||
|
|
|
@ -356,6 +356,14 @@ def make_mailtokens(apps):
|
|||
],
|
||||
)
|
||||
|
||||
mt_factory(slug='ballot_issued',
|
||||
desc="Recipients when a ballot is issued",
|
||||
to_slugs=['iesg',])
|
||||
|
||||
mt_factory(slug='ballot_issued_iana',
|
||||
desc="Recipients for IANA message when a ballot is issued",
|
||||
to_slugs=['iana_eval',])
|
||||
|
||||
mt_factory(slug='last_call_requested',
|
||||
desc="Recipients when AD requests a last call",
|
||||
to_slugs=['iesg_secretary',],
|
||||
|
|
|
@ -4904,6 +4904,28 @@
|
|||
"model": "mailtoken.mailtoken",
|
||||
"pk": "ballot_deferred"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"cc": [],
|
||||
"to": [
|
||||
"iesg"
|
||||
],
|
||||
"desc": "Recipients when a ballot is issued"
|
||||
},
|
||||
"model": "mailtoken.mailtoken",
|
||||
"pk": "ballot_issued"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"cc": [],
|
||||
"to": [
|
||||
"iana_eval"
|
||||
],
|
||||
"desc": "Recipients for IANA message when a ballot is issued"
|
||||
},
|
||||
"model": "mailtoken.mailtoken",
|
||||
"pk": "ballot_issued_iana"
|
||||
},
|
||||
{
|
||||
"fields": {
|
||||
"cc": [
|
||||
|
|
|
@ -1,38 +1,10 @@
|
|||
{% autoescape off %}To: Internet Engineering Steering Group <iesg@ietf.org>
|
||||
From: IESG Secretary <iesg-secretary@ietf.org>
|
||||
{% autoescape off %}To: {{ to }} {% if cc %}
|
||||
Cc: {{ cc }}
|
||||
{% endif %}From: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Reply-To: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Subject: Evaluation: {{ doc.name }}
|
||||
|
||||
{% filter wordwrap:73 %}Evaluation for {{ doc.title }} can be found at {{ doc_url }}
|
||||
{% endfilter %}
|
||||
Please return the full line with your position.
|
||||
|
||||
Yes No Block Abstain
|
||||
{% for fmt in active_ad_positions %}{{ fmt }}
|
||||
{% endfor %}{% if inactive_ad_positions %}
|
||||
|
||||
{% for fmt in inactive_ad_positions %}{{ fmt }}
|
||||
{% endfor %}{% endif %}
|
||||
|
||||
No "Block" positions, are needed for approval.
|
||||
|
||||
BLOCKING AND NON-BLOCKING COMMENTS
|
||||
==================================
|
||||
{% filter wordwrap:79 %}{% for p in ad_feedback %}{{ p.ad }}:
|
||||
|
||||
{% if p.discuss %}Blocking comment [{{ p.time }}]:
|
||||
{{ p.discuss }}
|
||||
|
||||
{% endif %}{% if p.comment %}Comment [{{ p.time }}]:
|
||||
{{ p.comment }}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}{% endfilter %}
|
||||
---- following is a DRAFT of message to be sent AFTER approval ---
|
||||
{{ approval_text }}
|
||||
|
||||
---- ballot text ----
|
||||
|
||||
{{ ballot_writeup }}
|
||||
|
||||
{% endautoescape%}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% load mail_filters %}{% autoescape off %}To: Internet Engineering Steering Group <iesg@ietf.org>
|
||||
From: IESG Secretary <iesg-secretary@ietf.org>
|
||||
{% load mail_filters %}{% autoescape off %}To: {{to}} {% if cc %}
|
||||
Cc: {{cc}}
|
||||
{%endif%}From: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Reply-To: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Subject: Evaluation: {{doc.title}}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{% load mail_filters %}{% autoescape off %}To: Internet Engineering Steering Group <iesg@ietf.org>
|
||||
From: IESG Secretary <iesg-secretary@ietf.org>
|
||||
{% load mail_filters %}{% autoescape off %}From: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Reply-To: IESG Secretary <iesg-secretary@ietf.org>
|
||||
Subject: Evaluation: {{ doc.file_tag }} to {{ doc|std_level_prompt }}
|
||||
|
||||
|
@ -8,32 +7,6 @@ Subject: Evaluation: {{ doc.file_tag }} to {{ doc|std_level_prompt }}
|
|||
{% 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 }}
|
||||
{% endfor %}{% if inactive_ad_positions %}
|
||||
|
||||
{% for fmt in inactive_ad_positions %}{{ fmt }}
|
||||
{% endfor %}{% endif %}
|
||||
|
||||
{% filter wordwrap:73 %}{{ needed_ballot_positions }}{% endfilter %}
|
||||
|
||||
DISCUSSES AND COMMENTS
|
||||
======================
|
||||
{% filter wordwrap:79 %}{% for pos in ad_feedback %}{{ pos.ad }}:
|
||||
|
||||
{% 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 }}{% if ballot_writeup %}
|
||||
|
||||
{{ ballot_writeup }}
|
||||
{% endif %}
|
||||
{% endautoescape%}
|
||||
|
|
Loading…
Reference in a new issue