EventDays -> EventDates rework

- events param is now one-indexed
This commit is contained in:
Jörn-Michael Miehe 2023-09-20 16:25:10 +02:00
parent 75dcea25fb
commit 82ab9ccddc
6 changed files with 128 additions and 169 deletions

View file

@ -8,7 +8,7 @@ from PIL import Image, ImageFont
from .advent_image import _XY, AdventImage from .advent_image import _XY, AdventImage
from .calendar_config import CalendarConfig, get_calendar_config from .calendar_config import CalendarConfig, get_calendar_config
from .config import Config, get_config from .config import Config, get_config
from .helpers import EventDays, Random, list_images_auto, load_image, set_len from .helpers import EventDates, Random, list_images_auto, load_image, set_len
from .webdav import WebDAV from .webdav import WebDAV
@ -22,18 +22,18 @@ async def get_all_sorted_days(
return sorted(set(door.day for door in cal_cfg.doors)) return sorted(set(door.day for door in cal_cfg.doors))
async def get_all_event_days( async def get_all_event_dates(
days: list[int] = Depends(get_all_sorted_days), days: list[int] = Depends(get_all_sorted_days),
) -> EventDays: ) -> EventDates:
""" """
Aktueller Kalender-Zeitraum Aktueller Kalender-Zeitraum
""" """
return EventDays.get( return EventDates(
today=date.today(), today=date.today(),
begin_month=12, begin_month=12,
begin_day=1, begin_day=1,
events_after=[day - 1 for day in days], events=[day - 1 for day in days],
closing_after=90, closing_after=90,
) )

View file

@ -7,7 +7,6 @@ from typing import Any, Self, Sequence, TypeVar
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from PIL import Image from PIL import Image
from pydantic import BaseModel
from .config import get_config from .config import get_config
from .webdav import WebDAV from .webdav import WebDAV
@ -72,25 +71,37 @@ async def api_return_image(img: Image.Image) -> StreamingResponse:
) )
class EventDays2: class EventDates:
""" """
Events in einem Ereigniszeitraum Events in einem Ereigniszeitraum
""" """
__overall_duration: timedelta __overall_duration: timedelta
__events: dict[int, date] dates: list[date]
@property
def first(self) -> date: def first(self) -> date:
"""Datum des ersten Ereignisses""" """Datum des ersten Ereignisses"""
return self.__events[min(self.__events.keys())] return self.dates[0]
def get_next(self, *, today: date) -> date | None:
"""Datum des nächsten Ereignisses"""
return next((event for event in self.dates if event > today), None)
@property
def next(self) -> date | None:
"""Datum des nächsten Ereignisses"""
return self.get_next(today=datetime.today().date())
@property
def last(self) -> date: def last(self) -> date:
"""Datum des letzten Ereignisses""" """Datum des letzten Ereignisses"""
return self.__events[max(self.__events.keys())] return self.dates[-1]
@property
def end(self) -> date: def end(self) -> date:
"""Letztes Datum des Ereigniszeitraums""" """Letztes Datum des Ereigniszeitraums"""
return self.__events[max(self.__events.keys())] return self.first + self.__overall_duration
def __init__( def __init__(
self, self,
@ -100,14 +111,15 @@ class EventDays2:
# month/day when events begin # month/day when events begin
begin_month: int, begin_month: int,
begin_day: int, begin_day: int,
# events: e.g. a 2 means there is an event 2 days after begin # events: e.g. a 2 means there is an event on the 2nd day
# -> assume sorted (asc) # i.e. 1 day after begin
events_after: list[int], # - assume sorted (ascending)
events: list[int],
# countdown to closing begins after last event # countdown to closing begins after last event
closing_after: int, closing_after: int,
) -> None: ) -> None:
# account for the last event, then add closing period # account for the last event, then add closing period
self.__overall_duration = timedelta(days=events_after[-1] + closing_after) self.__overall_duration = timedelta(days=events[-1] - 1 + closing_after)
# the events may begin last year, this year or next year # the events may begin last year, this year or next year
maybe_begin = ( maybe_begin = (
@ -121,65 +133,4 @@ class EventDays2:
) )
# all event dates # all event dates
self.__events = { self.dates = [begin + timedelta(days=event - 1) for event in events]
event_after: begin + timedelta(days=event_after - 1)
for event_after in events_after
}
class EventDays(BaseModel):
"""
Kenndaten eines Ereigniszeitraums:
- `first`: Datum des ersten Ereignisses
- `next`: Datum des nächsten Ereignisses
- `last`: Datum des letzten Ereignisses
- `end`: Letztes Datum des Ereigniszeitraums
"""
first: date
next: date | None
last: date
end: date
@classmethod
def get(
cls,
*,
# current date
today: date,
# month/day when events begin
begin_month: int,
begin_day: int,
# events: e.g. a 2 means there is an event 2 days after begin
# -> assume sorted (asc)
events_after: list[int],
# countdown to closing begins after last event
closing_after: int,
) -> Self:
"""
Kenndaten des aktuellen (laufenden oder zukünftigen) Ereigniszeitraums bestimmen
"""
# account for the last event, then add closing period
duration = timedelta(days=events_after[-1] + closing_after)
# the events may begin last year, this year or next year
maybe_begin = (
datetime(today.year + year_diff, begin_month, begin_day).date()
for year_diff in (-1, 0, +1)
)
# find the first begin where the end date is in the future
begin = next(begin for begin in maybe_begin if today <= (begin + duration))
# all event dates
events = [begin + timedelta(days=event_after) for event_after in events_after]
# return relevant event dates
return cls(
first=events[0],
next=next((event for event in events if event > today), None),
last=events[-1],
end=begin + duration,
)

