Projet

Général

Profil

0001-agendas-use-custom-urls-in-bookings-56820.patch

Emmanuel Cazenave, 21 septembre 2021 14:50

Télécharger (27,7 ko)

Voir les différences:

Subject: [PATCH] agendas: use custom urls in bookings (#56820)

 chrono/agendas/models.py                      |  7 ++
 .../agendas/events_reminder_body.html         |  2 +-
 .../agendas/events_reminder_body.txt          |  2 +-
 chrono/api/views.py                           | 25 +++++-
 .../manager_confirm_booking_cancellation.html |  8 +-
 .../manager_event_cancellation_report.html    |  2 +-
 .../chrono/manager_event_detail_fragment.html |  4 +-
 .../manager_meetings_agenda_day_view.html     |  2 +-
 .../manager_meetings_agenda_month_view.html   |  2 +-
 .../chrono/manager_resource_day_view.html     |  2 +-
 .../chrono/manager_resource_month_view.html   |  2 +-
 chrono/utils/requests_wrapper.py              | 17 ++++
 tests/api/test_fillslot.py                    | 34 +++++++
 tests/manager/test_all.py                     | 88 +++++++++++++++++++
 tests/manager/test_event.py                   | 58 ++++++++++++
 tests/manager/test_resource.py                | 38 ++++++++
 tests/test_agendas.py                         | 13 +++
 17 files changed, 290 insertions(+), 16 deletions(-)
chrono/agendas/models.py
50 50

  
51 51
from chrono.interval import Interval, IntervalSet
52 52
from chrono.utils.requests_wrapper import requests as requests_wrapper
53
from chrono.utils.requests_wrapper import translate_url
53 54

  
54 55
AGENDA_KINDS = (
55 56
    ('events', _('Events')),
......
1898 1899
        else:
1899 1900
            return ugettext('booked')
1900 1901

  
1902
    def get_form_url(self):
1903
        return translate_url(self.form_url)
1904

  
1905
    def get_backoffice_url(self):
1906
        return translate_url(self.backoffice_url)
1907

  
1901 1908

  
1902 1909
OpeningHour = collections.namedtuple('OpeningHour', ['begin', 'end'])
1903 1910

  
chrono/agendas/templates/agendas/events_reminder_body.html
30 30

  
31 31
{% if booking.form_url %}
32 32
{% with _("Edit or cancel booking") as button_label %}
33
{% include "emails/button-link.html" with url=booking.form_url label=button_label %}
33
{% include "emails/button-link.html" with url=booking.get_form_url label=button_label %}
34 34
{% endwith %}
35 35
{% endif %}
36 36
{% endblock %}
chrono/agendas/templates/agendas/events_reminder_body.txt
20 20
{% trans "More information:" %} {{ booking.event.url }}
21 21
{% endif %}
22 22
{% if booking.form_url %}
23
{% trans "If in need to cancel it, you can do so here:" %} {{ booking.form_url }}
23
{% trans "If in need to cancel it, you can do so here:" %} {{ booking.get_form_url }}
24 24
{% endif %}
25 25
{% endautoescape %}
26 26
{% endblock %}
chrono/api/views.py
17 17
import collections
18 18
import datetime
19 19
import itertools
20
import urllib.parse
20 21
import uuid
21 22

  
22 23
from django.conf import settings
......
644 645

  
645 646

  
646 647
def make_booking(event, payload, extra_data, primary_booking=None, in_waiting_list=False, color=None):
648
    def translate_url(field_name):
649
        res = payload.get(field_name, '')
650
        if res:
651
            source_url = urllib.parse.urlparse(res)
652
            for data in settings.KNOWN_SERVICES.values():
653
                for slug, service_data in data.items():
654
                    service_url = urllib.parse.urlparse(service_data['url'])
655
                    if source_url.netloc == service_url.netloc and source_url.scheme == service_url.scheme:
656
                        res = str(urllib.parse.urlunparse(('publik', slug, source_url.path, '', '', None)))
657
                        break
658
        return res
659

  
660
    backoffice_url, cancel_callback_url, form_url = (
661
        translate_url('backoffice_url'),
662
        translate_url('cancel_callback_url'),
663
        translate_url('form_url'),
664
    )
665

  
647 666
    return Booking(
648 667
        event_id=event.pk,
649 668
        in_waiting_list=getattr(event, 'in_waiting_list', in_waiting_list),
......
654 673
        user_last_name=payload.get('user_last_name') or payload.get('user_name') or '',
655 674
        user_email=payload.get('user_email', ''),
656 675
        user_phone_number=payload.get('user_phone_number', ''),
657
        form_url=payload.get('form_url', ''),
658
        backoffice_url=payload.get('backoffice_url', ''),
659
        cancel_callback_url=payload.get('cancel_callback_url', ''),
676
        form_url=form_url,
677
        backoffice_url=backoffice_url,
678
        cancel_callback_url=cancel_callback_url,
660 679
        user_display_label=payload.get('user_display_label', ''),
661 680
        extra_data=extra_data,
662 681
        color=color,
chrono/manager/templates/chrono/manager_confirm_booking_cancellation.html
7 7

  
8 8
{% block content %}
9 9
<form method="post">
10
  {% if object.backoffice_url and not object.cancel_callback_url %}
10
  {% if object.get_backoffice_url and not object.cancel_callback_url %}
11 11
  {% if not user.is_staff %}
12 12
  <p>{% trans "This booking has no callback url configured, cancellation must be handled from corresponding form." %}</p>
13
  <p><a href="{{ object.backoffice_url }}">{% trans "Open form" %}</a></p>
13
  <p><a href="{{ object.get_backoffice_url }}">{% trans "Open form" %}</a></p>
14 14
  {% else %}
15 15
  <div class="warningnotice">
16 16
  <p>{% trans "This booking has no callback url configured, cancellation should be handled from corresponding form in order to garantee a coherent situation." %}</p>
17 17
  </div>
18
  <p><a href="{{ object.backoffice_url }}">{% trans "Open form" %}</a></p>
18
  <p><a href="{{ object.get_backoffice_url }}">{% trans "Open form" %}</a></p>
19 19
  <p>{% trans "However, since you are an administrator, you can choose to cancel it anyway." %}</p>
20 20
  {% endif %}
21 21
  {% else %}
......
26 26
  </p>
27 27
  {% endif %}
28 28

  
29
  {% if not object.backoffice_url or object.cancel_callback_url or user.is_staff %}
29
  {% if not object.get_backoffice_url or object.cancel_callback_url or user.is_staff %}
30 30
  {% csrf_token %}
31 31
  <input type="hidden" name="next" value="{% firstof request.POST.next request.GET.next %}">
32 32
  {{ form.as_p }}
chrono/manager/templates/chrono/manager_event_cancellation_report.html
21 21
<p>{% trans "Cancellation failed for the following bookings:" %}</p>
22 22
<ul>
23 23
{% for booking, error in errors.items %}
24
<li><a href="{{ booking.backoffice_url }}">{{ booking.events_display }}</a>: {{ error }}</li>
24
<li><a href="{{ booking.get_backoffice_url }}">{{ booking.events_display }}</a>: {{ error }}</li>
25 25
{% endfor %}
26 26
</ul>
27 27
{% endblock %}
chrono/manager/templates/chrono/manager_event_detail_fragment.html
29 29
  <ul class="objects-list single-links">
30 30
    {% for booking in booked %}
31 31
    <li>
32
    <a {% if booking.backoffice_url %}href="{{ booking.backoffice_url }}"{% endif %}>{{ booking.events_display }}</a>
32
    <a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.events_display }}</a>
33 33
    {% if not booking.primary_booking %}
34 34
    <a rel="popup" class="delete" href="{% url 'chrono-manager-booking-cancel' pk=agenda.id booking_pk=booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
35 35
    {% else %}
......
53 53
<div>
54 54
  <ul class="objects-list single-links">
55 55
    {% for booking in waiting %}
56
    <li><a {% if booking.backoffice_url %}href="{{ booking.backoffice_url }}"{% endif %}>{{ booking.events_display }}</a></li>
56
    <li><a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.events_display }}</a></li>
