Merged in [18616] from rjsparks@nostrum.com:

Email requesters when an interim moves into the announcement queue. Partially addresses #3016 and #3099.
 - Legacy-Id: 18630
Note: SVN reference [18616] has been migrated to Git commit ff3f3bd99a
This commit is contained in:
Henrik Levkowetz 2020-10-23 18:00:27 +00:00
commit 517ebfa15b
6 changed files with 154 additions and 27 deletions

View file

@ -0,0 +1,41 @@
# 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')
interim_approved = MailTrigger.objects.get(slug='interim_approved')
interim_approved.desc = 'Recipients when an interim meeting is approved'
interim_approved.save()
interim_approved.to.set(Recipient.objects.filter(slug__in=('group_chairs','logged_in_person')))
interim_announce_requested = MailTrigger.objects.create(
slug='interim_announce_requested',
desc='Recipients when an interim announcement is requested',
)
interim_announce_requested.to.set(Recipient.objects.filter(slug='iesg_secretary'))
def reverse(apps,schema_editor):
MailTrigger = apps.get_model('mailtrigger', 'MailTrigger')
Recipient = apps.get_model('mailtrigger', 'Recipient')
MailTrigger.objects.filter(slug='interim_announce_requested').delete()
interim_approved = MailTrigger.objects.get(slug='interim_approved')
interim_approved.desc = 'Recipients when an interim meeting is approved and an announcement needs to be sent'
interim_approved.save()
interim_approved.to.set(Recipient.objects.filter(slug='iesg_secretary'))
class Migration(migrations.Migration):
dependencies = [
('mailtrigger', '0017_lc_to_yang_doctors'),
]
operations = [
migrations.RunPython(forward,reverse)
]

View file

@ -552,13 +552,31 @@ def send_interim_approval_request(meetings):
context, context,
cc=cc_list) cc=cc_list)
def send_interim_approval(user, meeting):
"""Send an email to chairs and whoever initiated the action that resulted in approval that an interim is approved"""
first_session = meeting.session_set.first()
(to_email,cc_list) = gather_address_lists('interim_approved',group=first_session.group,person=user.person)
from_email = (settings.SESSION_REQUEST_FROM_EMAIL)
subject = f'{meeting.number} interim approved'
template = 'meeting/interim_approval.txt'
context = {
'meeting': meeting,
}
send_mail(None,
to_email,
from_email,
subject,
template,
context,
cc=cc_list)
def send_interim_announcement_request(meeting): def send_interim_announcement_request(meeting):
"""Sends an email to the secretariat that an interim meeting is ready for """Sends an email to the secretariat that an interim meeting is ready for
announcement, includes the link to send the official announcement""" announcement, includes the link to send the official announcement"""
first_session = meeting.session_set.first() first_session = meeting.session_set.first()
group = first_session.group group = first_session.group
requester = session_requested_by(first_session) requester = session_requested_by(first_session)
(to_email, cc_list) = gather_address_lists('interim_approved') (to_email, cc_list) = gather_address_lists('interim_announce_requested')
from_email = (settings.SESSION_REQUEST_FROM_EMAIL) from_email = (settings.SESSION_REQUEST_FROM_EMAIL)
subject = '{group} - interim meeting ready for announcement'.format(group=group.acronym) subject = '{group} - interim meeting ready for announcement'.format(group=group.acronym)
template = 'meeting/interim_announcement_request.txt' template = 'meeting/interim_announcement_request.txt'

View file

