0002-agendas-allow-exceptions-to-recurring-events-50561.patch
chrono/agendas/migrations/0079_create_exceptions_desks.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2021-01-27 16:46 |
|
3 | ||
4 |
from __future__ import unicode_literals |
|
5 | ||
6 |
from django.db import migrations |
|
7 | ||
8 | ||
9 |
def create_exceptions_desk(apps, schema_editor): |
|
10 |
Agenda = apps.get_model('agendas', 'Agenda') |
|
11 |
Desk = apps.get_model('agendas', 'Desk') |
|
12 |
desks = [] |
|
13 | ||
14 |
for agenda in Agenda.objects.filter(kind='events'): |
|
15 |
desks.append(Desk(agenda=agenda, slug='_exceptions_holder')) |
|
16 |
Desk.objects.bulk_create(desks) |
|
17 | ||
18 | ||
19 |
class Migration(migrations.Migration): |
|
20 | ||
21 |
dependencies = [ |
|
22 |
('agendas', '0078_absence_reasons'), |
|
23 |
] |
|
24 | ||
25 |
operations = [ |
|
26 |
migrations.RunPython(create_exceptions_desk, migrations.RunPython.noop), |
|
27 |
] |
chrono/agendas/models.py | ||
---|---|---|
213 | 213 |
return self.label |
214 | 214 | |
215 | 215 |
def save(self, *args, **kwargs): |
216 |
created = bool(not self.pk) |
|
216 | 217 |
if not self.slug: |
217 | 218 |
self.slug = generate_slug(self) |
218 | 219 |
if self.kind != 'virtual': |
... | ... | |
223 | 224 |
if self.kind != 'events' and self.pk is None: |
224 | 225 |
self.default_view = 'day' |
225 | 226 |
super(Agenda, self).save(*args, **kwargs) |
227 |
if created and self.kind == 'events': |
|
228 |
desk = Desk.objects.create(agenda=self, slug='_exceptions_holder') |
|
229 |
desk.import_timeperiod_exceptions_from_settings() |
|
226 | 230 | |
227 | 231 |
@property |
228 | 232 |
def base_slug(self): |
... | ... | |
342 | 346 |
agenda['absence_reasons_group'] = ( |
343 | 347 |
self.absence_reasons_group.slug if self.absence_reasons_group else None |
344 | 348 |
) |
349 |
agenda['exceptions_desk'] = self.desk_set.get().export_json() |
|
345 | 350 |
elif self.kind == 'meetings': |
346 | 351 |
agenda['meetingtypes'] = [x.export_json() for x in self.meetingtype_set.filter(deleted=False)] |
347 | 352 |
agenda['desks'] = [desk.export_json() for desk in self.desk_set.all()] |
... | ... | |
359 | 364 |
if data['kind'] == 'events': |
360 | 365 |
events = data.pop('events') |
361 | 366 |
notifications_settings = data.pop('notifications_settings', None) |
367 |
exceptions_desk = data.pop('exceptions_desk', None) |
|
362 | 368 |
elif data['kind'] == 'meetings': |
363 | 369 |
meetingtypes = data.pop('meetingtypes') |
364 | 370 |
desks = data.pop('desks') |
... | ... | |
408 | 414 |
if notifications_settings: |
409 | 415 |
notifications_settings['agenda'] = agenda |
410 | 416 |
AgendaNotificationsSettings.import_json(notifications_settings) |
417 |
if exceptions_desk: |
|
418 |
exceptions_desk['agenda'] = agenda |
|
419 |
Desk.import_json(exceptions_desk) |
|
411 | 420 |
elif data['kind'] == 'meetings': |
412 | 421 |
if overwrite: |
413 | 422 |
MeetingType.objects.filter(agenda=agenda).delete() |
... | ... | |
653 | 662 |
recurring_events = self.prefetched_recurring_events |
654 | 663 |
else: |
655 | 664 |
recurring_events = self.event_set.filter(recurrence_rule__isnull=False) |
665 | ||
666 |
exceptions = self.get_recurrence_exceptions(min_start, max_start) |
|
656 | 667 |
for event in recurring_events: |
657 | 668 |
events.extend( |
658 | 669 |
event.get_recurrences( |
659 |
min_start, max_start, excluded_datetimes.get(event.pk), slug_separator=':' |
|
670 |
min_start, max_start, excluded_datetimes.get(event.pk), exceptions, slug_separator=':'
|
|
660 | 671 |
) |
661 | 672 |
) |
662 | 673 | |
... | ... | |
674 | 685 |
except (VariableDoesNotExist, TemplateSyntaxError): |
675 | 686 |
return |
676 | 687 | |
688 |
def get_recurrence_exceptions(self, min_start, max_start): |
|
689 |
return TimePeriodException.objects.filter( |
|
690 |
Q(desk__slug='_exceptions_holder', desk__agenda=self) |
|
691 |
| Q( |
|
692 |
unavailability_calendar__desks__slug='_exceptions_holder', |
|
693 |
unavailability_calendar__desks__agenda=self, |
|
694 |
), |
|
695 |
start_datetime__lt=max_start, |
|
696 |
end_datetime__gt=min_start, |
|
697 |
) |
|
698 | ||
677 | 699 |
def prefetch_desks_and_exceptions(self, with_sources=False): |
678 | 700 |
if self.kind == 'meetings': |
679 | 701 |
desks = self.desk_set.all() |
... | ... | |
1382 | 1404 |
event.save() |
1383 | 1405 |
return event |
1384 | 1406 | |
1385 |
def get_recurrences(self, min_datetime, max_datetime, excluded_datetimes=None, slug_separator='--'): |
|
1407 |
def get_recurrences( |
|
1408 |
self, min_datetime, max_datetime, excluded_datetimes=None, exceptions=None, slug_separator='--' |
|
1409 |
): |
|
1386 | 1410 |
recurrences = [] |
1387 | 1411 |
rrule_set = rruleset() |
1388 | 1412 |
# do not generate recurrences for existing events |
1389 | 1413 |
rrule_set._exdate = excluded_datetimes or [] |
1390 | 1414 | |
1415 |
if exceptions is None: |
|
1416 |
exceptions = self.agenda.get_recurrence_exceptions(min_datetime, max_datetime) |
|
1417 |
for exception in exceptions: |
|
1418 |
exception_start = localtime(exception.start_datetime) |
|
1419 |
event_start = localtime(self.start_datetime) |
|
1420 |
if event_start.time() < exception_start.time(): |
|
1421 |
exception_start += datetime.timedelta(days=1) |
|
1422 |
exception_start = exception_start.replace( |
|
1423 |
hour=event_start.hour, minute=event_start.minute, second=0, microsecond=0 |
|
1424 |
) |
|
1425 |
rrule_set.exrule( |
|
1426 |
rrule( |
|
1427 |
freq=DAILY, |
|
1428 |
dtstart=make_naive(exception_start), |
|
1429 |
until=make_naive(exception.end_datetime), |
|
1430 |
) |
|
1431 |
) |
|
1432 | ||
1391 | 1433 |
event_base = Event( |
1392 | 1434 |
agenda=self.agenda, |
1393 | 1435 |
primary_event=self, |
chrono/manager/templates/chrono/manager_events_agenda_settings.html | ||
---|---|---|
68 | 68 |
{% endfor %} |
69 | 69 |
</div> |
70 | 70 |
</div> |
71 | ||
72 |
{% if has_recurring_events %} |
|
73 |
<div class="section"> |
|
74 |
<h3>{% trans "Recurrence exceptions" %} |
|
75 |
<a rel="popup" class="button" href="{% url 'chrono-manager-desk-add-import-time-period-exceptions' pk=desk.pk %}">{% trans 'Configure' %}</a> |
|
76 |
</h3> |
|
77 |
<div> |
|
78 |
<ul class="objects-list single-links"> |
|
79 |
{% for exception in exceptions|slice:":5" %} |
|
80 |
<li><a rel="popup" {% if not exception.read_only %}href="{% url 'chrono-manager-time-period-exception-edit' pk=exception.pk %}"{% endif %}> |
|
81 |
{{ exception }} |
|
82 |
{% if not exception.read_only %} |
|
83 |
<a rel="popup" class="delete" href="{% url 'chrono-manager-time-period-exception-delete' pk=exception.id %}">{% trans "remove" %}</a> |
|
84 |
{% endif %} |
|
85 |
{% endfor %} |
|
86 |
{% if exceptions|length > 5 %} |
|
87 |
<li><a class="timeperiod-exception-all desk-{{ desk.pk }}" rel="popup" data-selector="div.timeperiod" href="{% url 'chrono-manager-time-period-exception-extract-list' pk=desk.id %}">({% trans 'see all exceptions' %})</a></li> |
|
88 |
{% endif %} |
|
89 |
<li><a class="add" rel="popup" href="{% url 'chrono-manager-agenda-add-time-period-exception' agenda_pk=object.pk pk=desk.pk %}">{% trans 'Add a time period exception' %}</a></li> |
|
90 |
</ul> |
|
91 |
</div> |
|
92 |
</div> |
|
93 |
{% endif %} |
|
94 | ||
71 | 95 |
{% endblock %} |
chrono/manager/views.py | ||
---|---|---|
1573 | 1573 |
) |
1574 | 1574 |
if self.agenda.kind == 'events': |
1575 | 1575 |
context['has_absence_reasons'] = AbsenceReasonGroup.objects.exists() |
1576 |
context['has_recurring_events'] = self.agenda.event_set.filter( |
|
1577 |
recurrence_rule__isnull=False |
|
1578 |
).exists() |
|
1579 |
desk = Desk.objects.get(agenda=self.agenda, slug='_exceptions_holder') |
|
1580 |
context['exceptions'] = TimePeriodException.objects.filter( |
|
1581 |
Q(desk=desk) | Q(unavailability_calendar__desks=desk), |
|
1582 |
end_datetime__gt=now(), |
|
1583 |
) |
|
1584 |
context['desk'] = desk |
|
1576 | 1585 |
return context |
1577 | 1586 | |
1578 | 1587 |
def get_events(self): |
tests/manager/test_all.py | ||
---|---|---|
2617 | 2617 | |
2618 | 2618 |
with CaptureQueriesContext(connection) as ctx: |
2619 | 2619 |
resp = app.get('/manage/agendas/%s/2020/11/11/' % agenda.pk) |
2620 |
assert len(ctx.captured_queries) == 5
|
|
2620 |
assert len(ctx.captured_queries) == 6
|
|
2621 | 2621 | |
2622 | 2622 |
assert len(resp.pyquery.find('.event-info')) == 2 |
2623 | 2623 |
assert 'abc' in resp.pyquery.find('.event-info')[0].text |
... | ... | |
2680 | 2680 | |
2681 | 2681 |
with CaptureQueriesContext(connection) as ctx: |
2682 | 2682 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, 2020, 11)) |
2683 |
assert len(ctx.captured_queries) == 7
|
|
2683 |
assert len(ctx.captured_queries) == 8
|
|
2684 | 2684 |
assert len(resp.pyquery.find('.event-info')) == 5 |
2685 | 2685 |
assert 'abc' in resp.pyquery.find('.event-info')[0].text |
2686 | 2686 |
assert 'abc' in resp.pyquery.find('.event-info')[1].text |
... | ... | |
4413 | 4413 |
assert resp.text.count('Swimming') == 2 # 1 booking + legend |
4414 | 4414 |
assert 'Booking colors:' in resp.text |
4415 | 4415 |
assert len(resp.pyquery.find('div.booking-colors span.booking-color-label')) == 2 |
4416 | ||
4417 | ||
4418 |
@override_settings( |
|
4419 |
EXCEPTIONS_SOURCES={ |
|
4420 |
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'}, |
|
4421 |
} |
|
4422 |
) |
|
4423 |
def test_recurring_events_manage_exceptions(settings, app, admin_user, freezer): |
|
4424 |
freezer.move_to('2021-07-01 12:10') |
|
4425 | ||
4426 |
app = login(app) |
|
4427 |
resp = app.get('/manage/') |
|
4428 |
resp = resp.click('New') |
|
4429 |
resp.form['label'] = 'Foo bar' |
|
4430 |
resp.form['kind'] = 'events' |
|
4431 |
resp = resp.form.submit().follow() |
|
4432 | ||
4433 |
agenda = Agenda.objects.get(label='Foo bar') |
|
4434 |
assert agenda.desk_set.count() == 1 |
|
4435 |
desk = agenda.desk_set.get(slug='_exceptions_holder') |
|
4436 | ||
4437 |
event = Event.objects.create(start_datetime=now(), places=10, agenda=agenda) |
|
4438 |
resp = app.get('/manage/agendas/%s/settings' % agenda.id) |
|
4439 |
assert not 'Recurrence exceptions' in resp.text |
|
4440 | ||
4441 |
event.repeat = 'daily' |
|
4442 |
event.save() |
|
4443 | ||
4444 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, 2021, 7)) |
|
4445 |
assert len(resp.pyquery.find('.event-info')) == 31 |
|
4446 | ||
4447 |
resp = app.get('/manage/agendas/%s/settings' % agenda.id) |
|
4448 |
assert 'Recurrence exceptions' in resp.text |
|
4449 | ||
4450 |
resp = resp.click('Add a time period exception') |
|
4451 |
resp.form['start_datetime_0'] = now().strftime('%Y-%m-%d') |
|
4452 |
resp.form['start_datetime_1'] = now().strftime('%H:%M') |
|
4453 |
resp.form['end_datetime_0'] = (now() + datetime.timedelta(days=7)).strftime('%Y-%m-%d') |
|
4454 |
resp.form['end_datetime_1'] = (now() + datetime.timedelta(days=7)).strftime('%H:%M') |
|
4455 |
resp = resp.form.submit().follow() |
|
4456 |
assert desk.timeperiodexception_set.count() == 1 |
|
4457 | ||
4458 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, 2021, 7)) |
|
4459 |
assert len(resp.pyquery.find('.event-info')) == 24 |
|
4460 | ||
4461 |
resp = app.get('/manage/agendas/%s/settings' % agenda.id) |
|
4462 |
resp = resp.click('Configure', href='exceptions') |
|
4463 |
resp = resp.click('enable').follow() |
|
4464 |
assert TimePeriodException.objects.count() > 1 |
|
4465 |
assert 'Bastille Day' in resp.text |
|
4466 | ||
4467 |
resp = app.get('/manage/agendas/%s/%s/%s/' % (agenda.id, 2021, 7)) |
|
4468 |
assert len(resp.pyquery.find('.event-info')) == 23 |
|
4469 | ||
4470 |
# add recurrence end date, which lead to recurrences creation |
|
4471 |
resp = app.get('/manage/agendas/%s/events/%s/edit' % (agenda.id, event.id)) |
|
4472 |
resp.form['recurrence_end_date'] = (now() + datetime.timedelta(days=31)).strftime('%Y-%m-%d') |
|
4473 |
resp = resp.form.submit() |
|
4474 | ||
4475 |
# recurrences corresponding to exceptions have not been created |
|
4476 |
assert Event.objects.count() == 24 |
tests/test_agendas.py | ||
---|---|---|
2006 | 2006 | |
2007 | 2007 |
events = agenda.get_open_events()[:8] |
2008 | 2008 |
assert [e.primary_event.slug for e in events] == ['c', 'b', 'a', 'd', 'c', 'b', 'a', 'd'] |
2009 | ||
2010 | ||
2011 |
@override_settings( |
|
2012 |
EXCEPTIONS_SOURCES={ |
|
2013 |
'holidays': {'class': 'workalendar.europe.France', 'label': 'Holidays'}, |
|
2014 |
} |
|
2015 |
) |
|
2016 |
def test_recurring_events_exceptions(freezer): |
|
2017 |
freezer.move_to('2021-05-01 12:00') |
|
2018 |
agenda = Agenda.objects.create(label='Agenda', kind='events') |
|
2019 |
desk = Desk.objects.get(slug='_exceptions_holder', agenda=agenda) |
|
2020 | ||
2021 |
event = Event.objects.create( |
|
2022 |
agenda=agenda, |
|
2023 |
start_datetime=now(), |
|
2024 |
repeat='daily', |
|
2025 |
places=5, |
|
2026 |
) |
|
2027 |
event.refresh_from_db() |
|
2028 |
start_datetime = localtime(event.start_datetime) |
|
2029 | ||
2030 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2031 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-01' |
|
2032 |
first_of_may = recurrences[0] |
|
2033 | ||
2034 |
recurrence = event.get_or_create_event_recurrence(first_of_may.start_datetime) |
|
2035 |
recurrence.delete() |
|
2036 | ||
2037 |
desk.import_timeperiod_exceptions_from_settings(enable=True) |
|
2038 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2039 |
# 05-01 is a holiday |
|
2040 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-02' |
|
2041 |
with pytest.raises(ValueError): |
|
2042 |
recurrence = event.get_or_create_event_recurrence(first_of_may.start_datetime) |
|
2043 |
first_event = recurrences[0] |
|
2044 | ||
2045 |
# exception before first_event start_datetime |
|
2046 |
time_period_exception = TimePeriodException.objects.create( |
|
2047 |
desk=desk, |
|
2048 |
start_datetime=first_event.start_datetime - datetime.timedelta(hours=1), |
|
2049 |
end_datetime=first_event.start_datetime - datetime.timedelta(minutes=30), |
|
2050 |
) |
|
2051 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2052 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-02' |
|
2053 | ||
2054 |
# exception wraps around first_event start_datetime |
|
2055 |
time_period_exception.end_datetime = first_event.start_datetime + datetime.timedelta(minutes=30) |
|
2056 |
time_period_exception.save() |
|
2057 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2058 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-03' |
|
2059 | ||
2060 |
# exception starts after first_event start_datetime |
|
2061 |
time_period_exception.start_datetime = first_event.start_datetime + datetime.timedelta(minutes=15) |
|
2062 |
time_period_exception.save() |
|
2063 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2064 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-02' |
|
2065 |
assert recurrences[1].start_datetime.strftime('%m-%d') == '05-03' |
|
2066 | ||
2067 |
# exception spans multiple days |
|
2068 |
time_period_exception.end_datetime = first_event.start_datetime + datetime.timedelta(days=3) |
|
2069 |
time_period_exception.save() |
|
2070 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2071 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-02' |
|
2072 |
assert recurrences[1].start_datetime.strftime('%m-%d') == '05-06' |
|
2073 | ||
2074 |
# move exception to unavailability calendar |
|
2075 |
unavailability_calendar = UnavailabilityCalendar.objects.create(label='Calendar') |
|
2076 |
time_period_exception.desk = None |
|
2077 |
time_period_exception.unavailability_calendar = unavailability_calendar |
|
2078 |
time_period_exception.save() |
|
2079 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2080 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-02' |
|
2081 |
assert recurrences[1].start_datetime.strftime('%m-%d') == '05-03' |
|
2082 | ||
2083 |
unavailability_calendar.desks.add(desk) |
|
2084 |
recurrences = event.get_recurrences(now(), now() + datetime.timedelta(days=7)) |
|
2085 |
assert recurrences[0].start_datetime.strftime('%m-%d') == '05-02' |
|
2086 |
assert recurrences[1].start_datetime.strftime('%m-%d') == '05-06' |
tests/test_api.py | ||
---|---|---|
306 | 306 |
resp = app.get('/api/agenda/', params={'with_open_events': '1'}) |
307 | 307 |
assert len(resp.json['data']) == 1 |
308 | 308 | |
309 |
for i in range(10): |
|
310 |
event_agenda = Agenda.objects.create(label='Foo bar', category=category_a) |
|
311 |
event = Event.objects.create(start_datetime=now(), places=10, agenda=event_agenda, repeat='daily') |
|
312 |
TimePeriodException.objects.create( |
|
313 |
desk=event_agenda.desk_set.get(), |
|
314 |
start_datetime=now(), |
|
315 |
end_datetime=now() + datetime.timedelta(hours=1), |
|
316 |
) |
|
317 | ||
309 | 318 |
with CaptureQueriesContext(connection) as ctx: |
310 | 319 |
resp = app.get('/api/agenda/', params={'with_open_events': '1'}) |
311 |
assert len(ctx.captured_queries) == 4
|
|
320 |
assert len(ctx.captured_queries) == 15
|
|
312 | 321 | |
313 | 322 | |
314 | 323 |
def test_agendas_meetingtypes_api(app, some_data, meetings_agenda): |
... | ... | |
6322 | 6331 | |
6323 | 6332 |
resp = app.get('/api/agenda/foo/meetings/mt5/datetimes/', params=make_date_filters(10, 0, 10, 30)) |
6324 | 6333 |
assert len(resp.json['data']) == 3 |
6334 | ||
6335 | ||
6336 |
def test_recurring_events_api_exceptions(app, user, freezer): |
|
6337 |
freezer.move_to('2021-01-12 12:05') # Tuesday |
|
6338 |
agenda = Agenda.objects.create( |
|
6339 |
label='Foo bar', kind='events', minimal_booking_delay=1, maximal_booking_delay=30 |
|
6340 |
) |
|
6341 |
event = Event.objects.create( |
|
6342 |
slug='abc', start_datetime=localtime(), repeat='weekly', places=5, agenda=agenda |
|
6343 |
) |
|
6344 | ||
6345 |
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) |
|
6346 |
data = resp.json['data'] |
|
6347 |
assert len(data) == 4 |
|
6348 |
assert data[0]['datetime'] == '2021-01-19 13:05:00' |
|
6349 | ||
6350 |
time_period_exception = TimePeriodException.objects.create( |
|
6351 |
desk=agenda.desk_set.get(), |
|
6352 |
start_datetime=datetime.date(year=2021, month=1, day=18), |
|
6353 |
end_datetime=datetime.date(year=2021, month=1, day=20), |
|
6354 |
) |
|
6355 |
resp = app.get('/api/agenda/%s/datetimes/' % agenda.slug) |
|
6356 |
assert len(resp.json['data']) == 3 |
|
6357 |
assert resp.json['data'][0]['datetime'] == '2021-01-26 13:05:00' |
|
6358 | ||
6359 |
# try to book excluded event |
|
6360 |
fillslot_url = data[0]['api']['fillslot_url'] |
|
6361 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
6362 |
resp = app.post(fillslot_url, status=400) |
|
6363 |
assert resp.json['err'] == 1 |
tests/test_import_export.py | ||
---|---|---|
58 | 58 |
agenda_meetings = Agenda.objects.create(label='Meetings Agenda', kind='meetings') |
59 | 59 |
MeetingType.objects.create(agenda=agenda_meetings, label='Meeting Type', duration=30) |
60 | 60 |
desk = Desk.objects.create(agenda=agenda_meetings, label='Desk') |
61 |
exceptions_desk = Desk.objects.get(agenda=agenda_events, slug='_exceptions_holder') |
|
61 | 62 | |
62 |
# add exception to meeting agenda |
|
63 | 63 |
tpx_start = make_aware(datetime.datetime(2017, 5, 22, 8, 0)) |
64 | 64 |
tpx_end = make_aware(datetime.datetime(2017, 5, 22, 12, 30)) |
65 | 65 |
TimePeriodException.objects.create(desk=desk, start_datetime=tpx_start, end_datetime=tpx_end) |
66 |
TimePeriodException.objects.create(desk=exceptions_desk, start_datetime=tpx_start, end_datetime=tpx_end) |
|
67 | ||
66 | 68 |
output = get_output_of_command('export_site') |
67 | 69 |
assert len(json.loads(output)['agendas']) == 2 |
68 | 70 |
import_site(data={}, clean=True) |
... | ... | |
87 | 89 |
assert Agenda.objects.count() == 2 |
88 | 90 |
first_imported_event = Agenda.objects.get(label='Events Agenda').event_set.first() |
89 | 91 |
assert first_imported_event.start_datetime == first_event.start_datetime |
90 |
assert TimePeriodException.objects.get().start_datetime == tpx_start |
|
91 |
assert TimePeriodException.objects.get().end_datetime == tpx_end |
|
92 |
assert TimePeriodException.objects.get(desk__agenda__kind='meetings').start_datetime == tpx_start |
|
93 |
assert TimePeriodException.objects.get(desk__agenda__kind='meetings').end_datetime == tpx_end |
|
94 |
assert TimePeriodException.objects.get(desk__agenda__kind='events').start_datetime == tpx_start |
|
95 |
assert TimePeriodException.objects.get(desk__agenda__kind='events').end_datetime == tpx_end |
|
92 | 96 | |
93 | 97 |
agenda1 = Agenda.objects.get(label='Events Agenda') |
94 | 98 |
agenda2 = Agenda.objects.get(label='Meetings Agenda') |
95 |
- |