"""Small cross-cutting helpers shared across modules (kept dependency-light on purpose).""" import re from datetime import date, datetime, timezone def iso(dt: date | None) -> str | None: """Serialize a date/datetime for a JSON response: ISO-8601, or None when the column is NULL. Single source of truth for the `x.isoformat() if x else None` idiom the routes had hand-rolled ~17 times — so a future tz/format tweak lands in one place. Accepts `date` too (datetime is a subclass) — e.g. MediaAsset.upload_date is a plain `date`.""" return dt.isoformat() if dt is not None else None # Single source of truth for "looks like an email" — used by every endpoint that accepts an # address (auth register/reset/demo, admin invites + demo whitelist, the setup wizard). Keep # the strictness in one place so routes don't drift between this and a looser "@"-in check. _EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") def valid_email(value: str | None) -> bool: return bool(value and _EMAIL_RE.match(value)) def now_utc() -> datetime: """Current time as a timezone-aware UTC datetime.""" return datetime.now(timezone.utc)