Merged in [18162] from jennifer@painless-security.com:
Notify IRSG when an IRSG ballot is created. Fixes #2978.
- Legacy-Id: 18177
Note: SVN reference [18162] has been migrated to Git commit 6e97a89786
This commit is contained in:
commit
4bc6a20661
|
@ -13,6 +13,7 @@ from django.urls import reverse as urlreverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
from ietf.doc.templatetags.mail_filters import std_level_prompt
|
||||||
|
|
||||||
from ietf.utils.mail import send_mail, send_mail_text
|
from ietf.utils.mail import send_mail, send_mail_text
|
||||||
from ietf.ipr.utils import iprs_from_docs, related_docs
|
from ietf.ipr.utils import iprs_from_docs, related_docs
|
||||||
|
@ -401,7 +402,7 @@ def generate_issue_ballot_mail(request, doc, ballot):
|
||||||
last_call_expires = e.expires if e else None
|
last_call_expires = e.expires if e else None
|
||||||
last_call_has_expired = last_call_expires and last_call_expires < datetime.datetime.now()
|
last_call_has_expired = last_call_expires and last_call_expires < datetime.datetime.now()
|
||||||
|
|
||||||
return render_to_string("doc/mail/issue_ballot_mail.txt",
|
return render_to_string("doc/mail/issue_iesg_ballot_mail.txt",
|
||||||
dict(doc=doc,
|
dict(doc=doc,
|
||||||
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
|
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
|
||||||
last_call_expires=last_call_expires,
|
last_call_expires=last_call_expires,
|
||||||
|
@ -413,6 +414,58 @@ def generate_issue_ballot_mail(request, doc, ballot):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _send_irsg_ballot_email(request, doc, ballot, subject, template):
|
||||||
|
"""Send email notification when IRSG ballot is issued"""
|
||||||
|
(to, cc) = gather_address_lists('irsg_ballot_issued', doc=doc)
|
||||||
|
sender = 'IESG Secretary <iesg-secretary@ietf.org>'
|
||||||
|
|
||||||
|
ballot_expired = ballot.duedate < datetime.datetime.now()
|
||||||
|
active_ballot = doc.active_ballot()
|
||||||
|
if active_ballot is None:
|
||||||
|
needed_bps = ''
|
||||||
|
else:
|
||||||
|
needed_bps = needed_ballot_positions(
|
||||||
|
doc,
|
||||||
|
list(active_ballot.active_balloter_positions().values())
|
||||||
|
)
|
||||||
|
|
||||||
|
return send_mail(
|
||||||
|
request=request,
|
||||||
|
frm=sender,
|
||||||
|
to=to,
|
||||||
|
cc=cc,
|
||||||
|
subject=subject,
|
||||||
|
extra={'Reply-To': [sender]},
|
||||||
|
template=template,
|
||||||
|
context=dict(
|
||||||
|
doc=doc,
|
||||||
|
doc_url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url(),
|
||||||
|
ballot_duedate=ballot.duedate,
|
||||||
|
ballot_expired=ballot_expired,
|
||||||
|
needed_ballot_positions=needed_bps,
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def email_irsg_ballot_issued(request, doc, ballot):
|
||||||
|
"""Send email notification when IRSG ballot is issued"""
|
||||||
|
return _send_irsg_ballot_email(
|
||||||
|
request,
|
||||||
|
doc,
|
||||||
|
ballot,
|
||||||
|
'IRSG ballot issued: %s to %s'%(doc.file_tag(), std_level_prompt(doc)),
|
||||||
|
'doc/mail/issue_irsg_ballot_mail.txt',
|
||||||
|
)
|
||||||
|
|
||||||
|
def email_irsg_ballot_closed(request, doc, ballot):
|
||||||
|
"""Send email notification when IRSG ballot is closed"""
|
||||||
|
return _send_irsg_ballot_email(
|
||||||
|
request,
|
||||||
|
doc,
|
||||||
|
ballot,
|
||||||
|
'IRSG ballot closed: %s to %s'%(doc.file_tag(), std_level_prompt(doc)),
|
||||||
|
"doc/mail/close_irsg_ballot_mail.txt",
|
||||||
|
)
|
||||||
|
|
||||||
def email_iana(request, doc, to, msg, cc=None):
|
def email_iana(request, doc, to, msg, cc=None):
|
||||||
# fix up message and send it with extra info on doc in headers
|
# fix up message and send it with extra info on doc in headers
|
||||||
import email
|
import email
|
||||||
|
|
|
@ -334,10 +334,22 @@ class BaseManipulationTests():
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
self.assertIsNone(draft.ballot_open('irsg-approve'))
|
self.assertIsNone(draft.ballot_open('irsg-approve'))
|
||||||
|
|
||||||
|
# No notifications should have been generated yet
|
||||||
|
self.assertEqual(len(outbox), 0)
|
||||||
|
|
||||||
r = self.client.post(url,{'irsg_button':'Yes', 'duedate':due })
|
r = self.client.post(url,{'irsg_button':'Yes', 'duedate':due })
|
||||||
self.assertEqual(r.status_code,302)
|
self.assertEqual(r.status_code,302)
|
||||||
self.assertIsNotNone(draft.ballot_open('irsg-approve'))
|
self.assertIsNotNone(draft.ballot_open('irsg-approve'))
|
||||||
self.assertEqual(len(outbox),0)
|
|
||||||
|
# Should have sent a notification about the new ballot
|
||||||
|
self.assertEqual(len(outbox), 1)
|
||||||
|
msg = outbox[0]
|
||||||
|
self.assertIn('IRSG ballot issued', msg['Subject'])
|
||||||
|
self.assertIn('iesg-secretary@ietf.org', msg['From'])
|
||||||
|
# Notifications are also sent to various doc-related addresses, not tested here
|
||||||
|
self.assertIn('irsg@irtf.org', msg['To'])
|
||||||
|
self.assertIn('irtf-chair@irtf.org', msg['CC'])
|
||||||
|
self.assertIn(str(due), get_payload_text(msg)) # ensure duedate is included
|
||||||
|
|
||||||
def test_take_and_email_position(self):
|
def test_take_and_email_position(self):
|
||||||
draft = RgDraftFactory()
|
draft = RgDraftFactory()
|
||||||
|
@ -379,11 +391,21 @@ class BaseManipulationTests():
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
self.assertIsNotNone(draft.ballot_open('irsg-approve'))
|
self.assertIsNotNone(draft.ballot_open('irsg-approve'))
|
||||||
|
|
||||||
|
# Should not have generated a notification yet
|
||||||
|
self.assertEqual(len(outbox), 0)
|
||||||
|
|
||||||
r = self.client.post(url,dict(irsg_button='Yes'))
|
r = self.client.post(url,dict(irsg_button='Yes'))
|
||||||
self.assertEqual(r.status_code, 302)
|
self.assertEqual(r.status_code, 302)
|
||||||
self.assertIsNone(draft.ballot_open('irsg-approve'))
|
self.assertIsNone(draft.ballot_open('irsg-approve'))
|
||||||
|
|
||||||
self.assertEqual(len(outbox), 0)
|
# Closing the ballot should have generated a notification
|
||||||
|
self.assertEqual(len(outbox), 1)
|
||||||
|
msg = outbox[0]
|
||||||
|
self.assertIn('IRSG ballot closed', msg['Subject'])
|
||||||
|
self.assertIn('iesg-secretary@ietf.org', msg['From'])
|
||||||
|
# Notifications are also sent to various doc-related addresses, not tested here
|
||||||
|
self.assertIn('irsg@irtf.org', msg['To'])
|
||||||
|
self.assertIn('irtf-chair@irtf.org', msg['CC'])
|
||||||
|
|
||||||
def test_view_outstanding_ballots(self):
|
def test_view_outstanding_ballots(self):
|
||||||
draft = RgDraftFactory()
|
draft = RgDraftFactory()
|
||||||
|
|
|
@ -235,7 +235,7 @@ def default_review_text(group, charter, by):
|
||||||
|
|
||||||
def generate_issue_ballot_mail(request, doc, ballot):
|
def generate_issue_ballot_mail(request, doc, ballot):
|
||||||
|
|
||||||
addrs=gather_address_lists('ballot_issued',doc=doc).as_strings()
|
addrs=gather_address_lists('iesg_ballot_issued',doc=doc).as_strings()
|
||||||
|
|
||||||
return render_to_string("doc/charter/issue_ballot_mail.txt",
|
return render_to_string("doc/charter/issue_ballot_mail.txt",
|
||||||
dict(doc=doc,
|
dict(doc=doc,
|
||||||
|
|
|
@ -26,7 +26,7 @@ from ietf.doc.utils import ( add_state_change_event, close_ballot, close_open_ba
|
||||||
from ietf.doc.mails import ( email_ballot_deferred, email_ballot_undeferred,
|
from ietf.doc.mails import ( email_ballot_deferred, email_ballot_undeferred,
|
||||||
extra_automation_headers, generate_last_call_announcement,
|
extra_automation_headers, generate_last_call_announcement,
|
||||||
generate_issue_ballot_mail, generate_ballot_writeup, generate_ballot_rfceditornote,
|
generate_issue_ballot_mail, generate_ballot_writeup, generate_ballot_rfceditornote,
|
||||||
generate_approval_mail )
|
generate_approval_mail, email_irsg_ballot_closed, email_irsg_ballot_issued )
|
||||||
from ietf.doc.lastcall import request_last_call
|
from ietf.doc.lastcall import request_last_call
|
||||||
from ietf.iesg.models import TelechatDate
|
from ietf.iesg.models import TelechatDate
|
||||||
from ietf.ietfauth.utils import has_role, role_required, is_authorized_in_doc_stream
|
from ietf.ietfauth.utils import has_role, role_required, is_authorized_in_doc_stream
|
||||||
|
@ -635,7 +635,7 @@ def ballot_writeupnotes(request, name):
|
||||||
|
|
||||||
msg = generate_issue_ballot_mail(request, doc, ballot)
|
msg = generate_issue_ballot_mail(request, doc, ballot)
|
||||||
|
|
||||||
addrs = gather_address_lists('ballot_issued',doc=doc).as_strings()
|
addrs = gather_address_lists('iesg_ballot_issued',doc=doc).as_strings()
|
||||||
override = {'To':addrs.to}
|
override = {'To':addrs.to}
|
||||||
if addrs.cc:
|
if addrs.cc:
|
||||||
override['CC'] = addrs.cc
|
override['CC'] = addrs.cc
|
||||||
|
@ -1095,6 +1095,8 @@ def issue_irsg_ballot(request, name):
|
||||||
prev_tags = []
|
prev_tags = []
|
||||||
new_tags = []
|
new_tags = []
|
||||||
|
|
||||||
|
email_irsg_ballot_issued(request, doc, ballot=e) # Send notification email
|
||||||
|
|
||||||
if doc.type_id == 'draft':
|
if doc.type_id == 'draft':
|
||||||
new_state = State.objects.get(used=True, type="draft-stream-irtf", slug='irsgpoll')
|
new_state = State.objects.get(used=True, type="draft-stream-irtf", slug='irsgpoll')
|
||||||
|
|
||||||
|
@ -1130,7 +1132,10 @@ def close_irsg_ballot(request, name):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
button = request.POST.get("irsg_button")
|
button = request.POST.get("irsg_button")
|
||||||
if button == 'Yes':
|
if button == 'Yes':
|
||||||
close_ballot(doc, by, "irsg-approve")
|
ballot = close_ballot(doc, by, "irsg-approve")
|
||||||
|
email_irsg_ballot_closed(request,
|
||||||
|
doc=doc,
|
||||||
|
ballot=IRSGBallotDocEvent.objects.get(pk=ballot.pk))
|
||||||
|
|
||||||
return HttpResponseRedirect(doc.get_absolute_url())
|
return HttpResponseRedirect(doc.get_absolute_url())
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ def send_conflict_eval_email(request,review):
|
||||||
doc_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
doc_url = settings.IDTRACKER_BASE_URL+review.get_absolute_url(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
addrs = gather_address_lists('ballot_issued',doc=review).as_strings()
|
addrs = gather_address_lists('iesg_ballot_issued',doc=review).as_strings()
|
||||||
override = {'To':addrs.to}
|
override = {'To':addrs.to}
|
||||||
if addrs.cc:
|
if addrs.cc:
|
||||||
override['Cc']=addrs.cc
|
override['Cc']=addrs.cc
|
||||||
|
|
|
@ -129,7 +129,7 @@ def send_status_change_eval_email(request,doc):
|
||||||
doc_url = settings.IDTRACKER_BASE_URL+doc.get_absolute_url(),
|
doc_url = settings.IDTRACKER_BASE_URL+doc.get_absolute_url(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
addrs = gather_address_lists('ballot_issued',doc=doc)
|
addrs = gather_address_lists('iesg_ballot_issued',doc=doc)
|
||||||
override = {'To':addrs.to }
|
override = {'To':addrs.to }
|
||||||
if addrs.cc:
|
if addrs.cc:
|
||||||
override['Cc'] = addrs.cc
|
override['Cc'] = addrs.cc
|
||||||
|
|
57
ietf/mailtrigger/migrations/0016_add_irsg_ballot_issued.py
Normal file
57
ietf/mailtrigger/migrations/0016_add_irsg_ballot_issued.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Copyright The IETF Trust 2019-2020, All Rights Reserved
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def replace_mailtrigger(MailTrigger, old_slug, new_slug):
|
||||||
|
"""Replace a MailTrigger with an equivalent using a different slug"""
|
||||||
|
# Per 0013_add_irsg_ballot_saved.py, can't just modify the existing because that
|
||||||
|
# will lose the many-to-many relations.
|
||||||
|
orig_mailtrigger = MailTrigger.objects.get(slug=old_slug)
|
||||||
|
new_mailtrigger = MailTrigger.objects.create(slug=new_slug)
|
||||||
|
new_mailtrigger.to.set(orig_mailtrigger.to.all())
|
||||||
|
new_mailtrigger.cc.set(orig_mailtrigger.cc.all())
|
||||||
|
new_mailtrigger.desc = orig_mailtrigger.desc
|
||||||
|
new_mailtrigger.save()
|
||||||
|
orig_mailtrigger.delete() # get rid of the obsolete MailTrigger
|
||||||
|
|
||||||
|
|
||||||
|
def forward(apps, schema_editor):
|
||||||
|
"""Forward migration: create irsg_ballot_issued and rename ballot_issued to iesg_ballot_issued"""
|
||||||
|
# Load historical models
|
||||||
|
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
|
||||||
|
Recipient = apps.get_model('mailtrigger', 'Recipient')
|
||||||
|
|
||||||
|
# Create the new MailTrigger
|
||||||
|
irsg_ballot_issued = MailTrigger.objects.create(
|
||||||
|
slug='irsg_ballot_issued',
|
||||||
|
desc='Recipients when a new IRSG ballot is issued',
|
||||||
|
)
|
||||||
|
irsg_ballot_issued.to.set(Recipient.objects.filter(slug='irsg'))
|
||||||
|
irsg_ballot_issued.cc.set(Recipient.objects.filter(slug__in=[
|
||||||
|
'doc_stream_manager', 'doc_affecteddoc_authors', 'doc_affecteddoc_group_chairs',
|
||||||
|
'doc_affecteddoc_notify', 'doc_authors', 'doc_group_chairs', 'doc_group_mail_list',
|
||||||
|
'doc_notify', 'doc_shepherd'
|
||||||
|
]))
|
||||||
|
|
||||||
|
# Replace existing 'ballot_issued' object with an 'iesg_ballot_issued'
|
||||||
|
replace_mailtrigger(MailTrigger, 'ballot_issued', 'iesg_ballot_issued')
|
||||||
|
|
||||||
|
|
||||||
|
def reverse(apps, shema_editor):
|
||||||
|
"""Reverse migration: rename iesg_ballot_issued to ballot_issued and remove irsg_ballot_issued"""
|
||||||
|
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
|
||||||
|
MailTrigger.objects.filter(slug='irsg_ballot_issued').delete()
|
||||||
|
replace_mailtrigger(MailTrigger, 'iesg_ballot_issued', 'ballot_issued')
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('mailtrigger', '0015_add_ad_approved_status_change'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(forward, reverse),
|
||||||
|
]
|
|
@ -3285,18 +3285,6 @@
|
||||||
"model": "mailtrigger.mailtrigger",
|
"model": "mailtrigger.mailtrigger",
|
||||||
"pk": "ballot_ednote_changed_late"
|
"pk": "ballot_ednote_changed_late"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fields": {
|
|
||||||
"cc": [],
|
|
||||||
"desc": "Recipients when a ballot is issued",
|
|
||||||
"to": [
|
|
||||||
"iesg",
|
|
||||||
"iesg_secretary"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"model": "mailtrigger.mailtrigger",
|
|
||||||
"pk": "ballot_issued"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"cc": [],
|
"cc": [],
|
||||||
|
@ -3707,6 +3695,18 @@
|
||||||
"model": "mailtrigger.mailtrigger",
|
"model": "mailtrigger.mailtrigger",
|
||||||
"pk": "group_personnel_change"
|
"pk": "group_personnel_change"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"cc": [],
|
||||||
|
"desc": "Recipients when a ballot is issued",
|
||||||
|
"to": [
|
||||||
|
"iesg",
|
||||||
|
"iesg_secretary"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"model": "mailtrigger.mailtrigger",
|
||||||
|
"pk": "iesg_ballot_issued"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"cc": [
|
"cc": [
|
||||||
|
@ -3819,6 +3819,27 @@
|
||||||
"model": "mailtrigger.mailtrigger",
|
"model": "mailtrigger.mailtrigger",
|
||||||
"pk": "ipr_posting_confirmation"
|
"pk": "ipr_posting_confirmation"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"cc": [
|
||||||
|
"doc_affecteddoc_authors",
|
||||||
|
"doc_affecteddoc_group_chairs",
|
||||||
|
"doc_affecteddoc_notify",
|
||||||
|
"doc_authors",
|
||||||
|
"doc_group_chairs",
|
||||||
|
"doc_group_mail_list",
|
||||||
|
"doc_notify",
|
||||||
|
"doc_shepherd",
|
||||||
|
"doc_stream_manager"
|
||||||
|
],
|
||||||
|
"desc": "Recipients when a new IRSG ballot is issued",
|
||||||
|
"to": [
|
||||||
|
"irsg"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"model": "mailtrigger.mailtrigger",
|
||||||
|
"pk": "irsg_ballot_issued"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
"cc": [
|
"cc": [
|
||||||
|
|
3
ietf/templates/doc/mail/close_irsg_ballot_mail.txt
Normal file
3
ietf/templates/doc/mail/close_irsg_ballot_mail.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{% 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 %}
|
||||||
|
|
||||||
|
{% endautoescape%}
|
7
ietf/templates/doc/mail/issue_irsg_ballot_mail.txt
Normal file
7
ietf/templates/doc/mail/issue_irsg_ballot_mail.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{% load ietf_filters %}{% load mail_filters %}{% autoescape off %}{% filter wordwrap:78 %}Evaluation for {{ doc.file_tag }} can be found at {{ doc_url }}
|
||||||
|
|
||||||
|
Ballot expire{% if ballot_expired %}d{% else %}s{% endif %} on: {{ ballot_duedate }}
|
||||||
|
{% endfilter %}
|
||||||
|
{% filter wordwrap:78 %}{{ needed_ballot_positions }}{% endfilter %}
|
||||||
|
|
||||||
|
{% endautoescape%}
|
Loading…
Reference in a new issue