Skip to content

Commit 1b217ec

Browse files
authored
Merge pull request #275 from City-of-Helsinki/KK-912-leeway-changes
KK-912 | Show event ticket for longer
2 parents caa3611 + 4d69543 commit 1b217ec

12 files changed

Lines changed: 164 additions & 153 deletions

File tree

children/schema.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,10 @@ def resolve_past_events(self: Child, info, **kwargs):
128128
past.
129129
"""
130130
events = self.project.events.user_can_view(info.context.user).published()
131-
past_enough_enrolled_occurrences = self.occurrences.filter(
132-
time__lt=timezone.now()
131+
132+
occurrences = self.occurrences.with_end_time()
133+
past_enough_enrolled_occurrences = occurrences.filter(
134+
end_time__lt=timezone.now()
133135
- timedelta(minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY)
134136
)
135137
return events.filter(occurrences__in=past_enough_enrolled_occurrences)

children/tests/snapshots/snap_test_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,8 @@
408408
"availableEvents": {"edges": []},
409409
"occurrences": {
410410
"edges": [
411-
{"node": {"time": "2020-12-11T23:29:00+00:00"}},
412-
{"node": {"time": "2020-12-11T23:31:00+00:00"}},
411+
{"node": {"time": "2020-12-11T22:59:00+00:00"}},
412+
{"node": {"time": "2020-12-11T23:01:00+00:00"}},
413413
]
414414
},
415415
"pastEvents": {

children/tests/test_api.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -884,18 +884,23 @@ def test_get_past_events(
884884
snapshot, guardian_api_client, child_with_user_guardian, project, venue
885885
):
886886
variables = {"id": to_global_id("ChildNode", child_with_user_guardian.id)}
887-
888-
past = timezone.now() - timedelta(
889-
minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY + 1
887+
duration = 30
888+
past = (
889+
timezone.now()
890+
- timedelta(minutes=duration)
891+
- timedelta(minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY + 1)
890892
)
891-
not_enough_past = timezone.now() - timedelta(
892-
minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY - 1
893+
not_enough_past = (
894+
timezone.now()
895+
- timedelta(minutes=duration)
896+
- timedelta(minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY - 1)
893897
)
894898
future = timezone.now() + timedelta(minutes=1)
895899

896900
# Enrolled occurrence in the past, event should be visible
897901
event = EventFactory(
898902
published_at=timezone.now(),
903+
duration=duration,
899904
project=project,
900905
name="enrolled occurrence in the past",
901906
)
@@ -906,6 +911,7 @@ def test_get_past_events(
906911
# Enrolled occurrence in the past but not enough, event should NOT be visible
907912
event_2 = EventFactory(
908913
published_at=timezone.now(),
914+
duration=duration,
909915
project=project,
910916
name="enrolled occurrence in the past but not enough",
911917
)
@@ -920,6 +926,7 @@ def test_get_past_events(
920926
# Event without an enrolment in the past, should NOT be visible
921927
event_3 = EventFactory(
922928
published_at=timezone.now(),
929+
duration=duration,
923930
project=project,
924931
name="unenrolled event in the past",
925932
)

common/tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ def setup_test_environment(settings):
3232
settings.MEDIA_ROOT = "test_media"
3333
settings.KUKKUU_HASHID_SALT = "topsecret"
3434
settings.KUKKUU_REMINDER_DAYS_IN_ADVANCE = 7
35+
settings.KUKKUU_DEFAULT_EVENT_DURATION = 120
36+
settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY = 30
3537
with translation.override("fi"), freeze_time("2020-12-12"):
3638
yield
3739
shutil.rmtree("test_media", ignore_errors=True)

events/filters.py

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
from datetime import timedelta
2-
31
import django_filters
4-
from django.conf import settings
5-
from django.utils import timezone
62
from graphql_relay import from_global_id
73

84
from children.models import Child
@@ -14,11 +10,12 @@
1410
class OccurrenceFilter(django_filters.FilterSet):
1511
date = django_filters.DateFilter(lookup_expr="date", field_name="time")
1612
time = django_filters.TimeFilter(method="filter_by_time", field_name="time")
17-
upcoming = django_filters.BooleanFilter(
18-
method="filter_by_upcoming", field_name="time"
19-
)
13+
upcoming = django_filters.BooleanFilter(method="filter_by_upcoming")
2014
upcoming_with_leeway = django_filters.BooleanFilter(
21-
method="filter_by_upcoming_with_leeway", field_name="time"
15+
method="filter_by_upcoming_with_leeway"
16+
)
17+
upcoming_with_ongoing = django_filters.BooleanFilter(
18+
method="filter_by_upcoming_with_ongoing"
2219
)
2320
venue_id = django_filters.CharFilter(
2421
field_name="venue", method="filter_by_venue_global_id"
@@ -49,20 +46,17 @@ def filter_by_time(self, qs, name, value):
4946

5047
def filter_by_upcoming(self, qs, name, value):
5148
if value:
52-
return qs.filter(**{name + "__gte": timezone.now()})
49+
return qs.upcoming()
5350
return qs
5451

5552
def filter_by_upcoming_with_leeway(self, qs, name, value):
5653
if value:
57-
return qs.filter(
58-
**{
59-
name
60-
+ "__gte": timezone.now()
61-
- timedelta(
62-
minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY
63-
)
64-
}
65-
)
54+
return qs.upcoming_with_leeway()
55+
return qs
56+
57+
def filter_by_upcoming_with_ongoing(self, qs, name, value):
58+
if value:
59+
return qs.upcoming_with_ongoing()
6660
return qs
6761

6862
def filter_by_venue_global_id(self, qs, name, value):
@@ -86,9 +80,6 @@ class EventFilter(django_filters.FilterSet):
8680
method="filter_by_available_for_child"
8781
)
8882
upcoming = django_filters.BooleanFilter(method="filter_by_upcoming")
89-
upcoming_with_leeway = django_filters.BooleanFilter(
90-
method="filter_by_upcoming_with_leeway"
91-
)
9283

9384
class Meta:
9485
model = Event
@@ -109,8 +100,3 @@ def filter_by_upcoming(self, qs, name, value):
109100
if value:
110101
return qs.upcoming()
111102
return qs
112-
113-
def filter_by_upcoming_with_leeway(self, qs, name, value):
114-
if value:
115-
return qs.upcoming_with_leeway()
116-
return qs

events/models.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,6 @@ def published(self):
4444
def upcoming(self):
4545
return self.filter(events__occurrences__time__gte=timezone.now()).distinct()
4646

47-
def upcoming_with_leeway(self):
48-
return self.filter(
49-
events__occurrences__time__gte=timezone.now()
50-
- timedelta(minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY)
51-
).distinct()
52-
5347

5448
class EventGroup(TimestampedModel, TranslatableModel):
5549
translations = TranslatedFields(
@@ -152,12 +146,6 @@ def unpublished(self):
152146
def upcoming(self):
153147
return self.filter(occurrences__time__gte=timezone.now()).distinct()
154148

155-
def upcoming_with_leeway(self):
156-
return self.filter(
157-
occurrences__time__gte=timezone.now()
158-
- timedelta(minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY)
159-
).distinct()
160-
161149
def available(self, child):
162150
"""
163151
A child's available events must match all of the following rules:
@@ -369,11 +357,38 @@ def send_free_spot_notifications_if_needed(self):
369357
obj.send_free_spot_notifications_if_needed()
370358

