0001-add-calendar-cell-model-16393.patch
combo/apps/chrono/README | ||
---|---|---|
1 |
Combo calendar cell |
|
2 |
================= |
|
3 | ||
4 |
To be visible, this cell needs a 'chrono' entry in settings.KNOWN_SERVICES |
|
5 | ||
6 |
Example of session_var definition |
|
7 | ||
8 |
{ |
|
9 |
"nusery_id": "southpark", |
|
10 |
"date_debut": "$start", |
|
11 |
"date_fin": "$end" |
|
12 |
} |
|
13 | ||
14 |
$start and $end will be replaced by event start_datetime and end_datetime |
|
15 |
and all keys in the session_vars dick will be prefixed by a <session_var_[key]>. |
|
16 |
The result will be: |
|
17 | ||
18 |
session_var_nursery_id=southapark&dsession_var_date_debut=2017-05-19T13:30:00&session_var_date_fin=2017-05-19T15:30:00 |
combo/apps/chrono/__init__.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2017 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import django.apps |
|
18 | ||
19 | ||
20 |
class AppConfig(django.apps.AppConfig): |
|
21 |
name = 'combo.apps.chrono' |
|
22 | ||
23 |
def get_before_urls(self): |
|
24 |
from . import urls |
|
25 |
return urls.urlpatterns |
|
26 | ||
27 | ||
28 |
default_app_config = 'combo.apps.chrono.AppConfig' |
combo/apps/chrono/migrations/0001_initial.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 |
import jsonfield.fields |
|
6 |
import datetime |
|
7 | ||
8 | ||
9 |
class Migration(migrations.Migration): |
|
10 | ||
11 |
dependencies = [ |
|
12 |
('auth', '0006_require_contenttypes_0002'), |
|
13 |
('data', '0025_jsoncell_varnames_str'), |
|
14 |
] |
|
15 | ||
16 |
operations = [ |
|
17 |
migrations.CreateModel( |
|
18 |
name='CalendarCell', |
|
19 |
fields=[ |
|
20 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
21 |
('placeholder', models.CharField(max_length=20)), |
|
22 |
('order', models.PositiveIntegerField()), |
|
23 |
('slug', models.SlugField(verbose_name='Slug', blank=True)), |
|
24 |
('extra_css_class', models.CharField(max_length=100, verbose_name='Extra classes for CSS styling', blank=True)), |
|
25 |
('public', models.BooleanField(default=True, verbose_name='Public')), |
|
26 |
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')), |
|
27 |
('last_update_timestamp', models.DateTimeField(auto_now=True)), |
|
28 |
('title', models.CharField(max_length=128, verbose_name='Title')), |
|
29 |
('events_source', models.URLField(verbose_name='Events source URL')), |
|
30 |
('form_url', models.URLField(verbose_name='Application form URL')), |
|
31 |
('session_vars', jsonfield.fields.JSONField(default=dict, verbose_name='Session vars')), |
|
32 |
('slot_duration', models.TimeField(default=datetime.time(0, 30), verbose_name='Slot duration')), |
|
33 |
('minimal_event_duration', models.TimeField(default=datetime.time(2, 0), verbose_name='Minimal event duration')), |
|
34 |
('business_hours_start', models.TimeField(default=datetime.time(8, 0), verbose_name='Business hour start')), |
|
35 |
('business_hours_end', models.TimeField(default=datetime.time(18, 0), verbose_name='Business hour end')), |
|
36 |
('event_default_title', models.CharField(max_length=32, verbose_name='Event default title')), |
|
37 |
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)), |
|
38 |
('page', models.ForeignKey(to='data.Page')), |
|
39 |
], |
|
40 |
options={ |
|
41 |
'verbose_name': 'Calendar Cell', |
|
42 |
}, |
|
43 |
), |
|
44 |
] |
combo/apps/chrono/models.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2017 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import json |
|
18 | ||
19 |
from django.db import models |
|
20 |
from django.utils.translation import ugettext_lazy as _ |
|
21 |
from django.utils.dateparse import parse_time |
|
22 | ||
23 |
from combo.data.models import CellBase |
|
24 |
from combo.data.library import register_cell_class |
|
25 |
from .utils import is_chrono_enabled |
|
26 | ||
27 |
from jsonfield import JSONField |
|
28 | ||
29 | ||
30 |
@register_cell_class |
|
31 |
class CalendarCell(CellBase): |
|
32 | ||
33 |
title = models.CharField(_('Title'), max_length=128) |
|
34 |
events_source = models.URLField(_('Events source URL')) |
|
35 |
form_url = models.URLField(_('Application form URL')) |
|
36 |
session_vars = JSONField(_('Session vars')) |
|
37 |
slot_duration = models.TimeField( |
|
38 |
_('Slot duration'), default=parse_time('00:30')) |
|
39 |
minimal_event_duration = models.TimeField( |
|
40 |
_('Minimal event duration'), default=parse_time('02:00')) |
|
41 |
business_hours_start = models.TimeField( |
|
42 |
_('Business hour start'), default=parse_time('08:00')) |
|
43 |
business_hours_end = models.TimeField( |
|
44 |
_('Business hour end'), default=parse_time('18:00')) |
|
45 |
event_default_title = models.CharField( |
|
46 |
_('Event default title'), max_length=32) |
|
47 | ||
48 |
template_name = 'chrono/calendar.html' |
|
49 | ||
50 |
class Meta: |
|
51 |
verbose_name = _('Calendar Cell') |
|
52 | ||
53 |
class Media: |
|
54 |
js = ( |
|
55 |
'xstatic/lib/moment.min.js', |
|
56 |
'xstatic/fullcalendar.min.js', |
|
57 |
'xstatic/locale-all.js', |
|
58 |
'chrono/js/calendar.js' |
|
59 |
) |
|
60 |
css = {'all': ('xstatic/fullcalendar.min.css',)} |
|
61 | ||
62 |
@classmethod |
|
63 |
def is_enabled(cls): |
|
64 |
return is_chrono_enabled() |
|
65 | ||
66 |
def render(self, context): |
|
67 |
include = ['title', 'slot_duration', 'business_hours_start', 'business_hours_end', |
|
68 |
'minimal_event_duration', 'event_default_title'] |
|
69 |
context['chrono'] = json.dumps({key: str(value) for key, value in vars(self).items() |
|
70 |
if key in include}) |
|
71 |
return super(CalendarCell, self).render(context) |
combo/apps/chrono/static/chrono/js/calendar.js | ||
---|---|---|
1 |
$(function() { |
|
2 | ||
3 |
function renderCal(cal_id){ |
|
4 |
$(cal_id).fullCalendar({ |
|
5 |
locale: locale, |
|
6 |
header: { |
|
7 |
left: "prev,next today", |
|
8 |
center: "title", |
|
9 |
right: "agendaWeek,agendaDay" |
|
10 |
}, |
|
11 |
defaultView: "agendaWeek", |
|
12 |
weekends: false, |
|
13 |
allDaySlot: false, |
|
14 |
slotDuration: chrono.slot_duration, |
|
15 |
forceEventDuration: true, |
|
16 |
defaultTimedEventDuration: chrono.default_event_duration, |
|
17 |
scrollTime: "08:00", |
|
18 |
businessHours: { |
|
19 |
start: chrono.business_hours_start, |
|
20 |
end: chrono.business_hours_end |
|
21 |
}, |
|
22 |
events: events_source_url, |
|
23 |
eventOverlap: function(stillEvent, movingEvent) { |
|
24 |
return true; |
|
25 |
}, |
|
26 |
editable: true, |
|
27 |
selectable: true, |
|
28 |
selectHelper: true, |
|
29 |
unselectAuto: false, |
|
30 |
selectConstraint: { |
|
31 |
start: chrono.business_hours_start, |
|
32 |
end: chrono.business_hours_end, |
|
33 |
id: 'available' |
|
34 |
}, |
|
35 |
select: function(start, end) { |
|
36 |
// can't select in the past |
|
37 |
if (start.isBefore(moment())){ |
|
38 |
$(cal_id).fullCalendar("unselect"); |
|
39 |
return false; |
|
40 |
} |
|
41 |
var title = chrono.event_default_title; |
|
42 |
var eventData; |
|
43 |
if (title && title.trim()) { |
|
44 |
eventData = { |
|
45 |
title: title, |
|
46 |
start: start, |
|
47 |
end: end |
|
48 |
}; |
|
49 |
$(cal_id).fullCalendar("renderEvent", eventData); |
|
50 |
} |
|
51 |
$(cal_id).fullCalendar("unselect"); |
|
52 |
}, |
|
53 |
eventRender: function(event, element) { |
|
54 |
var start = moment(event.start).fromNow(); |
|
55 |
element.attr("title", start); |
|
56 |
}, |
|
57 |
loading: function() { |
|
58 |
}, |
|
59 |
eventClick: function(calEvent, jsEvent, view){ |
|
60 |
if (calEvent.source){ |
|
61 |
console.log(calEvent.source); |
|
62 |
return false; |
|
63 |
} |
|
64 |
params = { |
|
65 |
start: calEvent.start.format(), |
|
66 |
end: calEvent.end.format() |
|
67 |
} |
|
68 |
form_url = events_booking_url + "?" + $.param(params) |
|
69 |
$.ajax({ |
|
70 |
type: "POST", |
|
71 |
url: events_booking_url, |
|
72 |
data: JSON.stringify({ |
|
73 |
start: calEvent.start.format(), |
|
74 |
end: calEvent.end.format() |
|
75 |
}), |
|
76 |
dataType: "json", |
|
77 |
success: function(response){ |
|
78 |
console.log(response.url); |
|
79 |
window.location = response.url; |
|
80 |
}, |
|
81 |
error: function(xhr, status, error){ |
|
82 |
console.log(JSON.stringify(status)); |
|
83 |
} |
|
84 |
}) |
|
85 |
}, |
|
86 |
}); |
|
87 |
} |
|
88 |
$("div[data-calendar]").each(function(i, cal){ |
|
89 |
renderCal("#" + cal.id); |
|
90 |
}); |
|
91 |
}); |
combo/apps/chrono/templates/chrono/calendar.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 | ||
3 |
<h2>{{title}}</h2> |
|
4 | ||
5 |
<script type="text/javascript"> |
|
6 |
var events_source_url = "{% url 'chrono-events' cell.pk %}"; |
|
7 |
var events_booking_url = "{% url 'chrono-booking' cell.pk %}"; |
|
8 |
var chrono = JSON.parse(unescape('{{chrono | safe }}')); |
|
9 |
var locale = "{{LANGUAGE_CODE|slice:"-3"}}" ; |
|
10 |
</script> |
|
11 | ||
12 |
<div id="calendar_{{cell.pk}}" data-calendar="{{cell.pk}}"> |
|
13 |
</div> |
combo/apps/chrono/urls.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2017 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf.urls import url |
|
18 | ||
19 |
from .views import EventsView, BookingView |
|
20 | ||
21 |
urlpatterns = [ |
|
22 |
url(r'^chrono/events/(?P<pk>[\w,-]+)/', EventsView.as_view(), name='chrono-events'), |
|
23 |
url(r'^chrono/book/(?P<pk>[\w,-]+)/', BookingView.as_view(), name='chrono-booking'), |
|
24 |
] |
combo/apps/chrono/utils.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2017 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf import settings |
|
18 |
from combo.utils import requests |
|
19 | ||
20 | ||
21 |
def get_chrono_service(): |
|
22 |
if hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get('chrono'): |
|
23 |
return settings.KNOWN_SERVICES['chrono'].values()[0] |
|
24 | ||
25 | ||
26 |
def is_chrono_enabled(): |
|
27 |
return get_chrono_service() |
|
28 | ||
29 | ||
30 |
def convert_widget_format(events): |
|
31 |
data = [] |
|
32 |
for event in events.get('data'): |
|
33 |
data.append({ |
|
34 |
'id': 'available', |
|
35 |
'title': event.get('text'), |
|
36 |
'start': event.get('datetime'), |
|
37 |
'editable': False, |
|
38 |
'durationEditable': False, |
|
39 |
'rendering': 'inverse-background' |
|
40 |
}) |
|
41 |
return data |
|
42 | ||
43 | ||
44 |
def get_chrono_events(chrono_url, **kwargs): |
|
45 |
response = requests.get( |
|
46 |
chrono_url, |
|
47 |
headers={'accept': 'application/json'}, |
|
48 |
**kwargs) |
|
49 |
data = convert_widget_format(response.json()) |
|
50 |
return data |
|
51 | ||
52 | ||
53 |
def build_session_vars(cell, data): |
|
54 |
session_vars = {} |
|
55 |
for key, value in cell.session_vars.items(): |
|
56 |
key = 'session_var_%s' % key |
|
57 |
value = value.strip('$') |
|
58 |
if data.get(value): |
|
59 |
session_vars.update({key: data[value]}) |
|
60 |
else: |
|
61 |
session_vars.update({key: value}) |
|
62 |
return session_vars |
combo/apps/chrono/views.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2017 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import json |
|
18 |
import urllib |
|
19 | ||
20 |
from django.http import JsonResponse |
|
21 |
from django.views.decorators.csrf import csrf_exempt |
|
22 |
from django.views.generic import View |
|
23 |
from django.views.generic.detail import SingleObjectMixin |
|
24 | ||
25 |
from .models import CalendarCell |
|
26 |
from .utils import get_chrono_events, build_session_vars |
|
27 | ||
28 | ||
29 |
class EventsView(SingleObjectMixin, View): |
|
30 | ||
31 |
http_method_names = ['get'] |
|
32 |
model = CalendarCell |
|
33 | ||
34 |
def get(self, request, *args, **kwargs): |
|
35 |
cell = self.get_object() |
|
36 |
data = get_chrono_events(cell.events_source) |
|
37 |
return JsonResponse(data, safe=False) |
|
38 | ||
39 | ||
40 |
class BookingView(SingleObjectMixin, View): |
|
41 | ||
42 |
http_method_names = ['post'] |
|
43 |
model = CalendarCell |
|
44 | ||
45 |
@csrf_exempt |
|
46 |
def dispatch(self, request, *args, **kwargs): |
|
47 |
return super(BookingView, self).dispatch(request, *args, **kwargs) |
|
48 | ||
49 |
def post(self, request, *args, **kwargs): |
|
50 |
data = json.loads(request.body) |
|
51 |
cell = self.get_object() |
|
52 |
params = build_session_vars(cell, data) |
|
53 |
url = '%s?%s' % (cell.form_url, urllib.urlencode(params)) |
|
54 |
return JsonResponse({'url': url}, safe=False) |
combo/settings.py | ||
---|---|---|
75 | 75 |
'combo.apps.notifications', |
76 | 76 |
'combo.apps.search', |
77 | 77 |
'combo.apps.usersearch', |
78 |
'combo.apps.chrono', |
|
78 | 79 |
'haystack', |
79 | 80 |
'xstatic.pkg.chartnew_js', |
81 |
'xstatic.pkg.fullcalendar', |
|
80 | 82 |
) |
81 | 83 | |
82 | 84 |
INSTALLED_APPS = plugins.register_plugins_apps(INSTALLED_APPS) |
debian/control | ||
---|---|---|
18 | 18 |
python-xstatic-chartnew-js, |
19 | 19 |
python-eopayment (>= 1.9), |
20 | 20 |
python-django-haystack (>= 2.4.0) |
21 |
python-xstatic-fullcalendar (>= 3.4.0) |
|
21 | 22 |
Recommends: python-django-mellon, python-whoosh |
22 | 23 |
Conflicts: python-lingo |
23 | 24 |
Description: Portal Management System (Python module) |
requirements.txt | ||
---|---|---|
11 | 11 |
djangorestframework>=3.3, <3.4 |
12 | 12 |
django-haystack |
13 | 13 |
whoosh |
14 |
-e git+git@repos.entrouvert.org:debian/xstatic-fullcalendar.git@1b71292fa08859bda1f4412bcfe77714d14cd861#egg=XStatic_fullCalendar |
setup.py | ||
---|---|---|
116 | 116 |
'djangorestframework>=3.3, <3.4', |
117 | 117 |
'django-haystack', |
118 | 118 |
'whoosh', |
119 |
'XStatic-fullcalendar>=3.4.0' |
|
119 | 120 |
], |
120 | 121 |
zip_safe=False, |
121 | 122 |
cmdclass={ |
tests/test_calendar.py | ||
---|---|---|
1 |
import json |
|
2 |
import urlparse |
|
3 | ||
4 |
import pytest |
|
5 |
import mock |
|
6 | ||
7 |
from django.utils.dateparse import parse_time |
|
8 | ||
9 |
from combo.data.models import Page |
|
10 |
from combo.apps.chrono.models import CalendarCell |
|
11 | ||
12 |
pytestmark = pytest.mark.django_db |
|
13 | ||
14 |
CHRONO_EVENTS = { |
|
15 |
"data": [ |
|
16 |
{ |
|
17 |
"disabled": True, |
|
18 |
"text": "19 mai 2017 08:00", |
|
19 |
"api": { |
|
20 |
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/86/" |
|
21 |
}, |
|
22 |
"id": 86, |
|
23 |
"datetime": "2017-05-19 08:00:00" |
|
24 |
}, |
|
25 |
{ |
|
26 |
"disabled": True, |
|
27 |
"text": "19 mai 2017 08:30", |
|
28 |
"api": { |
|
29 |
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/87/" |
|
30 |
}, |
|
31 |
"id": 87, |
|
32 |
"datetime": "2017-05-19 08:30:00" |
|
33 |
}, |
|
34 |
{ |
|
35 |
"disabled": True, |
|
36 |
"text": "19 mai 2017 09:00", |
|
37 |
"api": { |
|
38 |
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/88/" |
|
39 |
}, |
|
40 |
"id": 88, |
|
41 |
"datetime": "2017-05-19 09:00:00" |
|
42 |
}, |
|
43 |
{ |
|
44 |
"disabled": True, |
|
45 |
"text": "19 mai 2017 09:30", |
|
46 |
"api": { |
|
47 |
"fillslot_url": "http://example.net/api/agenda/whatever/fillslot/89/" |
|
48 |
}, |
|
49 |
"id": 89, |
|
50 |
"datetime": "2017-05-19 09:30:00" |
|
51 |
} |
|
52 |
] |
|
53 |
} |
|
54 | ||
55 | ||
56 |
class MockedRequestResponse(mock.Mock): |
|
57 | ||
58 |
def json(self): |
|
59 |
return json.loads(self.content) |
|
60 | ||
61 | ||
62 |
@pytest.fixture |
|
63 |
def cell(db): |
|
64 |
page = Page.objects.create() |
|
65 |
cell = CalendarCell( |
|
66 |
page=page, title='Example Of Calendar', order=1, |
|
67 |
events_source='http://example.net/api/events/', |
|
68 |
form_url='http://example.net/form/whatever/', |
|
69 |
session_vars={ |
|
70 |
"start_dt": "$start", "end_dt": "$end", |
|
71 |
"whatever_slug": "whatever" |
|
72 |
}, |
|
73 |
slot_duration=parse_time('00:30'), |
|
74 |
business_hours_start=parse_time('08:00'), |
|
75 |
business_hours_end=parse_time('18:00'), |
|
76 |
event_default_title='Available' |
|
77 |
) |
|
78 |
cell.save() |
|
79 |
return cell |
|
80 | ||
81 | ||
82 |
@mock.patch('combo.apps.chrono.utils.requests.get') |
|
83 |
def test_get_events(mocked_get, app, cell): |
|
84 |
mocked_get.return_value = MockedRequestResponse(content=json.dumps(CHRONO_EVENTS)) |
|
85 |
resp = app.get('/chrono/events/%s/' % cell.pk) |
|
86 |
assert len(resp.json) == 4 |
|
87 |
for datum in resp.json: |
|
88 |
assert datum['id'] == 'available' |
|
89 |
assert datum['rendering'] == 'inverse-background' |
|
90 | ||
91 | ||
92 |
def test_redirection_session_vars(app, cell, settings): |
|
93 |
params = { |
|
94 |
'start': '2017-05-19T10:30:23', |
|
95 |
'end': '2017-05-19T12:30:14', |
|
96 |
} |
|
97 |
resp = app.post_json('/chrono/book/%s/' % cell.pk, params=params) |
|
98 |
parsed = urlparse.urlparse(resp.json['url']) |
|
99 |
qs = urlparse.parse_qs(parsed.query) |
|
100 |
assert qs['session_var_whatever_slug'] == ['whatever'] |
|
101 |
assert qs['session_var_start_dt'] == [params['start']] |
|
102 |
assert qs['session_var_end_dt'] == [params['end']] |
|
0 |
- |