import itertools import random import re from typing import Any, Sequence from fastapi import Depends from PIL import ImageFont from ..config import Config, get_config from ..dav_common import dav_file_exists, dav_get_file, dav_list_files from ._image import AdventImage ########## # RANDOM # ########## class Random(random.Random): @classmethod async def get(cls, bonus_salt: Any = "") -> "Random": cfg = await get_config() return cls(f"{cfg.puzzle.solution}{bonus_salt}") async def set_length(seq: Sequence, length: int) -> list: # `seq` unendlich wiederholen infinite = itertools.cycle(seq) # Die ersten `length` einträge nehmen return list(itertools.islice(infinite, length)) async def shuffle(seq: Sequence, rnd: random.Random | None = None) -> list: # Zufallsgenerator rnd = rnd or await Random.get() # Elemente mischen return rnd.sample(seq, len(seq)) ######### # IMAGE # ######### async def get_letter( index: int, cfg: Config = Depends(get_config), ) -> str: return (await shuffle(cfg.puzzle.solution))[index] _RE_IMAGE_FILE = re.compile( r"\.(gif|jpe?g|tiff?|png|bmp)$", flags=re.IGNORECASE, ) async def list_images_auto() -> list[str]: """ Finde alle Bilder im "automatisch"-Verzeichnis """ ls = await dav_list_files(_RE_IMAGE_FILE, "/images_auto") ls = await set_length(ls, 24) return await shuffle(ls) async def load_image( file_name: str, ) -> AdventImage: """ Versuche, Bild aus Datei zu laden """ if not await dav_file_exists(file_name): raise RuntimeError(f"DAV-File {file_name} does not exist!") img_buffer = await dav_get_file(file_name) img_buffer.seek(0) return await AdventImage.load_standard(img_buffer) async def get_auto_image( index: int, letter: str = Depends(get_letter), images: list[str] = Depends(list_images_auto), ) -> AdventImage: """ Erstelle automatisch generiertes Bild """ # hier niemals RuntimeError! image = await load_image(images[index]) rnd = await Random.get(index) lena_ttf = await dav_get_file("fonts/Lena.ttf") lena_ttf.seek(0) # Buchstabe verstecken await image.hide_text( xy=tuple(rnd.choices(range(30, 470), k=2)), text=letter, font=ImageFont.truetype(lena_ttf, 50), ) return image async def get_image( index: int, letter: str = Depends(get_letter), images: list[str] = Depends(list_images_auto), ) -> AdventImage: """ Bild für einen Tag erstellen """ try: # Versuche, aus "manual"-Ordner zu laden return await load_image(f"images_manual/{index}.jpg") except RuntimeError: # Erstelle automatisch generiertes Bild return await get_auto_image(index=index, letter=letter, images=images)