from __future__ import annotations import pathlib from dataclasses import dataclass, field from datetime import datetime, time from typing import Any from zoneinfo import ZoneInfo import yaml from server.settings import settings @dataclass class Med: name: str dose: str @dataclass class Slot: id: str time: time label: str meds: list[Med] reminder_window_minutes: int = 90 @dataclass class MedsConfig: timezone: ZoneInfo slots: list[Slot] = field(default_factory=list) _config: MedsConfig | None = None def load_meds_config(path: str | None = None) -> MedsConfig: global _config if _config is not None: return _config p = pathlib.Path(path or settings.meds_path) raw = yaml.safe_load(p.read_text(encoding="utf-8")) tz = ZoneInfo(raw.get("timezone", "Europe/Berlin")) slots: list[Slot] = [] for item in raw.get("slots", []): h, m = str(item["time"]).split(":") slots.append( Slot( id=item["id"], time=time(int(h), int(m)), label=item.get("label", item["id"]), meds=[Med(**m) for m in item.get("meds", [])], reminder_window_minutes=int(item.get("reminder_window_minutes", 90)), ) ) _config = MedsConfig(timezone=tz, slots=slots) return _config def reload_meds_config() -> MedsConfig: global _config _config = None return load_meds_config() def slot_datetime(day: datetime, slot: Slot, tz: ZoneInfo) -> datetime: local = day.astimezone(tz) if day.tzinfo else day.replace(tzinfo=tz) return local.replace(hour=slot.time.hour, minute=slot.time.minute, second=0, microsecond=0) def today_str(tz: ZoneInfo) -> str: return datetime.now(tz).strftime("%Y-%m-%d") def slot_to_dict(slot: Slot) -> dict[str, Any]: return { "id": slot.id, "time": slot.time.strftime("%H:%M"), "label": slot.label, "meds": [{"name": m.name, "dose": m.dose} for m in slot.meds], "reminder_window_minutes": slot.reminder_window_minutes, }