From fb808ec6d9c2551bbd01cd0633b82b43ed0556c3 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Mon, 14 Feb 2022 17:31:01 +0100 Subject: [PATCH 2/3] reminders: split send code (#61234) --- .../commands/send_booking_reminders.py | 85 +------------------ chrono/agendas/management/commands/utils.py | 84 ++++++++++++++++++ tests/test_agendas.py | 6 +- 3 files changed, 91 insertions(+), 84 deletions(-) create mode 100644 chrono/agendas/management/commands/utils.py diff --git a/chrono/agendas/management/commands/send_booking_reminders.py b/chrono/agendas/management/commands/send_booking_reminders.py index 0902ef62..7b51ece6 100644 --- a/chrono/agendas/management/commands/send_booking_reminders.py +++ b/chrono/agendas/management/commands/send_booking_reminders.py @@ -15,22 +15,16 @@ # along with this program. If not, see . from datetime import datetime, timedelta -from smtplib import SMTPException import pytz from django.conf import settings -from django.core.mail import send_mail from django.core.management.base import BaseCommand from django.db.models import F -from django.db.transaction import atomic -from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist -from django.template.loader import render_to_string from django.utils import timezone, translation -from django.utils.translation import ugettext_lazy as _ -from requests import RequestException from chrono.agendas.models import Booking -from chrono.utils.requests_wrapper import requests + +from .utils import send_reminder SENDING_IN_PROGRESS = datetime(year=2, month=1, day=1, tzinfo=pytz.UTC) @@ -66,87 +60,16 @@ class Command(BaseCommand): **{f'{msg_type}_reminder_datetime__isnull': True}, ).select_related('event', 'event__agenda', 'event__agenda__reminder_settings') - bookings_list = list(bookings) + bookings_list = list(bookings.order_by('pk')) bookings_pk = list(bookings.values_list('pk', flat=True)) bookings.update(**{f'{msg_type}_reminder_datetime': SENDING_IN_PROGRESS}) try: for booking in bookings_list: - self.send_reminder(booking, msg_type) + send_reminder(booking, msg_type) finally: Booking.objects.filter( pk__in=bookings_pk, **{f'{msg_type}_reminder_datetime': SENDING_IN_PROGRESS} ).update( **{f'{msg_type}_reminder_datetime': None}, ) - - def send_reminder(self, booking, msg_type): - agenda = booking.event.agenda - kind = agenda.kind - days = getattr(agenda.reminder_settings, f'days_before_{msg_type}') - - ctx = { - 'booking': booking, - 'in_x_days': _('tomorrow') if days == 1 else _('in %s days') % days, - 'date': booking.event.start_datetime, - } - ctx.update(settings.TEMPLATE_VARS) - - for extra_info in ('email_extra_info', 'sms_extra_info'): - try: - ctx[extra_info] = Template(getattr(agenda.reminder_settings, extra_info)).render( - Context({'booking': booking}, autoescape=False) - ) - except (VariableDoesNotExist, TemplateSyntaxError): - pass - - if msg_type == 'email': - emails = set(booking.extra_emails) - if booking.user_email: - emails.add(booking.user_email) - - for email in emails: - self.send_email(email, booking, kind, ctx) - elif msg_type == 'sms': - phone_numbers = set(booking.extra_phone_numbers) - if booking.user_phone_number: - phone_numbers.add(booking.user_phone_number) - - if phone_numbers: - self.send_sms(list(phone_numbers), booking, kind, ctx) - - @staticmethod - def send_email(email, booking, kind, ctx): - subject = render_to_string('agendas/%s_reminder_subject.txt' % kind, ctx).strip() - body = render_to_string('agendas/%s_reminder_body.txt' % kind, ctx) - html_body = render_to_string('agendas/%s_reminder_body.html' % kind, ctx) - try: - with atomic(): - send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [email], html_message=html_body) - booking.email_reminder_datetime = timezone.now() - booking.save() - except SMTPException: - pass - - @staticmethod - def send_sms(phone_numbers, booking, kind, ctx): - if not settings.SMS_URL: - return - - message = render_to_string('agendas/%s_reminder_message.txt' % kind, ctx).strip() - payload = { - 'message': message, - 'from': settings.SMS_SENDER, - 'to': phone_numbers, - } - - try: - with atomic(): - request = requests.post( - settings.SMS_URL, json=payload, remote_service='auto', timeout=10, without_user=True - ) - request.raise_for_status() - booking.sms_reminder_datetime = timezone.now() - booking.save() - except RequestException: - pass diff --git a/chrono/agendas/management/commands/utils.py b/chrono/agendas/management/commands/utils.py new file mode 100644 index 00000000..4f16143c --- /dev/null +++ b/chrono/agendas/management/commands/utils.py @@ -0,0 +1,84 @@ +from smtplib import SMTPException + +from django.conf import settings +from django.core.mail import send_mail +from django.db.transaction import atomic +from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist +from django.template.loader import render_to_string +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ +from requests import RequestException + +from chrono.utils.requests_wrapper import requests + + +def send_reminder(booking, msg_type): + agenda = booking.event.agenda + kind = agenda.kind + days = getattr(agenda.reminder_settings, f'days_before_{msg_type}') + + ctx = { + 'booking': booking, + 'in_x_days': _('tomorrow') if days == 1 else _('in %s days') % days, + 'date': booking.event.start_datetime, + } + ctx.update(settings.TEMPLATE_VARS) + + for extra_info in ('email_extra_info', 'sms_extra_info'): + try: + ctx[extra_info] = Template(getattr(agenda.reminder_settings, extra_info)).render( + Context({'booking': booking}, autoescape=False) + ) + except (VariableDoesNotExist, TemplateSyntaxError): + pass + + if msg_type == 'email': + emails = set(booking.extra_emails) + if booking.user_email: + emails.add(booking.user_email) + + for email in emails: + send_email_reminder(email, booking, kind, ctx) + elif msg_type == 'sms': + phone_numbers = set(booking.extra_phone_numbers) + if booking.user_phone_number: + phone_numbers.add(booking.user_phone_number) + + if phone_numbers: + send_sms_reminder(list(phone_numbers), booking, kind, ctx) + + +def send_email_reminder(email, booking, kind, ctx): + subject = render_to_string('agendas/%s_reminder_subject.txt' % kind, ctx).strip() + body = render_to_string('agendas/%s_reminder_body.txt' % kind, ctx) + html_body = render_to_string('agendas/%s_reminder_body.html' % kind, ctx) + try: + with atomic(): + send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [email], html_message=html_body) + booking.email_reminder_datetime = timezone.now() + booking.save() + except SMTPException: + pass + + +def send_sms_reminder(phone_numbers, booking, kind, ctx): + if not settings.SMS_URL: + return + + message = render_to_string('agendas/%s_reminder_message.txt' % kind, ctx).strip() + payload = { + 'message': message, + 'from': settings.SMS_SENDER, + 'to': phone_numbers, + } + + try: + with atomic(): + request = requests.post( + settings.SMS_URL, json=payload, remote_service='auto', timeout=10, without_user=True + ) + request.raise_for_status() + booking.sms_reminder_datetime = timezone.now() + booking.save() + except RequestException: + pass diff --git a/tests/test_agendas.py b/tests/test_agendas.py index 61794d90..29096115 100644 --- a/tests/test_agendas.py +++ b/tests/test_agendas.py @@ -1737,7 +1737,7 @@ def test_agenda_reminders_retry(freezer): def send_mail_error(*args, **kwargs): raise smtplib.SMTPException - with mock.patch('chrono.agendas.management.commands.send_booking_reminders.send_mail') as mock_send: + with mock.patch('chrono.agendas.management.commands.utils.send_mail') as mock_send: mock_send.return_value = None mock_send.side_effect = send_mail_error call_command('send_booking_reminders') @@ -1787,7 +1787,7 @@ def test_agenda_reminders_retry(freezer): freezer.move_to('2020-01-02 15:00') with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send, mock.patch( - 'chrono.agendas.management.commands.send_booking_reminders.send_mail' + 'chrono.agendas.management.commands.utils.send_mail' ) as mock_send_mail: mock_response = mock.Mock(status_code=200) mock_send.return_value = mock_response @@ -1815,7 +1815,7 @@ def test_agenda_reminders_different_days_before(freezer): Booking.objects.create(event=event, user_email='t@test.org', user_phone_number='+336123456789') with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send_sms, mock.patch( - 'chrono.agendas.management.commands.send_booking_reminders.send_mail' + 'chrono.agendas.management.commands.utils.send_mail' ) as mock_send_mail: mock_response = mock.Mock(status_code=200) mock_send_sms.return_value = mock_response -- 2.30.2