Notify secretariat when conflict review/status change doc enters an announcement pending state. Fixes #2962. Commit ready for merge.

- Legacy-Id: 18159
This commit is contained in:
Jennifer Richards 2020-07-13 16:11:58 +00:00
parent fff927b085
commit 24140fac50
10 changed files with 262 additions and 4 deletions

View file

@ -46,7 +46,36 @@ def email_ad_approved_doc(request, doc, text):
dict(text=text,
docname=doc.filename_with_rev()),
bcc=bcc)
def email_ad_approved_conflict_review(request, review, ok_to_publish):
"""Email notification when AD approves a conflict review"""
conflictdoc = review.relateddocument_set.get(relationship__slug='conflrev').target.document
(to, cc) = gather_address_lists("ad_approved_conflict_review")
frm = request.user.person.formatted_email()
send_mail(request,
to,
frm,
"Approved: %s" % review.title,
"doc/conflict_review/ad_approval_pending_email.txt",
dict(ok_to_publish=ok_to_publish,
review=review,
conflictdoc=conflictdoc),
cc=cc)
def email_ad_approved_status_change(request, status_change, related_doc_info):
"""Email notification when AD approves a status change"""
(to, cc) = gather_address_lists("ad_approved_status_change")
frm = request.user.person.formatted_email()
send_mail(request,
to,
frm,
"Approved: %s" % status_change.title,
"doc/status_change/ad_approval_pending_email.txt",
dict(
related_doc_info=related_doc_info
),
cc=cc)
def email_stream_changed(request, doc, old_stream, new_stream, text=""):
"""Email the change text to the notify group and to the stream chairs"""
streams = []

View file

@ -306,13 +306,66 @@ class ConflictReviewTests(TestCase):
else:
self.assertIn( 'NOT be published', ''.join(wrap(get_payload_text(outbox[0]), 2**16)))
def test_approve_reqnopub(self):
"""Test secretariat approving a conf review FROM the appr-reqnopub-pend state"""
self.approve_test_helper('appr-reqnopub')
def test_approve_noprob(self):
"""Test secretariat approving a conf review FROM the appr-reqnopub-pend state"""
self.approve_test_helper('appr-noprob')
def approval_pend_notice_test_helper(self, approve_type, role):
"""Test notification email when review state changed to a -pend state
Sets up, clears outbox, and changes state. If notifications are sent,
asserts basic properties common to all approve_types.
Caller should inspect outbox to access notifications if any.
"""
doc = Document.objects.get(name='conflict-review-imaginary-irtf-submission')
url = urlreverse('ietf.doc.views_conflict_review.change_state',kwargs=dict(name=doc.name))
login_testing_unauthorized(self, role, url)
empty_outbox()
# Issue the request
pending_pk = str(State.objects.get(used=True,
slug=approve_type+'-pend',
type__slug='conflrev').pk)
r = self.client.post(url,dict(review_state=pending_pk,comment='some comment or other'))
# Check the results
self.assertEqual(r.status_code, 302)
# If we received a notification, check the common features for all approve_types
if len(outbox) > 0:
notification = outbox[0]
self.assertIn(doc.title, notification['Subject'])
self.assertIn('iesg-secretary@ietf.org', notification['To'])
self.assertTrue(notification['Subject'].startswith('Approved:'))
def test_approval_pend_notice_ad_reqnopub(self):
"""Test notification email when AD puts doc in appr-reqnopub-pend state"""
self.approval_pend_notice_test_helper('appr-reqnopub', 'ad')
self.assertEqual(len(outbox), 1)
self.assertIn('NOT be published', get_payload_text(outbox[0]))
def test_no_approval_pend_notice_secr_reqnopub(self):
"""Test notification email when secretariat puts doc in appr-reqnopub-pend state"""
self.approval_pend_notice_test_helper('appr-reqnopub', 'secretariat')
self.assertEqual(len(outbox), 0) # no notification should be sent
def test_approval_pend_notice_ad_noprob(self):
"""Test notification email when AD puts doc in appr-noprob-pend state"""
self.approval_pend_notice_test_helper('appr-noprob', 'ad')
self.assertEqual(len(outbox), 1)
self.assertIn('IESG has no problem', get_payload_text(outbox[0]))
def test_no_approval_pend_notice_secr_noprob(self):
"""Test notification email when secretariat puts doc in appr-noprob-pend state"""
self.approval_pend_notice_test_helper('appr-noprob', 'secretariat')
self.assertEqual(len(outbox), 0)
def setUp(self):
IndividualDraftFactory(name='draft-imaginary-independent-submission')
ConflictReviewFactory(name='conflict-review-imaginary-irtf-submission',review_of=IndividualDraftFactory(name='draft-imaginary-irtf-submission',stream_id='irtf'),notify='notifyme@example.net')

