Projet

Général

Profil

0003-manager-test-booking-reminders-sending-61234.patch

Valentin Deniaud, 15 février 2022 14:05

Télécharger (13,8 ko)

Voir les différences:

Subject: [PATCH 3/3] manager: test booking reminders sending (#61234)

 chrono/agendas/management/commands/utils.py   | 17 +---
 chrono/agendas/models.py                      | 14 +++
 chrono/manager/forms.py                       | 47 ++++++++-
 .../chrono/manager_agenda_settings.html       |  5 +
 .../chrono/manager_send_reminder_form.html    | 23 +++++
 chrono/manager/urls.py                        |  5 +
 chrono/manager/views.py                       | 26 +++++
 tests/manager/test_all.py                     | 98 +++++++++++++++++++
 8 files changed, 221 insertions(+), 14 deletions(-)
 create mode 100644 chrono/manager/templates/chrono/manager_send_reminder_form.html
chrono/agendas/management/commands/utils.py
32 32
        except (VariableDoesNotExist, TemplateSyntaxError):
33 33
            pass
34 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:
35
    if msg_type == 'email' and booking.emails:
36
        for email in booking.emails:
41 37
            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)
38
    elif msg_type == 'sms' and booking.phone_numbers:
39
        send_sms_reminder(booking.phone_numbers, booking, kind, ctx)
49 40

  
50 41

  
51 42
def send_email_reminder(email, booking, kind, ctx):
chrono/agendas/models.py
1897 1897
    def user_name(self):
1898 1898
        return ('%s %s' % (self.user_first_name, self.user_last_name)).strip()
1899 1899

  
1900
    @cached_property
1901
    def emails(self):
1902
        emails = set(self.extra_emails)
1903
        if self.user_email:
1904
            emails.add(self.user_email)
1905
        return list(emails)
1906

  
1907
    @cached_property
1908
    def phone_numbers(self):
1909
        phone_numbers = set(self.extra_phone_numbers)
1910
        if self.user_phone_number:
1911
            phone_numbers.add(self.user_phone_number)
1912
        return list(phone_numbers)
1913

  
1900 1914
    def cancel(self, trigger_callback=False):
1901 1915
        timestamp = now()
1902 1916
        with transaction.atomic():
