Skip to content

Commit e5306ed

Browse files
committed
Instrumented issuing ballots. Simplified the ballot-issued email significantly. Deferred adding ballot_saved mail for the automatic yes positions.
- Legacy-Id: 10068
1 parent f68b469 commit e5306ed

12 files changed

Lines changed: 79 additions & 217 deletions

File tree

ietf/doc/mails.py

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010

1111
from ietf.utils.mail import send_mail, send_mail_text
1212
from ietf.ipr.utils import iprs_from_docs, related_docs
13-
from ietf.doc.models import WriteupDocEvent, BallotPositionDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent
13+
from ietf.doc.models import WriteupDocEvent, LastCallDocEvent, DocAlias, ConsensusDocEvent
1414
from ietf.doc.utils import needed_ballot_positions, get_document_content
15-
from ietf.person.models import Person
1615
from ietf.group.models import Role
1716
from ietf.doc.models import Document
1817
from ietf.mailtoken.utils import gather_address_lists
@@ -308,66 +307,14 @@ def email_ballot_undeferred(request, doc, by, telechat_date):
308307
cc=cc)
309308

310309
def generate_issue_ballot_mail(request, doc, ballot):
311-
active_ads = Person.objects.filter(role__name="ad", role__group__state="active", role__group__type="area").distinct()
312310

313-
positions = BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad')
314-
315-
# format positions and setup discusses and comments
316-
ad_feedback = []
317-
seen = set()
318-
active_ad_positions = []
319-
inactive_ad_positions = []
320-
for p in positions:
321-
if p.ad in seen:
322-
continue
323-
324-
seen.add(p.ad)
325-
326-
def formatted(val):
327-
if val:
328-
return "[ X ]"
329-
else:
330-
return "[ ]"
331-
332-
fmt = u"%-21s%-10s%-11s%-9s%-10s" % (
333-
p.ad.plain_name()[:21],
334-
formatted(p.pos_id == "yes"),
335-
formatted(p.pos_id == "noobj"),
336-
formatted(p.pos_id == "discuss"),
337-
"[ R ]" if p.pos_id == "recuse" else formatted(p.pos_id == "abstain"),
338-
)
339-
340-
if p.ad in active_ads:
341-
active_ad_positions.append(fmt)
342-
if not p.pos_id == "discuss":
343-
p.discuss = ""
344-
if p.comment or p.discuss:
345-
ad_feedback.append(p)
346-
else:
347-
inactive_ad_positions.append(fmt)
348-
349-
active_ad_positions.sort()
350-
inactive_ad_positions.sort()
351-
ad_feedback.sort(key=lambda p: p.ad.plain_name())
352-
353311
e = doc.latest_event(LastCallDocEvent, type="sent_last_call")
354312
last_call_expires = e.expires if e else None
355313

