Merged in [16792] from sasha@dashcare.nl:
Fix #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 486b6daa29
This commit is contained in:
commit
be641ac22a
|
@ -25,7 +25,7 @@ from ietf.review.utils import (
|
|||
review_assignments_needing_reviewer_reminder, email_reviewer_reminder,
|
||||
review_assignments_needing_secretary_reminder, email_secretary_reminder,
|
||||
send_unavaibility_period_ending_reminder, send_reminder_all_open_reviews,
|
||||
send_review_reminder_overdue_assignment)
|
||||
send_review_reminder_overdue_assignment, send_reminder_unconfirmed_assignments)
|
||||
|
||||
today = datetime.date.today()
|
||||
|
||||
|
@ -47,3 +47,6 @@ print('\n'.join(overdue_reviews_reminders_sent)
|
|||
|
||||
open_reviews_reminders_sent = send_reminder_all_open_reviews(today)
|
||||
print('\n'.join(open_reviews_reminders_sent))
|
||||
|
||||
unconfirmed_assignment_reminders_sent = send_reminder_unconfirmed_assignments(today)
|
||||
print('\n'.join(unconfirmed_assignment_reminders_sent))
|
||||
|
|
|
@ -17,14 +17,15 @@ from ietf.doc.models import TelechatDocEvent
|
|||
from ietf.group.models import Role
|
||||
from ietf.iesg.models import TelechatDate
|
||||
from ietf.person.models import Person
|
||||
from ietf.review.models import ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings
|
||||
from ietf.review.models import ( ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings,
|
||||
ReviewTeamSettings )
|
||||
from ietf.review.utils import (
|
||||
suggested_review_requests_for_team,
|
||||
review_assignments_needing_reviewer_reminder, email_reviewer_reminder,
|
||||
review_assignments_needing_secretary_reminder, email_secretary_reminder,
|
||||
reviewer_rotation_list,
|
||||
send_unavaibility_period_ending_reminder, send_reminder_all_open_reviews,
|
||||
send_review_reminder_overdue_assignment)
|
||||
send_review_reminder_overdue_assignment, send_reminder_unconfirmed_assignments)
|
||||
from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewAssignmentStateName
|
||||
import ietf.group.views
|
||||
from ietf.utils.mail import outbox, empty_outbox
|
||||
|
@ -592,6 +593,34 @@ class ReviewTests(TestCase):
|
|||
self.assertTrue(reviewer.email_address() in log[0])
|
||||
self.assertTrue('1 open review' in log[0])
|
||||
|
||||
def test_send_reminder_unconfirmed_assignments(self):
|
||||
review_req = ReviewRequestFactory(state_id='assigned')
|
||||
reviewer = RoleFactory(name_id='reviewer', group=review_req.team, person__user__username='reviewer').person
|
||||
ReviewAssignmentFactory(review_request=review_req, state_id='assigned', assigned_on=review_req.time, reviewer=reviewer.email_set.first())
|
||||
RoleFactory(name_id='secr', group=review_req.team, person__user__username='reviewsecretary')
|
||||
today = datetime.date.today()
|
||||
|
||||
# By default, these reminders are disabled for all teams.
|
||||
empty_outbox()
|
||||
log = send_reminder_unconfirmed_assignments(today)
|
||||
self.assertEqual(len(outbox), 0)
|
||||
self.assertFalse(log)
|
||||
|
||||
ReviewTeamSettings.objects.update(remind_days_unconfirmed_assignments=1)
|
||||
|
||||
empty_outbox()
|
||||
log = send_reminder_unconfirmed_assignments(today)
|
||||
self.assertEqual(len(outbox), 1)
|
||||
self.assertIn(reviewer.email_address(), outbox[0]["To"])
|
||||
self.assertEqual(outbox[0]["Subject"], "Reminder: you have not responded to a review assignment")
|
||||
message = outbox[0].get_payload(decode=True).decode("utf-8")
|
||||
self.assertIn(review_req.team.acronym, message)
|
||||
self.assertIn('accept or reject the assignment on', message)
|
||||
self.assertIn(review_req.doc.name, message)
|
||||
self.assertEqual(len(log), 1)
|
||||
self.assertIn(reviewer.email_address(), log[0])
|
||||
self.assertIn('not accepted/rejected review assignment', log[0])
|
||||
|
||||
|
||||
class BulkAssignmentTests(TestCase):
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright The IETF Trust 2019, All Rights Reserved
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.23 on 2019-10-01 04:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('review', '0015_add_remind_days_open_reviews'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='reviewteamsettings',
|
||||
name='remind_days_unconfirmed_assignments',
|
||||
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'),
|
||||
),
|
||||
|
||||
]
|
|
@ -174,6 +174,11 @@ class ReviewTeamSettings(models.Model):
|
|||
review_results = models.ManyToManyField(ReviewResultName, default=get_default_review_results, related_name='reviewteamsettings_review_results_set')
|
||||
notify_ad_when = models.ManyToManyField(ReviewResultName, related_name='reviewteamsettings_notify_ad_set', blank=True)
|
||||
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")
|
||||
remind_days_unconfirmed_assignments = models.PositiveIntegerField(null=True, blank=True,
|
||||
verbose_name="Periodic reminder of not yet accepted or rejected review assignments to reviewer every X days",
|
||||
help_text="To send a periodic email reminder to reviewers of review assignments they have neither accepted"
|
||||
" nor rejected, enter the number of days between these reminders. Clear the field if you don't"
|
||||
" want these reminders to be sent.")
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % (self.group.acronym,)
|
||||
|
|
|
@ -28,10 +28,15 @@ from ietf.person.models import Person
|
|||
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream
|
||||
from ietf.review.models import (ReviewRequest, ReviewAssignment, ReviewRequestStateName, ReviewTypeName,
|
||||
ReviewerSettings, UnavailablePeriod, ReviewWish, NextReviewerInTeam,
|
||||
ReviewSecretarySettings)
|
||||
ReviewSecretarySettings, ReviewTeamSettings)
|
||||
from ietf.utils.mail import send_mail
|
||||
from ietf.doc.utils import extract_complete_replaces_ancestor_mapping_for_docs
|
||||
|
||||
# The origin date is used to have a single reference date for "every X days".
|
||||
# This date is arbitrarily chosen and has no special meaning, but should be consistent.
|
||||
ORIGIN_DATE_PERIODIC_REMINDERS = datetime.date(2019, 1, 1)
|
||||
|
||||
|
||||
def active_review_teams():
|
||||
return Group.objects.filter(reviewteamsettings__isnull=False,state="active")
|
||||
|
||||
|
@ -992,9 +997,7 @@ def send_review_reminder_overdue_assignment(remind_date):
|
|||
|
||||
def send_reminder_all_open_reviews(remind_date):
|
||||
log = []
|
||||
# The origin date is arbitrarily chosen, to have a single reference date for "every X days"
|
||||
origin_date = datetime.date(2019, 1, 1)
|
||||
days_since_origin = (remind_date - origin_date).days
|
||||
days_since_origin = (remind_date - ORIGIN_DATE_PERIODIC_REMINDERS).days
|
||||
relevant_reviewer_settings = ReviewerSettings.objects.filter(remind_days_open_reviews__isnull=False)
|
||||
|
||||
for reviewer_settings in relevant_reviewer_settings:
|
||||
|
@ -1027,6 +1030,47 @@ def send_reminder_all_open_reviews(remind_date):
|
|||
return log
|
||||
|
||||
|
||||
def send_reminder_unconfirmed_assignments(remind_date):
|
||||
"""
|
||||
Remind reviewers of any assigned ReviewAssignments which they have not
|
||||
accepted or rejected, if enabled in ReviewTeamSettings.
|
||||
"""
|
||||
log = []
|
||||
days_since_origin = (remind_date - ORIGIN_DATE_PERIODIC_REMINDERS).days
|
||||
relevant_review_team_settings = ReviewTeamSettings.objects.filter(
|
||||
remind_days_unconfirmed_assignments__isnull=False)
|
||||
|
||||
for review_team_settings in relevant_review_team_settings:
|
||||
if days_since_origin % review_team_settings.remind_days_unconfirmed_assignments != 0:
|
||||
continue
|
||||
|
||||
assignments = ReviewAssignment.objects.filter(
|
||||
state='assigned',
|
||||
review_request__team=review_team_settings.group,
|
||||
)
|
||||
if not assignments:
|
||||
continue
|
||||
|
||||
for assignment in assignments:
|
||||
to = assignment.reviewer.formatted_email()
|
||||
subject = "Reminder: you have not responded to a review assignment"
|
||||
domain = Site.objects.get_current().domain
|
||||
review_request_url = urlreverse("ietf.doc.views_review.review_request", kwargs={
|
||||
"name": assignment.review_request.doc.name,
|
||||
"request_id": assignment.review_request.pk
|
||||
})
|
||||
|
||||
send_mail(None, to, None, subject, "review/reviewer_reminder_unconfirmed_assignments.txt", {
|
||||
"review_request_url": "https://{}{}".format(domain, review_request_url),
|
||||
"assignment": assignment,
|
||||
"team": assignment.review_request.team,
|
||||
"remind_days": review_team_settings.remind_days_unconfirmed_assignments,
|
||||
})
|
||||
log.append("Emailed reminder to {} about not accepted/rejected review assignment {}".format(to, assignment.pk))
|
||||
|
||||
return log
|
||||
|
||||
|
||||
def review_assignments_needing_reviewer_reminder(remind_date):
|
||||
assignment_qs = ReviewAssignment.objects.filter(
|
||||
state__in=("assigned", "accepted"),
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{% 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.
|
||||
|
||||
The review assignment is for {{ assignment.review_request.doc.name }} in team {{ team.acronym }}.
|
||||
You can accept or reject the assignment on:
|
||||
{{ review_request_url }}
|
||||
|
||||
You are receiving this reminder because your team secretary has configured the Datatracker to remind you every {{ remind_days }} day{{ remind_days|pluralize }}.
|
||||
|
||||
{% endfilter %}{% endautoescape %}
|
Loading…
Reference in a new issue