chrono/manager/forms.py
26 26
from django.db import transaction
27 27
from django.forms import ValidationError
28 28
from django.utils.encoding import force_text
29
from django.utils.formats import date_format
29 30
from django.utils.six import StringIO
30
from django.utils.timezone import make_aware, now
31
from django.utils.timezone import localtime, make_aware, now
31 32
from django.utils.translation import ugettext_lazy as _
32 33

  
33 34
from chrono.agendas.models import (
......
926 927
            del self.fields['sms_extra_info']
927 928

  
928 929

  
930
class BookingChoiceField(forms.ModelChoiceField):
931
    def label_from_instance(self, obj):
932
        name = obj.user_name or obj.label or _('Anonymous')
933
        date = date_format(localtime(obj.creation_datetime), 'SHORT_DATETIME_FORMAT')
934
        emails = ', '.join(sorted(obj.emails)) or _('no email')
935
        phone_numbers = ', '.join(sorted(obj.phone_numbers)) or _('no phone number')
936

  
937
        if settings.SMS_URL:
938
            return '%s, %s, %s (%s)' % (name, emails, phone_numbers, date)
939
        else:
940
            return '%s, %s (%s)' % (name, emails, date)
941

  
942

  
943
class AgendaReminderTestForm(forms.Form):
944
    booking = BookingChoiceField(
945
        label=_('Booking'),
946
        queryset=Booking.objects.none(),
947
        help_text=_('Only the last ten bookings are displayed.'),
948
    )
949
    msg_type = forms.MultipleChoiceField(
950
        label=_('Send via'),
951
        choices=(('email', _('Email')), ('sms', _('SMS'))),
952
        widget=forms.CheckboxSelectMultiple(),
953
    )
954
    phone_number = forms.CharField(
955
        label=_('Phone number'),
956
        max_length=16,
957
        help_text=_('This will override phone number specified on booking creation, if any.'),
958
        required=False,
959
    )
960

  
961
    def __init__(self, *args, **kwargs):
962
        agenda = kwargs.pop('agenda')
963
        super().__init__(*args, **kwargs)
964
        self.fields['booking'].queryset = Booking.objects.filter(
965
            pk__in=Booking.objects.filter(event__agenda=agenda).order_by('-creation_datetime')[:10]
966
        ).order_by('-creation_datetime')
967

  
968
        if not settings.SMS_URL:
969
            self.fields['msg_type'].initial = ['email']
970
            self.fields['msg_type'].widget = forms.MultipleHiddenInput()
971
            del self.fields['phone_number']
972

  
973

  
929 974
class AgendasExportForm(forms.Form):
930 975
    agendas = forms.BooleanField(label=_('Agendas'), required=False, initial=True)
931 976
    unavailability_calendars = forms.BooleanField(
chrono/manager/templates/chrono/manager_agenda_settings.html
57 57
<a rel="popup" data-selector="#message-preview"  href="{% url 'chrono-manager-agenda-reminder-preview' pk=object.id type='sms' %}">{% trans "Preview SMS" %}</a>
58 58
{% endif %}
59 59
</p>
60
{% if agenda.reminder_settings.days_before_email or agenda.reminder_settings.days_before_sms %}
61
<p>
62
<a rel="popup" href="{% url 'chrono-manager-agenda-reminder-test' pk=object.pk %}">{% trans "Test reminder sending" %}</a>
63
</p>
64
{% endif %}
60 65
</div>
61 66
</div>
62 67
{% endblock %}
chrono/manager/templates/chrono/manager_send_reminder_form.html
1
{% extends "chrono/manager_agenda_view.html" %}
2
{% load i18n gadjo %}
3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
<a href="{% url 'chrono-manager-agenda-settings' agenda.pk %}">{% trans 'Settings' %}</a>
7
<a href="{% url 'chrono-manager-agenda-edit' agenda.pk %}">{{ title }}</a>
8
{% endblock %}
9

  
10
{% block appbar %}
11
<h2>{% trans "Test reminder sending" %}</h2>
12
{% endblock %}
13

  
14
{% block content %}
15
<form method="post" enctype="multipart/form-data">
16
  {% csrf_token %}
17
  {{ form|with_template }}
18
  <div class="buttons">
19
    <button class="submit-button">{% trans "Send" %}</button>
20
    <a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a>
21
  </div>
22
</form>
23
{% endblock %}
chrono/manager/urls.py
186 186
        views.agenda_reminder_settings,
187 187
        name='chrono-manager-agenda-reminder-settings',
188 188
    ),
189
    url(
190
        r'^agendas/(?P<pk>\d+)/reminder/test/$',
191
        views.agenda_reminder_test,
192
        name='chrono-manager-agenda-reminder-test',
193
    ),
189 194
    url(
190 195
        r'^agendas/(?P<pk>\d+)/reminder/preview/(?P<type>(email|sms))/$',
191 196
        views.agenda_reminder_preview,
chrono/manager/views.py
55 55
    View,
56 56
)
57 57

  
58
from chrono.agendas.management.commands.utils import send_reminder
58 59
from chrono.agendas.models import (
59 60
    AbsenceReason,
60 61
    AbsenceReasonGroup,
......
87 88
    AgendaEditForm,
88 89
    AgendaNotificationsForm,
89 90
    AgendaReminderForm,
91
    AgendaReminderTestForm,
90 92
    AgendaResourceForm,
91 93
    AgendaRolesForm,
92 94
    AgendasExportForm,
......
1901 1903
agenda_reminder_settings = AgendaReminderSettingsView.as_view()
1902 1904

  
1903 1905

  
1906
class AgendaReminderTestView(ManagedAgendaMixin, FormView):
1907
    template_name = 'chrono/manager_send_reminder_form.html'
1908
    form_class = AgendaReminderTestForm
1909

  
1910
    def get_form_kwargs(self):
1911
        kwargs = super(FormView, self).get_form_kwargs()
1912
        kwargs['agenda'] = self.agenda
1913
        return kwargs
1914

  
1915
    def form_valid(self, form):
1916
        booking = form.cleaned_data['booking']
1917

  
1918
        if form.cleaned_data.get('phone_number'):
1919
            booking.user_phone_number = form.cleaned_data['phone_number']
1920
            booking.extra_phone_numbers.clear()
1921

  
1922
        for msg_type in form.cleaned_data['msg_type']:
1923
            send_reminder(booking, msg_type)
1924
        return super().form_valid(form)
1925

  
1926

  
1927
agenda_reminder_test = AgendaReminderTestView.as_view()
1928

  
1929

  
1904 1930
class AgendaReminderPreviewView(ManagedAgendaMixin, TemplateView):
1905 1931
    template_name = 'chrono/manager_agenda_reminder_preview.html'
1906 1932

  
tests/manager/test_all.py
1 1
import datetime
2
import json
2 3
from unittest import mock
3 4

  
4 5
import freezegun
......
2977 2978
    assert '{{ booking.extra_data.xxx }}' in resp.text
2978 2979

  
2979 2980

  
2981
def test_manager_reminders_test_sending(app, admin_user, freezer, mailoutbox, settings):
2982
    agenda = Agenda.objects.create(label='Events', kind='events')
2983
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
2984
    AgendaReminderSettings.objects.create(
2985
        agenda=agenda,
2986
        days_before_email=1,
2987
        email_extra_info='Take your {{ booking.extra_data.document_type }}.',
2988
        days_before_sms=1,
2989
        sms_extra_info='Take {{ booking.extra_data.document_type }}.',
2990
    )
2991

  
2992
    login(app)
2993
    resp = app.get('/manage/agendas/%s/settings' % agenda.id)
2994
    resp = resp.click('Test reminder sending')
2995

  
2996
    assert 'phone_number' not in resp.form.fields
2997
    assert resp.form['msg_type'].attrs['type'] == 'hidden'
2998
    assert resp.form['booking'].options == [('', True, '---------')]
2999

  
3000
    # add bookings
3001
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
3002
    freezer.move_to('2020-01-01 14:00')
3003
    Booking.objects.create(user_first_name='oldest', user_email='t@test.org', event=event)
3004
    freezer.move_to('2020-01-02 14:00')
3005
    for _ in range(10):
3006
        Booking.objects.create(
3007
            event=event,
3008
            user_first_name='Jon',
3009
            user_last_name='Doe',
3010
            user_email='t@test.org',
3011
            user_phone_number='+336123456780',
3012
        )
3013
    freezer.move_to('2020-01-03 14:00')
3014
    last_booking = Booking.objects.create(
3015
        event=event,
3016
        user_first_name='Jane',
3017
        user_last_name='Doe',
3018
        user_email='t@test.org',
3019
        extra_emails=['u@test.org'],
3020
        user_phone_number='+33122334455',
3021
        extra_phone_numbers=['+33122334456'],
3022
        extra_data={'document_type': 'receipt'},
3023
    )
3024

  
3025
    resp = app.get('/manage/agendas/%s/reminder/test/' % agenda.id)
3026
    assert [x[2] for x in resp.form['booking'].options[:2]] == [
3027
        '---------',
3028
        'Jane Doe, t@test.org, u@test.org (01/03/2020 3 p.m.)',
3029
    ]
3030
    assert [x[2] for x in resp.form['booking'].options[2:]] == ['Jon Doe, t@test.org (01/02/2020 3 p.m.)'] * 9
3031

  
3032
    resp.form['booking'] = last_booking.pk
3033
    resp = resp.form.submit().follow()
3034

  
3035
    assert len(mailoutbox) == 2
3036
    assert {x.to[0] for x in mailoutbox} == {'t@test.org', 'u@test.org'}
3037
    assert all('Take your receipt' in mail.body for mail in mailoutbox)
3038
    mailoutbox.clear()
3039

  
3040
    settings.SMS_URL = 'https://passerelle.test.org/sms/send/'
3041
    settings.SMS_SENDER = 'EO'
3042

  
3043
    resp = app.get('/manage/agendas/%s/reminder/test/' % agenda.id)
3044
    assert [x[2] for x in resp.form['booking'].options[:2]] == [
3045
        '---------',
3046
        'Jane Doe, t@test.org, u@test.org, +33122334455, +33122334456 (01/03/2020 3 p.m.)',
3047
    ]
3048
    assert [x[2] for x in resp.form['booking'].options[2:]] == [
3049
        'Jon Doe, t@test.org, +336123456780 (01/02/2020 3 p.m.)'
3050
    ] * 9
3051

  
3052
    resp.form['booking'] = last_booking.pk
3053
    resp.form['msg_type'] = ['email', 'sms']
3054
    with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
3055
        mock_send.return_value = mock.Mock(status_code=200)
3056
        resp = resp.form.submit().follow()
3057

  
3058
    body = json.loads(mock_send.call_args[0][0].body.decode())
3059
    assert 'Take receipt' in body['message']
3060
    assert set(body['to']) == {'+33122334455', '+33122334456'}
3061

  
3062
    assert len(mailoutbox) == 2
3063
    mailoutbox.clear()
3064

  
3065
    resp = app.get('/manage/agendas/%s/reminder/test/' % agenda.id)
3066
    resp.form['booking'] = last_booking.pk
3067
    resp.form['msg_type'] = ['sms']
3068
    resp.form['phone_number'] = '+33333333333'
3069
    with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
3070
        mock_send.return_value = mock.Mock(status_code=200)
3071
        resp = resp.form.submit().follow()
3072

  
3073
    body = json.loads(mock_send.call_args[0][0].body.decode())
3074
    assert body['to'] == ['+33333333333']
3075
    assert len(mailoutbox) == 0
3076

  
3077

  
2980 3078
def test_manager_agenda_roles(app, admin_user, manager_user):
2981 3079
    agenda = Agenda.objects.create(label='Events', kind='events')
2982 3080
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
2983
-