View file

@ -23,7 +23,7 @@ from ietf.doc.views_status_change import default_approval_text
from ietf.group.models import Person
from ietf.iesg.models import TelechatDate
from ietf.utils.test_utils import TestCase
from ietf.utils.mail import outbox
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
from ietf.utils.test_utils import login_testing_unauthorized
@ -335,6 +335,52 @@ class StatusChangeTests(TestCase):
self.assertTrue(doc.latest_event(DocEvent,type="added_comment").desc.startswith('The following approval message was sent'))
def approval_pend_notice_test_helper(self, role):
"""Test notification email when review state changed to the appr-pend state"""
doc = Document.objects.get(name='status-change-imaginary-mid-review')
url = urlreverse('ietf.doc.views_status_change.change_state',kwargs=dict(name=doc.name))
# Add some status change related documents
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9999'),relationship_id='tois')
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc9998'),relationship_id='tohist')
# And a non-status change related document
doc.relateddocument_set.create(target=DocAlias.objects.get(name='rfc14'),relationship_id='updates')
login_testing_unauthorized(self, role, url)
empty_outbox()
# Issue the request
appr_pend_pk = str(State.objects.get(used=True,
slug='appr-pend',
type__slug='statchg').pk)
r = self.client.post(url,dict(new_state=appr_pend_pk,comment='some comment or other'))
# Check the results
self.assertEqual(r.status_code, 302)
if role == 'ad':
self.assertEqual(len(outbox), 1)
notification = outbox[0]
self.assertIn(doc.title, notification['Subject'])
self.assertIn('iesg-secretary@ietf.org', notification['To'])
self.assertTrue(notification['Subject'].startswith('Approved:'))
notification_text = get_payload_text(notification)
self.assertIn('The AD has approved changing the status', notification_text)
self.assertIn(DocAlias.objects.get(name='rfc9999').document.canonical_name(), notification_text)
self.assertIn(DocAlias.objects.get(name='rfc9998').document.canonical_name(), notification_text)
self.assertNotIn(DocAlias.objects.get(name='rfc14').document.canonical_name(), notification_text)
self.assertNotIn('No value found for', notification_text) # make sure all interpolation values were set
else:
self.assertEqual(len(outbox), 0)
def test_approval_pend_notice_ad(self):
"""Test that an approval notice is sent to secretariat when AD approves status change"""
self.approval_pend_notice_test_helper('ad')
def test_no_approval_pend_notice_secr(self):
"""Test that no approval notice is sent when secretariat approves status change"""
self.approval_pend_notice_test_helper('secretariat')
def test_edit_relations(self):
doc = Document.objects.get(name='status-change-imaginary-mid-review')
url = urlreverse('ietf.doc.views_status_change.edit_relations',kwargs=dict(name=doc.name))

View file

@ -19,7 +19,7 @@ from ietf.doc.models import ( BallotDocEvent, BallotPositionDocEvent, DocAlias,
Document, NewRevisionDocEvent, State )
from ietf.doc.utils import ( add_state_change_event, close_open_ballots,
create_ballot_if_not_open, update_telechat )
from ietf.doc.mails import email_iana
from ietf.doc.mails import email_iana, email_ad_approved_conflict_review
from ietf.doc.forms import AdForm
from ietf.group.models import Role, Group
from ietf.iesg.models import TelechatDate
@ -79,6 +79,15 @@ def change_state(request, name, option=None):
pos.save()
# Consider mailing that position to 'iesg_ballot_saved'
send_conflict_eval_email(request,review)
elif (new_state.slug in ("appr-reqnopub-pend", "appr-noprob-pend")
and has_role(request.user, "Area Director")):
if new_state.slug == "appr-noprob-pend":
ok_to_publish = True
else:
ok_to_publish = False
email_ad_approved_conflict_review(request,
review,
ok_to_publish)
return redirect('ietf.doc.views_doc.document_main', name=review.name)

View file

