api: new endpoint to get events and check status (#65770)

This commit is contained in:
Lauréline Guérin 2022-05-31 11:40:43 +02:00
parent 7ee7625f86
commit 4617dbdebb
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
5 changed files with 789 additions and 42 deletions

View File

@ -51,6 +51,22 @@ class CommaSeparatedStringField(serializers.ListField):
return super().to_internal_value(data)
class DateRangeMixin(metaclass=serializers.SerializerMetaclass):
datetime_formats = ['%Y-%m-%d', '%Y-%m-%d %H:%M', 'iso-8601']
date_start = serializers.DateTimeField(required=False, input_formats=datetime_formats)
date_end = serializers.DateTimeField(required=False, input_formats=datetime_formats)
class AgendaSlugsMixin(metaclass=serializers.SerializerMetaclass):
agendas = CommaSeparatedStringField(
required=False, child=serializers.SlugField(max_length=160, allow_blank=False)
)
def get_agenda_qs(self):
return Agenda.objects.filter(kind='events').select_related('events_type')
class SlotSerializer(serializers.Serializer):
label = serializers.CharField(max_length=250, allow_blank=True)
user_external_id = serializers.CharField(max_length=250, allow_blank=True)
@ -123,6 +139,18 @@ class MultipleAgendasEventsSlotsSerializer(EventsSlotsSerializer):
return value
class MultipleAgendasEventsCheckStatusSerializer(AgendaSlugsMixin, DateRangeMixin, serializers.Serializer):
user_external_id = serializers.CharField(required=False, max_length=250, allow_blank=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in ['agendas', 'user_external_id', 'date_start', 'date_end']:
self.fields[field].required = True
def validate_agendas(self, value):
return get_objects_from_slugs(value, qs=self.get_agenda_qs())
class RecurringFillslotsSerializer(MultipleAgendasEventsSlotsSerializer):
include_booked_events_detail = serializers.BooleanField(default=False)
@ -179,8 +207,16 @@ class BookingSerializer(serializers.ModelSerializer):
'user_presence_reason',
'color',
'extra_data',
'creation_datetime',
'cancellation_datetime',
]
read_only_fields = [
'id',
'in_waiting_list',
'extra_data',
'creation_datetime',
'cancellation_datetime',
]
read_only_fields = ['id', 'in_waiting_list', 'extra_data']
def to_representation(self, instance):
ret = super().to_representation(instance)
@ -248,13 +284,6 @@ class StatisticsFiltersSerializer(serializers.Serializer):
)
class DateRangeMixin(metaclass=serializers.SerializerMetaclass):
datetime_formats = ['%Y-%m-%d', '%Y-%m-%d %H:%M', 'iso-8601']
date_start = serializers.DateTimeField(required=False, input_formats=datetime_formats)
date_end = serializers.DateTimeField(required=False, input_formats=datetime_formats)
class DateRangeSerializer(DateRangeMixin, serializers.Serializer):
pass
@ -280,19 +309,13 @@ class DatetimesSerializer(DateRangeSerializer):
return attrs
class AgendaOrSubscribedSlugsMixin(DateRangeMixin):
agendas = CommaSeparatedStringField(
required=False, child=serializers.SlugField(max_length=160, allow_blank=False)
)
class AgendaOrSubscribedSlugsMixin(AgendaSlugsMixin):
subscribed = CommaSeparatedStringField(
required=False, child=serializers.SlugField(max_length=160, allow_blank=False)
)
user_external_id = serializers.CharField(required=False, max_length=250, allow_blank=False)
guardian_external_id = serializers.CharField(required=False, max_length=250, allow_blank=False)
def get_agenda_qs(self):
return Agenda.objects.filter(kind='events').select_related('events_type')
def validate(self, attrs):
super().validate(attrs)
if 'agendas' not in attrs and 'subscribed' not in attrs:
@ -350,7 +373,7 @@ class MultipleAgendasDatetimesSerializer(AgendaOrSubscribedSlugsMixin, Datetimes
return attrs
class AgendaOrSubscribedSlugsSerializer(AgendaOrSubscribedSlugsMixin, serializers.Serializer):
class AgendaOrSubscribedSlugsSerializer(AgendaOrSubscribedSlugsMixin, DateRangeMixin, serializers.Serializer):
pass
@ -363,16 +386,12 @@ class RecurringEventsListSerializer(AgendaOrSubscribedSlugsSerializer):
check_overlaps = serializers.BooleanField(default=False)
class AgendaSlugsSerializer(serializers.Serializer):
agendas = CommaSeparatedStringField(
required=True, child=serializers.SlugField(max_length=160, allow_blank=False)
)
class EventSerializer(serializers.ModelSerializer):
recurrence_days = StringOrListField(
required=False, child=serializers.IntegerField(min_value=0, max_value=6)
)
primary_event = serializers.SlugRelatedField(read_only=True, slug_field='slug')
agenda = serializers.SlugRelatedField(read_only=True, slug_field='slug')
class Meta:
model = Event
@ -386,31 +405,37 @@ class EventSerializer(serializers.ModelSerializer):
'places',
'waiting_list_places',
'label',
'slug',
'description',
'pricing',
'url',
'primary_event',
'agenda',
]
read_only_fields = ['slug']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.agenda.events_type and not self.instance.primary_event:
field_classes = {
'text': serializers.CharField,
'textarea': serializers.CharField,
'bool': serializers.NullBooleanField,
}
field_options = {
'text': {'allow_blank': True},
'textarea': {'allow_blank': True},
}
for custom_field in self.instance.agenda.events_type.get_custom_fields():
field_class = field_classes[custom_field['field_type']]
field_name = 'custom_field_%s' % custom_field['varname']
self.fields[field_name] = field_class(
required=False,
**(field_options.get(custom_field['field_type']) or {}),
)
if not self.instance.agenda.events_type:
return
field_classes = {
'text': serializers.CharField,
'textarea': serializers.CharField,
'bool': serializers.NullBooleanField,
}
field_options = {
'text': {'allow_blank': True},
'textarea': {'allow_blank': True},
}
for custom_field in self.instance.agenda.events_type.get_custom_fields():
field_class = field_classes[custom_field['field_type']]
field_name = 'custom_field_%s' % custom_field['varname']
self.fields[field_name] = field_class(
required=False,
read_only=self.instance.primary_event is not None,
**(field_options.get(custom_field['field_type']) or {}),
)
def validate(self, attrs):
if not self.instance.agenda.events_type:
@ -435,6 +460,25 @@ class EventSerializer(serializers.ModelSerializer):
attrs['custom_fields'] = custom_fields
return attrs
def to_representation(self, instance):
ret = super().to_representation(instance)
if not self.instance.agenda.events_type:
return ret
defaults = {
'text': '',
'textarea': '',
'bool': None,
}
custom_fields = self.instance.custom_fields
for custom_field in self.instance.agenda.events_type.get_custom_fields():
varname = custom_field['varname']
field_name = 'custom_field_%s' % varname
value = defaults[custom_field['field_type']]
if varname in custom_fields:
value = custom_fields[varname]
ret[field_name] = value
return ret
class AgendaSerializer(serializers.ModelSerializer):
edit_role = serializers.CharField(required=False, max_length=150)

