"""Canonical filesystem locations, resolved once from the repo root. Every path the backend needs (the climate cache, the accounts DB, logs, the Jinja templates, the frontend static dir, the bundled city data) is derived here from the repository root rather than from each module's own ``__file__``. That keeps the locations stable no matter how deep in the package tree a module lives, so moving a module never silently re-points the cache or the accounts DB. Callers that support an environment override (e.g. ``THERMOGRAPH_ACCOUNTS_DB``) keep doing so; these are only the defaults. """ import os _HERE = os.path.dirname(os.path.abspath(__file__)) def _repo_root() -> str: """Walk up from this file until a directory holds both ``backend/`` and ``frontend/`` — the repository root, in every checkout (dev, prod, worktree).""" d = _HERE while True: if os.path.isdir(os.path.join(d, "backend")) and os.path.isdir(os.path.join(d, "frontend")): return d parent = os.path.dirname(d) if parent == d: raise RuntimeError( "cannot locate the repo root (no ancestor has sibling backend/ and frontend/)" ) d = parent REPO_DIR = _repo_root() BACKEND_DIR = os.path.join(REPO_DIR, "backend") DATA_DIR = os.path.join(REPO_DIR, "data") LOGS_DIR = os.path.join(REPO_DIR, "logs") FRONTEND_DIR = os.path.join(REPO_DIR, "frontend") TEMPLATES_DIR = os.path.join(BACKEND_DIR, "templates") # Structured SSR copy (glossary, static-page SEO meta) — see # backend/web/content_loader.py and docs/README.md. A sibling of backend/ and # frontend/, not under either: it's the seed of a future thermograph-copy repo. CONTENT_DIR = os.path.join(REPO_DIR, "content") # Bundled reference data shipped alongside the code (generated by gen_cities / # gen_flavor), not runtime state — hence under backend/, not data/. CITIES_JSON = os.path.join(BACKEND_DIR, "cities.json") CITIES_FLAVOR_JSON = os.path.join(BACKEND_DIR, "cities_flavor.json")