EventDays -> EventDates rework
- events param is now one-indexed
This commit is contained in:
parent
75dcea25fb
commit
82ab9ccddc
6 changed files with 128 additions and 169 deletions
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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)
|
||||
|
|
88
api/test/test_event_dates.py
Normal file
88
api/test/test_event_dates.py
Normal 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)
|
|
@ -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()
|
Loading…
Reference in a new issue