Skip to content

Commit 1390ae0

Browse files
author
Sasha Romijn
committed
Fix ietf-tools#2354 - Make review_completed configurable per team and review type
This includes a migration to change mailtrigger slugs to be up to 64 characters instead of 32, as some slugs would not fit and require clunky abbreviations. A data migration creates triggers for existing teams, and they are also created on the fly if a trigger does not exist yet, providing a safe fallback for new review teams. The review_completed mailtrigger serves as the template for new triggers. This commit also includes tests for gather_address_lists(), as none existed. Commit ready for merge. - Legacy-Id: 16680
1 parent 88b7b45 commit 1390ae0

7 files changed

Lines changed: 165 additions & 7 deletions

File tree

ietf/doc/views_review.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,17 @@ def complete_review(request, name, assignment_id):
571571
if not (is_reviewer or can_manage_request):
572572
return HttpResponseForbidden("You do not have permission to perform this action")
573573

574-
(to, cc) = gather_address_lists('review_completed', review_req = assignment.review_request)
574+
team_acronym = assignment.review_request.team.acronym.lower()
575+
request_type = assignment.review_request.type
576+
mailtrigger_slug = 'review_completed_{}_{}'.format(team_acronym, request_type.slug)
577+
# Description is only used if the mailtrigger does not exist yet.
578+
mailtrigger_desc = 'Recipients when a {} {} review is completed'.format(team_acronym, request_type)
579+
to, cc = gather_address_lists(
580+
mailtrigger_slug,
581+
create_from_slug_if_not_exists='review_completed',
582+
desc_if_not_exists=mailtrigger_desc,
583+
review_req=assignment.review_request
584+
)
575585

576586
if request.method == "POST":
577587
form = CompleteReviewForm(assignment, is_reviewer,
@@ -588,7 +598,7 @@ def complete_review(request, name, assignment_id):
588598
strip_prefix(assignment.review_request.doc.name, "draft-"),
589599
form.cleaned_data["reviewed_rev"],
590600
assignment.review_request.team.acronym,
591-
assignment.review_request.type.slug,
601+
request_type.slug,
592602
xslugify(assignment.reviewer.person.ascii_parts()[3]),
593603
datetime.date.today().isoformat(),
594604
]
@@ -761,7 +771,8 @@ def complete_review(request, name, assignment_id):
761771
}
762772

