Skip to content

Commit a8e8b9e

Browse files
nouralmaarjsparks
andauthored
feat: split liaison_statement_posted mailtrigger into outgoing and incoming (#9553)
* fix: add new fixtures and mt slugs * fix: edit mt reverse func * chore: edit multiline and hash comments * fix: adjust migration * chore: remove stray whitespace --------- Co-authored-by: Robert Sparks <[email protected]>
1 parent d1cbdcb commit a8e8b9e

File tree

6 files changed

+217
-91
lines changed

6 files changed

+217
-91
lines changed

ietf/liaisons/mails.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
def send_liaison_by_email(request, liaison):
1515
subject = 'New Liaison Statement, "%s"' % (liaison.title)
1616
from_email = settings.LIAISON_UNIVERSAL_FROM
17-
(to_email, cc) = gather_address_lists('liaison_statement_posted',liaison=liaison)
17+
if liaison.is_outgoing():
18+
(to_email, cc) = gather_address_lists('liaison_statement_posted_outgoing',liaison=liaison)
19+
else:
20+
(to_email, cc) = gather_address_lists('liaison_statement_posted_incoming',liaison=liaison)
1821
1922
body = render_to_string('liaisons/liaison_mail.txt', dict(liaison=liaison))
2023

ietf/liaisons/tests.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -112,61 +112,61 @@ def test_help_pages(self):
112112

113113

114114
class UnitTests(TestCase):
115-
def test_get_cc(self):
116-
from ietf.liaisons.views import get_cc,EMAIL_ALIASES
115+
def test_get_contacts_for_liaison_messages_for_group_primary(self):
116+
from ietf.mailtrigger.utils import get_contacts_for_liaison_messages_for_group_primary,EMAIL_ALIASES
117117

118118
# test IETF
119-
cc = get_cc(Group.objects.get(acronym='ietf'))
119+
cc = get_contacts_for_liaison_messages_for_group_primary(Group.objects.get(acronym='ietf'))
120120
self.assertTrue(EMAIL_ALIASES['IESG'] in cc)
121121
self.assertTrue(EMAIL_ALIASES['IETFCHAIR'] in cc)
122122
# test IAB
123-
cc = get_cc(Group.objects.get(acronym='iab'))
123+
cc = get_contacts_for_liaison_messages_for_group_primary(Group.objects.get(acronym='iab'))
124124
self.assertTrue(EMAIL_ALIASES['IAB'] in cc)
125125
self.assertTrue(EMAIL_ALIASES['IABCHAIR'] in cc)
126126
# test an Area
127127
area = Group.objects.filter(type='area').first()
128-
cc = get_cc(area)
128+
cc = get_contacts_for_liaison_messages_for_group_primary(area)
129129
self.assertTrue(EMAIL_ALIASES['IETFCHAIR'] in cc)
130130
self.assertTrue(contacts_from_roles([area.ad_role()]) in cc)
131131
# test a Working Group
132132
wg = Group.objects.filter(type='wg').first()
133-
cc = get_cc(wg)
133+
cc = get_contacts_for_liaison_messages_for_group_primary(wg)
134134
self.assertTrue(contacts_from_roles([wg.parent.ad_role()]) in cc)
135135
self.assertTrue(contacts_from_roles([wg.get_chair()]) in cc)
136136
# test an SDO
137137
sdo = RoleFactory(name_id='liaiman',group__type_id='sdo',).group
138-
cc = get_cc(sdo)
138+
cc = get_contacts_for_liaison_messages_for_group_primary(sdo)
139139
self.assertTrue(contacts_from_roles([sdo.role_set.filter(name='liaiman').first()]) in cc)
140140
# test a cc_contact role
141141
cc_contact_role = RoleFactory(name_id='liaison_cc_contact', group=sdo)
142-
cc = get_cc(sdo)
142+
cc = get_contacts_for_liaison_messages_for_group_primary(sdo)
143143
self.assertIn(contact_email_from_role(cc_contact_role), cc)
144144

145-
def test_get_contacts_for_group(self):
146-
from ietf.liaisons.views import get_contacts_for_group, EMAIL_ALIASES
145+
def test_get_contacts_for_liaison_messages_for_group_secondary(self):
146+
from ietf.mailtrigger.utils import get_contacts_for_liaison_messages_for_group_secondary,EMAIL_ALIASES
147147

148-
# test explicit
148+
# test explicit group contacts
149149
sdo = GroupFactory(type_id='sdo')
150150
contact_email = RoleFactory(name_id='liaison_contact', group=sdo).email.address
151-
contacts = get_contacts_for_group(sdo)
151+
contacts = get_contacts_for_liaison_messages_for_group_secondary(sdo)
152152
self.assertIsNotNone(contact_email)
153153
self.assertIn(contact_email, contacts)
154154
# test area
155155
area = Group.objects.filter(type='area').first()
156-
contacts = get_contacts_for_group(area)
156+
contacts = get_contacts_for_liaison_messages_for_group_secondary(area)
157157
self.assertTrue(area.ad_role().email.address in contacts)
158158
# test wg
159159
wg = Group.objects.filter(type='wg').first()
160-
contacts = get_contacts_for_group(wg)
160+
contacts = get_contacts_for_liaison_messages_for_group_secondary(wg)
161161
self.assertTrue(wg.get_chair().email.address in contacts)
162162
# test ietf
163-
contacts = get_contacts_for_group(Group.objects.get(acronym='ietf'))
163+
contacts = get_contacts_for_liaison_messages_for_group_secondary(Group.objects.get(acronym='ietf'))
164164
self.assertTrue(EMAIL_ALIASES['IETFCHAIR'] in contacts)
165165
# test iab
166-
contacts = get_contacts_for_group(Group.objects.get(acronym='iab'))
166+
contacts = get_contacts_for_liaison_messages_for_group_secondary(Group.objects.get(acronym='iab'))
167167
self.assertTrue(EMAIL_ALIASES['IABCHAIR'] in contacts)
168168
# test iesg
169-
contacts = get_contacts_for_group(Group.objects.get(acronym='iesg'))
169+
contacts = get_contacts_for_liaison_messages_for_group_secondary(Group.objects.get(acronym='iesg'))
170170
self.assertTrue(EMAIL_ALIASES['IESG'] in contacts)
171171

172172
def test_needs_approval(self):
@@ -786,8 +786,11 @@ def test_add_incoming_liaison(self):
786786
self.assertTrue("Liaison Statement" in outbox[-1]["Subject"])
787787

788788
self.assertTrue('to_contacts@' in outbox[-1]['To'])
789+
self.assertTrue(submitter.email_address(), outbox[-1]['To'])
789790
self.assertTrue('cc@' in outbox[-1]['Cc'])
790791

792+
793+
791794
def test_add_outgoing_liaison(self):
792795
RoleFactory(name_id='liaiman',group__type_id='sdo', person__user__username='ulm-liaiman')
793796
wg = RoleFactory(name_id='chair',person__user__username='marschairman',group__acronym='mars').group
@@ -867,6 +870,8 @@ def test_add_outgoing_liaison(self):
867870
self.assertEqual(len(outbox), mailbox_before + 1)
868871
self.assertTrue("Liaison Statement" in outbox[-1]["Subject"])
869872
self.assertTrue('aread@' in outbox[-1]['To'])
873+
self.assertTrue(submitter.email_address(), outbox[-1]['Cc'])
874+
870875

871876
def test_add_outgoing_liaison_unapproved_post_only(self):
872877
RoleFactory(name_id='liaiman',group__type_id='sdo', person__user__username='ulm-liaiman')

ietf/liaisons/views.py

Lines changed: 6 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,6 @@
2727
from ietf.name.models import LiaisonStatementTagName
2828
from ietf.utils.response import permission_denied
2929

30-
EMAIL_ALIASES = {
31-
"IETFCHAIR": "The IETF Chair <[email protected]>",
32-
"IESG": "The IESG <[email protected]>",
33-
"IAB": "The IAB <[email protected]>",
34-
"IABCHAIR": "The IAB Chair <[email protected]>",
35-
}
36-
37-
3830
# -------------------------------------------------
3931
# Helper Functions
4032
# -------------------------------------------------
@@ -94,64 +86,6 @@ def contacts_from_roles(roles):
9486
emails = [ contact_email_from_role(r) for r in roles ]
9587
return ','.join(emails)
9688

97-
def get_cc(group):
98-
'''Returns list of emails to use as CC for group. Simplified refactor of IETFHierarchy
99-
get_cc() and get_from_cc()
100-
'''
101-
emails = []
102-
103-
# role based CCs
104-
if group.acronym in ('ietf','iesg'):
105-
emails.append(EMAIL_ALIASES['IESG'])
106-
emails.append(EMAIL_ALIASES['IETFCHAIR'])
107-
elif group.acronym in ('iab'):
108-
emails.append(EMAIL_ALIASES['IAB'])
109-
emails.append(EMAIL_ALIASES['IABCHAIR'])
110-
elif group.type_id == 'area':
111-
emails.append(EMAIL_ALIASES['IETFCHAIR'])
112-
ad_roles = group.role_set.filter(name='ad')
113-
emails.extend([ contact_email_from_role(r) for r in ad_roles ])
114-
elif group.type_id == 'wg':
115-
ad_roles = group.parent.role_set.filter(name='ad')
116-
emails.extend([ contact_email_from_role(r) for r in ad_roles ])
117-
chair_roles = group.role_set.filter(name='chair')
118-
emails.extend([ contact_email_from_role(r) for r in chair_roles ])
119-
if group.list_email:
120-
emails.append('{} Discussion List <{}>'.format(group.name,group.list_email))
121-
elif group.type_id == 'sdo':
122-
liaiman_roles = group.role_set.filter(name='liaiman')
123-
emails.extend([ contact_email_from_role(r) for r in liaiman_roles ])
124-
125-
# explicit CCs
126-
liaison_cc_roles = group.role_set.filter(name='liaison_cc_contact')
127-
emails.extend([ contact_email_from_role(r) for r in liaison_cc_roles ])
128-
129-
return emails
130-
131-
def get_contacts_for_group(group):
132-
'''Returns default contacts for groups as a comma separated string'''
133-
# use explicit default contacts if defined
134-
explicit_contacts = contacts_from_roles(group.role_set.filter(name='liaison_contact'))
135-
if explicit_contacts:
136-
return explicit_contacts
137-
138-
# otherwise construct based on group type
139-
contacts = []
140-
if group.type_id == 'area':
141-
roles = group.role_set.filter(name='ad')
142-
contacts.append(contacts_from_roles(roles))
143-
elif group.type_id == 'wg':
144-
roles = group.role_set.filter(name='chair')
145-
contacts.append(contacts_from_roles(roles))
146-
elif group.acronym == 'ietf':
147-
contacts.append(EMAIL_ALIASES['IETFCHAIR'])
148-
elif group.acronym == 'iab':
149-
contacts.append(EMAIL_ALIASES['IABCHAIR'])
150-
elif group.acronym == 'iesg':
151-
contacts.append(EMAIL_ALIASES['IESG'])
152-
153-
return ','.join(contacts)
154-
15589
def get_details_tabs(stmt, selected):
15690
return [
15791
t + (t[0].lower() == selected.lower(),)
@@ -207,6 +141,8 @@ def post_only(group,person):
207141
# -------------------------------------------------
208142
@can_submit_liaison_required
209143
def ajax_get_liaison_info(request):
144+
from ietf.mailtrigger.utils import get_contacts_for_liaison_messages_for_group_primary,get_contacts_for_liaison_messages_for_group_secondary
145+
210146
'''Returns dictionary of info to update entry form given the groups
211147
that have been selected
212148
'''
@@ -229,14 +165,14 @@ def ajax_get_liaison_info(request):
229165
result = {'response_contacts':[],'to_contacts': [], 'cc': [], 'needs_approval': False, 'post_only': False, 'full_list': []}
230166

231167
for group in from_groups:
232-
cc.extend(get_cc(group))
168+
cc.extend(get_contacts_for_liaison_messages_for_group_primary(group))
233169
does_need_approval.append(needs_approval(group,person))
234170
can_post_only.append(post_only(group,person))
235-
response_contacts.append(get_contacts_for_group(group))
171+
response_contacts.append(get_contacts_for_liaison_messages_for_group_secondary(group))
236172

237173
for group in to_groups:
238-
cc.extend(get_cc(group))
239-
to_contacts.append(get_contacts_for_group(group))
174+
cc.extend(get_contacts_for_liaison_messages_for_group_primary(group))
175+
to_contacts.append(get_contacts_for_liaison_messages_for_group_secondary(group))
240176

241177
# if there are from_groups and any need approval
242178
if does_need_approval:
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright The IETF Trust 2025, All Rights Reserved
2+
3+
from django.db import migrations
4+
5+
6+
def forward(apps, schema_editor):
7+
Mailtrigger = apps.get_model("mailtrigger", "MailTrigger")
8+
Recipient = apps.get_model("mailtrigger", "Recipient")
9+
recipients_to = Recipient.objects.get(pk="liaison_to_contacts")
10+
recipients_cc = list(
11+
Recipient.objects.filter(
12+
slug__in=(
13+
"liaison_cc",
14+
"liaison_coordinators",
15+
"liaison_response_contacts",
16+
"liaison_technical_contacts",
17+
)
18+
)
19+
)
20+
recipient_from = Recipient.objects.get(pk="liaison_from_contact")
21+
22+
liaison_posted_outgoing = Mailtrigger.objects.create(
23+
slug="liaison_statement_posted_outgoing",
24+
desc="Recipients for a message when a new outgoing liaison statement is posted",
25+
)
26+
liaison_posted_outgoing.to.add(recipients_to)
27+
liaison_posted_outgoing.cc.add(*recipients_cc)
28+
liaison_posted_outgoing.cc.add(recipient_from)
29+
30+
liaison_posted_incoming = Mailtrigger.objects.create(
31+
slug="liaison_statement_posted_incoming",
32+
desc="Recipients for a message when a new incoming liaison statement is posted",
33+
)
34+
liaison_posted_incoming.to.add(recipients_to)
35+
liaison_posted_incoming.cc.add(*recipients_cc)
36+
37+
Mailtrigger.objects.filter(slug=("liaison_statement_posted")).delete()
38+
39+
40+
def reverse(apps, schema_editor):
41+
Mailtrigger = apps.get_model("mailtrigger", "MailTrigger")
42+
Recipient = apps.get_model("mailtrigger", "Recipient")
43+
44+
Mailtrigger.objects.filter(
45+
slug__in=(
46+
"liaison_statement_posted_outgoing",
47+
"liaison_statement_posted_incoming",
48+
)
49+
).delete()
50+
51+
liaison_statement_posted = Mailtrigger.objects.create(
52+
slug="liaison_statement_posted",
53+
desc="Recipients for a message when a new liaison statement is posted",
54+
)
55+
56+
liaison_to_contacts = Recipient.objects.get(slug="liaison_to_contacts")
57+
recipients_ccs = Recipient.objects.filter(
58+
slug__in=(
59+
"liaison_cc",
60+
"liaison_coordinators",
61+
"liaison_response_contacts",
62+
"liaison_technical_contacts",
63+
)
64+
)
65+
liaison_statement_posted.to.add(liaison_to_contacts)
66+
liaison_statement_posted.cc.add(*recipients_ccs)
67+
68+
69+
class Migration(migrations.Migration):
70+
dependencies = [("mailtrigger", "0007_historicalrecipient_historicalmailtrigger")]
71+
72+
operations = [migrations.RunPython(forward, reverse)]

ietf/mailtrigger/utils.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
from ietf.utils.mail import excludeaddrs
1010

1111

12+
EMAIL_ALIASES = {
13+
"IETFCHAIR": "The IETF Chair <[email protected]>",
14+
"IESG": "The IESG <[email protected]>",
15+
"IAB": "The IAB <[email protected]>",
16+
"IABCHAIR": "The IAB Chair <[email protected]>",
17+
}
18+
19+
1220
class AddrLists(namedtuple("AddrLists", ["to", "cc"])):
1321
__slots__ = ()
1422

@@ -66,6 +74,69 @@ def get_mailtrigger(slug, create_from_slug_if_not_exists, desc_if_not_exists):
6674
return mailtrigger
6775

6876

77+
def get_contacts_for_liaison_messages_for_group_primary(group):
78+
from ietf.liaisons.views import contact_email_from_role
79+
80+
'''Returns list of emails to use in liaison message for group
81+
'''
82+
emails = []
83+
84+
# role based emails
85+
if group.acronym in ('ietf','iesg'):
86+
emails.append(EMAIL_ALIASES['IESG'])
87+
emails.append(EMAIL_ALIASES['IETFCHAIR'])
88+
elif group.acronym in ('iab'):
89+
emails.append(EMAIL_ALIASES['IAB'])
90+
emails.append(EMAIL_ALIASES['IABCHAIR'])
91+
elif group.type_id == 'area':
92+
emails.append(EMAIL_ALIASES['IETFCHAIR'])
93+
ad_roles = group.role_set.filter(name='ad')
94+
emails.extend([ contact_email_from_role(r) for r in ad_roles ])
95+
elif group.type_id == 'wg':
96+
ad_roles = group.parent.role_set.filter(name='ad')
97+
emails.extend([ contact_email_from_role(r) for r in ad_roles ])
98+
chair_roles = group.role_set.filter(name='chair')
99+
emails.extend([ contact_email_from_role(r) for r in chair_roles ])
100+
if group.list_email:
101+
emails.append('{} Discussion List <{}>'.format(group.name,group.list_email))
102+
elif group.type_id == 'sdo':
103+
liaiman_roles = group.role_set.filter(name='liaiman')
104+
emails.extend([ contact_email_from_role(r) for r in liaiman_roles ])
105+
106+
# explicit CCs
107+
liaison_cc_roles = group.role_set.filter(name='liaison_cc_contact')
108+
emails.extend([ contact_email_from_role(r) for r in liaison_cc_roles ])
109+
110+
return emails
111+
112+
113+
def get_contacts_for_liaison_messages_for_group_secondary(group):
114+
from ietf.liaisons.views import contacts_from_roles
115+
116+
'''Returns default contacts for groups as a comma separated string'''
117+
# use explicit default contacts if defined
118+
explicit_contacts = contacts_from_roles(group.role_set.filter(name='liaison_contact'))
119+
if explicit_contacts:
120+
return explicit_contacts
121+
122+
# otherwise construct based on group type
123+
contacts = []
124+
if group.type_id == 'area':
125+
roles = group.role_set.filter(name='ad')
126+
contacts.append(contacts_from_roles(roles))
127+
elif group.type_id == 'wg':
128+
roles = group.role_set.filter(name='chair')
129+
contacts.append(contacts_from_roles(roles))
130+
elif group.acronym == 'ietf':
131+
contacts.append(EMAIL_ALIASES['IETFCHAIR'])
132+
elif group.acronym == 'iab':
133+
contacts.append(EMAIL_ALIASES['IABCHAIR'])
134+
elif group.acronym == 'iesg':
135+
contacts.append(EMAIL_ALIASES['IESG'])
136+
137+
return ','.join(contacts)
138+
139+
69140
def gather_relevant_expansions(**kwargs):
70141
def starts_with(prefix):
71142
return MailTrigger.objects.filter(slug__startswith=prefix).values_list(

0 commit comments

Comments
 (0)