0001-agendas-use-custom-urls-in-bookings-56820.patch
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 |
- |