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/forms.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 import forms |
|
18 | ||
19 |
from .models import CalendarCell |
|
20 |
from .utils import get_calendar, get_agendas, get_formsdef |
|
21 | ||
22 | ||
23 |
class CalendarCellForm(forms.ModelForm): |
|
24 |
class Meta: |
|
25 |
model = CalendarCell |
|
26 |
fields = ( |
|
27 |
'title', 'agenda_reference', 'formdef_reference', |
|
28 |
'session_vars', 'slot_duration', 'minimal_event_duration', |
|
29 |
'business_hour_start', 'business_hour_start') |
|
30 | ||
31 |
def __init__(self, *args, **kwargs): |
|
32 |
super(CalendarCellForm, self).__init__(*args, **kwargs) |
|
33 |
agenda_references = get_agendas() |
|
34 |
formdef_references = get_formsdef() |
|
35 |
print(formdef_references) |
|
36 |
self.fields['agenda_reference'].widget = forms.Select(choices=agenda_references) |
|
37 |
self.fields['formdef_reference'].widget = forms.Select(choices=formdef_references) |
|
38 | ||
39 | ||
40 |
class CalenderForm(forms.Form): |
|
41 | ||
42 |
def __init__(self, *args, **kwargs): |
|
43 |
available_slots = kwargs.pop('available_slots') |
|
44 |
super(CalenderForm, self).__init__(*args, **kwargs) |
|
45 |
self.cleaned_data = {} |
|
46 |
calendar = get_calendar(available_slots) |
|
47 |
for index, (day, choices) in enumerate(calendar.iteritems()): |
|
48 |
self.fields['day_%s' % index] = forms.MultipleChoiceField( |
|
49 |
label=day, |
|
50 |
widget=forms.CheckboxSelectMultiple(), |
|
51 |
required=False, choices=choices) |
|
52 | ||
53 |
def as_div(self): |
|
54 |
return self._html_output( |
|
55 |
normal_row='<div><p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p></div>', |
|
56 |
error_row='%s', row_ender='</div>', |
|
57 |
help_text_html=' <span class="helptext">%s</span>', |
|
58 |
errors_on_separate_row=True) |
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 |
('agenda_reference', models.URLField(verbose_name='Events source URL')), |
|
30 |
('formdef_reference', 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_hour_start', models.TimeField(default=datetime.time(8, 0), verbose_name='Business hour start')), |
|
35 |
('business_hour_end', models.TimeField(default=datetime.time(18, 0), verbose_name='Business hour end')), |
|
36 |
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)), |
|
37 |
('page', models.ForeignKey(to='data.Page')), |
|
38 |
], |
|
39 |
options={ |
|
40 |
'verbose_name': 'Calendar Cell', |
|
41 |
}, |
|
42 |
), |
|
43 |
] |
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 |
from django.db import models |
|
18 |
from django.utils.translation import ugettext_lazy as _ |
|
19 |
from django.utils.dateparse import parse_time |
|
20 | ||
21 |
from combo.data.models import CellBase |
|
22 |
from combo.data.library import register_cell_class |
|
23 |
from .utils import is_chrono_enabled, get_chrono_events |
|
24 | ||
25 |
from jsonfield import JSONField |
|
26 | ||
27 | ||
28 |
@register_cell_class |
|
29 |
class CalendarCell(CellBase): |
|
30 | ||
31 |
title = models.CharField(_('Title'), max_length=128) |
|
32 |
agenda_reference = models.URLField(_('Events source URL')) |
|
33 |
formdef_reference = models.URLField(_('Application form URL')) |
|
34 |
session_vars = JSONField(_('Session vars')) |
|
35 |
slot_duration = models.TimeField( |
|
36 |
_('Slot duration'), default=parse_time('00:30')) |
|
37 |
minimal_event_duration = models.TimeField( |
|
38 |
_('Minimal event duration'), default=parse_time('02:00')) |
|
39 |
business_hour_start = models.TimeField( |
|
40 |
_('Business hour start'), default=parse_time('08:00')) |
|
41 |
business_hour_end = models.TimeField( |
|
42 |
_('Business hour end'), default=parse_time('18:00')) |
|
43 | ||
44 |
template_name = 'chrono/calendar.html' |
|
45 | ||
46 |
class Meta: |
|
47 |
verbose_name = _('Calendar Cell') |
|
48 | ||
49 |
def get_default_form_class(self): |
|
50 |
from .forms import CalendarCellForm |
|
51 |
return CalendarCellForm |
|
52 | ||
53 |
class Media: |
|
54 |
js = ( |
|
55 |
'chrono/js/calendar.js' |
|
56 |
) |
|
57 |
css = {'all': ('chrono/css/calendar.css',)} |
|
58 | ||
59 |
@classmethod |
|
60 |
def is_enabled(cls): |
|
61 |
return is_chrono_enabled() |
|
62 | ||
63 |
def save(self, *args, **kwargs): |
|
64 |
return super(CalendarCell, self).save(*args, **kwargs) |
|
65 | ||
66 |
def render(self, context): |
|
67 |
from .forms import CalenderForm |
|
68 |
form = CalenderForm( |
|
69 |
available_slots=get_chrono_events(self.agenda_reference)) |
|
70 |
context['form'] = form |
|
71 |
return super(CalendarCell, self).render(context) |
combo/apps/chrono/static/chrono/css/calendar.css | ||
---|---|---|
1 |
div.calendarcell div { |
|
2 |
float: left; |
|
3 |
} |
|
4 | ||
5 |
div.calendarcell input[type=checkbox]{ |
|
6 |
display: none; |
|
7 |
} |
|
8 | ||
9 |
div.calendarcell li { |
|
10 |
list-style-type: none; |
|
11 |
background-color: #ccc; |
|
12 |
margin: 5px; |
|
13 |
padding: 5px 5px; |
|
14 |
} |
|
15 | ||
16 |
div.calendarcell ul { |
|
17 |
margin: 0; |
|
18 |
padding: 0; |
|
19 |
} |
|
20 | ||
21 |
div,calendarcell input[checked]{ |
|
22 | ||
23 |
} |
combo/apps/chrono/static/chrono/js/calendar.js | ||
---|---|---|
1 |
$(function(){ |
|
2 |
// check if datetime are part of the same day |
|
3 |
var datetimes = $('input:checked') |
|
4 | ||
5 |
var date = datetimes.first().attr('name'); |
|
6 |
$('input:checked').each(function(index, value){ |
|
7 |
if ($(this).name != date){ |
|
8 |
// unchecked |
|
9 |
$(this).prop('checked', false); |
|
10 |
} |
|
11 |
}); |
|
12 |
}); |
combo/apps/chrono/templates/chrono/calendar.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 | ||
3 |
<h2>{{title}}</h2> |
|
4 | ||
5 |
<div id="calendar_{{cell.pk}}" data-calendar="{{cell.pk}}"> |
|
6 |
<form method="POST" action="{% url 'chrono-booking' cell.pk %}"> |
|
7 |
{% csrf_token %} |
|
8 |
{{ form.as_div }} |
|
9 |
<input type="submit" value="Submit"/> |
|
10 |
</form> |
|
11 |
</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 |
from django.conf import settings |
|
17 |
from django.utils.dateparse import parse_datetime |
|
18 |
from django.utils import formats |
|
19 | ||
20 | ||
21 |
from combo.utils import requests |
|
22 |
from combo.apps.wcs.utils import get_wcs_json |
|
23 | ||
24 | ||
25 |
def get_service(service_name): |
|
26 |
if hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get(service_name): |
|
27 |
return settings.KNOWN_SERVICES[service_name].values()[0] |
|
28 | ||
29 | ||
30 |
def get_chrono_service(): |
|
31 |
return get_service('chrono') |
|
32 | ||
33 | ||
34 |
def get_wcs_service(): |
|
35 |
return get_service('wcs') |
|
36 | ||
37 | ||
38 |
def is_chrono_enabled(): |
|
39 |
return get_chrono_service() |
|
40 | ||
41 | ||
42 |
def get_agendas(): |
|
43 |
chrono = get_chrono_service() |
|
44 |
url = chrono['url'] + 'api/agenda/' |
|
45 |
response = requests.get(url, headers={'accept': 'application/json'}) |
|
46 |
data = response.json() |
|
47 |
agendas = [(item['api']['datetimes_url'], item['text']) for item in data['data']] |
|
48 |
return agendas |
|
49 | ||
50 | ||
51 |
def get_formsdef(): |
|
52 |
wcs = get_wcs_service() |
|
53 |
url = wcs['url'] + '/api/formdefs/' |
|
54 |
response = requests.get(url, headers={'accept': 'application/json'}) |
|
55 |
data = response.json() |
|
56 |
forms = [(item['url'], item['title']) for item in data] |
|
57 |
return forms |
|
58 | ||
59 | ||
60 |
def get_chrono_events(chrono_url, **kwargs): |
|
61 |
response = requests.get( |
|
62 |
chrono_url, |
|
63 |
headers={'accept': 'application/json'}, |
|
64 |
**kwargs) |
|
65 |
return response.json().get('data', []) |
|
66 | ||
67 | ||
68 |
def get_calendar(events): |
|
69 |
calendar = {} |
|
70 |
for event in events: |
|
71 |
day = formats.date_format(parse_datetime(event['datetime']).date()) |
|
72 |
if day not in calendar: |
|
73 |
calendar[day] = [] |
|
74 |
event_datetime = parse_datetime(event['datetime']) |
|
75 |
calendar[day].append((event_datetime.isoformat(), event_datetime.strftime('%H:%M'))) |
|
76 |
return calendar |
|
77 | ||
78 | ||
79 |
def humanize_date(datetime): |
|
80 |
return formats.date_format( |
|
81 |
parse_datetime(datetime).date()) |
|
82 | ||
83 | ||
84 |
def build_session_vars(cell, data): |
|
85 |
session_vars = {} |
|
86 |
for key, value in cell.session_vars.items(): |
|
87 |
key = 'session_var_%s' % key |
|
88 |
value = value.strip('$') |
|
89 |
if data.get(value): |
|
90 |
session_vars.update({key: data[value]}) |
|
91 |
else: |
|
92 |
session_vars.update({key: value}) |
|
93 |
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 urllib |
|
18 | ||
19 |
from django.http import HttpResponseRedirect, JsonResponse |
|
20 |
from django.views.decorators.csrf import csrf_exempt |
|
21 |
from django.views.generic import View |
|
22 |
from django.views.generic.detail import SingleObjectMixin |
|
23 | ||
24 |
from .models import CalendarCell |
|
25 |
from .utils import get_chrono_events, build_session_vars |
|
26 | ||
27 | ||
28 |
class EventsView(SingleObjectMixin, View): |
|
29 | ||
30 |
http_method_names = ['get'] |
|
31 |
model = CalendarCell |
|
32 | ||
33 |
def get(self, request, *args, **kwargs): |
|
34 |
cell = self.get_object() |
|
35 |
data = get_chrono_events(cell.events_source) |
|
36 |
return JsonResponse(data, safe=False) |
|
37 | ||
38 | ||
39 |
class BookingView(SingleObjectMixin, View): |
|
40 | ||
41 |
http_method_names = ['post'] |
|
42 |
model = CalendarCell |
|
43 | ||
44 |
@csrf_exempt |
|
45 |
def dispatch(self, request, *args, **kwargs): |
|
46 |
return super(BookingView, self).dispatch(request, *args, **kwargs) |
|
47 | ||
48 |
def post(self, request, *args, **kwargs): |
|
49 |
cell = self.get_object() |
|
50 |
data = [request.POST.getlist(key) for key in request.POST if key != 'csrfmiddlewaretoken'] |
|
51 |
data = { |
|
52 |
'start': data[0][0], |
|
53 |
'end': data[0][-1] |
|
54 |
} |
|
55 |
params = build_session_vars(cell, data) |
|
56 |
url = '%s?%s' % (cell.formdef_reference, urllib.urlencode(params)) |
|
57 |
return HttpResponseRedirect(url) |
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) |
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 |
- |