Skip to content

Commit 6e97a89

Browse files
Notify IRSG when an IRSG ballot is created. Fixes ietf-tools#2978. Commit ready for merge.
- Legacy-Id: 18162
1 parent 24140fa commit 6e97a89

11 files changed

Lines changed: 189 additions & 21 deletions

ietf/doc/mails.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from django.utils.encoding import force_text
1414

1515
import debug # pyflakes:ignore
16+
from ietf.doc.templatetags.mail_filters import std_level_prompt
1617

1718
from ietf.utils.mail import send_mail, send_mail_text
1819
from ietf.ipr.utils import iprs_from_docs, related_docs
@@ -401,7 +402,7 @@ def generate_issue_ballot_mail(request, doc, ballot):
401402
last_call_expires = e.expires if e else None
402403
last_call_has_expired = last_call_expires and last_call_expires < datetime.datetime.now()
403404

404-
return render_to_string("doc/mail/issue_ballot_mail.txt",
405+
return render_to_string("doc/mail/issue_iesg_ballot_mail.txt",
405406
dict(doc=doc,
406407
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
407408
last_call_expires=last_call_expires,
@@ -413,6 +414,58 @@ def generate_issue_ballot_mail(request, doc, ballot):
413414
)
414415
)
415416

417+
def _send_irsg_ballot_email(request, doc, ballot, subject, template):
418+
"""Send email notification when IRSG ballot is issued"""
419+
(to, cc) = gather_address_lists('irsg_ballot_issued', doc=doc)
420+
sender = 'IESG Secretary <iesg-secretary@ietf.org>'
421+
422+
ballot_expired = ballot.duedate < datetime.datetime.now()
423+
active_ballot = doc.active_ballot()
424+
if active_ballot is None:
425+
needed_bps = ''
426+
else:
427+
needed_bps = needed_ballot_positions(
428+
doc,
429+
list(active_ballot.active_balloter_positions().values())
430+
)
431+
432+
return send_mail(
433+
request=request,
434+
frm=sender,
435+
to=to,
436+
cc=cc,
437+
subject=subject,
438+
extra={'Reply-To': [sender]},
439+
template=template,
440+
context=dict(
441+
doc=doc,
442+
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
443+
ballot_duedate=ballot.duedate,
444+
ballot_expired=ballot_expired,
445+
needed_ballot_positions=needed_bps,
446+
))
447+
448+
449+
def email_irsg_ballot_issued(request, doc, ballot):
450+
"""Send email notification when IRSG ballot is issued"""
451+
return _send_irsg_ballot_email(
452+
request,
453+
doc,
454+
ballot,
455+
'IRSG ballot issued: %s to %s'%(doc.file_tag(), std_level_prompt(doc)),
456+
'doc/mail/issue_irsg_ballot_mail.txt',
457+
)
458+
459+
def email_irsg_ballot_closed(request, doc, ballot):
460+
"""Send email notification when IRSG ballot is closed"""
461+
return _send_irsg_ballot_email(
462+
request,
463+
doc,
464+
ballot,
465+
'IRSG ballot closed: %s to %s'%(doc.file_tag(), std_level_prompt(doc)),
466+
"doc/mail/close_irsg_ballot_mail.txt",
467+
)
468+
416469
def email_iana(request, doc, to, msg, cc=None):
417470
# fix up message and send it with extra info on doc in headers
418471
import email

ietf/doc/tests_irsg_ballot.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,10 +334,22 @@ def test_issue_ballot(self):
334334
self.assertEqual(r.status_code, 302)
335335
self.assertIsNone(draft.ballot_open('irsg-approve'))
336336

337+
# No notifications should have been generated yet
338+
self.assertEqual(len(outbox), 0)
339+
337340
r = self.client.post(url,{'irsg_button':'Yes', 'duedate':due })
338341
self.assertEqual(r.status_code,302)
339342
self.assertIsNotNone(draft.ballot_open('irsg-approve'))
340-
self.assertEqual(len(outbox),0)
343+
344+
# Should have sent a notification about the new ballot
345+
self.assertEqual(len(outbox), 1)
346+
msg = outbox[0]
347+
self.assertIn('IRSG ballot issued', msg['Subject'])
348+
self.assertIn('iesg-secretary@ietf.org', msg['From'])
349+
# Notifications are also sent to various doc-related addresses, not tested here
350+
self.assertIn('irsg@irtf.org', msg['To'])
351+
self.assertIn('irtf-chair@irtf.org', msg['CC'])
352+
self.assertIn(str(due), get_payload_text(msg)) # ensure duedate is included
341353

342354
def test_take_and_email_position(self):
343355
draft = RgDraftFactory()
@@ -379,11 +391,21 @@ def test_close_ballot(self):
379391
self.assertEqual(r.status_code, 302)
380392
self.assertIsNotNone(draft.ballot_open('irsg-approve'))
381393

394+
# Should not have generated a notification yet
395+
self.assertEqual(len(outbox), 0)
396+
382397
r = self.client.post(url,dict(irsg_button='Yes'))
383398
self.assertEqual(r.status_code, 302)
384399
self.assertIsNone(draft.ballot_open('irsg-approve'))
385400

386-
self.assertEqual(len(outbox), 0)
401+
# Closing the ballot should have generated a notification
402+
self.assertEqual(len(outbox), 1)
403+
msg = outbox[0]
404+
self.assertIn('IRSG ballot closed', msg['Subject'])
405+
self.assertIn('iesg-secretary@ietf.org', msg['From'])
406+
# Notifications are also sent to various doc-related addresses, not tested here
407+
self.assertIn('irsg@irtf.org', msg['To'])
408+
self.assertIn('irtf-chair@irtf.org', msg['CC'])
387409

388410
def test_view_outstanding_ballots(self):
389411
draft = RgDraftFactory()

ietf/doc/utils_charter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ def default_review_text(group, charter, by):
235235

236236
def generate_issue_ballot_mail(request, doc, ballot):
237237

238-
addrs=gather_address_lists('ballot_issued',doc=doc).as_strings()
238+
addrs=gather_address_lists('iesg_ballot_issued',doc=doc).as_strings()
239239

240240
return render_to_string("doc/charter/issue_ballot_mail.txt",
241241
dict(doc=doc,

ietf/doc/views_ballot.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from ietf.doc.mails import ( email_ballot_deferred, email_ballot_undeferred,
2727
extra_automation_headers, generate_last_call_announcement,
2828
generate_issue_ballot_mail, generate_ballot_writeup, generate_ballot_rfceditornote,
29-
generate_approval_mail )
29+
generate_approval_mail, email_irsg_ballot_closed, email_irsg_ballot_issued )
3030
from ietf.doc.lastcall import request_last_call
3131
from ietf.iesg.models import TelechatDate
3232
from ietf.ietfauth.utils import has_role, role_required, is_authorized_in_doc_stream
@@ -635,7 +635,7 @@ def ballot_writeupnotes(request, name):
635635

636636
msg = generate_issue_ballot_mail(request, doc, ballot)
637637

638-
addrs = gather_address_lists('ballot_issued',doc=doc).as_strings()
638+
addrs = gather_address_lists('iesg_ballot_issued',doc=doc).as_strings()
639639
override = {'To':addrs.to}
640640
if addrs.cc:
641641
override['CC'] = addrs.cc
@@ -1095,6 +1095,8 @@ def issue_irsg_ballot(request, name):
10951095
prev_tags = []
10961096
new_tags = []
10971097

1098+
email_irsg_ballot_issued(request, doc, ballot=e) # Send notification email
1099+
10981100
if doc.type_id == 'draft':
10991101
new_state = State.objects.get(used=True, type="draft-stream-irtf", slug='irsgpoll')
11001102

@@ -1130,7 +1132,10 @@ def close_irsg_ballot(request, name):
11301132
if request.method == 'POST':
11311133
button = request.POST.get("irsg_button")
11321134
if button == 'Yes':
1133-
close_ballot(doc, by, "irsg-approve")
1135+
ballot = close_ballot(doc, by, "irsg-approve")
1136+
email_irsg_ballot_closed(request,
1137+
doc=doc,
1138+
ballot=IRSGBallotDocEvent.objects.get(pk=ballot.pk))
11341139

11351140
return HttpResponseRedirect(doc.get_absolute_url())
11361141

ietf/doc/views_conflict_review.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def send_conflict_eval_email(request,review):
147147
doc_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
148148
)
149149
)
150-
addrs = gather_address_lists('ballot_issued',doc=review).as_strings()
150+
addrs = gather_address_lists('iesg_ballot_issued',doc=review).as_strings()
151151
override = {'To':addrs.to}
152152
if addrs.cc:
153153
override['Cc']=addrs.cc