@ -2429,7 +2429,7 @@ class InterimTests(TestCase):
count = person.role_set.filter(name='chair',group__type__in=('wg', 'rg'), group__state__in=('active', 'proposed')).count() count = person.role_set.filter(name='chair',group__type__in=('wg', 'rg'), group__state__in=('active', 'proposed')).count()
self.assertEqual(count, len(q("#id_group option")) - 1) # -1 for options placeholder self.assertEqual(count, len(q("#id_group option")) - 1) # -1 for options placeholder
def do_interim_request_single_virtual(self): def do_interim_request_single_virtual(self, emails_expected):
make_meeting_test_data() make_meeting_test_data()
group = Group.objects.get(acronym='mars') group = Group.objects.get(acronym='mars')
date = datetime.date.today() + datetime.timedelta(days=30) date = datetime.date.today() + datetime.timedelta(days=30)
@ -2479,13 +2479,13 @@ class InterimTests(TestCase):
doc = session.materials.first() doc = session.materials.first()
path = os.path.join(doc.get_file_path(),doc.filename_with_rev()) path = os.path.join(doc.get_file_path(),doc.filename_with_rev())
self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path))
# check notice to secretariat # check notices to secretariat and chairs
self.assertEqual(len(outbox), length_before + 1) self.assertEqual(len(outbox), length_before + emails_expected)
return meeting return meeting
@override_settings(VIRTUAL_INTERIMS_REQUIRE_APPROVAL = True) @override_settings(VIRTUAL_INTERIMS_REQUIRE_APPROVAL = True)
def test_interim_request_single_virtual_settings_approval_required(self): def test_interim_request_single_virtual_settings_approval_required(self):
meeting = self.do_interim_request_single_virtual() meeting = self.do_interim_request_single_virtual(emails_expected=1)
self.assertEqual(meeting.session_set.last().schedulingevent_set.last().status_id,'apprw') self.assertEqual(meeting.session_set.last().schedulingevent_set.last().status_id,'apprw')
self.assertIn('New Interim Meeting Request', outbox[-1]['Subject']) self.assertIn('New Interim Meeting Request', outbox[-1]['Subject'])
self.assertIn('session-request@ietf.org', outbox[-1]['To']) self.assertIn('session-request@ietf.org', outbox[-1]['To'])
@ -2493,7 +2493,7 @@ class InterimTests(TestCase):
@override_settings(VIRTUAL_INTERIMS_REQUIRE_APPROVAL = False) @override_settings(VIRTUAL_INTERIMS_REQUIRE_APPROVAL = False)
def test_interim_request_single_virtual_settings_approval_not_required(self): def test_interim_request_single_virtual_settings_approval_not_required(self):
meeting = self.do_interim_request_single_virtual() meeting = self.do_interim_request_single_virtual(emails_expected=2)
self.assertEqual(meeting.session_set.last().schedulingevent_set.last().status_id,'scheda') self.assertEqual(meeting.session_set.last().schedulingevent_set.last().status_id,'scheda')
self.assertIn('iesg-secretary@ietf.org', outbox[-1]['To']) self.assertIn('iesg-secretary@ietf.org', outbox[-1]['To'])
self.assertIn('interim meeting ready for announcement', outbox[-1]['Subject']) self.assertIn('interim meeting ready for announcement', outbox[-1]['Subject'])
@ -3746,12 +3746,16 @@ class HasMeetingsTests(TestCase):
'session_set-MIN_NUM_FORMS':0, 'session_set-MIN_NUM_FORMS':0,
'session_set-MAX_NUM_FORMS':1000} 'session_set-MAX_NUM_FORMS':1000}
empty_outbox()
r = self.client.post(urlreverse("ietf.meeting.views.interim_request"),data) r = self.client.post(urlreverse("ietf.meeting.views.interim_request"),data)
self.assertRedirects(r,urlreverse('ietf.meeting.views.upcoming')) self.assertRedirects(r,urlreverse('ietf.meeting.views.upcoming'))
meeting = Meeting.objects.order_by('id').last() meeting = Meeting.objects.order_by('id').last()
self.assertEqual(meeting.type_id,'interim') self.assertEqual(meeting.type_id,'interim')
self.assertEqual(meeting.date,date) self.assertEqual(meeting.date,date)
self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year, group.acronym, next_num)) self.assertEqual(meeting.number,'interim-%s-%s-%s' % (date.year, group.acronym, next_num))
self.assertTrue(len(outbox)>0)
self.assertIn('interim approved',outbox[0]["Subject"])
self.assertIn(user.person.email().address,outbox[0]["To"])
self.client.logout() self.client.logout()