763773
try:
764-
initial['review_content'] = render_to_string('/group/%s/review/content_templates/%s.txt' % (assignment.review_request.team.acronym, assignment.review_request.type.slug), {'assignment':assignment,'today':datetime.date.today()})
774+
initial['review_content'] = render_to_string('/group/%s/review/content_templates/%s.txt' % (assignment.review_request.team.acronym,
775+
request_type.slug), {'assignment':assignment, 'today':datetime.date.today()})
765776
except TemplateDoesNotExist:
766777
pass
767778

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright The IETF Trust 2015-2019, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
# Generated by Django 1.11.23 on 2019-08-30 09:02
4+
from __future__ import unicode_literals
5+
6+
from django.db import migrations, models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('mailtrigger', '0007_add_review_mailtriggers'),
13+
]
14+
15+
operations = [
16+
migrations.AlterField(
17+
model_name='mailtrigger',
18+
name='slug',
19+
field=models.CharField(max_length=64, primary_key=True, serialize=False),
20+
),
21+
# The above migration will not update the ManyToMany tables, which also reference
22+
# the mailtrigger pk as varchar(32), so manual SQL is used.
23+
# https://code.djangoproject.com/ticket/25012
24+
migrations.RunSQL(
25+
sql='ALTER TABLE `mailtrigger_mailtrigger_to` MODIFY `mailtrigger_id` varchar(64);',
26+
reverse_sql='ALTER TABLE `mailtrigger_mailtrigger_to` MODIFY `mailtrigger_id` varchar(32);',
27+
),
28+
migrations.RunSQL(
29+
sql='ALTER TABLE `mailtrigger_mailtrigger_cc` MODIFY `mailtrigger_id` varchar(64);',
30+
reverse_sql='ALTER TABLE `mailtrigger_mailtrigger_cc` MODIFY `mailtrigger_id` varchar(32);',
31+
),
32+
]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright The IETF Trust 2019, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
4+
from __future__ import absolute_import, print_function, unicode_literals
5+
6+
from django.db import migrations
7+
8+
def forward(apps, schema_editor):
9+
ReviewTeamSettings = apps.get_model('review', 'ReviewTeamSettings')
10+
MailTrigger = apps.get_model('mailtrigger', 'Mailtrigger')
11+
Group = apps.get_model('group', 'Group')
12+
GroupFeatures = apps.get_model('group', 'GroupFeatures')
13+
14+
template = MailTrigger.objects.get(slug='review_completed')
15+
template.desc = 'Default template for recipients when an review is completed - ' \
16+
'customised mail triggers are used/created per team and review type.'
17+
template.save()
18+
19+
for group in Group.objects.all().only('pk', 'type', 'acronym'):
20+
if not GroupFeatures.objects.get(type=group.type).has_reviews:
21+
continue
22+
try:
23+
review_team = ReviewTeamSettings.objects.get(group=group.pk)
24+
except ReviewTeamSettings.DoesNotExist:
25+
continue
26+
team_acronym = group.acronym.lower()
27+
for review_type in review_team.review_types.all():
28+
slug = 'review_completed_{}_{}'.format(team_acronym, review_type.slug)
29+
desc = 'Recipients when a {} {} review is completed'.format(team_acronym, review_type)
30+
if MailTrigger.objects.filter(slug=slug):
31+
# Never overwrite existing triggers
32+
continue
33+
mailtrigger = MailTrigger.objects.create(slug=slug, desc=desc)
34+
mailtrigger.to.set(template.to.all())
35+
mailtrigger.cc.set(template.cc.all())
36+
37+
38+
def reverse(apps, schema_editor):
39+
pass
40+
41+
42+
class Migration(migrations.Migration):
43+
44+
dependencies = [
45+
('mailtrigger', '0008_lengthen_mailtrigger_slug'),
46+
('review', '0014_document_primary_key_cleanup'),
47+
('group', '0019_rename_field_document2'),
48+
]
49+
50+
operations = [
51+
migrations.RunPython(forward, reverse)
52+
]

ietf/mailtrigger/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def clean_duplicates(addrlist):
3535

3636
@python_2_unicode_compatible
3737
class MailTrigger(models.Model):
38-
slug = models.CharField(max_length=32, primary_key=True)
38+
slug = models.CharField(max_length=64, primary_key=True)
3939
desc = models.TextField(blank=True)
4040
to = models.ManyToManyField('Recipient', blank=True, related_name='used_in_to')
4141
cc = models.ManyToManyField('Recipient', blank=True, related_name='used_in_cc')

