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:
parent
fff927b085
commit
24140fac50
|
@ -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 = []
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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),
|
||||
]
|
|
@ -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),
|
||||
]
|
|
@ -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": [
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
Loading…
Reference in a new issue