Skip to content

Commit 09e38df

Browse files
committed
Merged in [16705] from sasha@dashcare.nl:
Fix ietf-tools#2337 - Send periodic reminders of open reviews every X days (opt-in) The interleaved_migrations_test currently fails due to the various migrations that have been added for individual tickets/commits (unless --permit-mixed-migrations is set). I think this is better fixed in a later cleanup, as doing it now could cause confusion when merging individual commits, and more migrations are likely to be added soon. - Legacy-Id: 16823 Note: SVN reference [16705] has been migrated to Git commit ec56a03
2 parents 4c3e869 + ec56a03 commit 09e38df

File tree

8 files changed

+115
-7
lines changed

8 files changed

+115
-7
lines changed

ietf/bin/send-review-reminders

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ import datetime
2424
from ietf.review.utils import (
2525
review_assignments_needing_reviewer_reminder, email_reviewer_reminder,
2626
review_assignments_needing_secretary_reminder, email_secretary_reminder,
27-
send_unavaibility_period_ending_reminder)
28-
27+
send_unavaibility_period_ending_reminder, send_reminder_all_open_reviews)
2928
today = datetime.date.today()
3029

3130
for assignment in review_assignments_needing_reviewer_reminder(today):
@@ -38,5 +37,9 @@ for assignment, secretary_role in review_assignments_needing_secretary_reminder(
3837
review_req = assignment.review_request
3938
print("Emailed reminder to {} for review of {} in {} (req. id {})".format(secretary_role.email.address, review_req.doc_id, review_req.team.acronym, review_req.pk))
4039

41-
reminders_sent = send_unavaibility_period_ending_reminder(today)
42-
print('\n'.join(reminders_sent))
40+
period_end_reminders_sent = send_unavaibility_period_ending_reminder(today)
41+
print('\n'.join(period_end_reminders_sent))
42+
43+
open_reviews_reminders_sent = send_reminder_all_open_reviews(today)
44+
print('\n'.join(open_reviews_reminders_sent))
45+

ietf/group/forms.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@ class EmailOpenAssignmentsForm(forms.Form):
273273
class ReviewerSettingsForm(forms.ModelForm):
274274
class Meta:
275275
model = ReviewerSettings
276-
fields = ['min_interval', 'filter_re', 'skip_next', 'remind_days_before_deadline','expertise']
276+
fields = ['min_interval', 'filter_re', 'skip_next', 'remind_days_before_deadline',
277+
'remind_days_open_reviews', 'expertise']
277278

278279
def __init__(self, *args, **kwargs):
279280
exclude_fields = kwargs.pop('exclude_fields', [])

ietf/group/tests_review.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
review_assignments_needing_reviewer_reminder, email_reviewer_reminder,
2424
review_assignments_needing_secretary_reminder, email_secretary_reminder,
2525
reviewer_rotation_list,
26-
send_unavaibility_period_ending_reminder)
26+
send_unavaibility_period_ending_reminder, send_reminder_all_open_reviews)
2727
from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewAssignmentStateName
2828
import ietf.group.views
2929
from ietf.utils.mail import outbox, empty_outbox
@@ -316,6 +316,7 @@ def test_change_reviewer_settings(self):
316316
"min_interval": "7",
317317
"filter_re": "test-[regexp]",
318318
"remind_days_before_deadline": "6",
319+
"remind_days_open_reviews": "8",
319320
"expertise": "Some expertise",
320321
})
321322
self.assertEqual(r.status_code, 302)
@@ -324,6 +325,7 @@ def test_change_reviewer_settings(self):
324325
self.assertEqual(settings.filter_re, "test-[regexp]")
325326
self.assertEqual(settings.skip_next, 0)
326327
self.assertEqual(settings.remind_days_before_deadline, 6)
328+
self.assertEqual(settings.remind_days_open_reviews, 8)
327329
self.assertEqual(settings.expertise, "Some expertise")
328330
self.assertEqual(len(outbox), 1)
329331
self.assertTrue("reviewer availability" in outbox[0]["subject"].lower())
@@ -546,6 +548,29 @@ def test_send_unavaibility_period_ending_reminder(self):
546548
self.assertTrue(reviewer.person.name in log[0])
547549
self.assertTrue(review_team.acronym in log[0])
548550