View File

@ -28,6 +28,11 @@ urlpatterns = [
views.agendas_events_fillslots,
name='api-agendas-events-fillslots',
),
url(
r'^agendas/events/check-status/$',
views.agendas_events_check_status,
name='api-agendas-events-check-status',
),
url(r'^agenda/(?P<agenda_identifier>[\w-]+)/$', views.agenda),
url(r'^agenda/(?P<agenda_identifier>[\w-]+)/datetimes/$', views.datetimes, name='api-agenda-datetimes'),
url(

View File

@ -2106,6 +2106,75 @@ class MultipleAgendasEventsFillslots(EventsFillslots):
agendas_events_fillslots = MultipleAgendasEventsFillslots.as_view()
class MultipleAgendasEventsCheckStatus(APIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = serializers.MultipleAgendasEventsCheckStatusSerializer
def get(self, request):
serializer = self.serializer_class(data=request.query_params)
if not serializer.is_valid():
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors, err=1)
agendas = serializer.validated_data['agendas']
agendas_by_id = {a.pk: a for a in agendas}
user_external_id = serializer.validated_data['user_external_id']
date_start = serializer.validated_data['date_start']
date_end = serializer.validated_data['date_end']
events = Event.objects.filter(
agenda__in=agendas,
agenda__subscriptions__user_external_id=user_external_id,
agenda__subscriptions__date_start__lte=F('start_datetime'),
agenda__subscriptions__date_end__gt=F('start_datetime'),
recurrence_days__isnull=True,
cancelled=False,
start_datetime__gte=date_start,
start_datetime__lt=date_end,
).prefetch_related(Prefetch('primary_event', queryset=Event.objects.all().order_by()))
booking_queryset = Booking.objects.filter(
event__in=events,
user_external_id=user_external_id,
).select_related('user_check_type')
bookings_by_event_id = collections.defaultdict(list)
for booking in booking_queryset:
bookings_by_event_id[booking.event_id].append(booking)
data = []
for event in events:
event.agenda = agendas_by_id[event.agenda_id] # agenda is already fetched, reuse it
check_status = {}
booking = None
if not event.checked:
check_status = {'status': 'error', 'error_reason': 'event-not-checked'}
elif not bookings_by_event_id[event.pk]:
check_status = {'status': 'not-booked'}
elif len(bookings_by_event_id[event.pk]) > 1:
check_status = {'status': 'error', 'error_reason': 'too-many-bookings-found'}
else:
booking = bookings_by_event_id[event.pk][0]
if booking.cancellation_datetime is not None:
check_status = {'status': 'cancelled'}
elif booking.user_was_present is None:
check_status = {'status': 'error', 'error_reason': 'booking-not-checked'}
else:
check_status = {
'status': 'presence' if booking.user_was_present else 'absence',
'check_type': booking.user_check_type.slug if booking.user_check_type else '',
}
data.append(
{
'event': serializers.EventSerializer(event).data,
'check_status': check_status,
'booking': serializers.BookingSerializer(booking).data if booking else {},
}
)
return Response({'err': 0, 'data': data})
agendas_events_check_status = MultipleAgendasEventsCheckStatus.as_view()
class SubscriptionFilter(filters.FilterSet):
date_start = filters.DateFilter(lookup_expr='gte')
date_end = filters.DateFilter(lookup_expr='lt')

View File

@ -4,7 +4,7 @@ from unittest import mock
import pytest
from django.db import connection
from django.test.utils import CaptureQueriesContext
from django.utils.timezone import make_aware, now
from django.utils.timezone import localtime, make_aware, now
from chrono.agendas.models import (
Agenda,
@ -155,6 +155,8 @@ def test_bookings_api(app, user):
'user_presence_reason': '',
'color': None,
'extra_data': None,
'cancellation_datetime': None,
'creation_datetime': localtime(meetings_booking1.creation_datetime).isoformat(),
},
{
'id': events_booking1.pk,
@ -169,6 +171,8 @@ def test_bookings_api(app, user):
'color': None,
'extra_data': None,
'event': resp.json['data'][1]['event'],
'cancellation_datetime': None,
'creation_datetime': localtime(events_booking1.creation_datetime).isoformat(),
},
{
'id': events_booking2.pk,
@ -183,6 +187,8 @@ def test_bookings_api(app, user):
'color': None,
'extra_data': None,
'event': resp.json['data'][1]['event'],
'cancellation_datetime': None,
'creation_datetime': localtime(events_booking2.creation_datetime).isoformat(),
},
]

