From 179c151f9e1fa3ea9830aef26d0152b0d02312ad Mon Sep 17 00:00:00 2001 From: Sasha Romijn Date: Thu, 5 Sep 2019 11:59:40 +0000 Subject: [PATCH] Fix #2231 - Send reminders before long UnavailabilityPeriods expire A notification is sent to the team secretary and reviewer, if an unavailability period lasting 30 days or more will end in 3 days. Commit ready for merge. - Legacy-Id: 16704 --- ietf/bin/send-review-reminders | 4 +- ietf/group/tests_review.py | 54 ++++++++++++++++++- ietf/review/utils.py | 28 +++++++++- .../review/reviewer_unavailability_ending.txt | 7 +++ 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 ietf/templates/review/reviewer_unavailability_ending.txt diff --git a/ietf/bin/send-review-reminders b/ietf/bin/send-review-reminders index 1b7f2668c..e7e369ab0 100755 --- a/ietf/bin/send-review-reminders +++ b/ietf/bin/send-review-reminders @@ -24,7 +24,7 @@ import datetime from ietf.review.utils import ( review_assignments_needing_reviewer_reminder, email_reviewer_reminder, review_assignments_needing_secretary_reminder, email_secretary_reminder, -) + email_unavaibility_period_ending_reminder) today = datetime.date.today() @@ -37,3 +37,5 @@ for assignment, secretary_role in review_assignments_needing_secretary_reminder( email_secretary_reminder(assignment.review_request, secretary_role) review_req = assignment.review_request 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)) + +print('\n'.join(email_unavaibility_period_ending_reminder(today))) diff --git a/ietf/group/tests_review.py b/ietf/group/tests_review.py index 66ee9a13e..ba4dd9be5 100644 --- a/ietf/group/tests_review.py +++ b/ietf/group/tests_review.py @@ -23,7 +23,7 @@ from ietf.review.utils import ( review_assignments_needing_reviewer_reminder, email_reviewer_reminder, review_assignments_needing_secretary_reminder, email_secretary_reminder, reviewer_rotation_list, -) + email_unavaibility_period_ending_reminder) from ietf.name.models import ReviewResultName, ReviewRequestStateName, ReviewAssignmentStateName import ietf.group.views from ietf.utils.mail import outbox, empty_outbox @@ -493,6 +493,58 @@ class ReviewTests(TestCase): self.assertEqual(len(outbox), 1) self.assertTrue(review_req.doc.name in outbox[0].get_payload(decode=True).decode("utf-8")) + def test_email_unavaibility_period_ending_reminder(self): + review_team = ReviewTeamFactory(acronym="reviewteam", name="Review Team", type_id="review", + list_email="reviewteam@ietf.org") + reviewer = RoleFactory(group=review_team, person__user__username='reviewer', + person__user__email='reviewer@example.com', + person__name='Some Reviewer', name_id='reviewer') + secretary = RoleFactory(group=review_team, person__user__username='reviewsecretary', + person__user__email='reviewsecretary@example.com', name_id='secr') + empty_outbox() + today = datetime.date.today() + UnavailablePeriod.objects.create( + team=review_team, + person=reviewer.person, + start_date=today - datetime.timedelta(days=40), + end_date=today + datetime.timedelta(days=3), + availability="unavailable", + ) + UnavailablePeriod.objects.create( + team=review_team, + person=reviewer.person, + # This object should be ignored, length is too short + start_date=today - datetime.timedelta(days=20), + end_date=today + datetime.timedelta(days=3), + availability="unavailable", + ) + UnavailablePeriod.objects.create( + team=review_team, + person=reviewer.person, + start_date=today - datetime.timedelta(days=40), + # This object should be ignored, end date is too far away + end_date=today + datetime.timedelta(days=4), + availability="unavailable", + ) + UnavailablePeriod.objects.create( + team=review_team, + person=reviewer.person, + # This object should be ignored, end date is too close + start_date=today - datetime.timedelta(days=40), + end_date=today + datetime.timedelta(days=2), + availability="unavailable", + ) + log = email_unavaibility_period_ending_reminder(today) + + self.assertEqual(len(outbox), 1) + self.assertTrue(reviewer.person.email_address() in outbox[0]["To"]) + self.assertTrue(secretary.person.email_address() in outbox[0]["To"]) + message = outbox[0].get_payload(decode=True).decode("utf-8") + self.assertTrue(reviewer.person.name in message) + self.assertTrue(review_team.acronym in message) + self.assertEqual(len(log), 1) + self.assertTrue(reviewer.person.name in log[0]) + self.assertTrue(review_team.acronym in log[0]) class BulkAssignmentTests(TestCase): diff --git a/ietf/review/utils.py b/ietf/review/utils.py index e26c075df..46b2f7b7b 100644 --- a/ietf/review/utils.py +++ b/ietf/review/utils.py @@ -395,7 +395,6 @@ def email_reviewer_availability_change(request, team, reviewer_role, msg, by): "reviewer_overview_url": url, "reviewer": reviewer_role.person, "team": team, - "msg": msg, "by": by, }, cc=cc) @@ -932,6 +931,33 @@ def make_assignment_choices(email_queryset, review_req): return [(r["email"].pk, r["label"]) for r in ranking] + +def email_unavaibility_period_ending_reminder(remind_date): + reminder_days = 3 + end_date = remind_date + datetime.timedelta(days=reminder_days) + min_start_date = end_date - datetime.timedelta(days=30) + periods = UnavailablePeriod.objects.filter(start_date__lte=min_start_date, end_date=end_date) + log = [] + for period in periods: + (to, cc) = gather_address_lists('review_availability_changed', group=period.team, reviewer=period.person) + domain = Site.objects.get_current().domain + url = urlreverse("ietf.group.views.reviewer_overview", kwargs={ "group_type": period.team.type_id, "acronym": period.team.acronym }) + + subject = "Reminder: unavailability period of {} is ending soon".format(period.person) + send_mail(None, to, None, subject, "review/reviewer_unavailability_ending.txt", { + "reviewer_overview_url": "https://{}{}".format(domain, url), + "reviewer": period.person, + "team": period.team, + "reminder_days": reminder_days, + "period_start": period.start_date.isoformat(), + "period_end": period.end_date.isoformat(), + }, cc=cc) + log.append("Emailed reminder to {} for ending of unavailability " + "of {} in {} soon (unavailability period id {})".format( + to, period.person, period.team.acronym,period.pk)) + return log + + def review_assignments_needing_reviewer_reminder(remind_date): assignment_qs = ReviewAssignment.objects.filter( state__in=("assigned", "accepted"), diff --git a/ietf/templates/review/reviewer_unavailability_ending.txt b/ietf/templates/review/reviewer_unavailability_ending.txt new file mode 100644 index 000000000..6a34d0b55 --- /dev/null +++ b/ietf/templates/review/reviewer_unavailability_ending.txt @@ -0,0 +1,7 @@ +{% load ietf_filters %}{% autoescape off %}{% filter wordwrap:78 %} +Reviewer {{ reviewer }} in {{ team.acronym }} will become available again in {{ reminder_days }} days. +This ends their unavailability which lasted from {{ period_start }} until {{ period_end }}. + +{{ reviewer_overview_url }} + +{% endfilter %}{% endautoescape %}