From 95dfe2a9dfb015e407b31642578af87f3d75c80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Tue, 12 Sep 2023 02:58:10 +0000 Subject: [PATCH 01/18] depends.py: get_days and get_solution_parts --- api/advent22_api/core/depends.py | 36 +++++++++++++++++++++++ api/advent22_api/core/sequence_helpers.py | 7 ++++- api/advent22_api/routers/admin.py | 9 +++++- 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/api/advent22_api/core/depends.py b/api/advent22_api/core/depends.py index c9bf5f1..7925258 100644 --- a/api/advent22_api/core/depends.py +++ b/api/advent22_api/core/depends.py @@ -5,6 +5,7 @@ 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 .image_helpers import list_images_auto, load_image from .sequence_helpers import Random, set_len, shuffle @@ -21,6 +22,41 @@ async def shuffle_solution( return "".join(await shuffle(cfg.puzzle.solution)) +async def get_days( + cal_cfg: CalendarConfig = Depends(get_calendar_config), +) -> list[int]: + """ + Alle Tage, für die es ein Türchen gibt + """ + + return list(set(door.day for door in cal_cfg.doors)) + + +async def get_solution_parts( + cfg: Config = Depends(get_config), + days: list[int] = Depends(get_days), +) -> dict[int, set[str]]: + """ + Lösung auf vorhandene Tage aufteilen + """ + + solution_length = len(cfg.puzzle.solution) + num_days = len(days) + + rnd = await Random.get() + solution_days = [ + *rnd.shuffled(days * (solution_length // num_days)), + *rnd.sample(days, solution_length % num_days), + ] + + result: dict[int, set[str]] = {} + for day, letter in zip(solution_days, cfg.puzzle.solution): + result[day] = result.get(day, set()) + result[day].add(letter) + + return result + + async def shuffle_images_auto( images: list[str] = Depends(list_images_auto), ) -> list[str]: diff --git a/api/advent22_api/core/sequence_helpers.py b/api/advent22_api/core/sequence_helpers.py index a4911d0..477c1b6 100644 --- a/api/advent22_api/core/sequence_helpers.py +++ b/api/advent22_api/core/sequence_helpers.py @@ -1,9 +1,11 @@ import itertools import random -from typing import Any, Self, Sequence +from typing import Any, Self, Sequence, TypeVar from .config import get_config +T = TypeVar("T") + class Random(random.Random): @classmethod @@ -11,6 +13,9 @@ class Random(random.Random): cfg = await get_config() return cls(f"{cfg.puzzle.solution}{cfg.puzzle.random_seed}{bonus_salt}") + def shuffled(self, population: Sequence[T]) -> Sequence[T]: + return self.sample(population, k=len(population)) + async def shuffle(seq: Sequence, rnd: random.Random | None = None) -> list: # Zufallsgenerator diff --git a/api/advent22_api/routers/admin.py b/api/advent22_api/routers/admin.py index ddcdde3..b0be7a9 100644 --- a/api/advent22_api/routers/admin.py +++ b/api/advent22_api/routers/admin.py @@ -5,13 +5,20 @@ from pydantic import BaseModel from ..core.calendar_config import CalendarConfig, get_calendar_config from ..core.config import Config, get_config -from ..core.depends import shuffle_solution +from ..core.depends import get_solution_parts, shuffle_solution from ..core.settings import SETTINGS from ._security import require_admin, user_is_admin router = APIRouter(prefix="/admin", tags=["admin"]) +@router.get("/parts") +async def get_parts( + shuffle=Depends(get_solution_parts), +) -> dict[int, set[str]]: + return shuffle + + @router.get("/is_admin") async def is_admin( is_admin: bool = Depends(user_is_admin), From 63d88c3a09e4f4c6274a21262c9136d6bf164630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Tue, 12 Sep 2023 07:27:59 +0000 Subject: [PATCH 02/18] implementation using get_part_for_day --- api/advent22_api/core/__init__.py | 0 api/advent22_api/core/depends.py | 42 ++++++++++++------------------- api/advent22_api/routers/admin.py | 22 ++++++++-------- api/advent22_api/routers/days.py | 9 +++---- ui/src/components/ConfigView.vue | 23 +++++++++++++---- 5 files changed, 48 insertions(+), 48 deletions(-) create mode 100644 api/advent22_api/core/__init__.py diff --git a/api/advent22_api/core/__init__.py b/api/advent22_api/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/advent22_api/core/depends.py b/api/advent22_api/core/depends.py index 7925258..33a43b8 100644 --- a/api/advent22_api/core/depends.py +++ b/api/advent22_api/core/depends.py @@ -12,16 +12,6 @@ from .sequence_helpers import Random, set_len, shuffle from .webdav import WebDAV -async def shuffle_solution( - cfg: Config = Depends(get_config), -) -> str: - """ - Lösung: Reihenfolge zufällig bestimmen - """ - - return "".join(await shuffle(cfg.puzzle.solution)) - - async def get_days( cal_cfg: CalendarConfig = Depends(get_calendar_config), ) -> list[int]: @@ -32,10 +22,10 @@ async def get_days( return list(set(door.day for door in cal_cfg.doors)) -async def get_solution_parts( +async def get_day_parts( cfg: Config = Depends(get_config), days: list[int] = Depends(get_days), -) -> dict[int, set[str]]: +) -> dict[int, str]: """ Lösung auf vorhandene Tage aufteilen """ @@ -49,10 +39,10 @@ async def get_solution_parts( *rnd.sample(days, solution_length % num_days), ] - result: dict[int, set[str]] = {} + result: dict[int, str] = {} for day, letter in zip(solution_days, cfg.puzzle.solution): - result[day] = result.get(day, set()) - result[day].add(letter) + result[day] = result.get(day, "") + result[day] += letter return result @@ -68,18 +58,18 @@ async def shuffle_images_auto( return await shuffle(ls) -async def get_part( +async def get_part_for_day( day: int, - shuffled_solution: str = Depends(shuffle_solution), + parts: dict[int, str] = Depends(get_day_parts), ) -> str: """ Heute angezeigter Teil der Lösung """ - return shuffled_solution[day] + return parts[day] -async def get_random( +async def get_random_for_day( day: int, ) -> Random: """ @@ -89,12 +79,12 @@ async def get_random( return await Random.get(day) -async def gen_auto_image( +async def gen_auto_image_for_day( day: int, auto_images: list[str] = Depends(shuffle_images_auto), cfg: Config = Depends(get_config), - rnd: Random = Depends(get_random), - part: str = Depends(get_part), + rnd: Random = Depends(get_random_for_day), + part: str = Depends(get_part_for_day), ) -> Image.Image: """ Automatisch generiertes Bild erstellen @@ -120,12 +110,12 @@ async def gen_auto_image( return image.img -async def get_image( +async def get_image_for_day( day: int, auto_images: list[str] = Depends(shuffle_images_auto), cfg: Config = Depends(get_config), - rnd: Random = Depends(get_random), - part: str = Depends(get_part), + rnd: Random = Depends(get_random_for_day), + part: str = Depends(get_part_for_day), ) -> Image.Image: """ Bild für einen Tag abrufen @@ -141,6 +131,6 @@ async def get_image( except RuntimeError: # Erstelle automatisch generiertes Bild - return await gen_auto_image( + return await gen_auto_image_for_day( day=day, auto_images=auto_images, cfg=cfg, rnd=rnd, part=part ) diff --git a/api/advent22_api/routers/admin.py b/api/advent22_api/routers/admin.py index b0be7a9..5a293cf 100644 --- a/api/advent22_api/routers/admin.py +++ b/api/advent22_api/routers/admin.py @@ -3,22 +3,15 @@ from datetime import date from fastapi import APIRouter, Depends from pydantic import BaseModel +from ..core import depends from ..core.calendar_config import CalendarConfig, get_calendar_config from ..core.config import Config, get_config -from ..core.depends import get_solution_parts, shuffle_solution from ..core.settings import SETTINGS from ._security import require_admin, user_is_admin router = APIRouter(prefix="/admin", tags=["admin"]) -@router.get("/parts") -async def get_parts( - shuffle=Depends(get_solution_parts), -) -> dict[int, set[str]]: - return shuffle - - @router.get("/is_admin") async def is_admin( is_admin: bool = Depends(user_is_admin), @@ -28,8 +21,12 @@ async def is_admin( class ConfigModel(BaseModel): class __Puzzle(BaseModel): + class __Part(BaseModel): + day: int + part: str + solution: str - shuffled: str + day_parts: list[__Part] begin: date end: date closing: date @@ -64,13 +61,16 @@ async def get_config_model( _: None = Depends(require_admin), cfg: Config = Depends(get_config), cal_cfg: CalendarConfig = Depends(get_calendar_config), - shuffled_solution: str = Depends(shuffle_solution), + day_parts: dict[int, str] = Depends(depends.get_day_parts), ) -> ConfigModel: return ConfigModel.model_validate( { "puzzle": { "solution": cfg.puzzle.solution, - "shuffled": shuffled_solution, + "day_parts": [ + {"day": day, "part": part} + for day, part in sorted(day_parts.items()) + ], "begin": date.today(), # TODO "end": date.today(), # TODO "closing": date.today(), # TODO diff --git a/api/advent22_api/routers/days.py b/api/advent22_api/routers/days.py index ece8e87..8507cea 100644 --- a/api/advent22_api/routers/days.py +++ b/api/advent22_api/routers/days.py @@ -4,8 +4,8 @@ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.responses import StreamingResponse from PIL import Image +from ..core import depends from ..core.config import get_config -from ..core.depends import get_image, get_part, shuffle_solution from ..core.image_helpers import api_return_image from ._security import user_can_view_door, user_is_admin, user_visible_doors @@ -17,9 +17,6 @@ async def startup() -> None: cfg = await get_config() print(cfg.puzzle.solution) - shuffled_solution = await shuffle_solution(cfg) - print(shuffled_solution) - @router.get("/date") async def get_date() -> str: @@ -43,7 +40,7 @@ async def get_visible_days( @router.get("/part/{day}") async def get_part_for_day( - part: str = Depends(get_part), + part: str = Depends(depends.get_part_for_day), ) -> str: """ Heutiger Lösungsteil @@ -57,7 +54,7 @@ async def get_part_for_day( response_class=StreamingResponse, ) async def get_image_for_day( - image: Image.Image = Depends(get_image), + image: Image.Image = Depends(depends.get_image_for_day), can_view: bool = Depends(user_can_view_door), is_admin: bool = Depends(user_is_admin), ) -> StreamingResponse: diff --git a/ui/src/components/ConfigView.vue b/ui/src/components/ConfigView.vue index 5cf05bd..b4e5ded 100644 --- a/ui/src/components/ConfigView.vue +++ b/ui/src/components/ConfigView.vue @@ -13,7 +13,18 @@
{{ admin_config_model.puzzle.solution }}
Reihenfolge
-
{{ admin_config_model.puzzle.shuffled }}
+
+ +
Offene Türchen
10
@@ -50,8 +61,10 @@
Türchen
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 - + + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + + @@ -125,7 +138,7 @@ import BulmaDrawer from "./bulma/Drawer.vue"; interface ConfigModel { puzzle: { solution: string; - shuffled: string; + day_parts: { day: number; part: string }[]; begin: string; end: string; closing: string; @@ -156,7 +169,7 @@ export default class extends Vue { public admin_config_model: ConfigModel = { puzzle: { solution: "ABCDEFGHIJKLMNOPQRSTUVWX", - shuffled: "AGFCINBEWLKQMXDURPOSJVHT", + day_parts: [], begin: "01.12.2023", end: "24.12.2023", closing: "01.04.2024", From 3316bf28223b1ae55c0d15a02f7effd27f62f562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Tue, 12 Sep 2023 13:50:02 +0000 Subject: [PATCH 03/18] major cleanup - `routers`: `admin`, `days`, `general`, `user` -> `admin`, `images` - `core.depends`: cleanup - `core.*_helpers`: joined in `core.helpers` --- api/advent22_api/core/__init__.py | 0 api/advent22_api/core/depends.py | 56 +++++++-------- .../core/{image_helpers.py => helpers.py} | 24 +++++++ api/advent22_api/core/sequence_helpers.py | 33 --------- api/advent22_api/routers/__init__.py | 6 +- api/advent22_api/routers/_security.py | 2 +- api/advent22_api/routers/admin.py | 34 +++++++++- api/advent22_api/routers/days.py | 68 ------------------- api/advent22_api/routers/general.py | 50 -------------- api/advent22_api/routers/images.py | 50 ++++++++++++++ api/advent22_api/routers/user.py | 12 ---- 11 files changed, 132 insertions(+), 203 deletions(-) delete mode 100644 api/advent22_api/core/__init__.py rename api/advent22_api/core/{image_helpers.py => helpers.py} (60%) delete mode 100644 api/advent22_api/core/sequence_helpers.py delete mode 100644 api/advent22_api/routers/days.py delete mode 100644 api/advent22_api/routers/general.py create mode 100644 api/advent22_api/routers/images.py delete mode 100644 api/advent22_api/routers/user.py diff --git a/api/advent22_api/core/__init__.py b/api/advent22_api/core/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/api/advent22_api/core/depends.py b/api/advent22_api/core/depends.py index 33a43b8..bd5d093 100644 --- a/api/advent22_api/core/depends.py +++ b/api/advent22_api/core/depends.py @@ -7,8 +7,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 .image_helpers import list_images_auto, load_image -from .sequence_helpers import Random, set_len, shuffle +from .helpers import Random, list_images_auto, load_image, set_len from .webdav import WebDAV @@ -19,7 +18,7 @@ async def get_days( Alle Tage, für die es ein Türchen gibt """ - return list(set(door.day for door in cal_cfg.doors)) + return sorted(set(door.day for door in cal_cfg.doors)) async def get_day_parts( @@ -47,18 +46,7 @@ async def get_day_parts( return result -async def shuffle_images_auto( - images: list[str] = Depends(list_images_auto), -) -> list[str]: - """ - Bilder: Reihenfolge zufällig bestimmen - """ - - ls = set_len(images, 24) - return await shuffle(ls) - - -async def get_part_for_day( +async def get_day_part( day: int, parts: dict[int, str] = Depends(get_day_parts), ) -> str: @@ -69,38 +57,43 @@ async def get_part_for_day( return parts[day] -async def get_random_for_day( - day: int, -) -> Random: +async def get_auto_image_names( + days: list[int] = Depends(get_days), + images: list[str] = Depends(list_images_auto), +) -> dict[int, str]: """ - Tagesabhängige Zufallszahlen + Bilder: Reihenfolge zufällig bestimmen """ - return await Random.get(day) + rnd = await Random.get() + ls = set_len(images, len(days)) + + return dict(zip(days, rnd.shuffled(ls))) -async def gen_auto_image_for_day( +async def gen_day_auto_image( day: int, - auto_images: list[str] = Depends(shuffle_images_auto), cfg: Config = Depends(get_config), - rnd: Random = Depends(get_random_for_day), - part: str = Depends(get_part_for_day), + auto_image_names: list[str] = Depends(get_auto_image_names), + day_part: str = Depends(get_day_part), ) -> Image.Image: """ Automatisch generiertes Bild erstellen """ # Datei existiert garantiert! - img = await load_image(auto_images[day]) + 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 part: + for letter in day_part: await image.hide_text( xy=cast(_XY, tuple(rnd.choices(range(30, 470), k=2))), text=letter, @@ -110,12 +103,11 @@ async def gen_auto_image_for_day( return image.img -async def get_image_for_day( +async def get_day_image( day: int, - auto_images: list[str] = Depends(shuffle_images_auto), cfg: Config = Depends(get_config), - rnd: Random = Depends(get_random_for_day), - part: str = Depends(get_part_for_day), + auto_image_names: list[str] = Depends(get_auto_image_names), + day_part: str = Depends(get_day_part), ) -> Image.Image: """ Bild für einen Tag abrufen @@ -131,6 +123,6 @@ async def get_image_for_day( except RuntimeError: # Erstelle automatisch generiertes Bild - return await gen_auto_image_for_day( - day=day, auto_images=auto_images, cfg=cfg, rnd=rnd, part=part + return await gen_day_auto_image( + day=day, cfg=cfg, auto_image_names=auto_image_names, day_part=day_part ) diff --git a/api/advent22_api/core/image_helpers.py b/api/advent22_api/core/helpers.py similarity index 60% rename from api/advent22_api/core/image_helpers.py rename to api/advent22_api/core/helpers.py index 8c67b29..e92dec8 100644 --- a/api/advent22_api/core/image_helpers.py +++ b/api/advent22_api/core/helpers.py @@ -1,11 +1,35 @@ +import itertools +import random import re from io import BytesIO +from typing import Any, Self, Sequence, TypeVar from fastapi.responses import StreamingResponse from PIL import Image +from .config import get_config from .webdav import WebDAV +T = TypeVar("T") + + +class Random(random.Random): + @classmethod + async def get(cls, bonus_salt: Any = "") -> Self: + cfg = await get_config() + return cls(f"{cfg.puzzle.solution}{cfg.puzzle.random_seed}{bonus_salt}") + + def shuffled(self, population: Sequence[T]) -> Sequence[T]: + return self.sample(population, k=len(population)) + + +def set_len(seq: Sequence[T], len: int) -> Sequence[T]: + # `seq` unendlich wiederholen + infinite = itertools.cycle(seq) + + # Die ersten `length` einträge nehmen + return list(itertools.islice(infinite, len)) + async def list_images_auto() -> list[str]: """ diff --git a/api/advent22_api/core/sequence_helpers.py b/api/advent22_api/core/sequence_helpers.py deleted file mode 100644 index 477c1b6..0000000 --- a/api/advent22_api/core/sequence_helpers.py +++ /dev/null @@ -1,33 +0,0 @@ -import itertools -import random -from typing import Any, Self, Sequence, TypeVar - -from .config import get_config - -T = TypeVar("T") - - -class Random(random.Random): - @classmethod - async def get(cls, bonus_salt: Any = "") -> Self: - cfg = await get_config() - return cls(f"{cfg.puzzle.solution}{cfg.puzzle.random_seed}{bonus_salt}") - - def shuffled(self, population: Sequence[T]) -> Sequence[T]: - return self.sample(population, k=len(population)) - - -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)) - - -def set_len(seq: Sequence, length: int) -> list: - # `seq` unendlich wiederholen - infinite = itertools.cycle(seq) - - # Die ersten `length` einträge nehmen - return list(itertools.islice(infinite, length)) diff --git a/api/advent22_api/routers/__init__.py b/api/advent22_api/routers/__init__.py index 82ac838..c841cfd 100644 --- a/api/advent22_api/routers/__init__.py +++ b/api/advent22_api/routers/__init__.py @@ -1,10 +1,8 @@ from fastapi import APIRouter -from . import admin, days, general, user +from . import admin, images router = APIRouter(prefix="/api") router.include_router(admin.router) -router.include_router(days.router) -router.include_router(general.router) -router.include_router(user.router) +router.include_router(images.router) diff --git a/api/advent22_api/routers/_security.py b/api/advent22_api/routers/_security.py index 24d4686..29f406a 100644 --- a/api/advent22_api/routers/_security.py +++ b/api/advent22_api/routers/_security.py @@ -50,7 +50,7 @@ async def user_visible_doors() -> int: return 0 -async def user_can_view_door( +async def user_can_view_day( day: int, ) -> bool: """ diff --git a/api/advent22_api/routers/admin.py b/api/advent22_api/routers/admin.py index 5a293cf..9c5ba15 100644 --- a/api/advent22_api/routers/admin.py +++ b/api/advent22_api/routers/admin.py @@ -3,9 +3,9 @@ from datetime import date from fastapi import APIRouter, Depends from pydantic import BaseModel -from ..core import depends -from ..core.calendar_config import CalendarConfig, get_calendar_config +from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config from ..core.config import Config, get_config +from ..core.depends import get_day_parts from ..core.settings import SETTINGS from ._security import require_admin, user_is_admin @@ -61,7 +61,7 @@ async def get_config_model( _: None = Depends(require_admin), cfg: Config = Depends(get_config), cal_cfg: CalendarConfig = Depends(get_calendar_config), - day_parts: dict[int, str] = Depends(depends.get_day_parts), + day_parts: dict[int, str] = Depends(get_day_parts), ) -> ConfigModel: return ConfigModel.model_validate( { @@ -92,3 +92,31 @@ async def get_config_model( }, } ) + + +@router.get("/doors") +async def get_doors( + cal_cfg: CalendarConfig = Depends(get_calendar_config), +) -> DoorsSaved: + """ + Türchen lesen + """ + + return cal_cfg.doors + + +@router.put("/doors") +async def put_doors( + doors: DoorsSaved, + cfg: Config = Depends(get_config), + cal_cfg: CalendarConfig = Depends(get_calendar_config), +) -> None: + """ + Türchen ändern + """ + + cal_cfg.doors = sorted( + doors, + key=lambda door: door.day, + ) + await cal_cfg.change(cfg) diff --git a/api/advent22_api/routers/days.py b/api/advent22_api/routers/days.py deleted file mode 100644 index 8507cea..0000000 --- a/api/advent22_api/routers/days.py +++ /dev/null @@ -1,68 +0,0 @@ -from datetime import date - -from fastapi import APIRouter, Depends, HTTPException, status -from fastapi.responses import StreamingResponse -from PIL import Image - -from ..core import depends -from ..core.config import get_config -from ..core.image_helpers import api_return_image -from ._security import user_can_view_door, user_is_admin, user_visible_doors - -router = APIRouter(prefix="/days", tags=["days"]) - - -@router.on_event("startup") -async def startup() -> None: - cfg = await get_config() - print(cfg.puzzle.solution) - - -@router.get("/date") -async def get_date() -> str: - """ - Aktuelles Server-Datum - """ - - return date.today().isoformat() - - -@router.get("/visible_days") -async def get_visible_days( - visible_doors: int = Depends(user_visible_doors), -) -> int: - """ - Sichtbare Türchen - """ - - return visible_doors - - -@router.get("/part/{day}") -async def get_part_for_day( - part: str = Depends(depends.get_part_for_day), -) -> str: - """ - Heutiger Lösungsteil - """ - - return part - - -@router.get( - "/image/{day}", - response_class=StreamingResponse, -) -async def get_image_for_day( - image: Image.Image = Depends(depends.get_image_for_day), - can_view: bool = Depends(user_can_view_door), - is_admin: bool = Depends(user_is_admin), -) -> StreamingResponse: - """ - Bild für einen Tag erstellen - """ - - if not (can_view or is_admin): - raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Wie unhöflich!!!") - - return await api_return_image(image) diff --git a/api/advent22_api/routers/general.py b/api/advent22_api/routers/general.py deleted file mode 100644 index 556698b..0000000 --- a/api/advent22_api/routers/general.py +++ /dev/null @@ -1,50 +0,0 @@ -from fastapi import APIRouter, Depends -from fastapi.responses import StreamingResponse - -from ..core.calendar_config import CalendarConfig, DoorsSaved, get_calendar_config -from ..core.config import Config, get_config -from ..core.image_helpers import api_return_image, load_image - -router = APIRouter(prefix="/general", tags=["general"]) - - -@router.get( - "/background", - response_class=StreamingResponse, -) -async def get_image_for_day( - cal_cfg: CalendarConfig = Depends(get_calendar_config), -) -> StreamingResponse: - """ - Hintergrundbild laden - """ - - return await api_return_image(await load_image(f"files/{cal_cfg.background}")) - - -@router.get("/doors") -async def get_doors( - cal_cfg: CalendarConfig = Depends(get_calendar_config), -) -> DoorsSaved: - """ - Türchen lesen - """ - - return cal_cfg.doors - - -@router.put("/doors") -async def put_doors( - doors: DoorsSaved, - cfg: Config = Depends(get_config), - cal_cfg: CalendarConfig = Depends(get_calendar_config), -) -> None: - """ - Türchen setzen - """ - - cal_cfg.doors = sorted( - doors, - key=lambda door: door.day, - ) - await cal_cfg.change(cfg) diff --git a/api/advent22_api/routers/images.py b/api/advent22_api/routers/images.py new file mode 100644 index 0000000..3f01981 --- /dev/null +++ b/api/advent22_api/routers/images.py @@ -0,0 +1,50 @@ +from fastapi import APIRouter, Depends, HTTPException, status +from fastapi.responses import StreamingResponse +from PIL import Image + +from ..core.calendar_config import CalendarConfig, get_calendar_config +from ..core.config import get_config +from ..core.depends import get_day_image +from ..core.helpers import api_return_image, load_image +from ._security import user_can_view_day, user_is_admin + +router = APIRouter(prefix="/images", tags=["images"]) + + +@router.on_event("startup") +async def startup() -> None: + cfg = await get_config() + print(cfg.puzzle.solution) + + +@router.get( + "/background", + response_class=StreamingResponse, +) +async def get_background( + cal_cfg: CalendarConfig = Depends(get_calendar_config), +) -> StreamingResponse: + """ + Hintergrundbild laden + """ + + return await api_return_image(await load_image(f"files/{cal_cfg.background}")) + + +@router.get( + "/{day}", + response_class=StreamingResponse, +) +async def get_image_for_day( + image: Image.Image = Depends(get_day_image), + can_view: bool = Depends(user_can_view_day), + is_admin: bool = Depends(user_is_admin), +) -> StreamingResponse: + """ + Bild für einen Tag erstellen + """ + + if not (can_view or is_admin): + raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Wie unhöflich!!!") + + return await api_return_image(image) diff --git a/api/advent22_api/routers/user.py b/api/advent22_api/routers/user.py deleted file mode 100644 index 46fa28b..0000000 --- a/api/advent22_api/routers/user.py +++ /dev/null @@ -1,12 +0,0 @@ -from fastapi import APIRouter, Depends - -from ._security import require_admin - -router = APIRouter(prefix="/user", tags=["user"]) - - -@router.get("/admin") -def check_admin( - _: None = Depends(require_admin), -) -> bool: - return True From 20375f2c9d261eda871f3fc2a83dcf2655c575ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Tue, 12 Sep 2023 13:55:08 +0000 Subject: [PATCH 04/18] reflect api changes in UI --- ui/src/components/Calendar.vue | 2 +- ui/src/components/CalendarAssistant.vue | 2 +- ui/src/components/DoorMapEditor.vue | 4 ++-- ui/src/components/calendar/CalendarDoor.vue | 2 +- ui/src/components/editor/DoorChooser.vue | 2 +- ui/src/components/editor/DoorPlacer.vue | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/src/components/Calendar.vue b/ui/src/components/Calendar.vue index 313d43a..e6d1930 100644 --- a/ui/src/components/Calendar.vue +++ b/ui/src/components/Calendar.vue @@ -3,7 +3,7 @@
- + this.$refs.multi_modal.show_image(data)) .catch(() => this.$refs.multi_modal.set_active(false)); } diff --git a/ui/src/components/DoorMapEditor.vue b/ui/src/components/DoorMapEditor.vue index 4186c64..b24a41b 100644 --- a/ui/src/components/DoorMapEditor.vue +++ b/ui/src/components/DoorMapEditor.vue @@ -76,7 +76,7 @@ export default class extends Vue { public doors: Door[] = []; private load_doors(): Promise { - return this.$advent22.api_get("general/doors").then((data) => { + return this.$advent22.api_get("admin/doors").then((data) => { this.doors.length = 0; for (const value of data) { @@ -96,7 +96,7 @@ export default class extends Vue { data.push(door.save()); } - return this.$advent22.api_put("general/doors", data); + return this.$advent22.api_put("admin/doors", data); } public mounted(): void { diff --git a/ui/src/components/calendar/CalendarDoor.vue b/ui/src/components/calendar/CalendarDoor.vue index cf4a803..b48d79e 100644 --- a/ui/src/components/calendar/CalendarDoor.vue +++ b/ui/src/components/calendar/CalendarDoor.vue @@ -25,7 +25,7 @@ export default class extends Vue { this.$emit("doorClick"); this.$advent22 - .api_get_blob(`days/image/${this.door.day}`) + .api_get_blob(`images/${this.door.day}`) .then((data) => this.$emit("doorSuccess", data)) .catch(([reason]) => { let msg = "Unbekannter Fehler, bitte wiederholen!"; diff --git a/ui/src/components/editor/DoorChooser.vue b/ui/src/components/editor/DoorChooser.vue index fc2c6d8..4c3e333 100644 --- a/ui/src/components/editor/DoorChooser.vue +++ b/ui/src/components/editor/DoorChooser.vue @@ -11,7 +11,7 @@
- +
- + Date: Tue, 12 Sep 2023 13:56:05 +0000 Subject: [PATCH 05/18] ConfigView doors list --- ui/src/components/ConfigView.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/src/components/ConfigView.vue b/ui/src/components/ConfigView.vue index b4e5ded..04a54ef 100644 --- a/ui/src/components/ConfigView.vue +++ b/ui/src/components/ConfigView.vue @@ -60,15 +60,15 @@
Türchen
- - - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 - - - - +
From f5825ae19b67d882a7f8b3ba60b0a838ea838ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Tue, 12 Sep 2023 13:59:47 +0000 Subject: [PATCH 06/18] mark TODOs for ConfigView --- ui/src/components/ConfigView.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/src/components/ConfigView.vue b/ui/src/components/ConfigView.vue index 04a54ef..7ac3994 100644 --- a/ui/src/components/ConfigView.vue +++ b/ui/src/components/ConfigView.vue @@ -7,6 +7,7 @@

Rätsel

Titel
+
Adventskalender 2023
Lösung
@@ -27,9 +28,11 @@
Offene Türchen
+
10
Nächstes Türchen in
+
dd-hh-mm-ss
Erstes Türchen
@@ -98,6 +101,7 @@
{{ admin_config_model.webdav.url }}
Zugangsdaten
+
*** @@ -114,6 +118,7 @@
{{ admin_config_model.webdav.config_file }}
UI-Admin
+
*** From c718ab7d42dbbfaf82a6235447df94da70c12029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Tue, 12 Sep 2023 14:55:08 +0000 Subject: [PATCH 07/18] UI: one-indexed days --- ui/src/components/Calendar.vue | 4 ++-- ui/src/components/ConfigView.vue | 18 ++++++++++-------- ui/src/components/DoorMapEditor.vue | 4 ---- ui/src/components/editor/PreviewDoor.vue | 7 ++++--- ui/src/lib/door.ts | 10 ++++++---- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/ui/src/components/Calendar.vue b/ui/src/components/Calendar.vue index e6d1930..2345beb 100644 --- a/ui/src/components/Calendar.vue +++ b/ui/src/components/Calendar.vue @@ -50,8 +50,8 @@ export default class extends Vue { multi_modal: MultiModal; }; - public door_hover(index: number) { - this.figure_caption = `Türchen ${index + 1}`; + public door_hover(day: number) { + this.figure_caption = `Türchen ${day}`; } public door_unhover() { diff --git a/ui/src/components/ConfigView.vue b/ui/src/components/ConfigView.vue index 7ac3994..60221f7 100644 --- a/ui/src/components/ConfigView.vue +++ b/ui/src/components/ConfigView.vue @@ -16,11 +16,12 @@
Reihenfolge