Skip to content

Commit be641ac

Browse files
committed
Merged in [16792] from sasha@dashcare.nl:
Fix ietf-tools#2475 - Send opt-in reminders for unconfirmed review assignments. If enabled for a team, reminders will be sent every X days to reviewers for review assignments they have not accepted or rejected. - Legacy-Id: 16846 Note: SVN reference [16792] has been migrated to Git commit 486b6da
2 parents 5460a5a + 486b6da commit be641ac

6 files changed

Lines changed: 119 additions & 7 deletions

File tree

ietf/bin/send-review-reminders

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ from ietf.review.utils import (
2525
review_assignments_needing_reviewer_reminder, email_reviewer_reminder,
2626
review_assignments_needing_secretary_reminder, email_secretary_reminder,
2727
send_unavaibility_period_ending_reminder, send_reminder_all_open_reviews,
28-
send_review_reminder_overdue_assignment)
28+
send_review_reminder_overdue_assignment, send_reminder_unconfirmed_assignments)
2929

3030
today = datetime.date.today()
3131

@@ -47,3 +47,6 @@ print('\n'.join(overdue_reviews_reminders_sent)
4747

4848
open_reviews_reminders_sent = send_reminder_all_open_reviews(today)
4949
print('\n'.join(open_reviews_reminders_sent))
50+
51+
unconfirmed_assignment_reminders_sent = send_reminder_unconfirmed_assignments(today)
52+
print('\n'.join(unconfirmed_assignment_reminders_sent))

ietf/group/tests_review.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@
1717
from ietf.group.models import Role
1818
from ietf.iesg.models import TelechatDate
1919
from ietf.person.models import Person
20-
from ietf.review.models import ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings
20+
from ietf.review.models import ( ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings,
21+
ReviewTeamSettings )
2122
from ietf.review.utils import (
2223
suggested_review_requests_for_team,
2324
review_assignments_needing_reviewer_reminder, email_reviewer_reminder,
2425
review_assignments_needing_secretary_reminder, email_secretary_reminder,
2526
reviewer_rotation_list,
2627
send_unavaibility_period_ending_reminder, send_reminder_all_open_reviews,
27-
send_review_reminder_overdue_assignment)
28+
send_review_reminder_overdue_assignment, send_reminder_unconfirmed_assignments)
2829
from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewAssignmentStateName
2930
import ietf.group.views
3031
from ietf.utils.mail import outbox, empty_outbox
@@ -592,6 +593,34 @@ def test_send_reminder_all_open_reviews(self):
592593
self.assertTrue(reviewer.email_address() in log[0])
593594
self.assertTrue('1 open review' in log[0])
594595

596+
def test_send_reminder_unconfirmed_assignments(self):
597+
review_req = ReviewRequestFactory(state_id='assigned')
598+
reviewer = RoleFactory(name_id='reviewer', group=review_req.team, person__user__username='reviewer').person
599+
ReviewAssignmentFactory(review_request=review_req, state_id='assigned', assigned_on=review_req.time, reviewer=reviewer.email_set.first())
600+
RoleFactory(name_id='secr', group=review_req.team, person__user__username='reviewsecretary')
601+
today = datetime.date.today()
602+
603+
# By default, these reminders are disabled for all teams.
604+
empty_outbox()
605+
log = send_reminder_unconfirmed_assignments(today)
606+
self.assertEqual(len(outbox), 0)
607+
self.assertFalse(log)
608+
609+
ReviewTeamSettings.objects.update(remind_days_unconfirmed_assignments=1)
610+
611+
empty_outbox()
612+
log = send_reminder_unconfirmed_assignments(today)
613+
self.assertEqual(len(outbox), 1)
614+
self.assertIn(reviewer.email_address(), outbox[0]["To"])
615+
self.assertEqual(outbox[0]["Subject"], "Reminder: you have not responded to a review assignment")
616+
message = outbox[0].get_payload(decode=True).decode("utf-8")
617+
self.assertIn(review_req.team.acronym, message)
618+
self.assertIn('accept or reject the assignment on', message)
619+
self.assertIn(review_req.doc.name, message)
620+
self.assertEqual(len(log), 1)
621+
self.assertIn(reviewer.email_address(), log[0])
622+
self.assertIn('not accepted/rejected review assignment', log[0])
623+
595624

596625
class BulkAssignmentTests(TestCase):
597626

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright The IETF Trust 2019, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
# Generated by Django 1.11.23 on 2019-10-01 04:40
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', '0015_add_remind_days_open_reviews'),
13+
]
14+
15+
operations = [
16+
migrations.AddField(
17+
model_name='reviewteamsettings',
18+
name='remind_days_unconfirmed_assignments',
19+
field=models.PositiveIntegerField(blank=True, help_text="To send a periodic email reminder to reviewers of review assignments that are not accepted yet, enter the number of days between these reminders. Clear the field if you don't want these reminders to be sent.", null=True, verbose_name='Periodic reminder of not yet accepted or rejected review assignments to reviewer every X days'),
20+
),
21+
22+
]

