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 .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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
)
|
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
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