Projet

Général

Profil

0002-reminders-split-send-code-61234.patch

Valentin Deniaud, 15 février 2022 14:05

Télécharger (9,96 ko)

Voir les différences:

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
chrono/agendas/management/commands/send_booking_reminders.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
from datetime import datetime, timedelta
18
from smtplib import SMTPException
19 18

  
20 19
import pytz
21 20
from django.conf import settings
22
from django.core.mail import send_mail
23 21
from django.core.management.base import BaseCommand
24 22
from django.db.models import F
25
from django.db.transaction import atomic
26
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist
27
from django.template.loader import render_to_string
28 23
from django.utils import timezone, translation
29
from django.utils.translation import ugettext_lazy as _
30
from requests import RequestException
31 24

  
32 25
from chrono.agendas.models import Booking
33
from chrono.utils.requests_wrapper import requests
26

  
27
from .utils import send_reminder
34 28

  
35 29
SENDING_IN_PROGRESS = datetime(year=2, month=1, day=1, tzinfo=pytz.UTC)
36 30

  
......
66 60
            **{f'{msg_type}_reminder_datetime__isnull': True},
67 61
        ).select_related('event', 'event__agenda', 'event__agenda__reminder_settings')
68 62

  
69
        bookings_list = list(bookings)
63
        bookings_list = list(bookings.order_by('pk'))
70 64
        bookings_pk = list(bookings.values_list('pk', flat=True))
71 65
        bookings.update(**{f'{msg_type}_reminder_datetime': SENDING_IN_PROGRESS})
72 66

  
73 67
        try:
74 68
            for booking in bookings_list:
75
                self.send_reminder(booking, msg_type)
69
                send_reminder(booking, msg_type)
76 70
        finally:
77 71
            Booking.objects.filter(
78 72
                pk__in=bookings_pk, **{f'{msg_type}_reminder_datetime': SENDING_IN_PROGRESS}
79 73
            ).update(
80 74
                **{f'{msg_type}_reminder_datetime': None},
81 75
            )
82

  
83
    def send_reminder(self, booking, msg_type):
84
        agenda = booking.event.agenda
85
        kind = agenda.kind
86
        days = getattr(agenda.reminder_settings, f'days_before_{msg_type}')
87

  
88
        ctx = {
89
            'booking': booking,
90
            'in_x_days': _('tomorrow') if days == 1 else _('in %s days') % days,
91
            'date': booking.event.start_datetime,
92
        }
93
        ctx.update(settings.TEMPLATE_VARS)
94

  
95
        for extra_info in ('email_extra_info', 'sms_extra_info'):
96
            try:
97
                ctx[extra_info] = Template(getattr(agenda.reminder_settings, extra_info)).render(
98
                    Context({'booking': booking}, autoescape=False)
99
                )
100
            except (VariableDoesNotExist, TemplateSyntaxError):
101
                pass
102

  
103
        if msg_type == 'email':
104
            emails = set(booking.extra_emails)
105
            if booking.user_email:
106
                emails.add(booking.user_email)
107

  
108
            for email in emails:
109
                self.send_email(email, booking, kind, ctx)
110
        elif msg_type == 'sms':
111
            phone_numbers = set(booking.extra_phone_numbers)
112
            if booking.user_phone_number:
113
                phone_numbers.add(booking.user_phone_number)
114

  
115
            if phone_numbers:
116
                self.send_sms(list(phone_numbers), booking, kind, ctx)
117

  
118
    @staticmethod
119
    def send_email(email, booking, kind, ctx):
120
        subject = render_to_string('agendas/%s_reminder_subject.txt' % kind, ctx).strip()
121
        body = render_to_string('agendas/%s_reminder_body.txt' % kind, ctx)
122
        html_body = render_to_string('agendas/%s_reminder_body.html' % kind, ctx)
123
        try:
124
            with atomic():
125
                send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [email], html_message=html_body)
126
                booking.email_reminder_datetime = timezone.now()
127
                booking.save()
128
        except SMTPException:
129
            pass
130

  
131
    @staticmethod
132
    def send_sms(phone_numbers, booking, kind, ctx):
133
        if not settings.SMS_URL:
134
            return
135

  
136
        message = render_to_string('agendas/%s_reminder_message.txt' % kind, ctx).strip()
137
        payload = {
138
            'message': message,
139
            'from': settings.SMS_SENDER,
140
            'to': phone_numbers,
141
        }
142

  
143
        try:
144
            with atomic():
145
                request = requests.post(
146
                    settings.SMS_URL, json=payload, remote_service='auto', timeout=10, without_user=True
147
                )
148
                request.raise_for_status()
149
                booking.sms_reminder_datetime = timezone.now()
150
                booking.save()
