Compare commits

..

5 Commits

Author SHA1 Message Date
Valentin Deniaud 5108f50129 manager: allow separate arrival/departure check for partial bookings (#80047)
gitea/chrono/pipeline/head This commit looks good Details
2023-11-13 17:15:37 +01:00
Thomas NOËL 6a411b1859 debian: add back memory-report to uwsgi default configuration (#80451)
gitea/chrono/pipeline/head This commit looks good Details
2023-11-13 11:32:57 +01:00
Lauréline Guérin 4291cc73db
api: iter MultipleAgendasEventsCheckStatus on user_checks (#82849)
gitea/chrono/pipeline/head This commit looks good Details
2023-11-10 08:38:05 +01:00
Lauréline Guérin e4864ea95b agendas: partial bookings, compute double booking check (#82848)
gitea/chrono/pipeline/head This commit looks good Details
2023-11-10 08:37:09 +01:00
Lauréline Guérin 9d1c33970c agendas: refresh_computed_times on booking (#82848) 2023-11-10 08:37:09 +01:00
8 changed files with 455 additions and 70 deletions

View File

@ -1621,23 +1621,22 @@ class Agenda(models.Model):
self.refresh_booking_computed_times()
def refresh_booking_computed_times(self):
bookings_queryset = Booking.objects.filter(
event__agenda__kind='events',
event__agenda__partial_bookings=True,
event__agenda=self,
event__check_locked=False,
event__invoiced=False,
event__cancelled=False,
cancellation_datetime__isnull=True,
)
booking_checks = BookingCheck.objects.filter(booking__in=bookings_queryset).select_related(
'booking', 'booking__event__agenda'
bookings_queryset = (
Booking.objects.filter(
event__agenda__kind='events',
event__agenda__partial_bookings=True,
event__agenda=self,
event__check_locked=False,
event__invoiced=False,
event__cancelled=False,
cancellation_datetime__isnull=True,
)
.prefetch_related('user_checks')
.select_related('event__agenda')
)
to_update = []
for booking_check in booking_checks:
changed = booking_check.refresh_computed_times()
if changed:
to_update.append(booking_check)
for booking in bookings_queryset:
to_update += booking.refresh_computed_times()
if to_update:
BookingCheck.objects.bulk_update(to_update, ['computed_start_time', 'computed_end_time'])
@ -2249,14 +2248,11 @@ class Event(models.Model):
event__invoiced=False,
event__cancelled=False,
cancellation_datetime__isnull=True,
)
booking_checks = BookingCheck.objects.filter(booking__in=bookings_queryset).select_related('booking')
).prefetch_related('user_checks')
to_update = []
for booking_check in booking_checks:
booking_check.booking.event = self # to avoid lots of querysets
changed = booking_check.refresh_computed_times()
if changed:
to_update.append(booking_check)
for booking in bookings_queryset:
booking.event = self # to avoid lots of querysets
to_update += booking.refresh_computed_times()
if to_update:
BookingCheck.objects.bulk_update(to_update, ['computed_start_time', 'computed_end_time'])
@ -2949,6 +2945,49 @@ class Booking(models.Model):
self.save()
self.event.set_is_checked()
def refresh_computed_times(self, commit=False):
to_update = []
user_checks = sorted(self.user_checks.all(), key=lambda a: a.start_time or datetime.time(0))
many_user_checks = len(user_checks) > 1
assert len(user_checks) < 3 # 2 user_checks max !
if user_checks and not many_user_checks:
# only one user_check
user_check = user_checks[0]
changed = user_check._refresh_computed_times()
if changed:
to_update.append(user_check)
elif many_user_checks:
# two user_checks
user_check1 = user_checks[0]
user_check2 = user_checks[1]
compute_one_first = True
if user_check1.presence != user_check2.presence:
# one user check is presence, the other is absence
if user_check1.presence is False:
# the second check is presence, it should be computed first
compute_one_first = False
if compute_one_first:
changed = user_check1._refresh_computed_times(adjust_end_to_booking=False)
if changed:
to_update.append(user_check1)
changed = user_check2._refresh_computed_times(other_user_check=user_check1)
if changed:
to_update.append(user_check2)
else:
changed = user_check2._refresh_computed_times(adjust_start_to_booking=False)
if changed:
to_update.append(user_check2)
changed = user_check1._refresh_computed_times(other_user_check=user_check2)
if changed:
to_update.append(user_check1)
if commit and to_update:
BookingCheck.objects.bulk_update(to_update, ['computed_start_time', 'computed_end_time'])
return to_update
def get_user_block(self):
template_vars = Context(settings.TEMPLATE_VARS, autoescape=False)
template_vars.update(
@ -3067,13 +3106,19 @@ class BookingCheck(models.Model):
next_slot = datetime.time(*divmod(previous_slot_minutes + minutes, 60))
return previous_slot, next_slot
def get_computed_start_time(self):
def get_computed_start_time(self, other_user_check=None, adjust_to_booking=True):
if self.start_time is None:
return None
start_time = self.start_time
if self.booking.start_time:
if self.booking.start_time and adjust_to_booking:
# adjust start_time to the start of the booking if requested
start_time = min(self.start_time, self.booking.start_time)
if other_user_check and other_user_check.computed_start_time and other_user_check.computed_end_time:
# if other user_check exists and is completely computed
if other_user_check.start_time < self.start_time:
# if other user_check is the first of the day, start_time is the end of other user_check
start_time = other_user_check.computed_end_time
if self.booking.event.agenda.invoicing_unit == 'minute':
return start_time
@ -3089,13 +3134,19 @@ class BookingCheck(models.Model):
# else take previous_slot
return previous_slot
def get_computed_end_time(self):
def get_computed_end_time(self, other_user_check=None, adjust_to_booking=True):
if self.end_time is None:
return None
end_time = self.end_time
if self.booking.end_time:
if self.booking.end_time and adjust_to_booking:
# adjust end_time to the end of the booking if requested
end_time = max(self.end_time, self.booking.end_time)
if other_user_check and other_user_check.computed_start_time and other_user_check.computed_end_time:
# if other user_check exists and is completely computed
if other_user_check.start_time > self.start_time:
# if other user_check is the second of the day, end_time is the start of other user_check
end_time = other_user_check.computed_start_time
if self.booking.event.agenda.invoicing_unit == 'minute':
return end_time
@ -3111,11 +3162,17 @@ class BookingCheck(models.Model):
# else take next_slot
return next_slot
def refresh_computed_times(self):
def _refresh_computed_times(
self, other_user_check=None, adjust_start_to_booking=True, adjust_end_to_booking=True
):
old_computed_start_time = self.computed_start_time
old_computed_end_time = self.computed_end_time
self.computed_start_time = self.get_computed_start_time()
self.computed_end_time = self.get_computed_end_time()
self.computed_start_time = self.get_computed_start_time(
other_user_check=other_user_check, adjust_to_booking=adjust_start_to_booking
)
self.computed_end_time = self.get_computed_end_time(
other_user_check=other_user_check, adjust_to_booking=adjust_end_to_booking
)
# return True if changed, else False
if (
old_computed_start_time == self.computed_start_time

View File

@ -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,

View File

@ -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(
{

View File

@ -661,13 +661,15 @@ class PartialBookingCheckForm(forms.ModelForm):
)
def save(self):
booking = self.instance.booking
if self.cleaned_data['presence'] is None:
self.instance.delete()
booking.refresh_computed_times(commit=True)
return self.instance
self.instance.refresh_computed_times()
return super().save()
super().save()
booking.refresh_computed_times(commit=True)
return self.instance
class EventsTimesheetForm(forms.Form):

1
debian/uwsgi.ini vendored
View File

@ -61,6 +61,7 @@ buffer-size = 32768
py-tracebacker = /run/chrono/py-tracebacker.sock.
stats = /run/chrono/stats.sock
memory-report = true
ignore-sigpipe = true

View File

@ -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,

View File

@ -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
@ -1398,8 +1398,8 @@ def test_events_check_status_events(app, user, partial_bookings):
booking1.mark_user_presence(
check_type_slug='foo-reason', start_time=datetime.time(7, 55), end_time=datetime.time(17, 15)
)
booking1.user_check.refresh_computed_times()
booking1.user_check.save()
booking1.refresh_computed_times(commit=True)
booking1.user_check.refresh_from_db()
assert booking1.user_check.computed_start_time == datetime.time(8, 0)
assert booking1.user_check.computed_end_time == datetime.time(17, 30)
booking2 = Booking.objects.create(event=event, user_external_id='child:42')
@ -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):

View File

@ -11,6 +11,7 @@ from django.core.management import call_command
from django.db import IntegrityError, connection, transaction
from django.db.models import Q
from django.test import override_settings
from django.test.utils import CaptureQueriesContext
from chrono.agendas.models import (
Agenda,
@ -4034,6 +4035,197 @@ def test_booking_get_computed_end_time(end_time, user_check_end_time, tolerance,
assert booking.user_check.get_computed_end_time() == expected
def test_booking_refresh_computed_times_only_one_user_check():
agenda = Agenda.objects.create(
label='Agenda', kind='events', invoicing_unit='half_hour', invoicing_tolerance=10
)
event = Event.objects.create(
agenda=agenda,
start_datetime=make_aware(datetime.datetime(2023, 5, 1, 14, 00)),
places=10,
)
booking = Booking.objects.create(
event=event, start_time=datetime.time(8, 0), end_time=datetime.time(17, 0)
)
# only one user_check, presence or absence, same behavior
booking_check = BookingCheck.objects.create(
booking=booking, presence=True, start_time=datetime.time(7, 45), end_time=datetime.time(17, 15)
)
booking.refresh_computed_times(commit=True)
booking_check.refresh_from_db()
assert booking_check.computed_start_time == datetime.time(7, 30)
assert booking_check.computed_end_time == datetime.time(17, 30)
booking_check.presence = False
booking.refresh_computed_times(commit=True)
booking_check.refresh_from_db()
assert booking_check.computed_start_time == datetime.time(7, 30)
assert booking_check.computed_end_time == datetime.time(17, 30)
@pytest.mark.parametrize(
'presence1, start_time1, end_time1, presence2, start_time2, end_time2, expected_start1, expected_end1, expected_start2, expected_end2',
[
# first presence, then absence
(
True,
datetime.time(7, 45),
datetime.time(12, 15),
False,
datetime.time(13, 0), # no overlapping
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(12, 30),
datetime.time(12, 30),
datetime.time(17, 0),
),
(
True,
datetime.time(7, 45),
datetime.time(12, 15),
False,
datetime.time(12, 15), # computed start should be 12H00
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(12, 30),
datetime.time(12, 30), # but it is adjusted to 12H30
datetime.time(17, 0),
),
# first absence, then presence
(
False,
datetime.time(7, 45),
datetime.time(12, 0), # no overlapping
True,
datetime.time(13, 15),
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(13, 0),
datetime.time(13, 0),
datetime.time(17, 0),
),
(
False,
datetime.time(7, 45),
datetime.time(12, 45), # computed end should be 13H00
True,
datetime.time(12, 45),
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(12, 30), # but it is adjusted to 12H30
datetime.time(12, 30),
datetime.time(17, 0),
),
# both absence
(
True,
datetime.time(7, 45),
datetime.time(12, 15),
True,
datetime.time(13, 0), # no overlapping
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(12, 30),
datetime.time(12, 30),
datetime.time(17, 0),
),
(
True,
datetime.time(7, 45),
datetime.time(12, 15),
True,
datetime.time(12, 15), # computed start should be 12H00
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(12, 30),
datetime.time(12, 30), # but it is adjusted to 12H30
datetime.time(17, 0),
),
# both presence
(
False,
datetime.time(7, 45),
datetime.time(12, 15),
False,
datetime.time(13, 0), # no overlapping
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(12, 30),
datetime.time(12, 30),
datetime.time(17, 0),
),
(
False,
datetime.time(7, 45),
datetime.time(12, 15),
False,
datetime.time(12, 15), # computed start should be 12H00
datetime.time(15, 0),
datetime.time(7, 30),
datetime.time(12, 30),
datetime.time(12, 30), # but it is adjusted to 12H30
datetime.time(17, 0),
),
],
)
def test_booking_refresh_computed_times_two_user_check(
presence1,
start_time1,
end_time1,
presence2,
start_time2,
end_time2,
expected_start1,
expected_end1,
expected_start2,
expected_end2,
):
agenda = Agenda.objects.create(
label='Agenda', kind='events', invoicing_unit='half_hour', invoicing_tolerance=10
)
event = Event.objects.create(
agenda=agenda,
start_datetime=make_aware(datetime.datetime(2023, 5, 1, 14, 00)),
places=10,
)
booking = Booking.objects.create(
event=event, start_time=datetime.time(8, 0), end_time=datetime.time(17, 0)
)
user_check1 = BookingCheck.objects.create(
booking=booking, presence=presence1, start_time=start_time1, end_time=end_time1
)
user_check2 = BookingCheck.objects.create(
booking=booking, presence=presence2, start_time=start_time2, end_time=end_time2
)
booking.refresh_computed_times(commit=True)
user_check1.refresh_from_db()
user_check2.refresh_from_db()
assert user_check1.computed_start_time == expected_start1
assert user_check1.computed_end_time == expected_end1
assert user_check2.computed_start_time == expected_start2
assert user_check2.computed_end_time == expected_end2
def test_booking_refresh_computed_times_more_than_two_user_check():
agenda = Agenda.objects.create(
label='Agenda', kind='events', invoicing_unit='half_hour', invoicing_tolerance=10
)
event = Event.objects.create(
agenda=agenda,
start_datetime=make_aware(datetime.datetime(2023, 5, 1, 14, 00)),
places=10,
)
booking = Booking.objects.create(
event=event, start_time=datetime.time(8, 0), end_time=datetime.time(17, 0)
)
BookingCheck.objects.create(booking=booking, presence=True)
BookingCheck.objects.create(booking=booking, presence=True)
BookingCheck.objects.create(booking=booking, presence=True)
with pytest.raises(AssertionError):
booking.refresh_computed_times(commit=True)
def test_agenda_refresh_booking_computed_times():
agenda = Agenda.objects.create(
label='Agenda',
@ -4061,7 +4253,9 @@ def test_agenda_refresh_booking_computed_times():
booking.user_check.save()
def test_booking(success):
agenda.refresh_booking_computed_times()
with CaptureQueriesContext(connection) as ctx:
agenda.refresh_booking_computed_times()
assert len(ctx.captured_queries) in [1, 3]
booking.refresh_from_db()
if success is True:
assert booking.user_check.computed_start_time == datetime.time(7, 0)
@ -4158,7 +4352,9 @@ def test_event_refresh_booking_computed_times():
booking.user_check.save()
def test_booking(success):
event.refresh_booking_computed_times()
with CaptureQueriesContext(connection) as ctx:
event.refresh_booking_computed_times()
assert len(ctx.captured_queries) in [1, 3]
booking.refresh_from_db()
if success is True:
assert booking.user_check.computed_start_time == datetime.time(7, 0)