View File

@ -1,9 +1,11 @@
import datetime
import pytest
from django.utils.timezone import localtime, now
from django.db import connection
from django.test.utils import CaptureQueriesContext
from django.utils.timezone import localtime, make_aware, now
from chrono.agendas.models import Agenda, Booking, Event, EventsType
from chrono.agendas.models import Agenda, Booking, CheckType, CheckTypeGroup, Event, EventsType, Subscription
pytestmark = pytest.mark.django_db
@ -702,6 +704,43 @@ def test_update_event(app, user):
}
def test_event_read_only_fields(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
agenda2 = Agenda.objects.create(label='Foo bar 2', kind='events')
event = Event.objects.create(
slug='event', start_datetime=now() + datetime.timedelta(days=5), places=1, agenda=agenda
)
app.authorization = ('Basic', ('john.doe', 'password'))
api_url = '/api/agenda/%s/event/' % agenda.slug
params = {
'slug': 'slug',
'agenda': agenda2.slug,
'primary_event': event.slug,
'start_datetime': now().isoformat(),
'places': 42,
}
resp = app.post(api_url, params=params)
assert resp.json['err'] == 0
new_event = Event.objects.latest('pk')
assert new_event.slug == 'foo-bar-event'
assert new_event.agenda == agenda
assert new_event.primary_event is None
api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, new_event.slug)
params = {
'slug': 'slug',
'agenda': agenda2.slug,
'primary_event': event.slug,
}
resp = app.patch(api_url, params=params)
assert resp.json['err'] == 0
new_event.refresh_from_db()
assert new_event.slug == 'foo-bar-event'
assert new_event.agenda == agenda
assert new_event.primary_event is None
@pytest.mark.freeze_time('2021-11-01 10:00')
def test_delete_event(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
@ -777,3 +816,587 @@ def test_delete_recurring_event_forbidden(app, user):
resp = app.delete('/api/agenda/%s/event/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
assert not Event.objects.exists()
def test_events_check_status_params(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
# missing user_external_id
resp = app.get(
'/api/agendas/events/check-status/',
params={'agendas': 'foo', 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['user_external_id'] == ['This field is required.']
# missing agendas
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'date_start': '2022-05-01', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['This field is required.']
# unknown agenda
resp = app.get(
'/api/agendas/events/check-status/',
params={
'user_external_id': 'child:42',
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar, foo']
Agenda.objects.create(label='Foo')
resp = app.get(
'/api/agendas/events/check-status/',
params={
'user_external_id': 'child:42',
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# wrong kind
wrong_agenda = Agenda.objects.create(label='Bar')
for kind in ['meetings', 'virtual']:
wrong_agenda.kind = kind
wrong_agenda.save()
resp = app.get(
'/api/agendas/events/check-status/',
params={
'user_external_id': 'child:42',
'agendas': 'foo, bar',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['agendas'] == ['invalid slugs: bar']
# missing date_start
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'agendas': 'foo', 'date_end': '2022-06-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_start'] == ['This field is required.']
# missing date_end
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'agendas': 'foo', 'date_start': '2022-05-01'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['date_end'] == ['This field is required.']
# bad date format
resp = app.get(
'/api/agendas/events/check-status/',
params={'user_external_id': 'child:42', 'agendas': 'foo', 'date_start': 'wrong', 'date_end': 'wrong'},
status=400,
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert 'wrong format' in resp.json['errors']['date_start'][0]
assert 'wrong format' in resp.json['errors']['date_end'][0]
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_status(app, user):
agenda = Agenda.objects.create(label='Foo')
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=now(),
places=10,
agenda=agenda,
checked=True,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
# not booked
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'not-booked',
}
assert resp.json['data'][0]['booking'] == {}
# 2 bookings found, error
booking = Booking.objects.create(event=event, user_external_id='child:42')
booking2 = Booking.objects.create(event=event, user_external_id='child:42')
Booking.objects.create(event=event, user_external_id='other')
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'error',
'error_reason': 'too-many-bookings-found',
}
assert resp.json['data'][0]['booking'] == {}
# booking cancelled
booking2.delete()
booking.cancellation_datetime = now()
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'cancelled',
}
assert list(resp.json['data'][0]['booking'].keys()) == [
'id',
'in_waiting_list',
'user_first_name',
'user_last_name',
'user_email',
'user_phone_number',
'user_was_present',
'user_absence_reason',
'user_presence_reason',
'color',
'extra_data',
'creation_datetime',
'cancellation_datetime',
]
assert resp.json['data'][0]['booking']['cancellation_datetime'] == localtime(now()).isoformat()
# booking not checked
booking.cancellation_datetime = None
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'error',
'error_reason': 'booking-not-checked',
}
assert resp.json['data'][0]['booking']['cancellation_datetime'] is None
# absence
booking.user_was_present = False
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'absence',
'check_type': '',
}
assert resp.json['data'][0]['booking']['user_was_present'] is False
assert resp.json['data'][0]['booking']['user_absence_reason'] == ''
assert resp.json['data'][0]['booking']['user_presence_reason'] == ''
# absence with check type
group = CheckTypeGroup.objects.create(label='Foo bar')
check_type = CheckType.objects.create(label='Foo reason', group=group, kind='absence')
booking.user_check_type = check_type
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'absence',
'check_type': 'foo-reason',
}
assert resp.json['data'][0]['booking']['user_was_present'] is False
assert resp.json['data'][0]['booking']['user_absence_reason'] == 'foo-reason'
assert resp.json['data'][0]['booking']['user_presence_reason'] == ''
# presence
booking.user_check_type = None
booking.user_was_present = True
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'presence',
'check_type': '',
}
assert resp.json['data'][0]['booking']['user_was_present'] is True
assert resp.json['data'][0]['booking']['user_absence_reason'] == ''
assert resp.json['data'][0]['booking']['user_presence_reason'] == ''
# presence with check type
check_type.kind = 'presence'
check_type.save()
booking.user_check_type = check_type
booking.save()
resp = app.get(url, params=params)
assert resp.json['err'] == 0
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['check_status'] == {
'status': 'presence',
'check_type': 'foo-reason',
}
assert resp.json['data'][0]['booking']['user_was_present'] is True
assert resp.json['data'][0]['booking']['user_absence_reason'] == ''
assert resp.json['data'][0]['booking']['user_presence_reason'] == 'foo-reason'
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_status_events(app, user):
events_type = EventsType.objects.create(
label='Foo',
custom_fields=[
{'varname': 'text', 'label': 'Text', 'field_type': 'text'},
{'varname': 'textarea', 'label': 'TextArea', 'field_type': 'textarea'},
{'varname': 'bool', 'label': 'Bool', 'field_type': 'bool'},
],
)
group = CheckTypeGroup.objects.create(label='Foo bar')
check_type = CheckType.objects.create(label='Foo reason', group=group, kind='absence')
agenda = Agenda.objects.create(label='Foo', events_type=events_type)
start_datetime = now()
# recurring event
recurring_event = Event.objects.create(
slug='recurring-event-slug',
label='Recurring Event Label',
start_datetime=start_datetime,
recurrence_days=[start_datetime.weekday()],
recurrence_end_date=start_datetime + datetime.timedelta(days=7),
places=10,
agenda=agenda,
custom_fields={
'text': 'foo',
'textarea': 'foo bar',
'bool': True,
},
)
recurring_event.create_all_recurrences()
first_event = recurring_event.recurrences.get()
first_event.checked = True
first_event.save()
event = Event.objects.create(
slug='event-slug',
label='Event Label',
start_datetime=start_datetime - datetime.timedelta(days=1),
places=10,
agenda=agenda,
checked=True,
)
# not checked event
notchecked_event = Event.objects.create(
slug='notchecked-event-slug',
label='Not Checked Event Label',
start_datetime=start_datetime - datetime.timedelta(days=2),
places=10,
agenda=agenda,
checked=False,
)
# cancelled event, not returned
Event.objects.create(
slug='cancelled',
label='Cancelled',
start_datetime=start_datetime,
places=10,
agenda=agenda,
cancelled=True,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
booking1 = Booking.objects.create(
event=first_event, user_external_id='child:42', user_was_present=True, user_check_type=check_type
)
booking2 = Booking.objects.create(
event=event, user_external_id='child:42', user_was_present=True, user_check_type=check_type
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
with CaptureQueriesContext(connection) as ctx:
resp = app.get(url, params=params)
assert len(ctx.captured_queries) == 5
assert resp.json['err'] == 0
assert resp.json['data'] == [
{
'event': {
'description': None,
'duration': None,
'label': 'Not Checked Event Label',
'slug': 'notchecked-event-slug',
'places': 10,
'pricing': None,
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'start_datetime': localtime(notchecked_event.start_datetime).isoformat(),
'url': None,
'waiting_list_places': 0,
'agenda': agenda.slug,
'primary_event': None,
'custom_field_bool': None,
'custom_field_text': '',
'custom_field_textarea': '',
},
'check_status': {'error_reason': 'event-not-checked', 'status': 'error'},
'booking': {},
},
{
'event': {
'description': None,
'duration': None,
'label': 'Event Label',
'slug': 'event-slug',
'places': 10,
'pricing': None,
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'start_datetime': localtime(event.start_datetime).isoformat(),
'url': None,
'waiting_list_places': 0,
'agenda': agenda.slug,
'primary_event': None,
'custom_field_bool': None,
'custom_field_text': '',
'custom_field_textarea': '',
},
'check_status': {'check_type': 'foo-reason', 'status': 'presence'},
'booking': {
'cancellation_datetime': None,
'color': None,
'creation_datetime': localtime(now()).isoformat(),
'extra_data': None,
'id': booking2.pk,
'in_waiting_list': False,
'user_absence_reason': '',
'user_email': '',
'user_first_name': '',
'user_last_name': '',
'user_phone_number': '',
'user_presence_reason': check_type.slug,
'user_was_present': True,
},
},
{
'event': {
'description': None,
'duration': None,
'label': 'Recurring Event Label',
'slug': 'recurring-event-slug--2022-05-30-1600',
'places': 10,
'pricing': None,
'publication_datetime': None,
'recurrence_days': None,
'recurrence_end_date': None,
'recurrence_week_interval': 1,
'start_datetime': localtime(first_event.start_datetime).isoformat(),
'url': None,
'waiting_list_places': 0,
'agenda': agenda.slug,
'primary_event': recurring_event.slug,
'custom_field_text': 'foo',
'custom_field_textarea': 'foo bar',
'custom_field_bool': True,
},
'check_status': {'check_type': 'foo-reason', 'status': 'presence'},
'booking': {
'cancellation_datetime': None,
'color': None,
'creation_datetime': localtime(now()).isoformat(),
'extra_data': None,
'id': booking1.pk,
'in_waiting_list': False,
'user_absence_reason': '',
'user_email': '',
'user_first_name': '',
'user_last_name': '',
'user_phone_number': '',
'user_presence_reason': check_type.slug,
'user_was_present': True,
},
},
]
@pytest.mark.freeze_time('2022-05-30 14:00')
def test_events_check_status_agendas_filter(app, user):
agenda1 = Agenda.objects.create(label='Foo')
agenda2 = Agenda.objects.create(label='Foo 2')
Event.objects.create(
slug='event-1',
label='Event 1',
start_datetime=now(),
places=10,
agenda=agenda1,
)
Event.objects.create(
slug='event-2',
label='Event 2',
start_datetime=now(),
places=10,
agenda=agenda2,
)
Subscription.objects.create(
agenda=agenda1,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
Subscription.objects.create(
agenda=agenda2,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo, foo-2',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.get(url, params=params)
assert len(resp.json['data']) == 2
assert resp.json['data'][0]['event']['slug'] == 'event-1'
assert resp.json['data'][1]['event']['slug'] == 'event-2'
params['agendas'] = 'foo'
resp = app.get(url, params=params)
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['event']['slug'] == 'event-1'
params['agendas'] = 'foo-2'
resp = app.get(url, params=params)
assert len(resp.json['data']) == 1
assert resp.json['data'][0]['event']['slug'] == 'event-2'
@pytest.mark.parametrize(
'event_date, expected',
[
# just before first day
((2022, 4, 30, 12, 0), False),
# first day
((2022, 5, 1, 12, 0), True),
# last day
((2022, 5, 31, 12, 0), True),
# just after last day
((2022, 6, 1, 12, 0), False),
],
)
def test_events_check_status_date_filter(app, user, event_date, expected):
agenda = Agenda.objects.create(label='Foo')
Event.objects.create(
slug='event',
label='Event',
start_datetime=make_aware(datetime.datetime(*event_date)),
places=10,
agenda=agenda,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2021, month=9, day=1),
date_end=datetime.date(year=2022, month=9, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-05-01',
'date_end': '2022-06-01',
}
resp = app.get(url, params=params)
assert len(resp.json['data']) == int(expected)
@pytest.mark.parametrize(
'event_date, expected',
[
# just before first day
((2022, 4, 30, 12, 0), False),
# first day
((2022, 5, 1, 12, 0), True),
# last day
((2022, 5, 31, 12, 0), True),
# just after last day
((2022, 6, 1, 12, 0), False),
],
)
def test_events_check_status_subscription_filter(app, user, freezer, event_date, expected):
agenda = Agenda.objects.create(label='Foo')
Event.objects.create(
slug='event',
label='Event',
start_datetime=make_aware(datetime.datetime(*event_date)),
places=10,
agenda=agenda,
)
Subscription.objects.create(
agenda=agenda,
user_external_id='child:42',
date_start=datetime.date(year=2022, month=5, day=1),
date_end=datetime.date(year=2022, month=6, day=1),
)
Subscription.objects.create(
agenda=agenda,
user_external_id='other',
date_start=datetime.date(year=2022, month=4, day=1),
date_end=datetime.date(year=2022, month=7, day=1),
)
app.authorization = ('Basic', ('john.doe', 'password'))
url = '/api/agendas/events/check-status/'
params = {
'user_external_id': 'child:42',
'agendas': 'foo',
'date_start': '2022-04-01',
'date_end': '2022-07-01',
}
resp = app.get(url, params=params)
assert len(resp.json['data']) == int(expected)