ietf/doc/views_status_change.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def send_status_change_eval_email(request,doc):
129129
doc_url = settings.IDTRACKER_BASE_URL+doc.get_absolute_url(),
130130
)
131131
)
132-
addrs = gather_address_lists('ballot_issued',doc=doc)
132+
addrs = gather_address_lists('iesg_ballot_issued',doc=doc)
133133
override = {'To':addrs.to }
134134
if addrs.cc:
135135
override['Cc'] = addrs.cc
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright The IETF Trust 2019-2020, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
4+
5+
from django.db import migrations
6+
7+
8+
def replace_mailtrigger(MailTrigger, old_slug, new_slug):
9+
"""Replace a MailTrigger with an equivalent using a different slug"""
10+
# Per 0013_add_irsg_ballot_saved.py, can't just modify the existing because that
11+
# will lose the many-to-many relations.
12+
orig_mailtrigger = MailTrigger.objects.get(slug=old_slug)
13+
new_mailtrigger = MailTrigger.objects.create(slug=new_slug)
14+
new_mailtrigger.to.set(orig_mailtrigger.to.all())
15+
new_mailtrigger.cc.set(orig_mailtrigger.cc.all())
16+
new_mailtrigger.desc = orig_mailtrigger.desc
17+
new_mailtrigger.save()
18+
orig_mailtrigger.delete() # get rid of the obsolete MailTrigger
19+
20+
21+
def forward(apps, schema_editor):
22+
"""Forward migration: create irsg_ballot_issued and rename ballot_issued to iesg_ballot_issued"""
23+
# Load historical models
24+
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
25+
Recipient = apps.get_model('mailtrigger', 'Recipient')
26+
27+
# Create the new MailTrigger
28+
irsg_ballot_issued = MailTrigger.objects.create(
29+
slug='irsg_ballot_issued',
30+
desc='Recipients when a new IRSG ballot is issued',
31+
)
32+
irsg_ballot_issued.to.set(Recipient.objects.filter(slug='irsg'))
33+
irsg_ballot_issued.cc.set(Recipient.objects.filter(slug__in=[
34+
'doc_stream_manager', 'doc_affecteddoc_authors', 'doc_affecteddoc_group_chairs',
35+
'doc_affecteddoc_notify', 'doc_authors', 'doc_group_chairs', 'doc_group_mail_list',
36+
'doc_notify', 'doc_shepherd'
37+
]))
38+
39+
# Replace existing 'ballot_issued' object with an 'iesg_ballot_issued'
40+
replace_mailtrigger(MailTrigger, 'ballot_issued', 'iesg_ballot_issued')
41+
42+
43+
def reverse(apps, shema_editor):
44+
"""Reverse migration: rename iesg_ballot_issued to ballot_issued and remove irsg_ballot_issued"""
45+
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
46+
MailTrigger.objects.filter(slug='irsg_ballot_issued').delete()
47+
replace_mailtrigger(MailTrigger, 'iesg_ballot_issued', 'ballot_issued')
48+
49+
50+
class Migration(migrations.Migration):
51+
dependencies = [
52+
('mailtrigger', '0013_add_irsg_ballot_saved'),
53+
]
54+
55+
operations = [
56+
migrations.RunPython(forward, reverse),
57+
]