ietf/mailtrigger/test_utils.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright The IETF Trust 2015-2019, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
4+
5+
from __future__ import absolute_import, print_function, unicode_literals
6+
7+
from ietf.doc.factories import WgDraftFactory
8+
from ietf.mailtrigger.models import MailTrigger
9+
from .utils import gather_address_lists
10+
from ietf.utils.test_utils import TestCase
11+
import six
12+
13+
14+
class GatherAddressListsTests(TestCase):
15+
def setUp(self):
16+
self.doc = WgDraftFactory(group__acronym='mars', rev='01')
17+
self.author_address = self.doc.name + '@ietf.org'
18+
19+
def test_regular_trigger(self):
20+
to, cc = gather_address_lists('doc_pulled_from_rfc_queue', doc=self.doc)
21+
# Despite its name, assertCountEqual also compares content, but does not care for ordering
22+
six.assertCountEqual(self, to, ['iana@iana.org', 'rfc-editor@rfc-editor.org'])
23+
six.assertCountEqual(self, cc, ['The IESG <iesg@ietf.org>', self.author_address,
24+
'mars-chairs@ietf.org', 'iesg-secretary@ietf.org'])
25+
26+
def test_skipped_recipient(self):
27+
to, cc = gather_address_lists('doc_pulled_from_rfc_queue', doc=self.doc,
28+
skipped_recipients=['mars-chairs@ietf.org', 'iana@iana.org'])
29+
six.assertCountEqual(self, to, ['rfc-editor@rfc-editor.org'])
30+
six.assertCountEqual(self, cc, ['The IESG <iesg@ietf.org>', self.author_address,
31+
'iesg-secretary@ietf.org'])
32+
33+
def test_trigger_does_not_exist(self):
34+
with self.assertRaises(MailTrigger.DoesNotExist):
35+
gather_address_lists('this-does-not-exist______', doc=self.doc)
36+
37+
def test_create_if_not_exists(self):
38+
new_slug = 'doc_pulled_from_rfc_special_autocreated'
39+
new_desc = 'Autocreated mailtrigger from doc_pulled_from_rfc_queue'
40+
to, cc = gather_address_lists(new_slug, doc=self.doc, desc_if_not_exists=new_desc,
41+
create_from_slug_if_not_exists='doc_pulled_from_rfc_queue')
42+
six.assertCountEqual(self, to, ['iana@iana.org', 'rfc-editor@rfc-editor.org'])
43+
six.assertCountEqual(self, cc, ['The IESG <iesg@ietf.org>', self.author_address,
44+
'mars-chairs@ietf.org', 'iesg-secretary@ietf.org'])
45+
new_trigger = MailTrigger.objects.get(slug=new_slug)
46+
self.assertEqual(new_trigger.desc, new_desc)

ietf/mailtrigger/utils.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ def as_strings(self,compact=True):
1919

2020
return namedtuple('AddrListsAsStrings',['to','cc'])(to=to_string,cc=cc_string)
2121

22-
def gather_address_lists(slug, skipped_recipients=None, **kwargs):
23-
mailtrigger = MailTrigger.objects.get(slug=slug)
22+
23+
def gather_address_lists(slug, skipped_recipients=None, create_from_slug_if_not_exists=None,
24+
desc_if_not_exists=None, **kwargs):
25+
mailtrigger = get_mailtrigger(slug, create_from_slug_if_not_exists, desc_if_not_exists)
2426

2527
to = set()
2628
for recipient in mailtrigger.to.all():
@@ -38,6 +40,21 @@ def gather_address_lists(slug, skipped_recipients=None, **kwargs):
3840

3941
return AddrLists(to=list(to),cc=list(cc))
4042

43+
44+
def get_mailtrigger(slug, create_from_slug_if_not_exists, desc_if_not_exists):
45+
try:
46+
mailtrigger = MailTrigger.objects.get(slug=slug)
47+
except MailTrigger.DoesNotExist:
48+
if create_from_slug_if_not_exists and desc_if_not_exists:
49+
template = MailTrigger.objects.get(slug=create_from_slug_if_not_exists)
50+
mailtrigger = MailTrigger.objects.create(slug=slug, desc=desc_if_not_exists)
51+
mailtrigger.to.set(template.to.all())
52+
mailtrigger.cc.set(template.cc.all())
53+
else:
54+
raise
55+
return mailtrigger
56+
57+
4158
def gather_relevant_expansions(**kwargs):
4259

4360
def starts_with(prefix):

ietf/name/fixtures/names.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3902,7 +3902,7 @@
39023902
"review_doc_all_parties",
39033903
"review_doc_group_mail_list"
39043904
],
3905-
"desc": "Recipients when an review is completed",
3905+
"desc": "Default template for recipients when an review is completed - customised mail triggers are used/created per team and review type.",
39063906
"to": [
39073907
"review_team_mail_list"
39083908
]

0 commit comments

Comments
 (0)