Skip to content

Commit c7f5f7e

Browse files
committed
feat(program_v2): add date, room dimensions to workflow
1 parent 89ccdeb commit c7f5f7e

File tree

2 files changed

+83
-11
lines changed

2 files changed

+83
-11
lines changed

backend/dimensions/models/dimension_dto.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def save_many(
4141
universe: Universe,
4242
dimension_dtos: list[Self],
4343
remove_others=False,
44+
remove_other_values=False,
4445
dimension_value_batch_size=200,
4546
) -> list[Dimension]:
4647
dimensions_upsert = (
@@ -120,14 +121,15 @@ def save_many(
120121
)
121122
logger.info("Saved %s dimension values", num_dvs)
122123

123-
for dim_dto, dim_dj in zip(dimension_dtos, django_dimensions, strict=True):
124-
values_to_keep = [choice.slug for choice in dim_dto.choices or []]
125-
_, deleted = dim_dj.values.exclude(slug__in=values_to_keep).delete()
126-
logger.info("Stale dimension value cleanup for %s deleted %s", dim_dto.slug, deleted or "nothing")
124+
if remove_other_values:
125+
for dim_dto, dim_dj in zip(dimension_dtos, django_dimensions, strict=True):
126+
values_to_keep = [choice.slug for choice in dim_dto.choices or []]
127+
_, deleted = dim_dj.values.exclude(slug__in=values_to_keep).delete()
128+
logger.info("Stale dimension value cleanup for %s deleted %s", dim_dto.slug, deleted or "nothing")
127129

128130
if remove_others:
129131
dimensions_to_keep = [dim_dto.slug for dim_dto in dimension_dtos]
130-
num_deleted_dvs, _ = universe.dimensions.exclude(slug__in=dimensions_to_keep).delete()
131-
logger.info("Deleted %s stale dimensions", num_deleted_dvs)
132+
_, deleted = universe.dimensions.exclude(slug__in=dimensions_to_keep).delete()
133+
logger.info("Stale dimension cleanup deleted %s", deleted)
132134

133135
return django_dimensions

backend/program_v2/workflow.py

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import logging
2-
from collections.abc import Sequence
2+
from collections.abc import Iterable, Sequence
3+
from datetime import timedelta
4+
from typing import ClassVar
35

46
from core.models.event import Event
57
from dimensions.models.dimension import Dimension
@@ -8,6 +10,7 @@
810
from forms.models.response import Response
911
from forms.models.survey import Survey
1012
from forms.models.workflow import Workflow
13+
from graphql_api.language import SUPPORTED_LANGUAGE_CODES
1114

1215
from .models.program import Program
1316

@@ -19,10 +22,18 @@
1922
sv="Dag",
2023
)
2124

22-
ROOM_DIMENSION_TITLE_LOCALIZED = dict(
23-
fi="Sali",
24-
en="Room",
25-
sv="Sal",
25+
ROOM_DIMENSION_DTO = DimensionDTO(
26+
slug="room",
27+
value_ordering=ValueOrdering.TITLE,
28+
is_public=True,
29+
is_list_filter=True,
30+
is_key_dimension=False,
31+
is_technical=True, # but the values are not
32+
title=dict(
33+
fi="Sali",
34+
en="Room",
35+
sv="Sal",
36+
),
2637
)
2738

2839
WEEKDAYS_LOCALIZED = dict(
@@ -165,6 +176,8 @@ def _setup_default_dimensions(cls, universe: Universe) -> Sequence[Dimension]:
165176
return DimensionDTO.save_many(
166177
universe,
167178
[
179+
cls._get_date_dimension_dto(universe),
180+
ROOM_DIMENSION_DTO,
168181
cls._get_form_dimension_dto(universe),
169182
STATE_DIMENSION_DTO,
170183
],
@@ -204,3 +217,60 @@ def _get_form_dimension_dto(cls, universe: Universe) -> DimensionDTO:
204217
).only("id", "slug")
205218
],
206219
)
220+
221+
date_cutoff_time: ClassVar[timedelta] = timedelta(hours=4) # 04:00 local time
222+
223+
def _get_date_dimension_value(self, program: Program) -> list[str]:
224+
"""
225+
Return the date dimension value for the programme.
226+
227+
The `date_cutoff_time` is used to determine at which time of the night the date changes.
228+
Use this to make the wee hours of the night belong to the previous day.
229+
"""
230+
tz = self.survey.event.timezone
231+
return [
232+
(sitem.start_time.astimezone(tz) - self.date_cutoff_time).date().isoformat()
233+
for sitem in program.schedule_items.all()
234+
]
235+
236+
@classmethod
237+
def _get_date_dimension_values(cls, universe: Universe) -> Iterable[DimensionValueDTO]:
238+
"""
239+
Return a list of DimensionValueDTOs for the date dimension.
240+
"""
241+
event: Event = universe.scope.event
242+
tz = event.timezone
243+
244+
if not event.start_time:
245+
raise ValueError(f"Event {event} has no start time")
246+
if not event.end_time:
247+
raise ValueError(f"Event {event} has no end time")
248+
249+
cur_date = event.start_time.astimezone(tz).date()
250+
end_date = event.end_time.astimezone(tz).date()
251+
252+
while cur_date <= end_date:
253+
yield DimensionValueDTO(
254+
slug=cur_date.isoformat(),
255+
title={
256+
lang_code: WEEKDAYS_LOCALIZED[lang_code][cur_date.weekday()]
257+
for lang_code in SUPPORTED_LANGUAGE_CODES
258+
},
259+
is_technical=True,
260+
)
261+
cur_date += timedelta(days=1)
262+
263+
@classmethod
264+
def _get_date_dimension_dto(cls, universe: Universe) -> DimensionDTO:
265+
"""
266+
Reusable date dimension for importers that define their own dimensions in `get_dimensions`.
267+
"""
268+
return DimensionDTO(
269+
slug="date",
270+
title=DATE_DIMENSION_TITLE_LOCALIZED,
271+
choices=list(cls._get_date_dimension_values(universe)),
272+
value_ordering=ValueOrdering.SLUG,
273+
is_public=True,
274+
is_list_filter=True,
275+
is_technical=True,
276+
)

0 commit comments

Comments
 (0)