551+
def test_send_reminder_all_open_reviews(self):
552+
review_req = ReviewRequestFactory(state_id='assigned')
553+
reviewer = RoleFactory(name_id='reviewer', group=review_req.team,person__user__username='reviewer').person
554+
ReviewAssignmentFactory(review_request=review_req, state_id='assigned', assigned_on=review_req.time, reviewer=reviewer.email_set.first())
555+
RoleFactory(name_id='secr', group=review_req.team, person__user__username='reviewsecretary')
556+
ReviewerSettingsFactory(team=review_req.team, person=reviewer, remind_days_open_reviews=1)
557+
558+
empty_outbox()
559+
today = datetime.date.today()
560+
log = send_reminder_all_open_reviews(today)
561+
562+
self.assertEqual(len(outbox), 1)
563+
self.assertTrue(reviewer.email_address() in outbox[0]["To"])
564+
self.assertEqual(outbox[0]["Subject"], "Reminder: you have 1 open review assignment")
565+
message = outbox[0].get_payload(decode=True).decode("utf-8")
566+
self.assertTrue(review_req.team.acronym in message)
567+
self.assertTrue('you have 1 open review' in message)
568+
self.assertTrue(review_req.doc.name in message)
569+
self.assertTrue(review_req.deadline.strftime('%Y-%m-%d') in message)
570+
self.assertEqual(len(log), 1)
571+
self.assertTrue(reviewer.email_address() in log[0])
572+
self.assertTrue('1 open review' in log[0])
573+
549574

550575
class BulkAssignmentTests(TestCase):
551576

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright The IETF Trust 2019, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
# Generated by Django 1.11.23 on 2019-09-05 05:03
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+
('review', '0014_document_primary_key_cleanup'),
13+
]
14+
15+
operations = [
16+
migrations.AddField(
17+
model_name='historicalreviewersettings',
18+
name='remind_days_open_reviews',
19+
field=models.PositiveIntegerField(blank=True, name="Periodic reminder of open reviews every X days", help_text="To get a periodic email reminder of all your open reviews, enter the number of days between these reminders. Clear the field if you don't want these reminders.", null=True),
20+
),
21+
migrations.AddField(
22+
model_name='reviewersettings',
23+
name='remind_days_open_reviews',
24+
field=models.PositiveIntegerField(blank=True, verbose_name="Periodic reminder of open reviews every X days", help_text="To get a periodic email reminder of all your open reviews, enter the number of days between these reminders. Clear the field if you don't want these reminders.", null=True),
25+
),
26+
]

ietf/review/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ class ReviewerSettings(models.Model):
3636
validators=[validate_regular_expression_string, ],
3737
help_text="Draft names matching this regular expression should not be assigned")
3838
skip_next = models.IntegerField(default=0, verbose_name="Skip next assignments")
39-
remind_days_before_deadline = models.IntegerField(null=True, blank=True, help_text="To get an email reminder in case you forget to do an assigned review, enter the number of days before review deadline you want to receive it. Clear the field if you don't want a reminder.")
39+
remind_days_before_deadline = models.IntegerField(null=True, blank=True, help_text="To get an email reminder in case you forget to do an assigned review, enter the number of days before review deadline you want to receive it. Clear the field if you don't want this reminder.")
40+
remind_days_open_reviews = models.PositiveIntegerField(null=True, blank=True, verbose_name="Periodic reminder of open reviews every X days", help_text="To get a periodic email reminder of all your open reviews, enter the number of days between these reminders. Clear the field if you don't want these reminders.")
4041
expertise = models.TextField(verbose_name="Reviewer's expertise in this team's area", max_length=2048, blank=True, help_text="Describe the reviewer's expertise in this team's area", default='')
4142

