From 67740aed03c792d3a86a73c105e92d17ede3517d Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 15 May 2024 15:08:14 -0300 Subject: [PATCH 1/3] refactor: send_reminders.py -> celery task --- ietf/nomcom/tasks.py | 10 ++++++++++ ietf/nomcom/tests.py | 45 +++++++++++++++++++++++++++++++++++++++++++- ietf/nomcom/utils.py | 24 +++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 ietf/nomcom/tasks.py diff --git a/ietf/nomcom/tasks.py b/ietf/nomcom/tasks.py new file mode 100644 index 0000000000..3d063a6b26 --- /dev/null +++ b/ietf/nomcom/tasks.py @@ -0,0 +1,10 @@ +# Copyright The IETF Trust 2024, All Rights Reserved + +from celery import shared_task + +from .utils import send_reminders + + +@shared_task +def send_nomcom_reminders_task(): + send_reminders() diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 9a615c91d1..1dadcf5305 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -44,10 +44,11 @@ from ietf.nomcom.factories import NomComFactory, FeedbackFactory, TopicFactory, \ nomcom_kwargs_for_year, provide_private_key_to_test_client, \ key +from ietf.nomcom.tasks import send_nomcom_reminders_task from ietf.nomcom.utils import get_nomcom_by_year, make_nomineeposition, \ get_hash_nominee_position, is_eligible, list_eligible, \ get_eligibility_date, suggest_affiliation, ingest_feedback_email, \ - decorate_volunteers_with_qualifications + decorate_volunteers_with_qualifications, send_reminders, _is_time_to_send_reminder from ietf.person.factories import PersonFactory, EmailFactory from ietf.person.models import Email, Person from ietf.stats.models import MeetingRegistration @@ -1208,6 +1209,41 @@ def tearDown(self): super().tearDown() def test_is_time_to_send(self): + self.nomcom.reminder_interval = 4 + today = date_today() + self.assertTrue( + is_time_to_send(self.nomcom, today + datetime.timedelta(days=4), today) + ) + for delta in range(4): + self.assertFalse( + _is_time_to_send_reminder( + self.nomcom, today + datetime.timedelta(days=delta), today + ) + ) + self.nomcom.reminder_interval = None + self.assertFalse(is_time_to_send(self.nomcom, today, today)) + self.nomcom.reminderdates_set.create(date=today) + self.assertTrue(is_time_to_send(self.nomcom, today, today)) + + def test_send_reminders(self): + messages_before = len(outbox) + self.nomcom.reminder_interval = 3 + self.nomcom.save() + send_reminders() + self.assertEqual(len(outbox), messages_before + 2) + self.assertIn('nominee1@example.org', outbox[-1]['To']) + self.assertIn('please complete', outbox[-1]['Subject']) + self.assertIn('nominee1@example.org', outbox[-2]['To']) + self.assertIn('please accept', outbox[-2]['Subject']) + messages_before = len(outbox) + self.nomcom.reminder_interval = 4 + self.nomcom.save() + send_reminders() + self.assertEqual(len(outbox), messages_before + 1) + self.assertIn('nominee2@example.org', outbox[-1]['To']) + self.assertIn('please accept', outbox[-1]['Subject']) + + def test_is_time_to_send_old(self): self.nomcom.reminder_interval = 4 today = date_today() self.assertTrue(is_time_to_send(self.nomcom,today+datetime.timedelta(days=4),today)) @@ -3048,3 +3084,10 @@ def test_reclassify_feedback_unrelated(self): self.assertEqual(fb.type_id, 'junk') self.assertEqual(Feedback.objects.filter(type='read').count(), 0) self.assertEqual(Feedback.objects.filter(type='junk').count(), 1) + + +class TaskTests(TestCase): + @mock.patch("ietf.nomcom.tasks.send_reminders") + def test_send_nomcom_reminders_task(self, mock_send): + send_nomcom_reminders_task() + self.assertEqual(mock_send.call_count, 1) diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py index 53e775deb9..ab155ef1d5 100644 --- a/ietf/nomcom/utils.py +++ b/ietf/nomcom/utils.py @@ -747,3 +747,27 @@ def ingest_feedback_email(message: bytes, year: int): email_original_message=message, ) from err log("Received nomcom email from %s" % feedback.author) + + +def _is_time_to_send_reminder(nomcom, send_date, nomination_date): + if nomcom.reminder_interval: + days_passed = (send_date - nomination_date).days + return days_passed > 0 and days_passed % nomcom.reminder_interval == 0 + else: + return bool(nomcom.reminderdates_set.filter(date=send_date)) + + +def send_reminders(): + from .models import NomCom, NomineePosition + for nomcom in NomCom.objects.filter(group__state__slug="active"): + nps = NomineePosition.objects.filter( + nominee__nomcom=nomcom, nominee__duplicated__isnull=True + ) + for nominee_position in nps.pending(): + if _is_time_to_send_reminder(nomcom, date_today(), nominee_position.time.date()): + send_accept_reminder_to_nominee(nominee_position) + log(f"Sent accept reminder to {nominee_position.nominee.email.address}") + for nominee_position in nps.accepted().without_questionnaire_response(): + if _is_time_to_send_reminder(nomcom, date_today(), nominee_position.time.date()): + send_questionnaire_reminder_to_nominee(nominee_position) + log(f"Sent questionnaire reminder to {nominee_position.nominee.email.address}") From 2bb15eac4352bc870315b3147879c0c8f8045c75 Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 15 May 2024 15:17:28 -0300 Subject: [PATCH 2/3] chore: add PeriodicTask --- ietf/utils/management/commands/periodic_tasks.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ietf/utils/management/commands/periodic_tasks.py b/ietf/utils/management/commands/periodic_tasks.py index 429cb4e14f..5284feb9cc 100644 --- a/ietf/utils/management/commands/periodic_tasks.py +++ b/ietf/utils/management/commands/periodic_tasks.py @@ -201,6 +201,16 @@ def create_default_tasks(self): ), ) + PeriodicTask.objects.get_or_create( + name="Send NomCom reminders", + task="ietf.nomcom.tasks.send_nomcom_reminders_task", + defaults=dict( + enabled=False, + crontab=self.crontabs["daily"], + description="Send acceptance and questionnaire reminders to nominees", + ), + ) + def show_tasks(self): for label, crontab in self.crontabs.items(): tasks = PeriodicTask.objects.filter(crontab=crontab).order_by( From dfbe294ab52721482fbe342c3ac7912cd8f3567d Mon Sep 17 00:00:00 2001 From: Jennifer Richards Date: Wed, 15 May 2024 15:20:13 -0300 Subject: [PATCH 3/3] chore: remove management command and tests --- .../management/commands/send_reminders.py | 37 ------------------ ietf/nomcom/tests.py | 39 ++----------------- 2 files changed, 4 insertions(+), 72 deletions(-) delete mode 100644 ietf/nomcom/management/commands/send_reminders.py diff --git a/ietf/nomcom/management/commands/send_reminders.py b/ietf/nomcom/management/commands/send_reminders.py deleted file mode 100644 index bc10425430..0000000000 --- a/ietf/nomcom/management/commands/send_reminders.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright The IETF Trust 2013-2020, All Rights Reserved -# -*- coding: utf-8 -*- - - -import syslog - -from django.core.management.base import BaseCommand - -from ietf.nomcom.models import NomCom, NomineePosition -from ietf.nomcom.utils import send_accept_reminder_to_nominee,send_questionnaire_reminder_to_nominee -from ietf.utils.timezone import date_today - - -def log(message): - syslog.syslog(message) - -def is_time_to_send(nomcom,send_date,nomination_date): - if nomcom.reminder_interval: - days_passed = (send_date - nomination_date).days - return days_passed > 0 and days_passed % nomcom.reminder_interval == 0 - else: - return bool(nomcom.reminderdates_set.filter(date=send_date)) - -class Command(BaseCommand): - help = ("Send acceptance and questionnaire reminders to nominees") - - def handle(self, *args, **options): - for nomcom in NomCom.objects.filter(group__state__slug='active'): - nps = NomineePosition.objects.filter(nominee__nomcom=nomcom,nominee__duplicated__isnull=True) - for nominee_position in nps.pending(): - if is_time_to_send(nomcom, date_today(), nominee_position.time.date()): - send_accept_reminder_to_nominee(nominee_position) - log('Sent accept reminder to %s' % nominee_position.nominee.email.address) - for nominee_position in nps.accepted().without_questionnaire_response(): - if is_time_to_send(nomcom, date_today(), nominee_position.time.date()): - send_questionnaire_reminder_to_nominee(nominee_position) - log('Sent questionnaire reminder to %s' % nominee_position.nominee.email.address) diff --git a/ietf/nomcom/tests.py b/ietf/nomcom/tests.py index 1dadcf5305..8f94cc7fc5 100644 --- a/ietf/nomcom/tests.py +++ b/ietf/nomcom/tests.py @@ -40,7 +40,6 @@ NomineePositionStateName, Feedback, FeedbackTypeName, \ Nomination, FeedbackLastSeen, TopicFeedbackLastSeen, ReminderDates, \ NomCom -from ietf.nomcom.management.commands.send_reminders import Command, is_time_to_send from ietf.nomcom.factories import NomComFactory, FeedbackFactory, TopicFactory, \ nomcom_kwargs_for_year, provide_private_key_to_test_client, \ key @@ -1208,11 +1207,11 @@ def tearDown(self): teardown_test_public_keys_dir(self) super().tearDown() - def test_is_time_to_send(self): + def test_is_time_to_send_reminder(self): self.nomcom.reminder_interval = 4 today = date_today() self.assertTrue( - is_time_to_send(self.nomcom, today + datetime.timedelta(days=4), today) + _is_time_to_send_reminder(self.nomcom, today + datetime.timedelta(days=4), today) ) for delta in range(4): self.assertFalse( @@ -1221,9 +1220,9 @@ def test_is_time_to_send(self): ) ) self.nomcom.reminder_interval = None - self.assertFalse(is_time_to_send(self.nomcom, today, today)) + self.assertFalse(_is_time_to_send_reminder(self.nomcom, today, today)) self.nomcom.reminderdates_set.create(date=today) - self.assertTrue(is_time_to_send(self.nomcom, today, today)) + self.assertTrue(_is_time_to_send_reminder(self.nomcom, today, today)) def test_send_reminders(self): messages_before = len(outbox) @@ -1243,36 +1242,6 @@ def test_send_reminders(self): self.assertIn('nominee2@example.org', outbox[-1]['To']) self.assertIn('please accept', outbox[-1]['Subject']) - def test_is_time_to_send_old(self): - self.nomcom.reminder_interval = 4 - today = date_today() - self.assertTrue(is_time_to_send(self.nomcom,today+datetime.timedelta(days=4),today)) - for delta in range(4): - self.assertFalse(is_time_to_send(self.nomcom,today+datetime.timedelta(days=delta),today)) - self.nomcom.reminder_interval = None - self.assertFalse(is_time_to_send(self.nomcom,today,today)) - self.nomcom.reminderdates_set.create(date=today) - self.assertTrue(is_time_to_send(self.nomcom,today,today)) - - def test_command(self): - c = Command() - messages_before=len(outbox) - self.nomcom.reminder_interval = 3 - self.nomcom.save() - c.handle(None,None) - self.assertEqual(len(outbox), messages_before + 2) - self.assertIn('nominee1@example.org', outbox[-1]['To']) - self.assertIn('please complete', outbox[-1]['Subject']) - self.assertIn('nominee1@example.org', outbox[-2]['To']) - self.assertIn('please accept', outbox[-2]['Subject']) - messages_before=len(outbox) - self.nomcom.reminder_interval = 4 - self.nomcom.save() - c.handle(None,None) - self.assertEqual(len(outbox), messages_before + 1) - self.assertIn('nominee2@example.org', outbox[-1]['To']) - self.assertIn('please accept', outbox[-1]['Subject']) - def test_remind_accept_view(self): url = reverse('ietf.nomcom.views.send_reminder_mail', kwargs={'year': NOMCOM_YEAR,'type':'accept'}) login_testing_unauthorized(self, CHAIR_USER, url)