356-
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
357-
approval_text = e.text if e else ""
358-
359-
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
360-
ballot_writeup = e.text if e else ""
361-
362314
return render_to_string("doc/mail/issue_ballot_mail.txt",
363315
dict(doc=doc,
364316
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
365-
active_ad_positions=active_ad_positions,
366-
inactive_ad_positions=inactive_ad_positions,
367-
ad_feedback=ad_feedback,
368317
last_call_expires=last_call_expires,
369-
approval_text=approval_text,
370-
ballot_writeup=ballot_writeup,
371318
needed_ballot_positions=
372319
needed_ballot_positions(doc,
373320
doc.active_ballot().active_ad_positions().values()

ietf/doc/tests_ballot.py

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ietf.iesg.models import TelechatDate
1313
from ietf.person.models import Person
1414
from ietf.utils.test_utils import TestCase
15-
from ietf.utils.mail import outbox
15+
from ietf.utils.mail import outbox, empty_outbox
1616
from ietf.utils.test_data import make_test_data
1717
from ietf.utils.test_utils import login_testing_unauthorized
1818

@@ -278,36 +278,8 @@ def test_issue_ballot(self):
278278
url = urlreverse('doc_ballot_writeupnotes', kwargs=dict(name=draft.name))
279279
login_testing_unauthorized(self, "ad", url)
280280

281-
ballot = draft.latest_event(BallotDocEvent, type="created_ballot")
282-
283-
def create_pos(num, vote, comment="", discuss=""):
284-
ad = Person.objects.get(name="Ad No%s" % num)
285-
e = BallotPositionDocEvent()
286-
e.doc = draft
287-
e.ballot = ballot
288-
e.by = ad
289-
e.ad = ad
290-
e.pos = BallotPositionName.objects.get(slug=vote)
291-
e.type = "changed_ballot_position"
292-
e.comment = comment
293-
if e.comment:
294-
e.comment_time = datetime.datetime.now()
295-
e.discuss = discuss
296-
if e.discuss:
297-
e.discuss_time = datetime.datetime.now()
298-
e.save()
299-
300-
# active
301-
create_pos(1, "yes", discuss="discuss1 " * 20)
302-
create_pos(2, "noobj", comment="comment2 " * 20)
303-
create_pos(3, "discuss", discuss="discuss3 " * 20, comment="comment3 " * 20)
304-
create_pos(4, "abstain")
305-
create_pos(5, "recuse")
306-
307-
# inactive
308-
create_pos(9, "yes")
309281

310-
mailbox_before = len(outbox)
282+
empty_outbox()
311283

312284
r = self.client.post(url, dict(
313285
ballot_writeup="This is a test.",
@@ -316,15 +288,12 @@ def create_pos(num, vote, comment="", discuss=""):
316288
draft = Document.objects.get(name=draft.name)
317289

318290
self.assertTrue(draft.latest_event(type="sent_ballot_announcement"))
319-
self.assertEqual(len(outbox), mailbox_before + 2)
320-
issue_email = outbox[-2]
321-
self.assertTrue("Evaluation:" in issue_email['Subject'])
322-
self.assertTrue("comment1" not in str(issue_email))
323-
self.assertTrue("comment2" in str(issue_email))
324-
self.assertTrue("comment3" in str(issue_email))
325-
self.assertTrue("discuss3" in str(issue_email))
326-
self.assertTrue("This is a test" in str(issue_email))
327-
self.assertTrue("The IESG has approved" in str(issue_email))
291+
self.assertEqual(len(outbox), 2)
292+
self.assertTrue('Evaluation:' in outbox[-2]['Subject'])
293+
self.assertTrue('iesg@' in outbox[-2]['To'])
294+
self.assertTrue('Evaluation:' in outbox[-1]['Subject'])
295+
self.assertTrue('drafts-eval@' in outbox[-1]['To'])
296+
self.assertTrue('X-IETF-Draft-string' in outbox[-1])
328297

329298
def test_edit_approval_text(self):
330299
draft = make_test_data()

ietf/doc/utils_charter.py

Lines changed: 4 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
from django.template.loader import render_to_string
44
from django.conf import settings
55

6-
from ietf.doc.models import NewRevisionDocEvent, WriteupDocEvent, BallotPositionDocEvent
7-
from ietf.person.models import Person
6+
from ietf.doc.models import NewRevisionDocEvent, WriteupDocEvent
87
from ietf.utils.history import find_history_active_at
98
from ietf.utils.mail import parse_preformatted
109
from ietf.mailtoken.utils import gather_address_lists
@@ -168,67 +167,14 @@ def default_review_text(group, charter, by):
168167
return (e1,e2)
169168

170169
def generate_issue_ballot_mail(request, doc, ballot):
171-
active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active", email__role__group__type="area").distinct()
172170

173-
seen = []
174-
positions = []
175-
for p in BallotPositionDocEvent.objects.filter(doc=doc, type="changed_ballot_position", ballot=ballot).order_by("-time", '-id').select_related('ad'):
176-
if p.ad not in seen:
177-
positions.append(p)
178-
seen.append(p.ad)
179-
180-
# format positions and setup blocking and non-blocking comments
181-
ad_feedback = []
182-
seen = set()
183-
active_ad_positions = []
184-
inactive_ad_positions = []
185-
for p in positions:
186-
if p.ad in seen:
187-
continue
188-
189-
seen.add(p.ad)
190-
191-
def formatted(val):
192-
if val:
193-
return "[ X ]"
194-
else:
195-
return "[ ]"
196-
197-
fmt = u"%-21s%-6s%-6s%-8s%-7s" % (
198-
p.ad.plain_name(),
199-
formatted(p.pos_id == "yes"),
200-
formatted(p.pos_id == "no"),
201-
formatted(p.pos_id == "block"),
202-
formatted(p.pos_id == "abstain"),
203-
)
204-
205-
if p.ad in active_ads:
206-
active_ad_positions.append(fmt)
207-
if not p.pos or not p.pos.blocking:
208-
p.discuss = ""
209-
if p.comment or p.discuss:
210-
ad_feedback.append(p)
211-
else:
212-
inactive_ad_positions.append(fmt)
213-
214-
active_ad_positions.sort()
215-
inactive_ad_positions.sort()
216-
ad_feedback.sort(key=lambda p: p.ad.plain_name())
217-
218-
e = doc.latest_event(WriteupDocEvent, type="changed_action_announcement")
219-
approval_text = e.text if e else ""
220-
221-
e = doc.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text")
222-
ballot_writeup = e.text if e else ""
171+
addrs=gather_address_lists('ballot_issued',doc=doc).as_strings()
223172

224173
return render_to_string("doc/charter/issue_ballot_mail.txt",
225174
dict(doc=doc,
226175
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
227-
active_ad_positions=active_ad_positions,
228-
inactive_ad_positions=inactive_ad_positions,
229-
ad_feedback=ad_feedback,
230-
approval_text=approval_text,
231-
ballot_writeup=ballot_writeup,
176+
to = addrs.to,
177+
cc = addrs.cc,
232178
)
233179
)
234180

ietf/doc/views_ballot.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,24 @@ def ballot_writeupnotes(request, name):
539539
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
540540
pos.save()
541541

542+
# Consider mailing this position to 'ballot_saved'
543+
542544
approval = doc.latest_event(WriteupDocEvent, type="changed_ballot_approval_text")
543545
if not approval:
544546
approval = generate_approval_mail(request, doc)
545547

546548
msg = generate_issue_ballot_mail(request, doc, ballot)
547-
send_mail_preformatted(request, msg)
549+
550+
addrs = gather_address_lists('ballot_issued',doc=doc).as_strings()
551+
override = {'To':addrs.to}
552+
if addrs.cc:
553+
override['CC'] = addrs.cc
554+
send_mail_preformatted(request, msg, override=override)
555+
556+
addrs = gather_address_lists('ballot_issued_iana',doc=doc).as_strings()
557+
override={ "To": "IANA <%s>"%settings.IANA_EVAL_EMAIL, "Bcc": None , "Reply-To": None}
558+
if addrs.cc:
559+
override['CC'] = addrs.cc
548560
send_mail_preformatted(request, msg, extra=extra_automation_headers(doc),
549561
override={ "To": "IANA <%s>"%settings.IANA_EVAL_EMAIL, "CC": None, "Bcc": None , "Reply-To": None})
550562

ietf/doc/views_charter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ def ballot_writeupnotes(request, name):
622622
pos.pos_id = "yes"
623623
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
624624
pos.save()
625+
# Consider mailing this position to 'ballot_saved'
625626

626627
msg = generate_issue_ballot_mail(request, charter, ballot)
627628
send_mail_preformatted(request, msg)

ietf/doc/views_conflict_review.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def change_state(request, name, option=None):
6969
pos.pos_id = "yes"
7070
pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.plain_name())
7171
pos.save()
72+
# Consider mailing that position to 'ballot_saved'
7273
send_conflict_eval_email(request,review)
7374

7475

@@ -114,11 +115,17 @@ def send_conflict_eval_email(request,review):
114115
doc_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
115116
)
116117
)
117-
send_mail_preformatted(request,msg)
118+
addrs = gather_address_lists('ballot_issued',doc=review).as_strings()
119+
override = {'To':addrs.to}
120+
if addrs.cc:
121+
override['Cc']=addrs.cc
122+
send_mail_preformatted(request,msg,override=override)
123+
addrs = gather_address_lists('ballot_issued_iana',doc=review).as_strings()
118124
email_iana(request,
119125
review.relateddocument_set.get(relationship__slug='conflrev').target.document,
120-
settings.IANA_EVAL_EMAIL,
121-
msg)
126+
addrs.to,
127+
msg,
128+
addrs.cc)
122129

123130
class UploadForm(forms.Form):
124131
content = forms.CharField(widget=forms.Textarea, label="Conflict review response", help_text="Edit the conflict review response.", required=False)

ietf/doc/views_status_change.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ def send_status_change_eval_email(request,doc):
107107
doc_url = settings.IDTRACKER_BASE_URL+doc.get_absolute_url(),
108108
)
109109
)
110-
send_mail_preformatted(request,msg)
110+
addrs = gather_address_lists('ballot_issued',doc=doc)
111+
override = {'To':addrs.to }
112+
if addrs.cc:
113+
override['Cc'] = addrs.cc
114+
send_mail_preformatted(request,msg,override=override)
111115

