# -*- coding: utf-8 -*- import datetime import pytest from django.db import connection from django.test.utils import CaptureQueriesContext from django.utils.timezone import localtime, now from chrono.agendas.models import ( AbsenceReason, AbsenceReasonGroup, Agenda, Category, Desk, Event, MeetingType, Resource, TimePeriodException, VirtualMember, ) pytestmark = pytest.mark.django_db def test_agendas_api(app): category_a = Category.objects.create(label='Category A') category_b = Category.objects.create(label='Category B') group = AbsenceReasonGroup.objects.create(label='Foo') reason = AbsenceReason.objects.create(group=group, label='Foo bar') reason2 = AbsenceReason.objects.create(group=group, label='Foo bar baz') event_agenda = Agenda.objects.create(label='Foo bar', category=category_a, absence_reasons_group=group) Desk.objects.create(agenda=event_agenda, slug='_exceptions_holder') event_agenda2 = Agenda.objects.create(label='Foo bar 2', category=category_a) Desk.objects.create(agenda=event_agenda2, slug='_exceptions_holder') event_agenda3 = Agenda.objects.create(label='Foo bar 3', absence_reasons_group=group) Desk.objects.create(agenda=event_agenda3, slug='_exceptions_holder') meetings_agenda1 = Agenda.objects.create(label='Foo bar Meeting', kind='meetings', category=category_b) Agenda.objects.create(label='Foo bar Meeting 2', kind='meetings') resource1 = Resource.objects.create(label='Resource 1', description='Foo bar Resource 1') resource2 = Resource.objects.create(label='Resource 2', description='Foo bar Resource 2') Resource.objects.create(label='Resource 3') meetings_agenda1.resources.add(resource1, resource2) Agenda.objects.create( label='Virtual Agenda', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=56 ) resp = app.get('/api/agenda/') assert resp.json == { 'data': [ { 'text': 'Foo bar', 'id': 'foo-bar', 'slug': 'foo-bar', 'kind': 'events', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, 'absence_reasons': [ {'id': reason.slug, 'slug': reason.slug, 'text': reason.label, 'label': reason.label}, {'id': reason2.slug, 'slug': reason2.slug, 'text': reason2.label, 'label': reason2.label}, ], 'api': { 'datetimes_url': 'http://testserver/api/agenda/foo-bar/datetimes/', 'fillslots_url': 'http://testserver/api/agenda/foo-bar/fillslots/', }, }, { 'text': 'Foo bar 2', 'id': 'foo-bar-2', 'kind': 'events', 'slug': 'foo-bar-2', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, 'api': { 'datetimes_url': 'http://testserver/api/agenda/foo-bar-2/datetimes/', 'fillslots_url': 'http://testserver/api/agenda/foo-bar-2/fillslots/', }, }, { 'text': 'Foo bar 3', 'id': 'foo-bar-3', 'kind': 'events', 'slug': 'foo-bar-3', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, 'absence_reasons': [ {'id': reason.slug, 'slug': reason.slug, 'text': reason.label, 'label': reason.label}, {'id': reason2.slug, 'slug': reason2.slug, 'text': reason2.label, 'label': reason2.label}, ], 'api': { 'datetimes_url': 'http://testserver/api/agenda/foo-bar-3/datetimes/', 'fillslots_url': 'http://testserver/api/agenda/foo-bar-3/fillslots/', }, }, { 'text': 'Foo bar Meeting', 'id': 'foo-bar-meeting', 'slug': 'foo-bar-meeting', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, 'kind': 'meetings', 'resources': [ {'id': 'resource-1', 'text': 'Resource 1', 'description': 'Foo bar Resource 1'}, {'id': 'resource-2', 'text': 'Resource 2', 'description': 'Foo bar Resource 2'}, ], 'api': { 'meetings_url': 'http://testserver/api/agenda/foo-bar-meeting/meetings/', 'desks_url': 'http://testserver/api/agenda/foo-bar-meeting/desks/', 'resources_url': 'http://testserver/api/agenda/foo-bar-meeting/resources/', 'fillslots_url': 'http://testserver/api/agenda/foo-bar-meeting/fillslots/', }, }, { 'text': 'Foo bar Meeting 2', 'id': 'foo-bar-meeting-2', 'slug': 'foo-bar-meeting-2', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, 'kind': 'meetings', 'resources': [], 'api': { 'meetings_url': 'http://testserver/api/agenda/foo-bar-meeting-2/meetings/', 'desks_url': 'http://testserver/api/agenda/foo-bar-meeting-2/desks/', 'resources_url': 'http://testserver/api/agenda/foo-bar-meeting-2/resources/', 'fillslots_url': 'http://testserver/api/agenda/foo-bar-meeting-2/fillslots/', }, }, { 'text': 'Virtual Agenda', 'id': 'virtual-agenda', 'slug': 'virtual-agenda', 'minimal_booking_delay': 1, 'maximal_booking_delay': 56, 'kind': 'virtual', 'api': { 'meetings_url': 'http://testserver/api/agenda/virtual-agenda/meetings/', 'desks_url': 'http://testserver/api/agenda/virtual-agenda/desks/', 'fillslots_url': 'http://testserver/api/agenda/virtual-agenda/fillslots/', }, }, ] } resp = app.get('/api/agenda/', params={'q': 'foo'}) assert len(resp.json['data']) == 5 resp = app.get('/api/agenda/', params={'q': 'MEET'}) assert len(resp.json['data']) == 2 resp = app.get('/api/agenda/', params={'q': ''}) assert len(resp.json['data']) == 0 with CaptureQueriesContext(connection) as ctx: resp = app.get('/api/agenda/') assert len(ctx.captured_queries) == 4 with CaptureQueriesContext(connection) as ctx: resp = app.get('/api/agenda/', params={'q': 'MEET'}) assert len(ctx.captured_queries) == 2 resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(resp.json['data']) == 0 resp = app.get('/api/agenda/', params={'category': ''}) assert len(resp.json['data']) == 6 resp = app.get('/api/agenda/', params={'category': '__none__'}) assert len(resp.json['data']) == 3 resp = app.get('/api/agenda/', params={'category': 'category-a'}) assert len(resp.json['data']) == 2 resp = app.get('/api/agenda/', params={'category': 'category-b'}) assert len(resp.json['data']) == 1 resp = app.get('/api/agenda/', params={'category': 'unknown'}) assert len(resp.json['data']) == 0 event1 = Event.objects.create( start_datetime=(localtime() + datetime.timedelta(days=5)).replace(hour=10, minute=0), places=20, agenda=event_agenda, ) event2 = Event.objects.create( start_datetime=(localtime() + datetime.timedelta(days=10)).replace(hour=10, minute=0), places=20, agenda=event_agenda, ) event3 = Event.objects.create( start_datetime=(localtime() + datetime.timedelta(days=15)).replace(hour=10, minute=0), places=20, agenda=event_agenda, ) # all events are free resp = app.get('/api/agenda/', params={'with_open_events': 'true'}) assert len(resp.json['data']) == 1 # one event is full Event.objects.filter(pk=event1.pk).update(full=True) resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(resp.json['data']) == 1 # all events are full Event.objects.update(full=True) resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(resp.json['data']) == 0 # event1 is not full but too soon Event.objects.filter(pk=event1.pk).update(full=False) event_agenda.minimal_booking_delay = 10 event_agenda.save() assert list(event_agenda.get_open_events()) == [event2, event3] resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(resp.json['data']) == 0 # event3 is not full but too late Event.objects.filter(pk=event3.pk).update(full=False) event_agenda.maximal_booking_delay = 12 event_agenda.save() assert list(event_agenda.get_open_events()) == [event2] resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(resp.json['data']) == 0 # events are not full but not published Event.objects.update(full=False) event_agenda.event_set.update(publication_date=now().date() + datetime.timedelta(days=20)) assert list(event_agenda.get_open_events()) == [] resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(resp.json['data']) == 0 # event recurrences are available Event.objects.create( start_datetime=now(), places=10, agenda=event_agenda, recurrence_days=list(range(7)), ) assert len(event_agenda.get_open_events()) == 2 resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(resp.json['data']) == 1 for _ in range(10): event_agenda = Agenda.objects.create(label='Foo bar', category=category_a) Desk.objects.create(agenda=event_agenda, slug='_exceptions_holder') Event.objects.create( start_datetime=now(), places=10, agenda=event_agenda, recurrence_days=[now().weekday()] ) TimePeriodException.objects.create( desk=event_agenda.desk_set.get(), start_datetime=now(), end_datetime=now() + datetime.timedelta(hours=1), ) with CaptureQueriesContext(connection) as ctx: resp = app.get('/api/agenda/', params={'with_open_events': '1'}) assert len(ctx.captured_queries) == 8 def test_agendas_meetingtypes_api(app, some_data, meetings_agenda): resp = app.get('/api/agenda/%s/meetings/' % meetings_agenda.slug) expected_resp = { 'data': [ { 'text': 'Blah', 'id': 'blah', 'duration': 30, 'api': { 'datetimes_url': 'http://testserver/api/agenda/foo-bar-meeting/meetings/blah/datetimes/', }, } ] } assert resp.json == expected_resp # deleted meeting type does not show up MeetingType.objects.create(agenda=meetings_agenda, slug='deleted-meeting-type', duration=43, deleted=True) resp = app.get('/api/agenda/%s/meetings/' % meetings_agenda.slug) assert resp.json == expected_resp # wrong kind agenda1 = Agenda.objects.filter(label=u'Foo bar')[0] resp = app.get('/api/agenda/%s/meetings/' % agenda1.slug, status=404) # unknown resp = app.get('/api/agenda/xxxx/meetings/', status=404) def test_agendas_meetingtype_api(app): agenda = Agenda.objects.create(label='Foo bar meeting', kind='meetings') MeetingType.objects.create(agenda=agenda, label='Blah', duration=30) resp = app.get('/api/agenda/%s/meetings/blah/' % agenda.slug) assert resp.json == { 'data': { 'text': 'Blah', 'id': 'blah', 'duration': 30, 'api': { 'datetimes_url': 'http://testserver/api/agenda/foo-bar-meeting/meetings/blah/datetimes/', }, } } # unknown resp = app.get('/api/agenda/xxxx/meetings/', status=404) resp = app.get('/api/agenda/%s/meetings/xxxx/' % agenda.slug, status=404) # wrong kind agenda.kind = 'virtual' agenda.save() resp = app.get('/api/agenda/%s/resources/' % agenda.slug, status=404) agenda.kind = 'events' agenda.save() resp = app.get('/api/agenda/%s/resources/' % agenda.slug, status=404) def test_agendas_desks_api(app): agenda = Agenda.objects.create(label='Foo bar', kind='meetings') Desk.objects.create(agenda=agenda, label='Desk 1') resp = app.get('/api/agenda/%s/desks/' % agenda.slug) assert resp.json == { 'data': [ { 'text': 'Desk 1', 'id': 'desk-1', } ] } # unknown resp = app.get('/api/agenda/xxxx/resources/', status=404) # wrong kind agenda.kind = 'virtual' agenda.save() resp = app.get('/api/agenda/%s/resources/' % agenda.slug, status=404) agenda.kind = 'events' agenda.save() resp = app.get('/api/agenda/%s/resources/' % agenda.slug, status=404) def test_agendas_resources_api(app): agenda = Agenda.objects.create(label='Foo bar', kind='meetings') resource1 = Resource.objects.create(label='Resource 1', description='Foo bar Resource 1') resource2 = Resource.objects.create(label='Resource 2') agenda.resources.add(resource1, resource2) resp = app.get('/api/agenda/%s/resources/' % agenda.slug) assert resp.json == { 'data': [ { 'text': 'Resource 1', 'id': 'resource-1', 'description': 'Foo bar Resource 1', }, { 'text': 'Resource 2', 'id': 'resource-2', 'description': '', }, ] } # unknown resp = app.get('/api/agenda/xxxx/resources/', status=404) # wrong kind agenda.kind = 'virtual' agenda.save() resp = app.get('/api/agenda/%s/resources/' % agenda.slug, status=404) agenda.kind = 'events' agenda.save() resp = app.get('/api/agenda/%s/resources/' % agenda.slug, status=404) def test_agenda_detail_api(app): agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0) event1 = Event.objects.create( start_datetime=(localtime() + datetime.timedelta(days=5)).replace(hour=10, minute=0), places=20, agenda=agenda, ) event2 = Event.objects.create( start_datetime=(localtime() + datetime.timedelta(days=10)).replace(hour=10, minute=0), places=20, agenda=agenda, ) event3 = Event.objects.create( start_datetime=(localtime() + datetime.timedelta(days=15)).replace(hour=10, minute=0), places=20, agenda=agenda, ) resp = app.get('/api/agenda/%s/' % agenda.slug) data = resp.json['data'] assert data['id'] == 'foo-bar' assert data['slug'] == 'foo-bar' assert data['text'] == 'Foo bar' assert data['kind'] == 'events' assert data['opened_events_available'] is True assert data['api']['datetimes_url'] == 'http://testserver/api/agenda/foo-bar/datetimes/' # one event is full Event.objects.filter(pk=event1.pk).update(full=True) resp = app.get('/api/agenda/%s/' % agenda.slug) assert resp.json['data']['opened_events_available'] is True # all events are full Event.objects.update(full=True) resp = app.get('/api/agenda/%s/' % agenda.slug) assert resp.json['data']['opened_events_available'] is False # event1 is not full but too soon Event.objects.filter(pk=event1.pk).update(full=False) agenda.minimal_booking_delay = 10 agenda.save() resp = app.get('/api/agenda/%s/' % agenda.slug) assert list(agenda.get_open_events()) == [event2, event3] assert resp.json['data']['opened_events_available'] is False # event3 is not full but too late Event.objects.filter(pk=event3.pk).update(full=False) agenda.maximal_booking_delay = 12 agenda.save() resp = app.get('/api/agenda/%s/' % agenda.slug) assert list(agenda.get_open_events()) == [event2] assert resp.json['data']['opened_events_available'] is False # events are not full but not published Event.objects.update(full=False) agenda.event_set.update(publication_date=now().date() + datetime.timedelta(days=20)) resp = app.get('/api/agenda/%s/' % agenda.slug) assert list(agenda.get_open_events()) == [] assert resp.json['data']['opened_events_available'] is False # unknown app.get('/api/agenda/whatever/', status=404) def test_agenda_detail_routing(app, meetings_agenda): api_url = '/api/agenda/%s/' % meetings_agenda.slug resp = app.get(api_url) assert isinstance(resp.json['data'], dict) # check it doesn't get confused with an agenda with "agenda" in its slug agenda = Agenda( label=u'Foo bar Agenda', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=56 ) agenda.save() api_url = '/api/agenda/%s/' % agenda.slug resp = app.get(api_url) assert isinstance(resp.json['data'], dict) def test_virtual_agenda_detail(app, virtual_meetings_agenda): resp = app.get('/api/agenda/%s/' % virtual_meetings_agenda.slug) assert resp.json == { 'data': { 'text': 'Virtual Agenda', 'id': 'virtual-agenda', 'slug': 'virtual-agenda', 'minimal_booking_delay': None, 'maximal_booking_delay': None, 'kind': 'virtual', 'api': { 'meetings_url': 'http://testserver/api/agenda/%s/meetings/' % virtual_meetings_agenda.slug, 'desks_url': 'http://testserver/api/agenda/%s/desks/' % virtual_meetings_agenda.slug, 'fillslots_url': 'http://testserver/api/agenda/%s/fillslots/' % virtual_meetings_agenda.slug, }, }, } def test_virtual_agendas_meetingtypes_api(app): virt_agenda = Agenda.objects.create(label=u'Virtual agenda', kind='virtual') # No meetings because no real agenda resp = app.get('/api/agenda/%s/meetings/' % virt_agenda.slug) assert resp.json == {'data': []} # One real agenda : every meetings exposed foo_agenda = Agenda.objects.create(label=u'Foo', kind='meetings') MeetingType.objects.create(agenda=foo_agenda, label='Meeting1', duration=30) MeetingType.objects.create(agenda=foo_agenda, label='Meeting2', duration=15) VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=foo_agenda) resp = app.get('/api/agenda/%s/meetings/' % virt_agenda.slug) assert resp.json == { 'data': [ { 'text': 'Meeting1', 'id': 'meeting1', 'duration': 30, 'api': { 'datetimes_url': 'http://testserver/api/agenda/virtual-agenda/meetings/meeting1/datetimes/', }, }, { 'text': 'Meeting2', 'id': 'meeting2', 'duration': 15, 'api': { 'datetimes_url': 'http://testserver/api/agenda/virtual-agenda/meetings/meeting2/datetimes/', }, }, ] } # Several real agendas bar_agenda = Agenda.objects.create(label=u'Bar', kind='meetings') MeetingType.objects.create(agenda=bar_agenda, label='Meeting Bar', duration=30) VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=bar_agenda) # Bar agenda has no meeting type: no meetings exposed resp = app.get('/api/agenda/%s/meetings/' % virt_agenda.slug) assert resp.json == {'data': []} # Bar agenda has a meetings wih different label, slug, duration: no meetings exposed mt = MeetingType.objects.create(agenda=bar_agenda, label='Meeting Type Bar', duration=15) resp = app.get('/api/agenda/%s/meetings/' % virt_agenda.slug) assert resp.json == {'data': []} # Bar agenda has a meetings wih same label, but different slug and duration: no meetings exposed mt.label = 'Meeting1' mt.save() resp = app.get('/api/agenda/%s/meetings/' % virt_agenda.slug) assert resp.json == {'data': []} # Bar agenda has a meetings wih same label and slug, but different duration: no meetings exposed mt.slug = 'meeting1' mt.save() resp = app.get('/api/agenda/%s/meetings/' % virt_agenda.slug) assert resp.json == {'data': []} # Bar agenda has a meetings wih same label, slug and duration: only this meeting exposed mt.duration = 30 mt.save() resp = app.get('/api/agenda/%s/meetings/' % virt_agenda.slug) assert resp.json == { 'data': [ { 'text': 'Meeting1', 'id': 'meeting1', 'duration': 30, 'api': { 'datetimes_url': 'http://testserver/api/agenda/virtual-agenda/meetings/meeting1/datetimes/', }, }, ] }