4243
def __str__(self):

ietf/review/utils.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from collections import defaultdict, namedtuple
1313

1414
from django.db.models import Q, Max, F
15+
from django.template.defaultfilters import pluralize
1516
from django.urls import reverse as urlreverse
1617
from django.contrib.sites.models import Site
1718

@@ -957,7 +958,44 @@ def send_unavaibility_period_ending_reminder(remind_date):
957958
to, period.person, period.team.acronym,period.pk))
958959
return log
959960

961+
962+
def send_reminder_all_open_reviews(remind_date):
963+
log = []
964+
# The origin date is arbitrarily chosen, to have a single reference date for "every X days"
965+
origin_date = datetime.date(2019, 1, 1)
966+
days_since_origin = (remind_date - origin_date).days
967+
relevant_reviewer_settings = ReviewerSettings.objects.filter(remind_days_open_reviews__isnull=False)
960968

969+
for reviewer_settings in relevant_reviewer_settings:
970+
if days_since_origin % reviewer_settings.remind_days_open_reviews != 0:
971+
continue
972+
973+
assignments = ReviewAssignment.objects.filter(
974+
state__in=("assigned", "accepted"),
975+
reviewer__person=reviewer_settings.person,
976+
)
977+
if not assignments:
978+
continue
979+
980+
to = reviewer_settings.person.formatted_email()
981+
subject = "Reminder: you have {} open review assignment{}".format(len(assignments), pluralize(len(assignments)))
982+
983+
domain = Site.objects.get_current().domain
984+
url = urlreverse("ietf.group.views.reviewer_overview",
985+
kwargs={"group_type": reviewer_settings.team.type_id,
986+
"acronym": reviewer_settings.team.acronym})
987+
988+
send_mail(None, to, None, subject, "review/reviewer_reminder_all_open_reviews.txt", {
989+
"reviewer_overview_url": "https://{}{}".format(domain, url),
990+
"assignments": assignments,
991+
"team": reviewer_settings.team,
992+
"remind_days": reviewer_settings.remind_days_open_reviews,
993+
})
994+
log.append("Emailed reminder to {} of their {} open reviews".format(to, len(assignments)))
995+
996+
return log
997+
998+
961999
def review_assignments_needing_reviewer_reminder(remind_date):
9621000
assignment_qs = ReviewAssignment.objects.filter(
9631001
state__in=("assigned", "accepted"),

ietf/templates/ietfauth/review_overview.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ <h2>Settings for {{ t }}</h2>
146146
<th>Remind days before deadline</th>
147147
<td>{{ t.reviewer_settings.remind_days_before_deadline|default:"(Do not remind)" }}</td>
148148
</tr>
149+
<tr>
150+
<th>Periodic reminder of open reviews every X days</th>
151+
<td>{{ t.reviewer_settings.remind_days_open_reviews|default:"(Do not remind)" }}</td>
152+
</tr>
149153
<tr>
150154
<th>Unavailable periods</th>
151155
<td>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% load ietf_filters %}{% autoescape off %}{% filter wordwrap:78 %}This is just a friendly reminder that you have {{ assignments|length }} open review{{ assignments|length|pluralize }} in team {{ team.acronym }}.
2+
3+
The following reviews are open:{% for assignment in assignments %}
4+
- {{ assignment.review_request.doc.name }} (deadline {{ assignment.review_request.deadline }})
5+
{% endfor %}
6+
7+
You are receiving this reminder because you have configured the Datatracker to remind you of all your open reviews every {{ remind_days }} day{{ remind_days|pluralize }}. You can see your reviews and change your settings here:
8+
9+
{{ reviewer_overview_url }}
10+
{% endfilter %}{% endautoescape %}

0 commit comments

Comments
 (0)