0001-agenda-add-support-for-remote-calendar-file-with-exc.patch
chrono/agendas/migrations/0020_desk_timeperiod_exceptions_remote_url.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('agendas', '0019_timeperiodexception'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='desk', |
|
16 |
name='timeperiod_exceptions_remote_url', |
|
17 |
field=models.URLField(null=True, verbose_name='URL to fetch time period exceptions from', blank=True), |
|
18 |
), |
|
19 |
] |
chrono/agendas/models.py | ||
---|---|---|
17 | 17 | |
18 | 18 |
import datetime |
19 | 19 |
import vobject |
20 |
import requests |
|
20 | 21 | |
21 | 22 |
from django.contrib.auth.models import Group |
22 | 23 |
from django.core.exceptions import ValidationError |
... | ... | |
43 | 44 |
dtime = localtime(dtime) |
44 | 45 |
return dtime.hour == 0 and dtime.minute == 0 |
45 | 46 | |
47 |
def get_remote_calendar(url): |
|
48 |
try: |
|
49 |
response = requests.get(url) |
|
50 |
response.raise_for_status() |
|
51 |
except requests.exceptions.HTTPError as e: |
|
52 |
raise ICSError(_('Failed to retrieve remote calendar (HTTP error %s).') % e.response.status_code) |
|
53 |
except requests.exceptions.ConnectionError: |
|
54 |
raise ICSError(_('Failed to retrieve remote calendar (connection error).')) |
|
55 |
except requests.exceptions.Timeout: |
|
56 |
raise ICSError(_('Failed to retrieve remote calendar (HTTP timeout).')) |
|
57 |
return response.text |
|
58 | ||
46 | 59 | |
47 | 60 |
class ICSError(Exception): |
48 | 61 |
pass |
... | ... | |
358 | 371 |
agenda = models.ForeignKey(Agenda) |
359 | 372 |
label = models.CharField(_('Label'), max_length=150) |
360 | 373 |
slug = models.SlugField(_('Identifier'), max_length=150) |
374 |
timeperiod_exceptions_remote_url = models.URLField(_('URL to fetch time period exceptions from'), |
|
375 |
null=True, blank=True) |
|
361 | 376 | |
362 | 377 |
def __unicode__(self): |
363 | 378 |
return self.label |
... | ... | |
417 | 432 |
in_two_weeks = self.get_exceptions_within_two_weeks() |
418 | 433 |
return self.timeperiodexception_set.count() == len(in_two_weeks) |
419 | 434 | |
435 |
def create_timeperiod_exceptions_from_remote_ics(self, url): |
|
436 |
return self.create_timeperiod_exceptions_from_ics(get_remote_calendar(url)) |
|
437 | ||
420 | 438 |
def create_timeperiod_exceptions_from_ics(self, data): |
421 | 439 |
try: |
422 | 440 |
parsed = vobject.readOne(data) |
chrono/manager/forms.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
import csv |
18 | 18 |
import datetime |
19 |
import requests |
|
19 | 20 | |
20 | 21 |
from django import forms |
21 | 22 |
from django.forms import ValidationError |
22 | 23 |
from django.utils.translation import ugettext_lazy as _ |
23 | 24 | |
24 | 25 |
from chrono.agendas.models import (Event, MeetingType, TimePeriod, Desk, |
25 |
TimePeriodException) |
|
26 |
TimePeriodException, ICSError, get_remote_calendar)
|
|
26 | 27 | |
27 | 28 |
from . import widgets |
28 | 29 | |
... | ... | |
77 | 78 |
exclude = [] |
78 | 79 | |
79 | 80 | |
80 |
class NewDeskForm(forms.ModelForm):
|
|
81 |
class DeskForm(forms.ModelForm): |
|
81 | 82 |
class Meta: |
82 | 83 |
model = Desk |
83 | 84 |
widgets = { |
84 | 85 |
'agenda': forms.HiddenInput(), |
85 | 86 |
} |
86 |
exclude = ['slug']
|
|
87 |
exclude = [] |
|
87 | 88 | |
89 |
def is_valid(self): |
|
90 |
if not self.data['timeperiod_exceptions_remote_url']: |
|
91 |
return super(DeskForm, self).is_valid() |
|
88 | 92 | |
89 |
class DeskForm(forms.ModelForm): |
|
93 |
try: |
|
94 |
get_remote_calendar(self.data['timeperiod_exceptions_remote_url']) |
|
95 |
except ICSError as e: |
|
96 |
self.add_error('timeperiod_exceptions_remote_url', e) |
|
97 |
return False |
|
98 |
return super(DeskForm, self).is_valid() |
|
99 | ||
100 | ||
101 |
class NewDeskForm(DeskForm): |
|
90 | 102 |
class Meta: |
91 | 103 |
model = Desk |
92 | 104 |
widgets = { |
93 | 105 |
'agenda': forms.HiddenInput(), |
94 | 106 |
} |
95 |
exclude = [] |
|
107 |
exclude = ['slug']
|
|
96 | 108 | |
97 | 109 | |
98 | 110 |
class TimePeriodExceptionForm(forms.ModelForm): |
... | ... | |
170 | 182 |
model = Desk |
171 | 183 |
fields = [] |
172 | 184 | |
173 |
ics_file = forms.FileField(label=_('ICS File'), |
|
185 |
ics_file = forms.FileField(label=_('ICS File'), required=False,
|
|
174 | 186 |
help_text=_('ICS file containing events which will be considered as exceptions')) |
187 |
ics_url = forms.URLField(label=_('URL'), required=False) |
|
188 | ||
189 |
def clean(self): |
|
190 |
cleaned_data = super(ExceptionsImportForm, self).clean() |
|
191 |
if not cleaned_data['ics_file'] and not cleaned_data['ics_url']: |
|
192 |
raise ValidationError(_('A file or an url should be filled.')) |
chrono/manager/management/commands/sync_desks_timeperiod_exceptions.py | ||
---|---|---|
1 |
# chrono - agendas system |
|
2 |
# Copyright (C) 2016-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 sys |
|
18 |
import logging |
|
19 | ||
20 |
from django.core.management.base import BaseCommand |
|
21 |
from chrono.agendas.models import Desk, ICSError |
|
22 | ||
23 | ||
24 |
class Command(BaseCommand): |
|
25 |
help = 'Synchronize time period exceptions from desks remote ics' |
|
26 | ||
27 |
def handle(self, **options): |
|
28 |
logger = logging.getLogger(__name__) |
|
29 |
for desk in Desk.objects.filter(timeperiod_exceptions_remote_url__isnull=False): |
|
30 |
try: |
|
31 |
desk.create_timeperiod_exceptions_from_remote_ics(desk.timeperiod_exceptions_remote_url) |
|
32 |
except ICSError as e: |
|
33 |
logger.warning(u'unable to create timeperiod exceptions for "%s": %s' % (desk, e)) |
chrono/manager/templates/chrono/manager_import_exceptions.html | ||
---|---|---|
11 | 11 |
{% endblock %} |
12 | 12 | |
13 | 13 |
{% block content %} |
14 | ||
15 | 14 |
<form method="post" enctype="multipart/form-data"> |
15 |
<p>{% trans "You can upload a file or specify an address to remote calendar." %}</p> |
|
16 | 16 |
{% csrf_token %} |
17 | 17 |
{{ form.as_p }} |
18 |
<p> |
|
19 |
</p> |
|
20 | 18 |
<div class="buttons"> |
21 | 19 |
<button>{% trans "Import" %}</button> |
22 | 20 |
<a class="cancel" href="{% url 'chrono-manager-agenda-view' pk=agenda.id %}">{% trans 'Cancel' %}</a> |
chrono/manager/views.py | ||
---|---|---|
396 | 396 | |
397 | 397 |
def form_valid(self, form): |
398 | 398 |
try: |
399 |
exceptions = form.instance.create_timeperiod_exceptions_from_ics(form.cleaned_data['ics_file']) |
|
399 |
if form.cleaned_data['ics_file']: |
|
400 |
exceptions = form.instance.create_timeperiod_exceptions_from_ics(form.cleaned_data['ics_file']) |
|
401 |
else: |
|
402 |
exceptions = form.instance.create_timeperiod_exceptions_from_remote_ics(form.cleaned_data['ics_url']) |
|
400 | 403 |
except ICSError as e: |
401 | 404 |
form.add_error(None, unicode(e)) |
402 | 405 |
return self.form_invalid(form) |
debian/chrono.cron.hourly | ||
---|---|---|
1 |
#!/bin/sh |
|
2 | ||
3 |
/sbin/runuser -u chrono /usr/bin/chrono-manage -- tenant_command sync_desks_timeperiod_exceptions --all-tenants |
|
4 |
debian/control | ||
---|---|---|
11 | 11 |
Depends: ${misc:Depends}, ${python:Depends}, |
12 | 12 |
python-django (>= 1.8), |
13 | 13 |
python-gadjo, |
14 |
python-intervaltree |
|
14 |
python-intervaltree, |
|
15 |
python-requests |
|
15 | 16 |
Recommends: python-django-mellon |
16 | 17 |
Description: Agendas System (Python module) |
17 | 18 |
requirements.txt | ||
---|---|---|
3 | 3 |
djangorestframework>=3.1, <3.7 |
4 | 4 |
django-jsonfield >= 0.9.3 |
5 | 5 |
intervaltree |
6 |
requests |
setup.py | ||
---|---|---|
107 | 107 |
'djangorestframework>=3.1, <3.7', |
108 | 108 |
'django-jsonfield >= 0.9.3', |
109 | 109 |
'intervaltree', |
110 |
'vobject' |
|
110 |
'vobject', |
|
111 |
'requests' |
|
111 | 112 |
], |
112 | 113 |
zip_safe=False, |
113 | 114 |
cmdclass={ |
tests/test_agendas.py | ||
---|---|---|
1 | 1 |
import pytest |
2 | 2 |
import datetime |
3 |
import mock |
|
4 |
import requests |
|
5 |
import logging |
|
3 | 6 | |
4 | 7 |
from django.utils.timezone import now, make_aware, localtime |
8 |
from django.core.management import call_command |
|
5 | 9 | |
6 | 10 |
from chrono.agendas.models import (Agenda, Event, Booking, MeetingType, |
7 | 11 |
Desk, TimePeriodException, ICSError) |
... | ... | |
220 | 224 |
with pytest.raises(ICSError) as e: |
221 | 225 |
exceptions_count = desk.create_timeperiod_exceptions_from_ics(ICS_SAMPLE_WITH_NO_EVENTS) |
222 | 226 |
assert str(e.value) == "The file doesn't contain any events." |
227 | ||
228 |
@mock.patch('chrono.agendas.models.requests.get') |
|
229 |
def test_timeperiodexception_creation_from_remote_ics(mocked_get): |
|
230 |
agenda = Agenda(label=u'Test 8 agenda') |
|
231 |
agenda.save() |
|
232 |
desk = Desk(label='Test 8 desk', agenda=agenda) |
|
233 |
desk.save() |
|
234 |
mocked_response = mock.Mock() |
|
235 |
mocked_response.text = ICS_SAMPLE |
|
236 |
mocked_get.return_value = mocked_response |
|
237 |
exceptions_count = desk.create_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics') |
|
238 |
assert exceptions_count == 2 |
|
239 | ||
240 |
@mock.patch('chrono.agendas.models.requests.get') |
|
241 |
def test_timeperiodexception_creation_from_unreachable_remote_ics(mocked_get): |
|
242 |
agenda = Agenda(label=u'Test 9 agenda') |
|
243 |
agenda.save() |
|
244 |
desk = Desk(label='Test 9 desk', agenda=agenda) |
|
245 |
desk.save() |
|
246 |
mocked_response = mock.Mock() |
|
247 |
mocked_response.content.return_value = ICS_SAMPLE |
|
248 |
mocked_get.return_value = mocked_response |
|
249 |
def mocked_requests_connection_error(*args, **kwargs): |
|
250 |
raise requests.exceptions.ConnectionError('unreachable') |
|
251 |
mocked_get.side_effect = mocked_requests_connection_error |
|
252 |
with pytest.raises(ICSError) as e: |
|
253 |
exceptions_count = desk.create_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics') |
|
254 |
assert str(e.value) == "Failed to retrieve remote calendar (connection error)." |
|
255 | ||
256 |
@mock.patch('chrono.agendas.models.requests.get') |
|
257 |
def test_timeperiodexception_creation_from_forbidden_remote_ics(mocked_get): |
|
258 |
agenda = Agenda(label=u'Test 10 agenda') |
|
259 |
agenda.save() |
|
260 |
desk = Desk(label='Test 10 desk', agenda=agenda) |
|
261 |
desk.save() |
|
262 |
mocked_response = mock.Mock() |
|
263 |
mocked_response.status_code = 403 |
|
264 |
mocked_get.return_value = mocked_response |
|
265 |
def mocked_requests_http_forbidden_error(*args, **kwargs): |
|
266 |
raise requests.exceptions.HTTPError(response=mocked_response) |
|
267 |
mocked_get.side_effect = mocked_requests_http_forbidden_error |
|
268 | ||
269 |
with pytest.raises(ICSError) as e: |
|
270 |
exceptions_count = desk.create_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics') |
|
271 |
assert str(e.value) == "Failed to retrieve remote calendar (HTTP error 403)." |
|
272 | ||
273 |
@mock.patch('chrono.agendas.models.requests.get') |
|
274 |
def test_timeperiodexception_creation_from_remote_ics_with_timeout_error(mocked_get): |
|
275 |
agenda = Agenda(label=u'Test 11 agenda') |
|
276 |
agenda.save() |
|
277 |
desk = Desk(label='Test 11 desk', agenda=agenda) |
|
278 |
desk.save() |
|
279 |
mocked_response = mock.Mock() |
|
280 |
mocked_get.return_value = mocked_response |
|
281 |
def mocked_requests_http_timeout_error(*args, **kwargs): |
|
282 |
raise requests.exceptions.Timeout(response=mocked_response) |
|
283 |
mocked_get.side_effect = mocked_requests_http_timeout_error |
|
284 | ||
285 |
with pytest.raises(ICSError) as e: |
|
286 |
exceptions_count = desk.create_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics') |
|
287 |
assert str(e.value) == "Failed to retrieve remote calendar (HTTP timeout)." |
|
288 | ||
289 |
@mock.patch('chrono.agendas.models.requests.get') |
|
290 |
def test_timeperiodexception_creation_from_remote_ics_with_ssl_error(mocked_get): |
|
291 |
agenda = Agenda(label=u'Test 11 agenda') |
|
292 |
agenda.save() |
|
293 |
desk = Desk(label='Test 11 desk', agenda=agenda) |
|
294 |
desk.save() |
|
295 |
mocked_response = mock.Mock() |
|
296 |
mocked_get.return_value = mocked_response |
|
297 |
def mocked_requests_http_ssl_error(*args, **kwargs): |
|
298 |
raise requests.exceptions.SSLError(response=mocked_response) |
|
299 |
mocked_get.side_effect = mocked_requests_http_ssl_error |
|
300 | ||
301 |
with pytest.raises(ICSError) as e: |
|
302 |
exceptions_count = desk.create_timeperiod_exceptions_from_remote_ics('http://example.com/sample.ics') |
|
303 |
assert str(e.value) == "Failed to retrieve remote calendar (connection error)." |
|
304 | ||
305 |
@mock.patch('chrono.agendas.models.requests.get') |
|
306 |
def test_sync_desks_timeperiod_exceptions_from_ics(mocked_get, caplog): |
|
307 |
agenda = Agenda(label=u'Test 11 agenda') |
|
308 |
agenda.save() |
|
309 |
desk = Desk(label='Test 11 desk', agenda=agenda, timeperiod_exceptions_remote_url='http:example.com/sample.ics') |
|
310 |
desk.save() |
|
311 |
mocked_response = mock.Mock() |
|
312 |
mocked_response.status_code = 403 |
|
313 |
mocked_get.return_value = mocked_response |
|
314 |
def mocked_requests_http_forbidden_error(*args, **kwargs): |
|
315 |
raise requests.exceptions.HTTPError(response=mocked_response) |
|
316 |
mocked_get.side_effect = mocked_requests_http_forbidden_error |
|
317 |
call_command('sync_desks_timeperiod_exceptions') |
|
318 |
records = caplog.records() |
|
319 |
assert len(records) == 1 |
|
320 |
for record in records: |
|
321 |
assert record.name == 'chrono.manager.management.commands.sync_desks_timeperiod_exceptions' |
|
322 |
assert record.levelno == logging.WARNING |
|
323 |
assert record.getMessage() == 'unable to create timeperiod exceptions for "Test 11 desk": Failed to retrieve remote calendar (HTTP error 403).' |
tests/test_manager.py | ||
---|---|---|
5 | 5 |
import datetime |
6 | 6 |
import pytest |
7 | 7 |
from webtest import TestApp, Upload |
8 |
import mock |
|
9 |
import requests |
|
8 | 10 | |
9 | 11 |
from chrono.wsgi import application |
10 | 12 | |
... | ... | |
699 | 701 |
assert 'Desk A' in resp.text |
700 | 702 |
assert 'Desk B' in resp.text |
701 | 703 | |
704 |
@mock.patch('chrono.manager.forms.requests.get') |
|
705 |
def test_meetings_agenda_add_desk_with_non_existing_exceptions_url(mocked_get, app, admin_user): |
|
706 |
app = login(app) |
|
707 |
resp = app.get('/manage/', status=200) |
|
708 |
resp = resp.click('New') |
|
709 |
resp.form['label'] = 'Foo bar' |
|
710 |
resp.form['kind'] = 'meetings' |
|
711 |
resp = resp.form.submit() |
|
712 |
agenda = Agenda.objects.get(slug='foo-bar') |
|
713 |
resp = app.get('/manage/agendas/%s/' % agenda.id, status=200) |
|
714 |
mocked_response = mock.Mock() |
|
715 |
mocked_response.status_code = 403 |
|
716 |
mocked_get.return_value = mocked_response |
|
717 |
def mocked_requests_http_forbidden_error(*args, **kwargs): |
|
718 |
raise requests.exceptions.HTTPError(response=mocked_response) |
|
719 |
mocked_get.side_effect = mocked_requests_http_forbidden_error |
|
720 |
resp = resp.click('New Desk') |
|
721 |
resp.form['label'] = 'Desk A' |
|
722 |
resp.form['timeperiod_exceptions_remote_url'] = 'http://nowhere.com/unknown.ics' |
|
723 |
resp = resp.form.submit(status=200) |
|
724 |
assert 'Failed to retrieve remote calendar (HTTP error 403).' in resp.text |
|
725 | ||
726 |
@mock.patch('chrono.manager.forms.requests.get') |
|
727 |
def test_meetings_agenda_add_desk_with_unreachable_exceptions_url(mocked_get, app, admin_user): |
|
728 |
app = login(app) |
|
729 |
resp = app.get('/manage/', status=200) |
|
730 |
resp = resp.click('New') |
|
731 |
resp.form['label'] = 'Foo bar' |
|
732 |
resp.form['kind'] = 'meetings' |
|
733 |
resp = resp.form.submit() |
|
734 |
agenda = Agenda.objects.get(slug='foo-bar') |
|
735 |
resp = app.get('/manage/agendas/%s/' % agenda.id, status=200) |
|
736 |
mocked_response = mock.Mock() |
|
737 |
mocked_get.return_value = mocked_response |
|
738 |
def mocked_requests_connection_error(*args, **kwargs): |
|
739 |
raise requests.exceptions.ConnectionError('unreachable') |
|
740 |
mocked_get.side_effect = mocked_requests_connection_error |
|
741 |
resp = resp.click('New Desk') |
|
742 |
resp.form['label'] = 'Desk A' |
|
743 |
resp.form['timeperiod_exceptions_remote_url'] = 'http://nowhere.com/unknown.ics' |
|
744 |
resp = resp.form.submit(status=200) |
|
745 |
assert 'Failed to retrieve remote calendar (connection error).' in resp.text |
|
746 | ||
702 | 747 | |
703 | 748 |
def test_meetings_agenda_delete_desk(app, admin_user): |
704 | 749 |
app = login(app) |
... | ... | |
840 | 885 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
841 | 886 |
assert 'Import exceptions from .ics' in resp.content |
842 | 887 |
resp = resp.click('upload') |
888 |
assert "You can upload a file or specify an address to remote calendar." in resp |
|
889 |
resp = resp.form.submit(status=200) |
|
890 |
assert "A file or an url should be filled." in resp.body |
|
843 | 891 |
resp.form['ics_file'] = Upload('exceptions.ics', 'invalid content', 'text/calendar') |
844 | 892 |
resp = resp.form.submit(status=200) |
845 | 893 |
assert 'File format is invalid' in resp.content |
... | ... | |
889 | 937 |
assert TimePeriodException.objects.count() == 1 |
890 | 938 |
resp = resp.follow() |
891 | 939 |
assert 'An exception has been imported.' in resp.content |
940 | ||
941 |
@mock.patch('chrono.agendas.models.requests.get') |
|
942 |
def test_agenda_import_time_period_exception_from_remote_ics(mocked_get, app, admin_user): |
|
943 |
agenda = Agenda.objects.create(label='New Example', kind='meetings') |
|
944 |
desk = Desk.objects.create(agenda=agenda, label='New Desk') |
|
945 |
MeetingType(agenda=agenda, label='Bar').save() |
|
946 |
login(app) |
|
947 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
948 |
assert 'Import exceptions from .ics' not in resp.content |
|
949 | ||
950 |
TimePeriod.objects.create(weekday=1, desk=desk, |
|
951 |
start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)) |
|
952 | ||
953 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
954 |
resp = resp.click('upload') |
|
955 | ||
956 |
assert 'ics_file' in resp.form.fields |
|
957 |
assert 'ics_url' in resp.form.fields |
|
958 |
resp.form['ics_url'] = 'http://example.com/foo.ics' |
|
959 |
mocked_response = mock.Mock() |
|
960 |
mocked_get.return_value = mocked_response |
|
961 |
def mocked_requests_connection_error(*args, **kwargs): |
|
962 |
raise requests.exceptions.ConnectionError('unreachable') |
|
963 |
mocked_get.side_effect = mocked_requests_connection_error |
|
964 |
resp = resp.form.submit(status=200) |
|
965 |
assert 'Failed to retrieve remote calendar (connection error).' in resp.content |
|
966 | ||
967 |
@mock.patch('chrono.agendas.models.requests.get') |
|
968 |
def test_agenda_import_time_period_exception_from_forbidden_remote_ics(mocked_get, app, admin_user): |
|
969 |
agenda = Agenda.objects.create(label='New Example', kind='meetings') |
|
970 |
desk = Desk.objects.create(agenda=agenda, label='New Desk') |
|
971 |
MeetingType(agenda=agenda, label='Bar').save() |
|
972 |
login(app) |
|
973 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
974 |
assert 'Import exceptions from .ics' not in resp.content |
|
975 | ||
976 |
TimePeriod.objects.create(weekday=1, desk=desk, |
|
977 |
start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)) |
|
978 | ||
979 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
980 |
resp = resp.click('upload') |
|
981 |
resp.form['ics_url'] = 'http://example.com/foo.ics' |
|
982 |
mocked_response = mock.Mock() |
|
983 |
mocked_response.status_code = 403 |
|
984 |
mocked_get.return_value = mocked_response |
|
985 |
def mocked_requests_http_forbidden_error(*args, **kwargs): |
|
986 |
raise requests.exceptions.HTTPError(response=mocked_response) |
|
987 |
mocked_get.side_effect = mocked_requests_http_forbidden_error |
|
988 |
resp = resp.form.submit(status=200) |
|
989 |
assert 'Failed to retrieve remote calendar (HTTP error 403).' in resp.content |
|
990 | ||
991 |
@mock.patch('chrono.agendas.models.requests.get') |
|
992 |
def test_agenda_import_time_period_exception_from_remote_ics_with_timeout_error(mocked_get, app, admin_user): |
|
993 |
agenda = Agenda.objects.create(label='New Example', kind='meetings') |
|
994 |
desk = Desk.objects.create(agenda=agenda, label='New Desk') |
|
995 |
MeetingType(agenda=agenda, label='Bar').save() |
|
996 |
login(app) |
|
997 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
998 |
assert 'Import exceptions from .ics' not in resp.content |
|
999 | ||
1000 |
TimePeriod.objects.create(weekday=1, desk=desk, |
|
1001 |
start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)) |
|
1002 | ||
1003 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
1004 |
resp = resp.click('upload') |
|
1005 |
resp.form['ics_url'] = 'http://example.com/foo.ics' |
|
1006 |
mocked_response = mock.Mock() |
|
1007 |
mocked_get.return_value = mocked_response |
|
1008 |
def mocked_requests_http_timeout_error(*args, **kwargs): |
|
1009 |
raise requests.exceptions.Timeout(response=mocked_response) |
|
1010 |
mocked_get.side_effect = mocked_requests_http_timeout_error |
|
1011 |
resp = resp.form.submit(status=200) |
|
1012 |
assert 'Failed to retrieve remote calendar (HTTP timeout).' in resp.content |
|
1013 | ||
1014 |
@mock.patch('chrono.agendas.models.requests.get') |
|
1015 |
def test_agenda_import_time_period_exception_from_remote_ics_with_ssl_error(mocked_get, app, admin_user): |
|
1016 |
agenda = Agenda.objects.create(label='New Example', kind='meetings') |
|
1017 |
desk = Desk.objects.create(agenda=agenda, label='New Desk') |
|
1018 |
MeetingType(agenda=agenda, label='Bar').save() |
|
1019 |
login(app) |
|
1020 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
1021 |
assert 'Import exceptions from .ics' not in resp.content |
|
1022 |
TimePeriod.objects.create(weekday=1, desk=desk, |
|
1023 |
start_time=datetime.time(10, 0), end_time=datetime.time(12, 0)) |
|
1024 | ||
1025 |
resp = app.get('/manage/agendas/%d/' % agenda.pk) |
|
1026 |
resp = resp.click('upload') |
|
1027 |
resp.form['ics_url'] = 'http://example.com/foo.ics' |
|
1028 |
mocked_response = mock.Mock() |
|
1029 |
mocked_get.return_value = mocked_response |
|
1030 |
def mocked_requests_http_ssl_error(*args, **kwargs): |
|
1031 |
raise requests.exceptions.SSLError |
|
1032 |
mocked_get.side_effect = mocked_requests_http_ssl_error |
|
1033 |
resp = resp.form.submit(status=200) |
|
1034 |
assert 'Failed to retrieve remote calendar (connection error).' in resp.content |
|
892 |
- |