View file

@ -72,6 +72,7 @@ from ietf.meeting.helpers import can_edit_interim_request
from ietf.meeting.helpers import can_request_interim_meeting, get_announcement_initial from ietf.meeting.helpers import can_request_interim_meeting, get_announcement_initial
from ietf.meeting.helpers import sessions_post_save, is_interim_meeting_approved from ietf.meeting.helpers import sessions_post_save, is_interim_meeting_approved
from ietf.meeting.helpers import send_interim_cancellation_notice from ietf.meeting.helpers import send_interim_cancellation_notice
from ietf.meeting.helpers import send_interim_approval
from ietf.meeting.helpers import send_interim_approval_request from ietf.meeting.helpers import send_interim_approval_request
from ietf.meeting.helpers import send_interim_announcement_request from ietf.meeting.helpers import send_interim_announcement_request
from ietf.meeting.utils import finalize, sort_accept_tuple, condition_slide_order from ietf.meeting.utils import finalize, sort_accept_tuple, condition_slide_order
@ -3025,8 +3026,10 @@ def interim_request(request):
if requires_approval: if requires_approval:
send_interim_approval_request(meetings=[meeting]) send_interim_approval_request(meetings=[meeting])
elif not has_role(request.user, 'Secretariat'): else:
send_interim_announcement_request(meeting=meeting) send_interim_approval(request.user, meeting=meeting)
if not has_role(request.user, 'Secretariat'):
send_interim_announcement_request(meeting=meeting)
# series require special handling, each session gets it's own # series require special handling, each session gets it's own
# meeting object we won't see this on edit because series are # meeting object we won't see this on edit because series are
@ -3056,8 +3059,10 @@ def interim_request(request):
if requires_approval: if requires_approval:
send_interim_approval_request(meetings=series) send_interim_approval_request(meetings=series)
elif not has_role(request.user, 'Secretariat'): else:
send_interim_announcement_request(meeting=meeting) send_interim_approval(request.user, meeting=meeting)
if not has_role(request.user, 'Secretariat'):
send_interim_announcement_request(meeting=meeting)
messages.success(request, 'Interim meeting request submitted') messages.success(request, 'Interim meeting request submitted')
return redirect(upcoming) return redirect(upcoming)

View file

