api: iter MultipleAgendasEventsCheckStatus on user_checks (#82849)
gitea/chrono/pipeline/head This commit looks good
Details
gitea/chrono/pipeline/head This commit looks good
Details
This commit is contained in:
parent
e4864ea95b
commit
4291cc73db
|
@ -323,6 +323,10 @@ class BookingSerializer(serializers.ModelSerializer):
|
|||
'cancellation_datetime',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user_check = kwargs.pop('user_check', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if 'color' in data:
|
||||
# legacy
|
||||
|
@ -341,19 +345,17 @@ class BookingSerializer(serializers.ModelSerializer):
|
|||
ret.pop('user_absence_reason', None)
|
||||
ret.pop('user_presence_reason', None)
|
||||
else:
|
||||
user_was_present = self.instance.user_check.presence if self.instance.user_check else None
|
||||
user_was_present = self.user_check.presence if self.user_check else None
|
||||
ret['user_was_present'] = user_was_present
|
||||
ret['user_absence_reason'] = (
|
||||
self.instance.user_check.type_slug if user_was_present is False else None
|
||||
)
|
||||
ret['user_presence_reason'] = (
|
||||
self.instance.user_check.type_slug if user_was_present is True else None
|
||||
)
|
||||
ret['user_absence_reason'] = self.user_check.type_slug if user_was_present is False else None
|
||||
ret['user_presence_reason'] = self.user_check.type_slug if user_was_present is True else None
|
||||
if self.instance.event.agenda.kind == 'events' and self.instance.event.agenda.partial_bookings:
|
||||
self.instance.user_check_start_time = self.instance.user_check.start_time
|
||||
self.instance.user_check_end_time = self.instance.user_check.end_time
|
||||
self.instance.computed_start_time = self.instance.user_check.computed_start_time
|
||||
self.instance.computed_end_time = self.instance.user_check.computed_end_time
|
||||
self.instance.user_check_start_time = self.user_check.start_time if self.user_check else None
|
||||
self.instance.user_check_end_time = self.user_check.end_time if self.user_check else None
|
||||
self.instance.computed_start_time = (
|
||||
self.user_check.computed_start_time if self.user_check else None
|
||||
)
|
||||
self.instance.computed_end_time = self.user_check.computed_end_time if self.user_check else None
|
||||
for key in ['', 'user_check_', 'computed_']:
|
||||
start_key, end_key, minutes_key = (
|
||||
'%sstart_time' % key,
|
||||
|
|
|
@ -2204,7 +2204,7 @@ class MultipleAgendasEventsCheckStatus(APIView):
|
|||
booking_queryset = Booking.objects.filter(
|
||||
event__in=events,
|
||||
user_external_id=user_external_id,
|
||||
).prefetch_related('user_checks')
|
||||
).prefetch_related(Prefetch('user_checks', queryset=BookingCheck.objects.order_by('start_time')))
|
||||
bookings_by_event_id = collections.defaultdict(list)
|
||||
for booking in booking_queryset:
|
||||
bookings_by_event_id[booking.event_id].append(booking)
|
||||
|
@ -2212,33 +2212,61 @@ class MultipleAgendasEventsCheckStatus(APIView):
|
|||
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]
|
||||
booking.event = event # prevent db calls
|
||||
if booking.cancellation_datetime is not None:
|
||||
check_status = {'status': 'cancelled'}
|
||||
elif not booking.user_check:
|
||||
check_status = {'status': 'error', 'error_reason': 'booking-not-checked'}
|
||||
else:
|
||||
check_status = {
|
||||
'status': 'presence' if booking.user_check.presence else 'absence',
|
||||
'check_type': booking.user_check.type_slug,
|
||||
|
||||
if check_status:
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': {},
|
||||
}
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking).data if booking else {},
|
||||
)
|
||||
continue
|
||||
|
||||
booking = bookings_by_event_id[event.pk][0]
|
||||
booking.event = event # prevent db calls
|
||||
if booking.cancellation_datetime is not None:
|
||||
check_status = {'status': 'cancelled'}
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking).data,
|
||||
}
|
||||
)
|
||||
continue
|
||||
user_check = None
|
||||
for user_check in booking.user_checks.all():
|
||||
# user_checks are prefetched and ordered
|
||||
check_status = {
|
||||
'status': 'presence' if user_check.presence else 'absence',
|
||||
'check_type': user_check.type_slug,
|
||||
}
|
||||
)
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking, user_check=user_check).data,
|
||||
}
|
||||
)
|
||||
if user_check is None:
|
||||
# no user_check object
|
||||
check_status = {'status': 'error', 'error_reason': 'booking-not-checked'}
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking).data,
|
||||
}
|
||||
)
|
||||
|
||||
return Response({'err': 0, 'data': data})
|
||||
|
||||
|
@ -2564,7 +2592,12 @@ class BookingsAPI(ListAPIView):
|
|||
|
||||
data = []
|
||||
for booking in bookings:
|
||||
serialized_booking = self.serializer_class(booking).data
|
||||
user_checks = booking.user_checks.all()
|
||||
user_check = None
|
||||
if len(user_checks) == 1:
|
||||
# XXX which behavior for partial bookings, and multi-check ?
|
||||
user_check = user_checks[0]
|
||||
serialized_booking = self.serializer_class(booking, user_check=user_check).data
|
||||
if booking.event.agenda.kind == 'events':
|
||||
serialized_booking['event'] = get_event_detail(request, booking.event)
|
||||
data.append(serialized_booking)
|
||||
|
@ -2627,7 +2660,12 @@ class BookingAPI(APIView):
|
|||
def get(self, request, *args, **kwargs):
|
||||
self.check_booking()
|
||||
|
||||
serializer = self.serializer_class(self.booking)
|
||||
user_checks = self.booking.user_checks.all()
|
||||
user_check = None
|
||||
if len(user_checks) == 1:
|
||||
# XXX which behavior for partial bookings, and multi-check ?
|
||||
user_check = user_checks[0]
|
||||
serializer = self.serializer_class(self.booking, user_check=user_check)
|
||||
response = serializer.data
|
||||
response.update(
|
||||
{
|
||||
|
|
|
@ -153,6 +153,7 @@ def test_bookings_api(app, user):
|
|||
label='Event A', agenda=events_agenda, start_datetime=now() + datetime.timedelta(days=3), places=10
|
||||
)
|
||||
events_booking1 = Booking.objects.create(event=events_event, user_external_id='enfant-1234')
|
||||
events_booking1.mark_user_presence()
|
||||
events_booking2 = Booking.objects.create(event=events_event, user_external_id='enfant-1234')
|
||||
Booking.objects.create(event=events_event, user_external_id='foobar')
|
||||
Booking.objects.create(event=events_event)
|
||||
|
@ -214,7 +215,7 @@ def test_bookings_api(app, user):
|
|||
'user_last_name': '',
|
||||
'user_email': '',
|
||||
'user_phone_number': '',
|
||||
'user_was_present': None,
|
||||
'user_was_present': True,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': None,
|
||||
'use_color_for': None,
|
||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
|||
from django.db import connection
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
|
||||
from chrono.agendas.models import Agenda, Booking, Event, EventsType, Subscription
|
||||
from chrono.agendas.models import Agenda, Booking, BookingCheck, Event, EventsType, Subscription
|
||||
from chrono.utils.timezone import localtime, make_aware, now
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
@ -1590,6 +1590,94 @@ def test_events_check_status_events(app, user, partial_bookings):
|
|||
'label': '',
|
||||
}
|
||||
|
||||
if partial_bookings:
|
||||
booking1.mark_user_presence(
|
||||
check_type_slug='foo-reason', start_time=datetime.time(7, 55), end_time=datetime.time(12, 15)
|
||||
)
|
||||
BookingCheck.objects.create(
|
||||
booking=booking1,
|
||||
start_time=datetime.time(13, 00),
|
||||
end_time=datetime.time(17, 15),
|
||||
presence=False,
|
||||
)
|
||||
booking1.refresh_computed_times(commit=True)
|
||||
user_check1, user_check2 = booking1.user_checks.all().order_by('start_time')
|
||||
assert user_check1.computed_start_time == datetime.time(8, 0)
|
||||
assert user_check1.computed_end_time == datetime.time(12, 30)
|
||||
assert user_check2.computed_start_time == datetime.time(12, 30)
|
||||
assert user_check2.computed_end_time == datetime.time(17, 30)
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
resp = app.get(url, params=params)
|
||||
assert len(ctx.captured_queries) == 6
|
||||
assert resp.json['err'] == 0
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][0]['event']['slug'] == 'notchecked-event-slug'
|
||||
assert resp.json['data'][0]['check_status'] == {
|
||||
'error_reason': 'event-not-checked',
|
||||
'status': 'error',
|
||||
}
|
||||
assert resp.json['data'][0]['booking'] == {}
|
||||
assert resp.json['data'][1]['event']['slug'] == 'event-slug'
|
||||
assert resp.json['data'][1]['check_status'] == {
|
||||
'check_type': 'foo-reason',
|
||||
'status': 'presence',
|
||||
}
|
||||
assert {
|
||||
'id': booking2.pk,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': 'foo-reason',
|
||||
'user_was_present': True,
|
||||
'start_time': None,
|
||||
'end_time': None,
|
||||
'duration': None,
|
||||
'user_check_start_time': None,
|
||||
'user_check_end_time': None,
|
||||
'user_check_duration': None,
|
||||
'computed_start_time': None,
|
||||
'computed_end_time': None,
|
||||
'computed_duration': None,
|
||||
}.items() <= resp.json['data'][1]['booking'].items()
|
||||
assert resp.json['data'][2]['event']['slug'] == 'recurring-event-slug--2022-05-30-1600'
|
||||
assert resp.json['data'][2]['check_status'] == {
|
||||
'check_type': 'foo-reason',
|
||||
'status': 'presence',
|
||||
}
|
||||
assert {
|
||||
'id': booking1.pk,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': 'foo-reason',
|
||||
'user_was_present': True,
|
||||
'start_time': '08:00:00',
|
||||
'end_time': '17:00:00',
|
||||
'duration': 540,
|
||||
'user_check_start_time': '07:55:00',
|
||||
'user_check_end_time': '12:15:00',
|
||||
'user_check_duration': 260,
|
||||
'computed_start_time': '08:00:00',
|
||||
'computed_end_time': '12:30:00',
|
||||
'computed_duration': 270,
|
||||
}.items() <= resp.json['data'][2]['booking'].items()
|
||||
assert resp.json['data'][3]['event']['slug'] == 'recurring-event-slug--2022-05-30-1600'
|
||||
assert resp.json['data'][3]['check_status'] == {
|
||||
'check_type': None,
|
||||
'status': 'absence',
|
||||
}
|
||||
assert {
|
||||
'id': booking1.pk,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': None,
|
||||
'user_was_present': False,
|
||||
'start_time': '08:00:00',
|
||||
'end_time': '17:00:00',
|
||||
'duration': 540,
|
||||
'user_check_start_time': '13:00:00',
|
||||
'user_check_end_time': '17:15:00',
|
||||
'user_check_duration': 255,
|
||||
'computed_start_time': '12:30:00',
|
||||
'computed_end_time': '17:30:00',
|
||||
'computed_duration': 300,
|
||||
}.items() <= resp.json['data'][3]['booking'].items()
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-05-30 14:00')
|
||||
def test_events_check_status_agendas_filter(app, user):
|
||||
|
|
Loading…
Reference in New Issue