"""Shared utility helpers.""" from __future__ import annotations from copy import deepcopy from datetime import datetime, timezone from html import escape import json from pathlib import Path from typing import Any ROOT = Path(__file__).resolve().parent.parent STORAGE_DIR = ROOT / "storage" STATIC_DIR = ROOT / "static" def html(value: Any) -> str: """Handle html.""" return escape(str(value if value is not None else ""), quote=True) def deep_copy(value: Any) -> Any: """Handle deep copy.""" return deepcopy(value) def utc_now() -> datetime: """Handle utc now.""" return datetime.now(timezone.utc) def current_year() -> int: """Return year.""" return utc_now().year def iso_now() -> str: """Handle iso now.""" return utc_now().isoformat(timespec="seconds") def parse_dt(value: str) -> datetime: """Parse dt.""" return datetime.fromisoformat(value.replace("Z", "+00:00")) def read_json(path: Path, default: Any) -> Any: """Read json.""" if not path.exists(): return deep_copy(default) try: return json.loads(path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return deep_copy(default) def write_json(path: Path, payload: Any) -> None: """Write json.""" path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") def bool_from_form(value: str | None) -> bool: """Handle bool from form.""" return value in {"on", "true", "1", "yes", "y"} def clean_text(value: Any, max_length: int = 500, allow_newlines: bool = False) -> str: """Clean text.""" text = str(value if value is not None else "") cleaned = [] for char in text: if char in "\r\n\t": if allow_newlines: cleaned.append("\n" if char in "\r\n" else "\t") else: cleaned.append(" ") elif ord(char) >= 32: cleaned.append(char) return "".join(cleaned).strip()[:max_length] def clean_choice(value: Any, allowed: set[str], default: str) -> str: """Clean choice.""" candidate = clean_text(value, 80) return candidate if candidate in allowed else default def list_html(items: list[str]) -> str: """Return html.""" return "