View file

@ -3,11 +3,11 @@ from datetime import date
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from pydantic import BaseModel from pydantic import BaseModel
from advent22_api.core.helpers import EventDays from advent22_api.core.helpers import EventDates
from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config
from ..core.config import Config, get_config from ..core.config import Config, get_config
from ..core.depends import get_all_event_days, get_all_image_names, get_all_parts from ..core.depends import get_all_event_dates, get_all_image_names, get_all_parts
from ..core.settings import SETTINGS from ..core.settings import SETTINGS
from ._security import require_admin, user_is_admin from ._security import require_admin, user_is_admin
@ -59,7 +59,7 @@ async def get_config_model(
_: None = Depends(require_admin), _: None = Depends(require_admin),
cfg: Config = Depends(get_config), cfg: Config = Depends(get_config),
cal_cfg: CalendarConfig = Depends(get_calendar_config), cal_cfg: CalendarConfig = Depends(get_calendar_config),
event_days: EventDays = Depends(get_all_event_days), event_dates: EventDates = Depends(get_all_event_dates),
) -> ConfigModel: ) -> ConfigModel:
""" """
Kombiniert aus privaten `settings`, `config` und `calendar_config` Kombiniert aus privaten `settings`, `config` und `calendar_config`
@ -69,10 +69,10 @@ async def get_config_model(
{ {
"puzzle": { "puzzle": {
"solution": cfg.puzzle.solution, "solution": cfg.puzzle.solution,
"first": event_days.first, "first": event_dates.first,
"next": event_days.next, "next": event_dates.next,
"last": event_days.last, "last": event_dates.last,
"end": event_days.end, "end": event_dates.end,
"seed": cfg.puzzle.random_seed, "seed": cfg.puzzle.random_seed,
}, },
"calendar": { "calendar": {

View file

@ -5,8 +5,8 @@ from fastapi.responses import StreamingResponse
from PIL import Image from PIL import Image
from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config
from ..core.depends import get_all_event_days, get_day_image from ..core.depends import get_all_event_dates, get_day_image
from ..core.helpers import EventDays, api_return_image, load_image from ..core.helpers import EventDates, api_return_image, load_image
from ._security import user_can_view_day, user_is_admin from ._security import user_can_view_day, user_is_admin
router = APIRouter(prefix="/user", tags=["user"]) router = APIRouter(prefix="/user", tags=["user"])
@ -58,16 +58,16 @@ async def get_doors(
@router.get("/next_door") @router.get("/next_door")
async def get_next_door( async def get_next_door(
event_days: EventDays = Depends(get_all_event_days), event_dates: EventDates = Depends(get_all_event_dates),
) -> int | None: ) -> int | None:
""" """
Zeit in ms, bis das nächste Türchen öffnet Zeit in ms, bis das nächste Türchen öffnet
""" """
if event_days.next is None: if event_dates.next is None:
return None return None
dt = datetime.combine(event_days.next, datetime.min.time()) dt = datetime.combine(event_dates.next, datetime.min.time())
td = dt - datetime.now() td = dt - datetime.now()
return int(td.total_seconds() * 1000) return int(td.total_seconds() * 1000)

View file

@ -0,0 +1,88 @@
from datetime import date
from advent22_api.core.helpers import EventDates
def test_get_before():
today = date(2023, 11, 30)
ed = EventDates(
today=today,
begin_month=12,
begin_day=1,
events=list(range(1, 25)),
closing_after=5,
)
assert ed.first == date(2023, 12, 1)
assert ed.get_next(today=today) == date(2023, 12, 1)
assert ed.last == date(2023, 12, 24)
assert ed.end == date(2023, 12, 29)
def test_get_after():
today = date(2023, 12, 30)
ed = EventDates(
today=today,
begin_month=12,
begin_day=1,
events=list(range(1, 25)),
closing_after=5,
)
assert ed.first == date(2024, 12, 1)
assert ed.get_next(today=today) == date(2024, 12, 1)
assert ed.last == date(2024, 12, 24)
assert ed.end == date(2024, 12, 29)
def test_get_during_events():
today = date(2023, 12, 10)
ed = EventDates(
today=today,
begin_month=12,
begin_day=1,
events=list(range(1, 25)),
closing_after=5,
)
assert ed.first == date(2023, 12, 1)
assert ed.get_next(today=today) == date(2023, 12, 11)
assert ed.last == date(2023, 12, 24)
assert ed.end == date(2023, 12, 29)
def test_get_during_closing():
today = date(2023, 12, 29)
ed = EventDates(
today=today,
begin_month=12,
begin_day=1,
events=list(range(1, 25)),
closing_after=5,
)
assert ed.first == date(2023, 12, 1)
assert ed.get_next(today=today) is None
assert ed.last == date(2023, 12, 24)
assert ed.end == date(2023, 12, 29)
def test_get_during_wrap():
today = date(2024, 1, 1)
ed = EventDates(
today=today,
begin_month=12,
begin_day=1,
events=list(range(1, 25)),
closing_after=8,
)
assert ed.first == date(2023, 12, 1)
assert ed.get_next(today=today) is None
assert ed.last == date(2023, 12, 24)
assert ed.end == date(2024, 1, 1)

View file

@ -1,80 +0,0 @@
from datetime import datetime
from advent22_api.core.helpers import EventDays
class TestEventDays:
@staticmethod
def test_get_before():
ed = EventDays.get(
today=datetime(2023, 11, 30).date(),
begin_month=12,
begin_day=1,
events_after=list(range(24)),
closing_after=5,
)
assert ed.first == datetime(2023, 12, 1).date()
assert ed.next == datetime(2023, 12, 1).date()
assert ed.last == datetime(2023, 12, 24).date()
assert ed.end == datetime(2023, 12, 29).date()
@staticmethod
def test_get_after():
ed = EventDays.get(
today=datetime(2023, 12, 30).date(),
begin_month=12,
begin_day=1,
events_after=list(range(24)),
closing_after=5,
)
assert ed.first == datetime(2024, 12, 1).date()
assert ed.next == datetime(2024, 12, 1).date()
assert ed.last == datetime(2024, 12, 24).date()
assert ed.end == datetime(2024, 12, 29).date()
@staticmethod
def test_get_during_events():
ed = EventDays.get(
today=datetime(2023, 12, 10).date(),
begin_month=12,
begin_day=1,
events_after=list(range(24)),
closing_after=5,
)
assert ed.first == datetime(2023, 12, 1).date()
assert ed.next == datetime(2023, 12, 11).date()
assert ed.last == datetime(2023, 12, 24).date()
assert ed.end == datetime(2023, 12, 29).date()
@staticmethod
def test_get_during_closing():
ed = EventDays.get(
today=datetime(2023, 12, 29).date(),
begin_month=12,
begin_day=1,
events_after=list(range(24)),
closing_after=5,
)
assert ed.first == datetime(2023, 12, 1).date()
assert ed.next is None
assert ed.last == datetime(2023, 12, 24).date()
assert ed.end == datetime(2023, 12, 29).date()
@staticmethod
def test_get_during_wrap():
ed = EventDays.get(
today=datetime(2024, 1, 1).date(),
begin_month=12,
begin_day=1,
events_after=list(range(24)),
closing_after=8,
)
assert ed.first == datetime(2023, 12, 1).date()
assert ed.next is None
assert ed.last == datetime(2023, 12, 24).date()
assert ed.end == datetime(2024, 1, 1).date()