112116
class UploadForm(forms.Form):
113117
content = forms.CharField(widget=forms.Textarea, label="Status change text", help_text="Edit the status change text.", required=False)

ietf/mailtoken/migrations/0002_auto_20150809_1314.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,14 @@ def mt_factory(slug,desc,to_slugs,cc_slugs=[]):
356356
],
357357
)
358358

359+
mt_factory(slug='ballot_issued',
360+
desc="Recipients when a ballot is issued",
361+
to_slugs=['iesg',])
362+
363+
mt_factory(slug='ballot_issued_iana',
364+
desc="Recipients for IANA message when a ballot is issued",
365+
to_slugs=['iana_eval',])
366+
359367
mt_factory(slug='last_call_requested',
360368
desc="Recipients when AD requests a last call",
361369
to_slugs=['iesg_secretary',],

ietf/name/fixtures/names.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4904,6 +4904,28 @@
49044904
"model": "mailtoken.mailtoken",
49054905
"pk": "ballot_deferred"
49064906
},
4907+
{
4908+
"fields": {
4909+
"cc": [],
4910+
"to": [
4911+
"iesg"
4912+
],
4913+
"desc": "Recipients when a ballot is issued"
4914+
},
4915+
"model": "mailtoken.mailtoken",
4916+
"pk": "ballot_issued"
4917+
},
4918+
{
4919+
"fields": {
4920+
"cc": [],
4921+
"to": [
4922+
"iana_eval"
4923+
],
4924+
"desc": "Recipients for IANA message when a ballot is issued"
4925+
},
4926+
"model": "mailtoken.mailtoken",
4927+
"pk": "ballot_issued_iana"
4928+
},
49074929
{
49084930
"fields": {
49094931
"cc": [
Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,10 @@
1-
{% autoescape off %}To: Internet Engineering Steering Group <iesg@ietf.org>
2-
From: IESG Secretary <iesg-secretary@ietf.org>
1+
{% autoescape off %}To: {{ to }} {% if cc %}
2+
Cc: {{ cc }}
3+
{% endif %}From: IESG Secretary <iesg-secretary@ietf.org>
34
Reply-To: IESG Secretary <iesg-secretary@ietf.org>
45
Subject: Evaluation: {{ doc.name }}
56

67
{% filter wordwrap:73 %}Evaluation for {{ doc.title }} can be found at {{ doc_url }}
78
{% endfilter %}
8-
Please return the full line with your position.
9-
10-
Yes No Block Abstain
11-
{% for fmt in active_ad_positions %}{{ fmt }}
12-
{% endfor %}{% if inactive_ad_positions %}
13-
14-
{% for fmt in inactive_ad_positions %}{{ fmt }}
15-
{% endfor %}{% endif %}
16-
17-
No "Block" positions, are needed for approval.
18-
19-
BLOCKING AND NON-BLOCKING COMMENTS
20-
==================================
21-
{% filter wordwrap:79 %}{% for p in ad_feedback %}{{ p.ad }}:
22-
23-
{% if p.discuss %}Blocking comment [{{ p.time }}]:
24-
{{ p.discuss }}
25-
26-
{% endif %}{% if p.comment %}Comment [{{ p.time }}]:
27-
{{ p.comment }}
28-
29-
{% endif %}
30-
{% endfor %}{% endfilter %}
31-
---- following is a DRAFT of message to be sent AFTER approval ---
32-
{{ approval_text }}
33-
34-
---- ballot text ----
35-
36-
{{ ballot_writeup }}
379

3810
{% endautoescape%}

0 commit comments

Comments
 (0)