ietf/review/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ class ReviewTeamSettings(models.Model):
174174
review_results = models.ManyToManyField(ReviewResultName, default=get_default_review_results, related_name='reviewteamsettings_review_results_set')
175175
notify_ad_when = models.ManyToManyField(ReviewResultName, related_name='reviewteamsettings_notify_ad_set', blank=True)
176176
secr_mail_alias = models.CharField(verbose_name="Email alias for all of the review team secretaries", max_length=255, blank=True, help_text="Email alias for all of the review team secretaries")
177+
remind_days_unconfirmed_assignments = models.PositiveIntegerField(null=True, blank=True,
178+
verbose_name="Periodic reminder of not yet accepted or rejected review assignments to reviewer every X days",
179+
help_text="To send a periodic email reminder to reviewers of review assignments they have neither accepted"
180+
" nor rejected, enter the number of days between these reminders. Clear the field if you don't"
181+
" want these reminders to be sent.")
177182

178183
def __str__(self):
179184
return "%s" % (self.group.acronym,)

ietf/review/utils.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,15 @@
2828
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream
2929
from ietf.review.models import (ReviewRequest, ReviewAssignment, ReviewRequestStateName, ReviewTypeName,
3030
ReviewerSettings, UnavailablePeriod, ReviewWish, NextReviewerInTeam,
31-
ReviewSecretarySettings)
31+
ReviewSecretarySettings, ReviewTeamSettings)
3232
from ietf.utils.mail import send_mail
3333
from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs
3434

35+
# The origin date is used to have a single reference date for "every X days".
36+
# This date is arbitrarily chosen and has no special meaning, but should be consistent.
37+
ORIGIN_DATE_PERIODIC_REMINDERS = datetime.date(2019, 1, 1)
38+
39+
3540
def active_review_teams():
3641
return Group.objects.filter(reviewteamsettings__isnull=False,state="active")
3742

@@ -992,9 +997,7 @@ def send_review_reminder_overdue_assignment(remind_date):
992997

993998
def send_reminder_all_open_reviews(remind_date):
994999
log = []
995-
# The origin date is arbitrarily chosen, to have a single reference date for "every X days"
996-
origin_date = datetime.date(2019, 1, 1)
997-
days_since_origin = (remind_date - origin_date).days
1000+
days_since_origin = (remind_date - ORIGIN_DATE_PERIODIC_REMINDERS).days
9981001
relevant_reviewer_settings = ReviewerSettings.objects.filter(remind_days_open_reviews__isnull=False)
9991002

10001003
for reviewer_settings in relevant_reviewer_settings:
@@ -1027,6 +1030,47 @@ def send_reminder_all_open_reviews(remind_date):
10271030
return log
10281031

10291032

1033+
def send_reminder_unconfirmed_assignments(remind_date):
1034+
"""
1035+
Remind reviewers of any assigned ReviewAssignments which they have not
1036+
accepted or rejected, if enabled in ReviewTeamSettings.
1037+
"""
1038+
log = []
1039+
days_since_origin = (remind_date - ORIGIN_DATE_PERIODIC_REMINDERS).days
1040+
relevant_review_team_settings = ReviewTeamSettings.objects.filter(
1041+
remind_days_unconfirmed_assignments__isnull=False)
1042+
1043+
for review_team_settings in relevant_review_team_settings:
1044+
if days_since_origin % review_team_settings.remind_days_unconfirmed_assignments != 0:
1045+
continue
1046+
1047+
assignments = ReviewAssignment.objects.filter(
1048+
state='assigned',
1049+
review_request__team=review_team_settings.group,
1050+
)
1051+
if not assignments:
1052+
continue
1053+
1054+
for assignment in assignments:
1055+
to = assignment.reviewer.formatted_email()
1056+
subject = "Reminder: you have not responded to a review assignment"
1057+
domain = Site.objects.get_current().domain
1058+
review_request_url = urlreverse("ietf.doc.views_review.review_request", kwargs={
1059+
"name": assignment.review_request.doc.name,
1060+
"request_id": assignment.review_request.pk
1061+
})
1062+
1063+
send_mail(None, to, None, subject, "review/reviewer_reminder_unconfirmed_assignments.txt", {
1064+
"review_request_url": "https://{}{}".format(domain, review_request_url),
1065+
"assignment": assignment,
1066+
"team": assignment.review_request.team,
1067+
"remind_days": review_team_settings.remind_days_unconfirmed_assignments,
1068+
})
1069+
log.append("Emailed reminder to {} about not accepted/rejected review assignment {}".format(to, assignment.pk))
1070+
1071+
return log
1072+
1073+
10301074
def review_assignments_needing_reviewer_reminder(remind_date):
10311075
assignment_qs = ReviewAssignment.objects.filter(
10321076
state__in=("assigned", "accepted"),
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{% load ietf_filters %}{% autoescape off %}{% filter wordwrap:78 %}This is just a friendly reminder that you have a review assignment which you have neither accepted nor rejected.
2+
3+
The review assignment is for {{ assignment.review_request.doc.name }} in team {{ team.acronym }}.
4+
You can accept or reject the assignment on:
5+
{{ review_request_url }}
6+
7+
You are receiving this reminder because your team secretary has configured the Datatracker to remind you every {{ remind_days }} day{{ remind_days|pluralize }}.
8+
9+
{% endfilter %}{% endautoescape %}

0 commit comments

Comments
 (0)