57 57
    {% endfor %}
58 58
  </ul>
59 59
</div>
chrono/manager/templates/chrono/manager_meetings_agenda_day_view.html
46 46
          <div class="booking{% if booking.color %} booking-color-{{ booking.color.index }}{% endif %}"
47 47
              style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
48 48
            ><span class="start-time">{{booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
49
            <a {% if booking.backoffice_url %}href="{{booking.backoffice_url}}"{% endif %}>{{ booking.meetings_display }}</a>
49
            <a {% if booking.get_backoffice_url %}href="{{booking.get_backoffice_url}}"{% endif %}>{{ booking.meetings_display }}</a>
50 50
            <a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=booking.event.agenda_id booking_pk=booking.pk %}?next={{ request.path }}">{% trans "Cancel" %}</a>
51 51
            {% if booking.color %}<span class="booking-color-label booking-bg-color-{{ booking.color.index }}">{{ booking.color }}</span>{% endif %}
52 52
          </div>
chrono/manager/templates/chrono/manager_meetings_agenda_month_view.html
36 36
      {% for slot in day.infos.booked_slots %}
37 37
      <div class="booking{% if slot.booking.color %} booking-color-{{ slot.booking.color.index }}{% endif %}" style="left:{{ slot.css_left|stringformat:".1f" }}%;height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%;width:{{ slot.css_width|stringformat:".1f" }}%;">
38 38
        <span class="start-time">{{slot.booking.event.start_datetime|date:"TIME_FORMAT"}}</span>
39
        <a {% if slot.booking.backoffice_url %}href="{{slot.booking.backoffice_url}}"{% endif %}>{{ slot.booking.meetings_display }}</a>
39
        <a {% if slot.booking.get_backoffice_url %}href="{{slot.booking.get_backoffice_url}}"{% endif %}>{{ slot.booking.meetings_display }}</a>
40 40
          <a rel="popup" class="cancel" href="{% url 'chrono-manager-booking-cancel' pk=slot.booking.event.agenda_id booking_pk=slot.booking.id %}?next={{ request.path }}">{% trans "Cancel" %}</a>
41 41
        {% if not single_desk %}<span class="desk">{{ slot.desk }}</span>{% endif %}
42 42
        {% if slot.booking.color %}<span class="booking-color-label booking-bg-color-{{ slot.booking.color.index }}">{{ slot.booking.color }}</span>{% endif %}
chrono/manager/templates/chrono/manager_resource_day_view.html
40 40
                      <div class="booking"
41 41
                          style="height: {{ booking.css_height }}%; min-height: {{ booking.css_height }}%; top: {{ booking.css_top }}%;"
42 42
                        ><span class="start-time">{{ booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
43
                        <a {% if booking.backoffice_url %}href="{{ booking.backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
43
                        <a {% if booking.get_backoffice_url %}href="{{ booking.get_backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
44 44
                      </div>
45 45
                    {% endfor %}
46 46
                </td>
chrono/manager/templates/chrono/manager_resource_month_view.html
49 49
                {% for slot in day.infos.booked_slots %}
50 50
                <div class="booking" style="height:{{ slot.css_height|stringformat:".1f" }}%;min-height:{{ slot.css_height|stringformat:".1f" }}%;top:{{ slot.css_top|stringformat:".1f" }}%">
51 51
                    <span class="start-time">{{ slot.booking.event.start_datetime|date:"TIME_FORMAT" }}</span>
52
                    <a {% if slot.booking.backoffice_url %}href="{{ slot.booking.backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
52
                    <a {% if slot.booking.get_backoffice_url %}href="{{ slot.booking.get_backoffice_url }}"{% endif %}>{{ booking.meetings_display }}</a>
53 53
                </div>
54 54
                {% endfor %}
55 55
                {% endif %}
chrono/utils/requests_wrapper.py
30 30
from .signature import sign_url
31 31

  
32 32

  
33
def translate_url(url):
34
    if not url:
35
        return ''
36
    source_url = parse.urlparse(url)
37
    if source_url.scheme != 'publik':
38
        return url
39
    for data in settings.KNOWN_SERVICES.values():
40
        for slug, service_data in data.items():
41
            if slug == source_url.netloc:
42
                service_url = parse.urlparse(service_data['url'])
43
                return parse.urlunparse(
44
                    (service_url.scheme, service_url.netloc, source_url.path, '', '', None)
45
                )
46
    return ''
47

  
48

  
33 49
class NothingInCacheException(Exception):
34 50
    pass
35 51

  
......
45 61

  
46 62
class Requests(RequestsSession):
47 63
    def request(self, method, url, **kwargs):
64
        url = translate_url(url)
48 65
        remote_service = kwargs.pop('remote_service', None)
49 66
        cache_duration = kwargs.pop('cache_duration', 15)
50 67
        invalidate_cache = kwargs.pop('invalidate_cache', False)
tests/api/test_fillslot.py
2507 2507
        resp.json['err_desc']
2508 2508
        == 'Some events belong to agendas that are not present in querystring: second-agenda'
2509 2509
    )
2510

  
2511

  
2512
def test_url_translation(app, some_data, user):
2513
    app.authorization = ('Basic', ('john.doe', 'password'))
2514
    agenda_id = Agenda.objects.filter(label='Foo bar')[0].id
2515
    assert Booking.objects.count() == 0
2516
    resp = app.get('/api/agenda/%s/datetimes/' % agenda_id)
2517
    fillslot_url = resp.json['data'][0]['api']['fillslot_url']
2518

  
2519
    params = {
2520
        'backoffice_url': 'https://demarches.example.com/backoffice/foo/bar',
2521
        'cancel_callback_url': 'https://demarches.example.com/foo/bar/jump',
2522
        'form_url': 'https://demarches.example.com/foo/bar',
2523
    }
2524

  
2525
    # https://demarches.example.com not in KNOWN_SERVICES, no URL translation
2526
    resp = app.post_json(fillslot_url, params=params)
2527
    booking = Booking.objects.get(pk=resp.json['booking_id'])
2528
    assert booking.backoffice_url == 'https://demarches.example.com/backoffice/foo/bar'
2529
    assert booking.cancel_callback_url == 'https://demarches.example.com/foo/bar/jump'
2530
    assert booking.form_url == 'https://demarches.example.com/foo/bar'
2531

  
2532
    # http://example.org/ is in KNOWN_SERVICES, translation happens
2533
    params = {
2534
        'backoffice_url': 'http://example.org/backoffice/foo/bar',
2535
        'cancel_callback_url': 'http://example.org/foo/bar/jump',
2536
        'form_url': 'http://example.org/foo/bar',
2537
    }
2538

  
2539
    resp = app.post_json(fillslot_url, params=params)
2540
    booking = Booking.objects.get(pk=resp.json['booking_id'])
2541
    assert booking.backoffice_url == 'publik://default/backoffice/foo/bar'
2542
    assert booking.cancel_callback_url == 'publik://default/foo/bar/jump'
2543
    assert booking.form_url == 'publik://default/foo/bar'
tests/manager/test_all.py
1180 1180
    assert resp.pyquery.find('.exception-hours span')[1].text == 'Exception for the afternoon'
1181 1181

  
1182 1182

  
1183
@pytest.mark.parametrize(
1184
    'view',
1185
    (
1186
        '/manage/agendas/%(agenda)s/%(year)d/%(month)d/%(day)d/',
1187
        '/manage/agendas/%(agenda)s/%(year)d/%(month)d/',
1188
    ),
1189
)
1190
def test_agenda_day_month_view_backoffice_url_translation(
1191
    app, admin_user, manager_user, api_user, settings, view
1192
):
1193
    agenda = Agenda.objects.create(label='New Example', kind='meetings')
1194
    desk = Desk.objects.create(agenda=agenda, label='New Desk')
1195
    desk.save()
1196
    today = datetime.date.today()
1197
    meetingtype = MeetingType(agenda=agenda, label='Bar', duration=30)
1198
    meetingtype.save()
1199
    timeperiod = TimePeriod.objects.create(
1200
        desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 30)
1201
    )
1202
    timeperiod.save()
1203
    app.authorization = ('Basic', ('john.doe', 'password'))
1204
    login(app)
1205

  
1206
    resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
1207
    booking_url = resp.json['data'][0]['api']['fillslot_url']
1208

  
1209
    # unkown service, backoffice url stored and displayed as is
1210
    backoffice_url = 'http://example.net/foo/bar/'
1211
    resp = app.post(booking_url, params={'backoffice_url': backoffice_url})
1212
    cancel_url = resp.json['api']['cancel_url']
1213
    booking_id = resp.json['booking_id']
1214
    booking = Booking.objects.get(pk=booking_id)
1215
    assert booking.backoffice_url == backoffice_url
1216
    date = booking.event.start_datetime
1217
    url = view % {'agenda': agenda.id, 'year': date.year, 'month': date.month, 'day': date.day}
1218
    resp = app.get(url)
1219
    assert resp.text.count('div class="booking') == 1
1220
    assert backoffice_url in resp.text
1221

  
1222
    # reset booking
1223
    resp = app.post(cancel_url)
1224
    assert resp.json['err'] == 0
1225

  
1226
    # known service, backoffice url stored translated and displayed as it was passed
1227
    backoffice_url = 'http://example.org/backoffice/bar/'
1228
    resp = app.post(booking_url, params={'backoffice_url': backoffice_url})
1229
    cancel_url = resp.json['api']['cancel_url']
1230
    booking_id = resp.json['booking_id']
1231
    booking = Booking.objects.get(pk=booking_id)
1232
    assert booking.backoffice_url == 'publik://default/backoffice/bar/'
1233
    date = booking.event.start_datetime
1234
    resp = app.get(url)
1235
    assert resp.text.count('div class="booking') == 1
1236
    assert backoffice_url in resp.text
1237

  
1238

  
1183 1239
@pytest.mark.parametrize('kind', ['meetings', 'virtual'])
1184 1240
def test_agenda_day_view_late_meeting(app, admin_user, kind):
1185 1241
    today = datetime.date.today()
......
2658 2714
    app.get('/manage/agendas/%s/bookings/%s/cancel' % (agenda.pk, booking4.pk), status=404)
2659 2715

  
2660 2716

  
2717
def test_booking_cancellation_meetings_agenda_backoffice_url_translation(
2718
    app, admin_user, manager_user, managers_group, api_user
2719
):
2720
    agenda = Agenda.objects.create(label='Passeports', kind='meetings', view_role=managers_group)
2721
    desk = Desk.objects.create(agenda=agenda, label='Desk A')
2722
    meetingtype = MeetingType(agenda=agenda, label='passeport', duration=20)
2723
    meetingtype.save()
2724
    today = datetime.date(2018, 11, 10)  # fixed day
2725
    timeperiod_weekday = today.weekday()
2726
    timeperiod = TimePeriod(
2727
        desk=desk, weekday=timeperiod_weekday, start_time=datetime.time(10, 0), end_time=datetime.time(18, 0)
2728
    )
2729
    timeperiod.save()
2730

  
2731
    # book a slot
2732
    app.authorization = ('Basic', ('john.doe', 'password'))
2733
    bookings_resp = app.get('/api/agenda/%s/meetings/%s/datetimes/' % (agenda.slug, meetingtype.slug))
2734
    booking_url = bookings_resp.json['data'][0]['api']['fillslot_url']
2735
    booking_json = app.post_json(booking_url, params={'backoffice_url': 'http://example.org/'}).json
2736

  
2737
    booking = Booking.objects.get(pk=booking_json['booking_id'])
2738
    assert booking.backoffice_url == 'publik://default/'
2739
    date = booking.event.start_datetime
2740
    month_view_url = '/manage/agendas/%s/%d/%d/' % (agenda.id, date.year, date.month)
2741

  
2742
    app.reset()
2743
    login(app, username='admin', password='admin')
2744
    resp = app.get(month_view_url)
2745
    resp = resp.click('Cancel')
2746
    assert 'http://example.org/' in resp.text
2747

  
2748

  
2661 2749
def test_agenda_notifications(app, admin_user, managers_group):
2662 2750
    agenda = Agenda.objects.create(label='Events', kind='events')
2663 2751
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
tests/manager/test_event.py
412 412
    assert 'overbooking' in resp.text
413 413

  
414 414

  
415
def test_event_detail_backoffice_url_translation(app, admin_user):
416
    agenda = Agenda(label='Foo bar')
417
    agenda.save()
418
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
419
    event = Event(start_datetime=make_aware(datetime.datetime(2016, 2, 15, 17, 0)), places=10, agenda=agenda)
420
    event.save()
421
    Booking.objects.create(event=event, backoffice_url='publik://default/foo/')
422
    app = login(app)
423
    resp = app.get('/manage/agendas/%s/events/%s/' % (agenda.pk, event.pk))
424
    assert 'http://example.org/foo/' in resp.text
425

  
426

  
415 427
def test_delete_event(app, admin_user):
416 428
    agenda = Agenda(label='Foo bar')
417 429
    agenda.save()
......
987 999
    assert Booking.objects.filter(cancellation_datetime__isnull=False).count() == 5
988 1000

  
989 1001

  
1002
def test_event_cancellation_error_report_backofice_url_translation(app, admin_user):
1003
    agenda = Agenda.objects.create(label='Events', kind='events')
1004
    event = Event.objects.create(
1005
        label='xyz', start_datetime=now() + datetime.timedelta(days=1), places=10, agenda=agenda
1006
    )
1007
    day = event.start_datetime
1008

  
1009
    def mocked_requests_connection_error(*args, **kwargs):
1010
        raise requests.exceptions.ConnectionError('unreachable')
1011

  
1012
    for _ in range(5):
1013
        Booking.objects.create(
1014
            event=event,
1015
            cancel_callback_url='http://example.org/jump/trigger/',
1016
            backoffice_url='publik://default/',
1017
        )
1018

  
1019
    login(app)
1020
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
1021
    resp = resp.click('Cancellation error reports')
1022
    assert 'No error report' in resp.text
1023

  
1024
    resp = app.get('/manage/agendas/%d/events/%d/cancel' % (agenda.pk, event.pk))
1025
    resp = resp.form.submit().follow()
1026
    assert 'Cancellation in progress' in resp.text
1027

  
1028
    with mock.patch('chrono.utils.requests_wrapper.RequestsSession.send') as mock_send:
1029
        mock_response = mock.Mock(status_code=200)
1030
        mock_send.return_value = mock_response
1031
        mock_send.side_effect = mocked_requests_connection_error
1032
        call_command('cancel_events')
1033

  
1034
    event.refresh_from_db()
1035
    assert not event.cancelled and not event.cancellation_scheduled
1036
    assert not Booking.objects.filter(cancellation_datetime__isnull=False).exists()
1037

  
1038
    resp = app.get('/manage/agendas/%s/%d/%d/' % (agenda.id, day.year, day.month))
1039
    assert 'Errors occured during cancellation of event "xyz".' in resp.text
1040

  
1041
    resp = resp.click('Cancellation error reports')
1042
    assert '(5 failures)' in resp.text
1043

  
1044
    resp = resp.click(str(event))
1045
    assert 'http://example.org/' in resp.text
1046

  
1047

  
990 1048
def test_event_cancellation_forbidden(app, admin_user):
991 1049
    agenda = Agenda.objects.create(label='Events', kind='events')
992 1050
    event = Event.objects.create(
tests/manager/test_resource.py
560 560
    resp = resp.follow()
561 561
    assert '/manage/resource/%s/' % resource.pk not in resp.text
562 562
    assert '/manage/agendas/%s/resource/%s/delete/' % (agenda.pk, resource.pk) not in resp.text
563

  
564

  
565
@pytest.mark.parametrize(
566
    'view',
567
    (
568
        '/manage/resource/%(resource)s/%(year)d/%(month)d/%(day)d/',
569
        '/manage/resource/%(resource)s/%(year)d/%(month)d/',
570
    ),
571
)
572
def test_agenda_day_month_view_backoffice_url_translation(
573
    app, admin_user, manager_user, api_user, settings, view
574
):
575
    today = datetime.date.today()
576
    resource = Resource.objects.create(label='Foo bar')
577
    agenda = Agenda.objects.create(label='Agenda', kind='meetings')
578
    desk = Desk.objects.create(agenda=agenda, label='Desk')
579
    meetingtype = MeetingType.objects.create(agenda=agenda, label='Bar', duration=30)
580
    TimePeriod.objects.create(
581
        desk=desk, weekday=today.weekday(), start_time=datetime.time(10, 0), end_time=datetime.time(18, 30)
582
    )
583

  
584
    login(app)
585
    url = view % {'resource': resource.id, 'year': today.year, 'month': today.month, 'day': today.day}
586

  
587
    # book some slots
588
    for hour, minute in [(10, 30), (14, 0)]:
589
        event = Event.objects.create(
590
            agenda=agenda,
591
            places=1,
592
            desk=desk,
593
            meeting_type=meetingtype,
594
            start_datetime=now().replace(hour=hour, minute=minute),
595
        )
596
        event.resources.add(resource)
597
        Booking.objects.create(event=event, backoffice_url='publik://default/foo/')
598

  
599
    resp = app.get(url)
600
    assert 'http://example.org/foo/' in resp.text
tests/test_agendas.py
1784 1784
    assert 'Edit or cancel booking' in mail.alternatives[0][0]
1785 1785
    assert 'href="https://example.org/"' in mail.alternatives[0][0]
1786 1786

  
1787
    # check url translation
1788
    Booking.objects.all().delete()
1789
    mailoutbox.clear()
1790
    freezer.move_to('2020-01-01 14:00')
1791
    Booking.objects.create(event=event, user_email='t@test.org', form_url='publik://default/someform/1/')
1792
    freezer.move_to('2020-01-02 15:00')
1793
    call_command('send_booking_reminders')
1794

  
1795
    mail = mailoutbox[0]
1796
    assert 'If in need to cancel it, you can do so here: http://example.org/someform/1/' in mail.body
1797
    assert 'Edit or cancel booking' in mail.alternatives[0][0]
1798
    assert 'href="http://example.org/someform/1/"' in mail.alternatives[0][0]
1799

  
1787 1800

  
1788 1801
@override_settings(SMS_URL='https://passerelle.test.org/sms/send/', SMS_SENDER='EO', TIME_ZONE='UTC')
1789 1802
def test_agenda_reminders_sms_content(freezer):
1790
-