manager: test booking reminders sending (#61234)

This commit is contained in:
Valentin Deniaud 2022-02-15 10:52:24 +01:00
parent 52a45656a1
commit aaf66f3930
8 changed files with 231 additions and 13 deletions

View File

@ -32,20 +32,11 @@ def send_reminder(booking, msg_type):
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:
if msg_type == 'email' and booking.emails:
for email in booking.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)
elif msg_type == 'sms' and booking.phone_numbers:
send_sms_reminder(booking.phone_numbers, booking, kind, ctx)
def send_email_reminder(email, booking, kind, ctx):

View File

@ -1933,6 +1933,20 @@ class Booking(models.Model):
def user_name(self):
return ('%s %s' % (self.user_first_name, self.user_last_name)).strip()
@cached_property
def emails(self):
emails = set(self.extra_emails)
if self.user_email:
emails.add(self.user_email)
return list(emails)
@cached_property
def phone_numbers(self):
phone_numbers = set(self.extra_phone_numbers)
if self.user_phone_number:
phone_numbers.add(self.user_phone_number)
return list(phone_numbers)
def cancel(self, trigger_callback=False):
timestamp = now()
with transaction.atomic():

View File

@ -30,6 +30,7 @@ from django.core.exceptions import FieldDoesNotExist
from django.db import transaction
from django.forms import ValidationError
from django.utils.encoding import force_text
from django.utils.formats import date_format
from django.utils.six import StringIO
from django.utils.timezone import localtime, make_aware, now
from django.utils.translation import ugettext_lazy as _
@ -1213,6 +1214,55 @@ class AgendaReminderForm(forms.ModelForm):
del self.fields['sms_extra_info']
class BookingChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
name = obj.user_name or obj.label or _('Anonymous')
date = date_format(localtime(obj.creation_datetime), 'SHORT_DATETIME_FORMAT')
emails = ', '.join(sorted(obj.emails)) or _('no email')
phone_numbers = ', '.join(sorted(obj.phone_numbers)) or _('no phone number')
if settings.SMS_URL:
return '%s, %s, %s (%s)' % (name, emails, phone_numbers, date)
else:
return '%s, %s (%s)' % (name, emails, date)
class AgendaReminderTestForm(forms.Form):
booking = BookingChoiceField(
label=_('Booking'),
queryset=Booking.objects.none(),
help_text=_('Only the last ten bookings are displayed.'),
)
msg_type = forms.MultipleChoiceField(
label=_('Send via'),
choices=(('email', _('Email')), ('sms', _('SMS'))),
widget=forms.CheckboxSelectMultiple(),
)
email = forms.EmailField(
label=_('Email'),
help_text=_('This will override email specified on booking creation, if any.'),
required=False,
)
phone_number = forms.CharField(
label=_('Phone number'),
max_length=16,
help_text=_('This will override phone number specified on booking creation, if any.'),
required=False,
)
def __init__(self, *args, **kwargs):
agenda = kwargs.pop('agenda')
super().__init__(*args, **kwargs)
self.fields['booking'].queryset = Booking.objects.filter(
pk__in=Booking.objects.filter(event__agenda=agenda).order_by('-creation_datetime')[:10]
).order_by('-creation_datetime')
if not settings.SMS_URL:
self.fields['msg_type'].initial = ['email']
self.fields['msg_type'].widget = forms.MultipleHiddenInput()
del self.fields['phone_number']
class AgendasExportForm(forms.Form):
agendas = forms.BooleanField(label=_('Agendas'), required=False, initial=True)
unavailability_calendars = forms.BooleanField(

View File

@ -57,6 +57,11 @@
<a rel="popup" data-selector="#message-preview" href="{% url 'chrono-manager-agenda-reminder-preview' pk=object.id type='sms' %}">{% trans "Preview SMS" %}</a>
{% endif %}
</p>
{% if agenda.reminder_settings.days_before_email or agenda.reminder_settings.days_before_sms %}
<p>
<a rel="popup" href="{% url 'chrono-manager-agenda-reminder-test' pk=object.pk %}">{% trans "Test reminder sending" %}</a>
</p>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "chrono/manager_agenda_view.html" %}
{% load i18n gadjo %}
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'chrono-manager-agenda-settings' agenda.pk %}">{% trans 'Settings' %}</a>
<a href="{% url 'chrono-manager-agenda-edit' agenda.pk %}">{{ title }}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans "Test reminder sending" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form|with_template }}
<div class="buttons">
<button class="submit-button">{% trans "Send" %}</button>
<a class="cancel" href="{% url 'chrono-manager-agenda-settings' pk=agenda.id %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -186,6 +186,11 @@ urlpatterns = [
views.agenda_reminder_settings,
name='chrono-manager-agenda-reminder-settings',
),
url(
r'^agendas/(?P<pk>\d+)/reminder/test/$',
views.agenda_reminder_test,
name='chrono-manager-agenda-reminder-test',
),
url(
r'^agendas/(?P<pk>\d+)/reminder/preview/(?P<type>(email|sms))/$',
views.agenda_reminder_preview,

View File

@ -58,6 +58,7 @@ from django.views.generic import (
from django.views.generic.dates import MonthMixin, YearMixin
from weasyprint import HTML
from chrono.agendas.management.commands.utils import send_reminder
from chrono.agendas.models import (
AbsenceReason,
AbsenceReasonGroup,
@ -93,6 +94,7 @@ from .forms import (
AgendaEditForm,
AgendaNotificationsForm,
AgendaReminderForm,
AgendaReminderTestForm,
AgendaResourceForm,
AgendaRolesForm,
AgendasExportForm,
@ -1916,6 +1918,34 @@ class AgendaReminderSettingsView(AgendaEditView):
agenda_reminder_settings = AgendaReminderSettingsView.as_view()
class AgendaReminderTestView(ManagedAgendaMixin, FormView):
template_name = 'chrono/manager_send_reminder_form.html'
form_class = AgendaReminderTestForm
def get_form_kwargs(self):
kwargs = super(FormView, self).get_form_kwargs()
kwargs['agenda'] = self.agenda
return kwargs
def form_valid(self, form):
booking = form.cleaned_data['booking']
if form.cleaned_data.get('phone_number'):
booking.user_phone_number = form.cleaned_data['phone_number']
booking.extra_phone_numbers.clear()
if form.cleaned_data.get('email'):
booking.user_email = form.cleaned_data['email']
booking.extra_emails.clear()
for msg_type in form.cleaned_data['msg_type']:
send_reminder(booking, msg_type)
return super().form_valid(form)
agenda_reminder_test = AgendaReminderTestView.as_view()
class AgendaReminderPreviewView(ManagedAgendaMixin, TemplateView):
template_name = 'chrono/manager_agenda_reminder_preview.html'

View File

@ -1,4 +1,5 @@
import datetime
import json
from unittest import mock
import freezegun
@ -2631,6 +2632,105 @@ def test_manager_reminders_preview(app, admin_user):
assert '{{ booking.extra_data.xxx }}' in resp.text
def test_manager_reminders_test_sending(app, admin_user, freezer, mailoutbox, settings):
agenda = Agenda.objects.create(label='Events', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
AgendaReminderSettings.objects.create(
agenda=agenda,
days_before_email=1,
email_extra_info='Take your {{ booking.extra_data.document_type }}.',
days_before_sms=1,
sms_extra_info='Take {{ booking.extra_data.document_type }}.',
)
login(app)
resp = app.get('/manage/agendas/%s/settings' % agenda.id)
resp = resp.click('Test reminder sending')
assert 'phone_number' not in resp.form.fields
assert resp.form['msg_type'].attrs['type'] == 'hidden'
assert resp.form['booking'].options == [('', True, '---------')]
# add bookings
event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10)
freezer.move_to('2020-01-01 14:00')
Booking.objects.create(user_first_name='oldest', user_email='t@test.org', event=event)
freezer.move_to('2020-01-02 14:00')
for _ in range(10):
Booking.objects.create(
event=event,
user_first_name='Jon',
user_last_name='Doe',
user_email='t@test.org',
user_phone_number='+336123456780',
)
freezer.move_to('2020-01-03 14:00')
last_booking = Booking.objects.create(
event=event,
user_first_name='Jane',
user_last_name='Doe',
user_email='t@test.org',
extra_emails=['u@test.org'],
user_phone_number='+33122334455',
extra_phone_numbers=['+33122334456'],
extra_data={'document_type': 'receipt'},
)
resp = app.get('/manage/agendas/%s/reminder/test/' % agenda.id)
assert [x[2] for x in resp.form['booking'].options[:2]] == [
'---------',
'Jane Doe, t@test.org, u@test.org (01/03/2020 3 p.m.)',
]
assert [x[2] for x in resp.form['booking'].options[2:]] == ['Jon Doe, t@test.org (01/02/2020 3 p.m.)'] * 9
resp.form['booking'] = last_booking.pk
resp = resp.form.submit().follow()
assert len(mailoutbox) == 2
assert {x.to[0] for x in mailoutbox} == {'t@test.org', 'u@test.org'}
assert all('Take your receipt' in mail.body for mail in mailoutbox)
mailoutbox.clear()
settings.SMS_URL = 'https://passerelle.test.org/sms/send/'
settings.SMS_SENDER = 'EO'
resp = app.get('/manage/agendas/%s/reminder/test/' % agenda.id)
assert [x[2] for x in resp.form['booking'].options[:2]] == [
'---------',
'Jane Doe, t@test.org, u@test.org, +33122334455, +33122334456 (01/03/2020 3 p.m.)',
]
assert [x[2] for x in resp.form['booking'].options[2:]] == [
'Jon Doe, t@test.org, +336123456780 (01/02/2020 3 p.m.)'
] * 9
resp.form['booking'] = last_booking.pk
resp.form['msg_type'] = ['email', 'sms']
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
mock_send.return_value = mock.Mock(status_code=200)
resp = resp.form.submit().follow()
body = json.loads(mock_send.call_args[0][0].body.decode())
assert 'Take receipt' in body['message']
assert set(body['to']) == {'+33122334455', '+33122334456'}
assert len(mailoutbox) == 2
mailoutbox.clear()
resp = app.get('/manage/agendas/%s/reminder/test/' % agenda.id)
resp.form['booking'] = last_booking.pk
resp.form['msg_type'] = ['sms', 'email']
resp.form['email'] = 'v@test.org'
resp.form['phone_number'] = '+33333333333'
with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
mock_send.return_value = mock.Mock(status_code=200)
resp = resp.form.submit().follow()
body = json.loads(mock_send.call_args[0][0].body.decode())
assert body['to'] == ['+33333333333']
assert len(mailoutbox) == 1
assert mailoutbox[0].to == ['v@test.org']
def test_manager_agenda_roles(app, admin_user, manager_user):
agenda = Agenda.objects.create(label='Events', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')