ietf/name/fixtures/names.json

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,18 +3264,6 @@
32643264
"model": "mailtrigger.mailtrigger",
32653265
"pk": "ballot_ednote_changed_late"
32663266
},
3267-
{
3268-
"fields": {
3269-
"cc": [],
3270-
"desc": "Recipients when a ballot is issued",
3271-
"to": [
3272-
"iesg",
3273-
"iesg_secretary"
3274-
]
3275-
},
3276-
"model": "mailtrigger.mailtrigger",
3277-
"pk": "ballot_issued"
3278-
},
32793267
{
32803268
"fields": {
32813269
"cc": [],
@@ -3686,6 +3674,18 @@
36863674
"model": "mailtrigger.mailtrigger",
36873675
"pk": "group_personnel_change"
36883676
},
3677+
{
3678+
"fields": {
3679+
"cc": [],
3680+
"desc": "Recipients when a ballot is issued",
3681+
"to": [
3682+
"iesg",
3683+
"iesg_secretary"
3684+
]
3685+
},
3686+
"model": "mailtrigger.mailtrigger",
3687+
"pk": "iesg_ballot_issued"
3688+
},
36893689
{
36903690
"fields": {
36913691
"cc": [
@@ -3798,6 +3798,27 @@
37983798
"model": "mailtrigger.mailtrigger",
37993799
"pk": "ipr_posting_confirmation"
38003800
},
3801+
{
3802+
"fields": {
3803+
"cc": [
3804+
"doc_affecteddoc_authors",
3805+
"doc_affecteddoc_group_chairs",
3806+
"doc_affecteddoc_notify",
3807+
"doc_authors",
3808+
"doc_group_chairs",
3809+
"doc_group_mail_list",
3810+
"doc_notify",
3811+
"doc_shepherd",
3812+
"doc_stream_manager"
3813+
],
3814+
"desc": "Recipients when a new IRSG ballot is issued",
3815+
"to": [
3816+
"irsg"
3817+
]
3818+
},
3819+
"model": "mailtrigger.mailtrigger",
3820+
"pk": "irsg_ballot_issued"
3821+
},
38013822
{
38023823
"fields": {
38033824
"cc": [
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{% load ietf_filters %}{% load mail_filters %}{% autoescape off %} {% filter wordwrap:78 %}The IRSG ballot for {{ doc.file_tag }} has been closed. The evaluation for this document can be found at {{ doc_url }}{% endfilter %}
2+
3+
{% endautoescape%}
File renamed without changes.

0 commit comments

Comments
 (0)