forked from CGM_Public/pretix_original
add support for check-in into overlapping events (#2039)
When events overlap, check-in only worked for the currently running event. If events run back-to-back, it can happen, that admission should start earlier and overlaps the currently running event. This checks if an overlapping event has started even if the current event is still running.
This commit is contained in:
committed by
GitHub
parent
8921ccb8c1
commit
a0b3c70e2a
@@ -187,6 +187,16 @@ class EventSelectionView(APIView):
|
|||||||
qs = qs.annotate(has_cl=Exists(has_cl)).filter(has_cl=True)
|
qs = qs.annotate(has_cl=Exists(has_cl)).filter(has_cl=True)
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
|
def _max_first_date_event(self, a, b):
|
||||||
|
if a and b:
|
||||||
|
return a if a.first_date > b.first_date else b
|
||||||
|
return a or b
|
||||||
|
|
||||||
|
def _min_first_date_event(self, a, b):
|
||||||
|
if a and b:
|
||||||
|
return a if a.first_date < b.first_date else b
|
||||||
|
return a or b
|
||||||
|
|
||||||
def get(self, request, format=None):
|
def get(self, request, format=None):
|
||||||
device = request.auth
|
device = request.auth
|
||||||
current_event = None
|
current_event = None
|
||||||
@@ -200,27 +210,35 @@ class EventSelectionView(APIView):
|
|||||||
|
|
||||||
if current_event:
|
if current_event:
|
||||||
current_ev = current_subevent or current_event
|
current_ev = current_subevent or current_event
|
||||||
|
else:
|
||||||
|
current_ev = None
|
||||||
|
|
||||||
|
# The event that is selected might not currently be running. We cannot rely on all events having a proper end date.
|
||||||
|
# Also, if events run back-to-back, the later event can overlap the earlier event due to its admission-time.
|
||||||
|
# In any case, we'll need to decide between the current event, the event that last started (and might still be running) and the
|
||||||
|
# event that starts next (and might already be letting people in), so let's get these as well!
|
||||||
|
# No matter if current event is given in query_params, always check whether another event already
|
||||||
|
# started – overlaps can happen through e.g. admission-time overlapping or misconfig).
|
||||||
|
# Note that last_started here means either admission started or the event itself started.
|
||||||
|
last_started_ev = self._max_first_date_event(
|
||||||
|
self.base_event_qs.filter(first_date__lte=now()).last(),
|
||||||
|
self.base_subevent_qs.filter(first_date__lte=now()).last()
|
||||||
|
)
|
||||||
|
if last_started_ev and current_ev != last_started_ev and \
|
||||||
|
last_started_ev.date_to and now() < last_started_ev.date_to:
|
||||||
|
return self._suggest_event(current_event, last_started_ev)
|
||||||
|
|
||||||
|
if current_event:
|
||||||
current_ev_start = current_ev.date_admission or current_ev.date_from
|
current_ev_start = current_ev.date_admission or current_ev.date_from
|
||||||
tz = current_event.timezone
|
tz = current_event.timezone
|
||||||
if current_ev.date_to and current_ev_start < now() < current_ev.date_to:
|
if current_ev.date_to and current_ev_start <= now() < current_ev.date_to:
|
||||||
# The event that is selected is currently running. Good enough.
|
# The event that is selected is currently running. Good enough.
|
||||||
return Response(status=status.HTTP_304_NOT_MODIFIED)
|
return Response(status=status.HTTP_304_NOT_MODIFIED)
|
||||||
|
|
||||||
# The event that is selected is not currently running. We cannot rely on all events having a proper end date.
|
upcoming_ev = self._min_first_date_event(
|
||||||
# In any case, we'll need to decide between the event that last started (and might still be running) and the
|
self.base_event_qs.filter(first_date__gt=now()).first(),
|
||||||
# event that starts next (and might already be letting people in), so let's get these two!
|
self.base_subevent_qs.filter(first_date__gt=now()).first()
|
||||||
last_started_ev = self.base_event_qs.filter(first_date__lte=now()).last() or self.base_subevent_qs.filter(
|
)
|
||||||
first_date__lte=now()).last()
|
|
||||||
|
|
||||||
upcoming_event = self.base_event_qs.filter(first_date__gt=now()).first()
|
|
||||||
upcoming_subevent = self.base_subevent_qs.filter(first_date__gt=now()).first()
|
|
||||||
if upcoming_event and upcoming_subevent:
|
|
||||||
if upcoming_event.first_date > upcoming_subevent.first_date:
|
|
||||||
upcoming_ev = upcoming_subevent
|
|
||||||
else:
|
|
||||||
upcoming_ev = upcoming_event
|
|
||||||
else:
|
|
||||||
upcoming_ev = upcoming_event or upcoming_subevent
|
|
||||||
|
|
||||||
if not upcoming_ev and not last_started_ev:
|
if not upcoming_ev and not last_started_ev:
|
||||||
# Ooops, no events here
|
# Ooops, no events here
|
||||||
@@ -232,12 +250,8 @@ class EventSelectionView(APIView):
|
|||||||
# No event upcoming, so let's take the next one
|
# No event upcoming, so let's take the next one
|
||||||
return self._suggest_event(current_event, last_started_ev)
|
return self._suggest_event(current_event, last_started_ev)
|
||||||
|
|
||||||
if last_started_ev.date_to and now() < last_started_ev.date_to:
|
|
||||||
# The event that last started is currently running. Good enough.
|
|
||||||
return self._suggest_event(current_event, last_started_ev)
|
|
||||||
|
|
||||||
if not current_event:
|
if not current_event:
|
||||||
tz = (upcoming_event or last_started_ev).timezone
|
tz = (upcoming_ev or last_started_ev).timezone
|
||||||
|
|
||||||
lse_d = last_started_ev.date_from.astimezone(tz).date()
|
lse_d = last_started_ev.date_from.astimezone(tz).date()
|
||||||
upc_d = upcoming_ev.date_from.astimezone(tz).date()
|
upc_d = upcoming_ev.date_from.astimezone(tz).date()
|
||||||
|
|||||||
@@ -116,6 +116,15 @@ def test_choose_between_events(device_client, device):
|
|||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert resp.data['event']['slug'] == 'e2'
|
assert resp.data['event']['slug'] == 'e2'
|
||||||
|
|
||||||
|
# check for overlapping events
|
||||||
|
e2.date_admission = tz.localize(datetime(2020, 1, 10, 14, 45))
|
||||||
|
e2.save()
|
||||||
|
with freeze_time("2020-01-10T14:45:00+09:00"):
|
||||||
|
resp = device_client.get(f'/api/v1/device/eventselection?current_event=e1')
|
||||||
|
assert resp.status_code == 200
|
||||||
|
resp = device_client.get(f'/api/v1/device/eventselection?current_event=e2')
|
||||||
|
assert resp.status_code == 304
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_choose_between_subevents(device_client, device):
|
def test_choose_between_subevents(device_client, device):
|
||||||
@@ -207,6 +216,15 @@ def test_choose_between_subevents(device_client, device):
|
|||||||
assert resp.data['event']['slug'] == 'e1'
|
assert resp.data['event']['slug'] == 'e1'
|
||||||
assert resp.data['subevent'] == se2.pk
|
assert resp.data['subevent'] == se2.pk
|
||||||
|
|
||||||
|
# check for overlapping events
|
||||||
|
se2.date_admission = tz.localize(datetime(2020, 1, 10, 14, 45))
|
||||||
|
se2.save()
|
||||||
|
with freeze_time("2020-01-10T14:45:00+09:00"):
|
||||||
|
resp = device_client.get(f'/api/v1/device/eventselection?current_event=e1¤t_subevent={se1.pk}')
|
||||||
|
assert resp.status_code == 200
|
||||||
|
resp = device_client.get(f'/api/v1/device/eventselection?current_event=e1¤t_subevent={se2.pk}')
|
||||||
|
assert resp.status_code == 304
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_require_gate(device_client, device):
|
def test_require_gate(device_client, device):
|
||||||
|
|||||||
Reference in New Issue
Block a user