0002-manager-import-export-events-update-event-if-slug-ex.patch
chrono/manager/forms.py | ||
---|---|---|
26 | 26 |
from django.utils.encoding import force_text |
27 | 27 |
from django.utils.six import StringIO |
28 | 28 |
from django.utils.timezone import make_aware |
29 |
from django.utils.timezone import now |
|
29 | 30 |
from django.utils.translation import ugettext_lazy as _ |
30 | 31 | |
31 | 32 |
from chrono.agendas.models import ( |
... | ... | |
42 | 43 |
Category, |
43 | 44 |
AgendaNotificationsSettings, |
44 | 45 |
WEEKDAYS_LIST, |
46 |
generate_slug, |
|
45 | 47 |
) |
46 | 48 | |
47 | 49 |
from . import widgets |
... | ... | |
351 | 353 |
dialect = None |
352 | 354 | |
353 | 355 |
events = [] |
354 |
slugs = set() |
|
356 |
warnings = {} |
|
357 |
events_by_slug = {e.slug: e for e in Event.objects.filter(agenda=self.agenda_pk)} |
|
358 |
event_ids_with_bookings = set( |
|
359 |
Booking.objects.filter( |
|
360 |
event__agenda=self.agenda_pk, cancellation_datetime__isnull=True |
|
361 |
).values_list('event_id', flat=True) |
|
362 |
) |
|
363 |
seen_slugs = set(events_by_slug.keys()) |
|
355 | 364 |
for i, csvline in enumerate(csv.reader(StringIO(content), dialect=dialect)): |
356 | 365 |
if not csvline: |
357 | 366 |
continue |
... | ... | |
359 | 368 |
raise ValidationError(_('Invalid file format. (line %d)') % (i + 1)) |
360 | 369 |
if i == 0 and csvline[0].strip('#') in ('date', 'Date', _('date'), _('Date')): |
361 | 370 |
continue |
362 |
event = Event() |
|
363 |
event.agenda_id = self.agenda_pk |
|
371 | ||
372 |
# label needed to generate a slug |
|
373 |
label = None |
|
374 |
if len(csvline) >= 5: |
|
375 |
label = force_text(csvline[4]) |
|
376 | ||
377 |
# get or create event |
|
378 |
event = None |
|
379 |
slug = None |
|
380 |
if len(csvline) >= 6: |
|
381 |
slug = force_text(csvline[5]) if csvline[5] else None |
|
382 |
# get existing event if relevant |
|
383 |
if slug and slug in seen_slugs: |
|
384 |
event = events_by_slug[slug] |
|
385 |
# update label |
|
386 |
event.label = label |
|
387 |
if event is None: |
|
388 |
# new event |
|
389 |
event = Event(agenda_id=self.agenda_pk, label=label) |
|
390 |
# generate a slug if not provided |
|
391 |
event.slug = slug or generate_slug(event, seen_slugs=seen_slugs, agenda=self.agenda_pk) |
|
392 |
# maintain caches |
|
393 |
seen_slugs.add(event.slug) |
|
394 |
events_by_slug[event.slug] = event |
|
395 | ||
364 | 396 |
for datetime_fmt in ( |
365 | 397 |
'%Y-%m-%d %H:%M', |
366 | 398 |
'%d/%m/%Y %H:%M', |
... | ... | |
369 | 401 |
'%d/%m/%Y %H:%M:%S', |
370 | 402 |
): |
371 | 403 |
try: |
372 |
event_datetime = datetime.datetime.strptime('%s %s' % tuple(csvline[:2]), datetime_fmt) |
|
404 |
event_datetime = make_aware( |
|
405 |
datetime.datetime.strptime('%s %s' % tuple(csvline[:2]), datetime_fmt) |
|
406 |
) |
|
373 | 407 |
except ValueError: |
374 | 408 |
continue |
375 |
event.start_datetime = make_aware(event_datetime) |
|
409 |
if ( |
|
410 |
event.pk is not None |
|
411 |
and event.start_datetime != event_datetime |
|
412 |
and event.start_datetime > now() |
|
413 |
and event.pk in event_ids_with_bookings |
|
414 |
and event.pk not in warnings |
|
415 |
): |
|
416 |
# event start datetime has changed, event is not past and has not cancelled bookings |
|
417 |
# => warn the user |
|
418 |
warnings[event.pk] = event |
|
419 |
event.start_datetime = event_datetime |
|
376 | 420 |
break |
377 | 421 |
else: |
378 | 422 |
raise ValidationError(_('Invalid file format. (date/time format, line %d)') % (i + 1)) |
... | ... | |
387 | 431 |
raise ValidationError( |
388 | 432 |
_('Invalid file format. (number of places in waiting list, line %d)') % (i + 1) |
389 | 433 |
) |
390 |
if len(csvline) >= 5: |
|
391 |
event.label = force_text(csvline[4]) |
|
392 |
exclude = ['desk', 'meeting_type'] |
|
393 |
if len(csvline) >= 6: |
|
394 |
event.slug = force_text(csvline[5]) if csvline[5] else None |
|
395 |
if event.slug and event.slug in slugs: |
|
396 |
raise ValidationError(_('File contains duplicated event identifiers: %s') % event.slug) |
|
397 |
else: |
|
398 |
slugs.add(event.slug) |
|
399 |
else: |
|
400 |
exclude += ['slug'] |
|
434 | ||
401 | 435 |
column_index = 7 |
402 | 436 |
for more_attr in ('description', 'pricing', 'url'): |
403 | 437 |
if len(csvline) >= column_index: |
... | ... | |
421 | 455 |
raise ValidationError(_('Invalid file format. (duration, line %d)') % (i + 1)) |
422 | 456 | |
423 | 457 |
try: |
424 |
event.full_clean(exclude=exclude)
|
|
458 |
event.full_clean(exclude=['desk', 'meeting_type'])
|
|
425 | 459 |
except ValidationError as e: |
426 | 460 |
errors = [_('Invalid file format:\n')] |
427 | 461 |
for label, field_errors in e.message_dict.items(): |
... | ... | |
435 | 469 |
raise ValidationError(errors) |
436 | 470 |
events.append(event) |
437 | 471 |
self.events = events |
472 |
self.warnings = warnings |
|
438 | 473 | |
439 | 474 |
@staticmethod |
440 | 475 |
def get_verbose_name(field_name): |
chrono/manager/templates/chrono/manager_events_agenda_settings.html | ||
---|---|---|
2 | 2 |
{% load i18n %} |
3 | 3 | |
4 | 4 |
{% block agenda-extra-management-actions %} |
5 |
<a rel="popup" href="{% url 'chrono-manager-agenda-export-events' pk=object.pk %}">{% trans 'Export Events' %}</a> |
|
5 | 6 |
<a rel="popup" href="{% url 'chrono-manager-agenda-import-events' pk=object.id %}">{% trans 'Import Events' %}</a> |
6 | 7 |
<a rel="popup" href="{% url 'chrono-manager-agenda-add-event' pk=object.id %}">{% trans 'New Event' %}</a> |
7 | 8 |
{% endblock %} |
chrono/manager/urls.py | ||
---|---|---|
73 | 73 |
views.agenda_import_events, |
74 | 74 |
name='chrono-manager-agenda-import-events', |
75 | 75 |
), |
76 |
url( |
|
77 |
r'^agendas/(?P<pk>\d+)/export-events$', |
|
78 |
views.agenda_export_events, |
|
79 |
name='chrono-manager-agenda-export-events', |
|
80 |
), |
|
76 | 81 |
url( |
77 | 82 |
r'^agendas/(?P<pk>\d+)/notifications$', |
78 | 83 |
views.agenda_notifications_settings, |
chrono/manager/views.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import csv |
|
17 | 18 |
import datetime |
18 | 19 |
import itertools |
19 | 20 |
import json |
... | ... | |
23 | 24 | |
24 | 25 |
from django.contrib import messages |
25 | 26 |
from django.core.exceptions import PermissionDenied |
26 |
from django.db.models import Q, F
|
|
27 |
from django.db.models import Q |
|
27 | 28 |
from django.db.models import Min, Max |
28 | 29 |
from django.http import Http404, HttpResponse, HttpResponseRedirect |
29 | 30 |
from django.shortcuts import get_object_or_404 |
... | ... | |
1330 | 1331 |
template_name = 'chrono/manager_import_events.html' |
1331 | 1332 |
agenda = None |
1332 | 1333 | |
1334 |
def set_agenda(self, **kwargs): |
|
1335 |
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events') |
|
1336 | ||
1333 | 1337 |
def get_form_kwargs(self): |
1334 | 1338 |
kwargs = super(AgendaImportEventsView, self).get_form_kwargs() |
1335 | 1339 |
kwargs['agenda_pk'] = self.kwargs['pk'] |
... | ... | |
1338 | 1342 |
def form_valid(self, form): |
1339 | 1343 |
if form.events: |
1340 | 1344 |
# existing event slugs for this agenda |
1341 |
seen_slugs = set(self.agenda.event_set.values_list('slug', flat=True)) |
|
1342 | 1345 |
for event in form.events: |
1343 |
event.agenda_id = self.kwargs['pk']
|
|
1344 |
event.save(seen_slugs=seen_slugs) # optimization: seen_slugs
|
|
1346 |
event.agenda = self.agenda
|
|
1347 |
event.save()
|
|
1345 | 1348 |
messages.info(self.request, _('%d events have been imported.') % len(form.events)) |
1349 |
for event in form.warnings.values(): |
|
1350 |
messages.warning( |
|
1351 |
self.request, |
|
1352 |
_('Event "%s" start date has changed. Do not forget to notify the registrants.') |
|
1353 |
% (event.label or event.slug), |
|
1354 |
) |
|
1346 | 1355 |
return super(AgendaImportEventsView, self).form_valid(form) |
1347 | 1356 | |
1348 | 1357 | |
1349 | 1358 |
agenda_import_events = AgendaImportEventsView.as_view() |
1350 | 1359 | |
1351 | 1360 | |
1361 |
class AgendaExportEventsView(ManagedAgendaMixin, View): |
|
1362 |
template_name = 'chrono/manager_export_events.html' |
|
1363 |
agenda = None |
|
1364 | ||
1365 |
def set_agenda(self, **kwargs): |
|
1366 |
self.agenda = get_object_or_404(Agenda, pk=kwargs.get('pk'), kind='events') |
|
1367 | ||
1368 |
def get(self, request, *args, **kwargs): |
|
1369 |
response = HttpResponse(content_type='text/csv') |
|
1370 |
today = datetime.date.today() |
|
1371 |
response['Content-Disposition'] = 'attachment; filename="export_agenda__events_{}_{}.csv"'.format( |
|
1372 |
self.agenda.slug, today.strftime('%Y%m%d') |
|
1373 |
) |
|
1374 |
writer = csv.writer(response) |
|
1375 |
# headers |
|
1376 |
writer.writerow( |
|
1377 |
[ |
|
1378 |
_('date'), |
|
1379 |
_('time'), |
|
1380 |
_('number of places'), |
|
1381 |
_('number of places in waiting list'), |
|
1382 |
_('label'), |
|
1383 |
_('identifier'), |
|
1384 |
_('description'), |
|
1385 |
_('pricing'), |
|
1386 |
_('URL'), |
|
1387 |
_('publication date'), |
|
1388 |
_('duration'), |
|
1389 |
] |
|
1390 |
) |
|
1391 |
for event in self.agenda.event_set.all(): |
|
1392 |
start_datetime = localtime(event.start_datetime) |
|
1393 |
writer.writerow( |
|
1394 |
[ |
|
1395 |
start_datetime.strftime('%Y-%m-%d'), |
|
1396 |
start_datetime.strftime('%H:%M'), |
|
1397 |
event.places, |
|
1398 |
event.waiting_list_places, |
|
1399 |
event.label, |
|
1400 |
event.slug, |
|
1401 |
event.description, |
|
1402 |
event.pricing, |
|
1403 |
event.url, |
|
1404 |
event.publication_date.strftime('%Y-%m-%d') if event.publication_date else '', |
|
1405 |
event.duration, |
|
1406 |
] |
|
1407 |
) |
|
1408 |
return response |
|
1409 | ||
1410 | ||
1411 |
agenda_export_events = AgendaExportEventsView.as_view() |
|
1412 | ||
1413 | ||
1352 | 1414 |
class AgendaNotificationsSettingsView(ManagedAgendaMixin, UpdateView): |
1353 | 1415 |
template_name = 'chrono/manager_agenda_notifications_form.html' |
1354 | 1416 |
model = AgendaNotificationsSettings |
tests/test_manager.py | ||
---|---|---|
17 | 17 |
from django.utils.encoding import force_text |
18 | 18 |
from django.utils.timezone import make_aware, now, localtime |
19 | 19 | |
20 |
import datetime |
|
21 | 20 |
import freezegun |
22 | 21 |
import pytest |
23 | 22 |
import requests |
... | ... | |
1383 | 1382 |
assert Event.objects.count() == 0 |
1384 | 1383 | |
1385 | 1384 | |
1385 |
def test_export_events(app, admin_user): |
|
1386 |
agenda = Agenda.objects.create(label=u'Foo bar') |
|
1387 | ||
1388 |
app = login(app) |
|
1389 |
resp = app.get('/manage/agendas/%s/export-events' % agenda.id) |
|
1390 |
csv_export = resp.text |
|
1391 |
assert ( |
|
1392 |
csv_export |
|
1393 |
== 'date,time,number of places,number of places in waiting list,label,identifier,description,pricing,URL,publication date,duration\r\n' |
|
1394 |
) |
|
1395 | ||
1396 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id) |
|
1397 |
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,00:30,10', 'text/csv') |
|
1398 |
resp.form.submit(status=302) |
|
1399 | ||
1400 |
resp = app.get('/manage/agendas/%s/export-events' % agenda.id) |
|
1401 |
csv_export = resp.text |
|
1402 |
assert ( |
|
1403 |
csv_export |
|
1404 |
== 'date,time,number of places,number of places in waiting list,label,identifier,description,pricing,URL,publication date,duration\r\n' |
|
1405 |
'2016-09-16,00:30,10,0,,foo-bar-event,,,,,\r\n' |
|
1406 |
) |
|
1407 | ||
1408 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id) |
|
1409 |
resp.form['events_csv_file'] = Upload( |
|
1410 |
't.csv', |
|
1411 |
b'2016-09-16,23:30,10,5,label,slug,"description\nfoobar",pricing,url,2016-10-16,90', |
|
1412 |
'text/csv', |
|
1413 |
) |
|
1414 |
resp.form.submit(status=302) |
|
1415 |
resp = app.get('/manage/agendas/%s/export-events' % agenda.id) |
|
1416 |
csv_export = resp.text |
|
1417 |
assert ( |
|
1418 |
csv_export |
|
1419 |
== 'date,time,number of places,number of places in waiting list,label,identifier,description,pricing,URL,publication date,duration\r\n' |
|
1420 |
'2016-09-16,00:30,10,0,,foo-bar-event,,,,,\r\n' |
|
1421 |
'2016-09-16,23:30,10,5,label,slug,"description\nfoobar",pricing,url,2016-10-16,90\r\n' |
|
1422 |
) |
|
1423 | ||
1424 | ||
1425 |
def test_export_events_wrong_kind(app, admin_user): |
|
1426 |
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings') |
|
1427 | ||
1428 |
app = login(app) |
|
1429 |
app.get('/manage/agendas/%s/export-events' % agenda.id, status=404) |
|
1430 |
agenda.kind = 'virtual' |
|
1431 |
agenda.save() |
|
1432 |
app.get('/manage/agendas/%s/export-events' % agenda.id, status=404) |
|
1433 | ||
1434 | ||
1386 | 1435 |
def test_import_events(app, admin_user): |
1387 | 1436 |
agenda = Agenda(label=u'Foo bar') |
1388 | 1437 |
agenda.save() |
... | ... | |
1528 | 1577 |
assert event.slug == 'slug' |
1529 | 1578 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
1530 | 1579 |
resp.form['events_csv_file'] = Upload('t.csv', b'2016-09-16,18:00,10,5,label,slug', 'text/csv') |
1531 |
resp = resp.form.submit(status=200) |
|
1532 |
assert 'Event with this Agenda and Identifier already exists.' in resp.text |
|
1533 |
assert '__all__' not in resp.text |
|
1580 |
resp = resp.form.submit(status=302) |
|
1581 |
assert Event.objects.count() == 1 |
|
1582 |
event = Event.objects.latest('pk') |
|
1583 |
assert event.slug == 'slug' |
|
1534 | 1584 | |
1535 | 1585 |
# additional optional attributes |
1536 | 1586 |
Event.objects.all().delete() |
... | ... | |
1581 | 1631 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
1582 | 1632 |
resp.form['events_csv_file'] = Upload( |
1583 | 1633 |
't.csv', |
1584 |
b'2016-09-16,18:00,10,5,labela,,,pricing,\n' |
|
1585 |
b'2016-09-17,18:00,10,5,labela,,,pricing,\n' |
|
1586 |
b'2016-09-18,18:00,10,5,labela,,,pricing,\n' |
|
1634 |
b'2016-09-16,18:00,10,5,labela,labelb,,pricing,\n'
|
|
1635 |
b'2016-09-17,18:00,10,5,labela,labelb-1,,pricing,\n'
|
|
1636 |
b'2016-09-18,18:00,10,5,labela,labelb-2,,pricing,\n'
|
|
1587 | 1637 |
b'2016-09-18,18:00,10,5,labelb,,,pricing,\n' |
1588 | 1638 |
b'2016-09-18,18:00,10,5,labelb,,,pricing,\n', |
1589 | 1639 |
'text/csv', |
1590 | 1640 |
) |
1591 | 1641 |
with CaptureQueriesContext(connection) as ctx: |
1592 | 1642 |
resp = resp.form.submit(status=302) |
1593 |
assert len(ctx.captured_queries) == 24
|
|
1643 |
assert len(ctx.captured_queries) == 22
|
|
1594 | 1644 |
assert Event.objects.count() == 5 |
1645 |
assert set(Event.objects.values_list('slug', flat=True)) == set( |
|
1646 |
['labelb', 'labelb-1', 'labelb-2', 'labelb-3', 'labelb-4'] |
|
1647 |
) |
|
1595 | 1648 | |
1596 | 1649 |
# forbidden numerical slug |
1597 | 1650 |
Event.objects.all().delete() |
... | ... | |
1601 | 1654 |
assert 'value cannot be a number' in resp.text |
1602 | 1655 |
assert 'Identifier:' in resp.text # verbose_name is shown, not field name ('slug:') |
1603 | 1656 | |
1604 |
# handle duplicated slug |
|
1605 |
Event.objects.all().delete() |
|
1657 | ||
1658 |
def test_import_events_existing_event(app, admin_user, freezer): |
|
1659 |
agenda = Agenda.objects.create(label=u'Foo bar') |
|
1660 | ||
1661 |
app = login(app) |
|
1606 | 1662 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
1607 | 1663 |
resp.form['events_csv_file'] = Upload( |
1608 |
't.csv', b'2016-09-16,18:00,10,5,label,slug\n' b'2016-09-16,18:00,10,5,label,slug\n', 'text/csv',
|
|
1664 |
't.csv', b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,18:00,10,5,label,slug\n', 'text/csv', |
|
1609 | 1665 |
) |
1610 |
resp = resp.form.submit(status=200) |
|
1611 |
assert 'duplicated event identifiers' in resp.text |
|
1666 |
resp.form.submit(status=302) |
|
1667 |
assert agenda.event_set.count() == 1 |
|
1668 |
event = Event.objects.latest('pk') |
|
1669 | ||
1670 |
def check_import(date, time, with_alert): |
|
1671 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
|
1672 |
resp.form['events_csv_file'] = Upload( |
|
1673 |
't.csv', b'%s,%s,10,5,label,slug\n' % (date.encode(), time.encode()), 'text/csv', |
|
1674 |
) |
|
1675 |
resp = resp.form.submit(status=302).follow() |
|
1676 |
assert agenda.event_set.count() == 1 |
|
1677 |
event.refresh_from_db() |
|
1678 |
if with_alert: |
|
1679 |
assert ( |
|
1680 |
'<li class="warning">Event "label" start date has changed. Do not forget to notify the registrants.</li>' |
|
1681 |
in resp.text |
|
1682 |
) |
|
1683 |
else: |
|
1684 |
assert ( |
|
1685 |
'<li class="warning">Event "label" start date has changed. Do not forget to notify the registrants.</li>' |
|
1686 |
not in resp.text |
|
1687 |
) |
|
1688 |
assert event.start_datetime == make_aware( |
|
1689 |
datetime.datetime(*[int(v) for v in date.split('-')], *[int(v) for v in time.split(':')]) |
|
1690 |
) |
|
1691 | ||
1692 |
# change date or time |
|
1693 |
# event in the past, no alert, with or without booking |
|
1694 |
Booking.objects.create( |
|
1695 |
event=event, cancellation_datetime=make_aware(datetime.datetime(2017, 5, 20, 10, 30)) |
|
1696 |
) |
|
1697 |
check_import('2016-09-15', '18:00', False) # change date |
|
1698 |
check_import('2016-09-15', '17:00', False) # change time |
|
1699 |
# available booking |
|
1700 |
Booking.objects.create(event=event) |
|
1701 |
check_import('2016-09-14', '17:00', False) # change date |
|
1702 |
check_import('2016-09-14', '16:00', False) # change time |
|
1703 | ||
1704 |
# date in the future |
|
1705 |
freezer.move_to('2016-09-01') |
|
1706 |
# warn if available booking only |
|
1707 |
check_import('2016-09-13', '16:00', True) # change date |
|
1708 |
check_import('2016-09-13', '15:00', True) # change time |
|
1709 |
# no available booking |
|
1710 |
Booking.objects.all().delete() |
|
1711 |
Booking.objects.create( |
|
1712 |
event=event, cancellation_datetime=make_aware(datetime.datetime(2017, 5, 20, 10, 30)) |
|
1713 |
) |
|
1714 |
check_import('2016-09-12', '15:00', False) # change date |
|
1715 |
check_import('2016-09-12', '14:00', False) # change time |
|
1716 | ||
1717 |
# check there is a message per changed event |
|
1718 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
|
1719 |
resp.form['events_csv_file'] = Upload( |
|
1720 |
't.csv', b'2016-09-16,18:00,10,5,label,slug\n2016-09-16,19:00,10,5,label,other_slug\n', 'text/csv', |
|
1721 |
) |
|
1722 |
resp.form.submit(status=302) |
|
1723 |
assert agenda.event_set.count() == 2 |
|
1724 |
event2 = Event.objects.latest('pk') |
|
1725 |
Booking.objects.create(event=event) |
|
1726 |
Booking.objects.create(event=event2) |
|
1727 | ||
1728 |
resp = app.get('/manage/agendas/%s/import-events' % agenda.id, status=200) |
|
1729 |
resp.form['events_csv_file'] = Upload( |
|
1730 |
't.csv', |
|
1731 |
b'2016-09-17,18:00,10,5,label,slug\n2016-09-17,19:00,10,5,,other_slug\n2016-09-17,20:00,10,5,,other_slug\n', |
|
1732 |
'text/csv', |
|
1733 |
) |
|
1734 |
resp = resp.form.submit(status=302).follow() |
|
1735 |
assert agenda.event_set.count() == 2 |
|
1736 |
assert ( |
|
1737 |
resp.text.count( |
|
1738 |
'Event "label" start date has changed. Do not forget to notify the registrants.' |
|
1739 |
) |
|
1740 |
== 1 |
|
1741 |
) |
|
1742 |
assert ( |
|
1743 |
resp.text.count( |
|
1744 |
'Event "other_slug" start date has changed. Do not forget to notify the registrants.' |
|
1745 |
) |
|
1746 |
== 1 |
|
1747 |
) |
|
1748 | ||
1749 | ||
1750 |
def test_import_events_wrong_kind(app, admin_user): |
|
1751 |
agenda = Agenda.objects.create(label=u'Foo bar', kind='meetings') |
|
1752 | ||
1753 |
app = login(app) |
|
1754 |
app.get('/manage/agendas/%s/import-events' % agenda.id, status=404) |
|
1755 |
agenda.kind = 'virtual' |
|
1756 |
agenda.save() |
|
1757 |
app.get('/manage/agendas/%s/import-events' % agenda.id, status=404) |
|
1612 | 1758 | |
1613 | 1759 | |
1614 | 1760 |
def test_add_meetings_agenda(app, admin_user): |
... | ... | |
3216 | 3362 |
agenda.save() |
3217 | 3363 | |
3218 | 3364 |
app = login(app) |
3219 |
resp = app.get('/manage/agendas/%s/settings' % agenda.id) |
|
3220 | 3365 |
with freezegun.freeze_time('2020-06-15'): |
3221 |
resp = resp.click('Export')
|
|
3366 |
resp = app.get('/manage/agendas/%s/export' % agenda.id)
|
|
3222 | 3367 |
assert resp.headers['content-type'] == 'application/json' |
3223 | 3368 |
assert resp.headers['content-disposition'] == 'attachment; filename="export_agenda_foo-bar_20200615.json"' |
3224 | 3369 |
agenda_export = resp.text |
3225 |
- |