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 .calendar_config import CalendarConfig, get_calendar_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
@ -22,18 +22,18 @@ async def get_all_sorted_days(
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),
) -> EventDays:
) -> EventDates:
"""
Aktueller Kalender-Zeitraum
"""
return EventDays.get(
return EventDates(
today=date.today(),
begin_month=12,
begin_day=1,
events_after=[day - 1 for day in days],
events=[day - 1 for day in days],
closing_after=90,
)

View file

@ -7,7 +7,6 @@ from typing import Any, Self, Sequence, TypeVar
from fastapi.responses import StreamingResponse
from PIL import Image
from pydantic import BaseModel
from .config import get_config
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
"""
__overall_duration: timedelta
__events: dict[int, date]
dates: list[date]
@property
def first(self) -> date:
"""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:
"""Datum des letzten Ereignisses"""
return self.__events[max(self.__events.keys())]
return self.dates[-1]
@property
def end(self) -> date:
"""Letztes Datum des Ereigniszeitraums"""
return self.__events[max(self.__events.keys())]
return self.first + self.__overall_duration
def __init__(
self,
@ -100,14 +111,15 @@ class EventDays2:
# 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],
# events: e.g. a 2 means there is an event on the 2nd day
# i.e. 1 day after begin
# - assume sorted (ascending)
events: list[int],
# countdown to closing begins after last event
closing_after: int,
) -> None:
# 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
maybe_begin = (
@ -121,65 +133,4 @@ class EventDays2:
)
# all event dates
self.__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,
)
self.dates = [begin + timedelta(days=event - 1) for event in events]

View file

@ -3,11 +3,11 @@ from datetime import date
from fastapi import APIRouter, Depends
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.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 ._security import require_admin, user_is_admin
@ -59,7 +59,7 @@ async def get_config_model(
_: None = Depends(require_admin),
cfg: Config = Depends(get_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:
"""
Kombiniert aus privaten `settings`, `config` und `calendar_config`
@ -69,10 +69,10 @@ async def get_config_model(
{
"puzzle": {
"solution": cfg.puzzle.solution,
"first": event_days.first,
"next": event_days.next,
"last": event_days.last,
"end": event_days.end,
"first": event_dates.first,
"next": event_dates.next,
"last": event_dates.last,
"end": event_dates.end,
"seed": cfg.puzzle.random_seed,
},
"calendar": {

View file

@ -5,8 +5,8 @@ from fastapi.responses import StreamingResponse
from PIL import Image
from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config
from ..core.depends import get_all_event_days, get_day_image
from ..core.helpers import EventDays, api_return_image, load_image
from ..core.depends import get_all_event_dates, get_day_image
from ..core.helpers import EventDates, api_return_image, load_image
from ._security import user_can_view_day, user_is_admin
router = APIRouter(prefix="/user", tags=["user"])
@ -58,16 +58,16 @@ async def get_doors(
@router.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:
"""
Zeit in ms, bis das nächste Türchen öffnet
"""
if event_days.next is None:
if event_dates.next is 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()
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()