@ -18,6 +18,7 @@ from django.conf import settings
from django.utils.encoding import force_text
import debug # pyflakes:ignore
from ietf.doc.mails import email_ad_approved_status_change
from ietf.doc.models import ( Document, DocAlias, State, DocEvent, BallotDocEvent,
BallotPositionDocEvent, NewRevisionDocEvent, WriteupDocEvent, STATUSCHANGE_RELATIONS )
@ -90,6 +91,21 @@ def change_state(request, name, option=None):
dict(doc=status_change,
url = status_change.get_absolute_url(),
))
elif new_state.slug == 'appr-pend' and has_role(request.user, "Area Director"):
related_docs = status_change.relateddocument_set.filter(
relationship__slug__in=STATUSCHANGE_RELATIONS
)
related_doc_info = [
dict(title=rel_doc.target.document.title,
canonical_name=rel_doc.target.document.canonical_name(),
newstatus=newstatus(rel_doc))
for rel_doc in related_docs
]
email_ad_approved_status_change(
request,
status_change,
related_doc_info=related_doc_info,
)
return redirect('ietf.doc.views_doc.document_main', name=status_change.name)
else:

View file

@ -0,0 +1,31 @@
# Copyright The IETF Trust 2020, All Rights Reserved
from django.db import migrations
def forward(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
Recipient = apps.get_model('mailtrigger', 'Recipient')
ad_approved_conflict_review = MailTrigger.objects.create(
slug='ad_approved_conflict_review',
desc='Recipients when AD approves a conflict review pending announcement',
)
ad_approved_conflict_review.to.add(
Recipient.objects.get(pk='iesg_secretary')
)
def reverse(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
MailTrigger.objects.filter(slug='ad_approved_conflict_review').delete()
class Migration(migrations.Migration):
dependencies = [
('mailtrigger', '0013_add_irsg_ballot_saved'),
]
operations = [
migrations.RunPython(forward, reverse),
]

View file

@ -0,0 +1,31 @@
# Copyright The IETF Trust 2020, All Rights Reserved
from django.db import migrations
def forward(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
Recipient = apps.get_model('mailtrigger', 'Recipient')
ad_approved_conflict_review = MailTrigger.objects.create(
slug='ad_approved_status_change',
desc='Recipients when AD approves a status change pending announcement',
)
ad_approved_conflict_review.to.add(
Recipient.objects.get(pk='iesg_secretary')
)
def reverse(apps, schema_editor):
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
MailTrigger.objects.filter(slug='ad_approved_status_change').delete()
class Migration(migrations.Migration):
dependencies = [
('mailtrigger', '0014_add_ad_approved_conflict_review'),
]
operations = [
migrations.RunPython(forward, reverse),
]

View file

@ -3124,6 +3124,28 @@
"model": "group.groupfeatures",
"pk": "wg"
},
{
"fields": {
"cc": [],
"desc": "Recipients when AD approves a conflict review pending announcement",
"to": [
"iesg_secretary"
]
},
"model": "mailtrigger.mailtrigger",
"pk": "ad_approved_conflict_review"
},
{
"fields": {
"cc": [],
"desc": "Recipients when AD approves a status change pending announcement",
"to": [
"iesg_secretary"
]
},
"model": "mailtrigger.mailtrigger",
"pk": "ad_approved_status_change"
},
{
"fields": {
"cc": [

View file

@ -0,0 +1,12 @@
{% load ietf_filters %}{% load mail_filters %}{% autoescape off %}
{% filter wordwrap:78 %}{{ review.title }} has been approved and the result is ready to announce.
{% if ok_to_publish %}
The IESG has no problem with the publication of '{{ conflictdoc.title }}' {{ conflictdoc.file_tag|safe }} as {{ conflictdoc|std_level_prompt_with_article }}.
{% else %}
The IESG recommends that '{{ conflictdoc.title }}' {{ conflictdoc.file_tag|safe }} NOT be published as {{ conflictdoc|std_level_prompt_with_article }}.
{% endif %}
{% endfilter %}
{% endautoescape %}

View file

@ -0,0 +1,9 @@
{% load ietf_filters %}{% load mail_filters %}{% autoescape off %}
{% filter wordwrap:78 %}The AD has approved changing the status of the following {% if related_doc_info|length == 1 %}document{% else %}documents{% endif %}:
{% for relateddoc in related_doc_info %}- {{relateddoc.title }}
({{relateddoc.canonical_name }}) to {{ relateddoc.newstatus }}
{% endfor %}
An announcement has not yet been sent.
{% endfilter %}
{% endautoescape %}