@ -3760,6 +3760,17 @@
"model": "mailtrigger.mailtrigger", "model": "mailtrigger.mailtrigger",
"pk": "iesg_ballot_saved" "pk": "iesg_ballot_saved"
}, },
{
"fields": {
"cc": [],
"desc": "Recipients when an interim announcement is requested",
"to": [
"iesg_secretary"
]
},
"model": "mailtrigger.mailtrigger",
"pk": "interim_announce_requested"
},
{ {
"fields": { "fields": {
"cc": [ "cc": [
@ -3777,9 +3788,10 @@
{ {
"fields": { "fields": {
"cc": [], "cc": [],
"desc": "Recipients when an interim meeting is approved and an announcement needs to be sent", "desc": "Recipients when an interim meeting is approved",
"to": [ "to": [
"iesg_secretary" "group_chairs",
"logged_in_person"
] ]
}, },
"model": "mailtrigger.mailtrigger", "model": "mailtrigger.mailtrigger",
@ -10509,6 +10521,17 @@
"model": "name.importantdatename", "model": "name.importantdatename",
"pk": "01cutoff" "pk": "01cutoff"
}, },
{
"fields": {
"default_offset_days": -57,
"desc": "Preliminary BOF proposals requested. To request a BOF, please see instructions on requesting a BOF at https://www.ietf.org/how/bofs/bof-procedures/",
"name": "Preliminary BOF proposals requested",
"order": 0,
"used": true
},
"model": "name.importantdatename",
"pk": "bofproposals"
},
{ {
"fields": { "fields": {
"default_offset_days": -36, "default_offset_days": -36,
@ -10630,6 +10653,17 @@
"model": "name.importantdatename", "model": "name.importantdatename",
"pk": "ietf-108-go-ahead" "pk": "ietf-108-go-ahead"
}, },
{
"fields": {
"default_offset_days": -70,
"desc": "Announcement of whether the meeting will be in-person or online.",
"name": "Online or in-person Announcement",
"order": 0,
"used": true
},
"model": "name.importantdatename",
"pk": "online-announcement"
},
{ {
"fields": { "fields": {
"default_offset_days": -82, "default_offset_days": -82,
@ -11897,6 +11931,26 @@
"model": "name.rolename", "model": "name.rolename",
"pk": "trac-editor" "pk": "trac-editor"
}, },
{
"fields": {
"desc": "Can operate the YangCatalog, change its configuration, and edit its data",
"name": "YangCatalog Administrator",
"order": 0,
"used": true
},
"model": "name.rolename",
"pk": "yc_admin"
},
{
"fields": {
"desc": "Can grant user api rights and browse the YangCatalog directory structure",
"name": "YangCatalog Operator",
"order": 0,
"used": true
},
"model": "name.rolename",
"pk": "yc_operator"
},
{ {
"fields": { "fields": {
"desc": "Audio streaming support", "desc": "Audio streaming support",
@ -12047,6 +12101,16 @@
"model": "name.sessionstatusname", "model": "name.sessionstatusname",
"pk": "notmeet" "pk": "notmeet"
}, },
{
"fields": {
"desc": "",
"name": "Rescheduled",
"order": 0,
"used": true
},
"model": "name.sessionstatusname",
"pk": "resched"
},
{ {
"fields": { "fields": {
"desc": "", "desc": "",
@ -12107,16 +12171,6 @@
"model": "name.slidesubmissionstatusname", "model": "name.slidesubmissionstatusname",
"pk": "rejected" "pk": "rejected"
}, },
{
"fields": {
"desc": "",
"name": "Rescheduled",
"order": 0,
"used": true
},
"model": "name.sessionstatusname",
"pk": "resched"
},
{ {
"fields": { "fields": {
"desc": "", "desc": "",
@ -14954,7 +15008,7 @@
"fields": { "fields": {
"command": "xym", "command": "xym",
"switch": "--version", "switch": "--version",
"time": "2020-07-23T00:12:27.508", "time": "2020-10-09T00:17:03.141",
"used": true, "used": true,
"version": "xym 0.4.8" "version": "xym 0.4.8"
}, },
@ -14965,7 +15019,7 @@
"fields": { "fields": {
"command": "pyang", "command": "pyang",
"switch": "--version", "switch": "--version",
"time": "2020-07-23T00:12:28.886", "time": "2020-10-09T00:17:04.888",
"used": true, "used": true,
"version": "pyang 2.3.2" "version": "pyang 2.3.2"
}, },
@ -14976,7 +15030,7 @@
"fields": { "fields": {
"command": "yanglint", "command": "yanglint",
"switch": "--version", "switch": "--version",
"time": "2020-07-23T00:12:29.140", "time": "2020-10-09T00:17:05.228",
"used": true, "used": true,
"version": "yanglint SO 1.6.7" "version": "yanglint SO 1.6.7"
}, },
@ -14987,9 +15041,9 @@
"fields": { "fields": {
"command": "xml2rfc", "command": "xml2rfc",
"switch": "--version", "switch": "--version",
"time": "2020-07-23T00:12:30.892", "time": "2020-10-09T00:17:07.630",
"used": true, "used": true,
"version": "xml2rfc 2.47.0" "version": "xml2rfc 3.2.1"
}, },
"model": "utils.versioninfo", "model": "utils.versioninfo",
"pk": 4 "pk": 4

View file

@ -0,0 +1,5 @@
{% load ams_filters %}{% load ietf_filters %}
An interim meeting for {{ group.acronym }} has been approved or does not require additional approval.
A message has been sent to the secretariat requesting the interim be announced.
{% include "meeting/interim_info.txt" %}