from datetime import date from io import BytesIO from typing import cast from fastapi import Depends 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 .webdav import WebDAV async def get_all_sorted_days( cal_cfg: CalendarConfig = Depends(get_calendar_config), ) -> list[int]: """ Alle Tage, für die es ein Türchen gibt """ return sorted(set(door.day for door in cal_cfg.doors)) async def get_all_event_days( days: list[int] = Depends(get_all_sorted_days), ) -> EventDays: """ Aktueller Kalender-Zeitraum """ return EventDays.get( today=date.today(), begin_month=9, begin_day=1, events_after=[day - 1 for day in days], closing_after=90, ) async def get_all_parts( cfg: Config = Depends(get_config), days: list[int] = Depends(get_all_sorted_days), ) -> dict[int, str]: """ Lösung auf vorhandene Tage aufteilen """ solution_length = len(cfg.puzzle.solution) num_days = len(days) rnd = await Random.get() solution_days = [ # wie oft passen die Tage "ganz" in die Länge der Lösung? # zB 26 Buchstaben // 10 Tage == 2 mal => 2 Zeichen pro Tag *rnd.shuffled(days * (solution_length // num_days)), # wie viele Buchstaben bleiben übrig? # zB 26 % 10 == 6 Buchstaben => an 6 Tagen ein Zeichen mehr *rnd.sample(days, solution_length % num_days), ] result: dict[int, str] = {} for day, letter in zip(solution_days, cfg.puzzle.solution): result[day] = result.get(day, "") result[day] += letter return result async def get_all_auto_image_names( days: list[int] = Depends(get_all_sorted_days), images: list[str] = Depends(list_images_auto), ) -> dict[int, str]: """ Bilder: Reihenfolge zufällig bestimmen """ rnd = await Random.get() ls = set_len(images, len(days)) return dict(zip(days, rnd.shuffled(ls))) async def get_all_image_names( auto_image_names: dict[int, str] = Depends(get_all_auto_image_names), ) -> dict[int, str]: """ Bilder "auto" und "manual" zu Tagen zuordnen """ # TODO penner # "manual"-Bilder erkennen (hier neue variable anlegen) return auto_image_names async def gen_day_auto_image( day: int, cfg: Config = Depends(get_config), auto_image_names: list[str] = Depends(get_all_auto_image_names), day_parts: dict[int, str] = Depends(get_all_parts), ) -> Image.Image: """ Automatisch generiertes Bild erstellen """ # Datei existiert garantiert! img = await load_image(auto_image_names[day]) image = await AdventImage.from_img(img) rnd = await Random.get(day) font = ImageFont.truetype( font=BytesIO(await WebDAV.read_bytes(f"files/{cfg.server.font}")), size=50, ) # Buchstaben verstecken for letter in day_parts[day]: await image.hide_text( xy=cast(_XY, tuple(rnd.choices(range(30, 470), k=2))), text=letter, font=font, ) return image.img async def get_day_image( day: int, cfg: Config = Depends(get_config), auto_image_names: list[str] = Depends(get_all_auto_image_names), day_parts: dict[int, str] = Depends(get_all_parts), ) -> Image.Image: """ Bild für einen Tag abrufen """ try: # Versuche, aus "manual"-Ordner zu laden img = await load_image(f"images_manual/{day}.jpg") # Als AdventImage verarbeiten image = await AdventImage.from_img(img) return image.img except RuntimeError: # Erstelle automatisch generiertes Bild return await gen_day_auto_image( day=day, cfg=cfg, auto_image_names=auto_image_names, day_parts=day_parts )