371359
def upcoming(self):
372-
return self.filter(time__gt=timezone.now())
360+
return self.filter(time__gte=timezone.now())
361+
362+
def upcoming_with_leeway(self):
363+
return self.filter(
364+
time__gte=timezone.now()
365+
- timedelta(minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY)
366+
)
367+
368+
def upcoming_with_ongoing(self):
369+
"""Return occurrences that are upcoming or still ongoing (with added leeway)."""
370+
qs = self.with_end_time()
371+
return qs.filter(
372+
end_time__gte=timezone.now()
373+
- timedelta(minutes=settings.KUKKUU_ENROLLED_OCCURRENCE_IN_PAST_LEEWAY)
374+
)
373375

374376
def in_past(self):
375377
return self.exclude(time__gt=timezone.now())
376378

379+
def with_end_time(self):
380+
return self.annotate(
381+
end_time=ExpressionWrapper(
382+
F("time")
383+
+ Coalesce(
384+
F("event__duration"),
385+
settings.KUKKUU_DEFAULT_EVENT_DURATION,
386+
)
387+
* timedelta(minutes=1),
388+
output_field=models.DateTimeField(),
389+
),
390+
)
391+
377392

378393
class Occurrence(TimestampedModel):
379394
time = models.DateTimeField(verbose_name=_("time"))
@@ -506,7 +521,20 @@ def delete(self):
506521
enrolment.delete()
507522

508523
def upcoming(self):
509-
return self.filter(occurrence__time__gt=timezone.now())
524+
return self.filter(occurrence__time__gte=timezone.now())
525+
526+
def with_end_time(self):
527+
return self.annotate(
528+
end_time=ExpressionWrapper(
529+
F("occurrence__time")
530+
+ Coalesce(
531+
F("occurrence__event__duration"),
532+
settings.KUKKUU_DEFAULT_EVENT_DURATION,
533+
)
534+
* timedelta(minutes=1),
535+
output_field=models.DateTimeField(),
536+
),
537+
)
510538

