from __future__ import annotations from collections.abc import Callable from datetime import datetime from flask import Flask, request from templates import load_templates, parse_color, save_templates def _fmt_dt(dt: datetime | None) -> str: if dt is None: return "—" try: return dt.isoformat(timespec="seconds") except Exception: return str(dt) HTML_TEMPLATE = ( "
Edits are saved to the templates JSON file on disk.
" "{content}" "" ) def get_html_template(content) -> str: return HTML_TEMPLATE.format(content=content) def _as_hex_color(value: int | str | None) -> str: if isinstance(value, int): return f"#{value:06X}" if isinstance(value, str): try: return f"#{parse_color(value):06X}" except ValueError: return "#000000" return "#000000" def create_app( *, get_state: Callable[[], dict], get_next_event: Callable[[], dict], ) -> Flask: app = Flask(__name__) @app.get("/") def index() -> str: state = get_state() or {} next_event = get_next_event() or {} locations = state.get("last_locations") or [] locations_html = "".join(f"Total: {len(locations)}
" f"Edits are saved to the templates JSON file on disk.
" "" ) @app.post("/admin") def admin_post() -> tuple[str, int]: templates_path = app.config.get("TEMPLATES_PATH", "templates.json") current = load_templates(templates_path) errors: list[str] = [] updated: dict[str, dict] = {} for key in current.keys(): text = request.form.get(f"{key}__text", "").strip() color_raw = request.form.get(f"{key}__color", "").strip() if not color_raw: color_raw = request.form.get( f"{key}__color_picker", "").strip() image_url = request.form.get(f"{key}__image_url", "").strip() if not text: errors.append(f"{key}: text is required") continue try: color = parse_color(color_raw) except Exception as e: errors.append(f"{key}: invalid color ({e})") continue tpl: dict = {"text": text, "color": color} if image_url: tpl["image_url"] = image_url updated[key] = tpl if errors: msg = "Errors:
{msg}
Saved.
" "" ), 200 return app