151
        except RequestException:
152
            pass
chrono/agendas/management/commands/utils.py
1
from smtplib import SMTPException
2

  
3
from django.conf import settings
4
from django.core.mail import send_mail
5
from django.db.transaction import atomic
6
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist
7
from django.template.loader import render_to_string
8
from django.utils import timezone
9
from django.utils.translation import ugettext_lazy as _
10
from requests import RequestException
11

  
12
from chrono.utils.requests_wrapper import requests
13

  
14

  
15
def send_reminder(booking, msg_type):
16
    agenda = booking.event.agenda
17
    kind = agenda.kind
18
    days = getattr(agenda.reminder_settings, f'days_before_{msg_type}')
19

  
20
    ctx = {
21
        'booking': booking,
22
        'in_x_days': _('tomorrow') if days == 1 else _('in %s days') % days,
23
        'date': booking.event.start_datetime,
24
    }
25
    ctx.update(settings.TEMPLATE_VARS)
26

  
27
    for extra_info in ('email_extra_info', 'sms_extra_info'):
28
        try:
29
            ctx[extra_info] = Template(getattr(agenda.reminder_settings, extra_info)).render(
30
                Context({'booking': booking}, autoescape=False)
31
            )
32
        except (VariableDoesNotExist, TemplateSyntaxError):
33
            pass
34

  
35
    if msg_type == 'email':
36
        emails = set(booking.extra_emails)
37
        if booking.user_email:
38
            emails.add(booking.user_email)
39

  
40
        for email in emails:
41
            send_email_reminder(email, booking, kind, ctx)
42
    elif msg_type == 'sms':
43
        phone_numbers = set(booking.extra_phone_numbers)
44
        if booking.user_phone_number:
45
            phone_numbers.add(booking.user_phone_number)
46

  
47
        if phone_numbers:
48
            send_sms_reminder(list(phone_numbers), booking, kind, ctx)
49

  
50

  
51
def send_email_reminder(email, booking, kind, ctx):
52
    subject = render_to_string('agendas/%s_reminder_subject.txt' % kind, ctx).strip()
53
    body = render_to_string('agendas/%s_reminder_body.txt' % kind, ctx)
54
    html_body = render_to_string('agendas/%s_reminder_body.html' % kind, ctx)
55
    try:
56
        with atomic():
57
            send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, [email], html_message=html_body)
58
            booking.email_reminder_datetime = timezone.now()
59
            booking.save()
60
    except SMTPException:
61
        pass
62

  
63

  
64
def send_sms_reminder(phone_numbers, booking, kind, ctx):
65
    if not settings.SMS_URL:
66
        return
67

  
68
    message = render_to_string('agendas/%s_reminder_message.txt' % kind, ctx).strip()
69
    payload = {
70
        'message': message,
71
        'from': settings.SMS_SENDER,
72
        'to': phone_numbers,
73
    }
74

  
75
    try:
76
        with atomic():
77
            request = requests.post(
78
                settings.SMS_URL, json=payload, remote_service='auto', timeout=10, without_user=True
79
            )
80
            request.raise_for_status()
81
            booking.sms_reminder_datetime = timezone.now()
82
            booking.save()
83
    except RequestException:
84
        pass
tests/test_agendas.py
1737 1737
    def send_mail_error(*args, **kwargs):
1738 1738
        raise smtplib.SMTPException
1739 1739

  
1740
    with mock.patch('chrono.agendas.management.commands.send_booking_reminders.send_mail') as mock_send:
1740
    with mock.patch('chrono.agendas.management.commands.utils.send_mail') as mock_send:
1741 1741
        mock_send.return_value = None
1742 1742
        mock_send.side_effect = send_mail_error
1743 1743
        call_command('send_booking_reminders')
......
1787 1787
    freezer.move_to('2020-01-02 15:00')
1788 1788

  
1789 1789
    with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send, mock.patch(
1790
        'chrono.agendas.management.commands.send_booking_reminders.send_mail'
1790
        'chrono.agendas.management.commands.utils.send_mail'
1791 1791
    ) as mock_send_mail:
1792 1792
        mock_response = mock.Mock(status_code=200)
1793 1793
        mock_send.return_value = mock_response
......
1815 1815
    Booking.objects.create(event=event, user_email='t@test.org', user_phone_number='+336123456789')
1816 1816

  
1817 1817
    with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send_sms, mock.patch(
1818
        'chrono.agendas.management.commands.send_booking_reminders.send_mail'
1818
        'chrono.agendas.management.commands.utils.send_mail'
1819 1819
    ) as mock_send_mail:
1820 1820
        mock_response = mock.Mock(status_code=200)
1821 1821
        mock_send_sms.return_value = mock_response
1822
-