511539
def send_reminder_notifications(self):
512540
today = timezone.localtime().date()
@@ -531,15 +559,7 @@ def send_feedback_notifications(self):
531559
delay = timedelta(minutes=settings.KUKKUU_FEEDBACK_NOTIFICATION_DELAY)
532560

533561
enrolments = (
534-
self.annotate(
535-
end_time=ExpressionWrapper(
536-
F("occurrence__time")
537-
# use 120min as the default value for duration
538-
+ Coalesce(F("occurrence__event__duration"), 120)
539-
* timedelta(minutes=1),
540-
output_field=models.DateTimeField(),
541-
),
542-
)
562+
self.with_end_time()
543563
.filter(
544564
feedback_notification_sent_at=None,
545565
child__guardians__isnull=False,

events/schema.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,6 @@ class Query:
10001000
EventOrEventGroupConnection,
10011001
project_id=graphene.ID(),
10021002
upcoming=graphene.Boolean(),
1003-
upcoming_with_leeway=graphene.Boolean(),
10041003
)
10051004
occurrences = DjangoFilterConnectionField(OccurrenceNode)
10061005

@@ -1020,10 +1019,7 @@ def resolve_events_and_event_groups(self, info, **kwargs):
10201019
event_qs = event_qs.filter(project_id=project_id)
10211020
event_group_qs = event_group_qs.filter(project_id=project_id)
10221021

1023-
if kwargs.get("upcoming_with_leeway", False):
1024-
event_qs = event_qs.upcoming_with_leeway()
1025-
event_group_qs = event_group_qs.upcoming_with_leeway()
1026-
elif kwargs.get("upcoming", False):
1022+
if kwargs.get("upcoming", False):
10271023
event_qs = event_qs.upcoming()
10281024
event_group_qs = event_group_qs.upcoming()
10291025

events/tests/snapshots/snap_test_api.py

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -524,15 +524,15 @@
524524
}
525525
}
526526

527-
snapshots["test_events_and_event_groups_query_upcoming_filter[False-False] 1"] = {
527+
snapshots["test_events_and_event_groups_query_upcoming_filter[False] 1"] = {
528528
"data": {
529529
"eventsAndEventGroups": {
530530
"edges": [{"node": {"__typename": "EventNode", "name": "In the future"}}]
531531
}
532532
}
533533
}
534534

535-
snapshots["test_events_and_event_groups_query_upcoming_filter[False-True] 1"] = {
535+
snapshots["test_events_and_event_groups_query_upcoming_filter[True] 1"] = {
536536
"data": {
537537
"eventsAndEventGroups": {
538538
"edges": [
@@ -542,28 +542,6 @@
542542
}
543543
}
544544

545-
snapshots["test_events_and_event_groups_query_upcoming_filter[True-False] 1"] = {
546-
"data": {
547-
"eventsAndEventGroups": {
548-
"edges": [
549-
{"node": {"__typename": "EventNode", "name": "Within leeway"}},
550-
{"node": {"__typename": "EventNode", "name": "In the future"}},
551-
]
552-
}
553-
}
554-
}
555-
556-
snapshots["test_events_and_event_groups_query_upcoming_filter[True-True] 1"] = {
557-
"data": {
558-
"eventsAndEventGroups": {
559-
"edges": [
560-
{"node": {"__typename": "EventGroupNode", "name": "Within leeway"}},
561-
{"node": {"__typename": "EventGroupNode", "name": "In the future"}},
562-
]
563-
}
564-
}
565-
}
566-
567545
snapshots["test_events_query_normal_user 1"] = {
568546
"data": {
569547
"events": {
@@ -1004,6 +982,23 @@
1004982
}
1005983
}
1006984

985+
snapshots["test_occurrences_filter_by_upcoming_with_ongoing[False] 1"] = {
986+
"data": {
987+
"occurrences": {
988+
"edges": [
989+
{"node": {"time": "2020-12-11T22:29:00+00:00"}},
990+
{"node": {"time": "2020-12-11T22:31:00+00:00"}},
991+
]
992+
}
993+
}
994+
}
995+
996+
snapshots["test_occurrences_filter_by_upcoming_with_ongoing[True] 1"] = {
997+
"data": {
998+
"occurrences": {"edges": [{"node": {"time": "2020-12-11T22:31:00+00:00"}}]}
999+
}
1000+
}
1001+
10071002
snapshots["test_occurrences_filter_by_venue 1"] = {
10081003
"data": {
10091004
"occurrences": {
@@ -1527,7 +1522,7 @@
15271522
"data": {
15281523
"verifyTicket": {
15291524
"eventName": "Record card my. Sure sister return.",
1530-
"occurrenceTime": "2020-12-13T00:00:00+00:00",
1525+
"occurrenceTime": "2020-12-12T00:00:00+00:00",
15311526
"validity": True,
15321527
"venueName": "Remember stay public high concern glass person.",
15331528
}

0 commit comments

Comments
 (0)