* Centralize filesystem paths in a single module Add paths.py, which resolves the repo root once and derives the cache, accounts DB, logs, templates, frontend and bundled-city-data locations from it. Replace the 13 per-module `dirname(__file__)/..` anchors with references to it, so a module's location no longer determines where the app reads its data. Env overrides (accounts DB, VAPID, IndexNow) are unchanged; every resolved path is byte-identical to before. Groundwork for moving modules into packages without re-pointing paths. Claude-Session: https://claude.ai/code/session_01XXxmNFy9cZ6Gh8Y9thZn62 * Split the backend into domain packages Group the flat backend modules into packages that mirror their concerns: data/ climate, grading, scoring, grid, places, cities, city_events, store web/ app, views, homepage, content, schemas notifications/ notify, digest, push, mailer, discord, discord_interactions, discord_link accounts/ models, users, api_accounts, db core/ metrics, singleton, audit Intra-project imports are rewritten to the package-qualified form. The entry scripts (indexnow, warm_cities, migrate, gen_cities, gen_flavor) and paths.py stay at the backend/ root, and backend/app.py becomes a shim re-exporting web.app:app so the launch target stays `app:app` — run.sh, the systemd units, and CI need no change. Verified: full suite (318) passes, `uvicorn app:app` boots and serves the home/SEO/static/API surfaces, and every root script imports clean. Claude-Session: https://claude.ai/code/session_01XXxmNFy9cZ6Gh8Y9thZn62
42 lines
1.7 KiB
Python
42 lines
1.7 KiB
Python
"""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")
|
|
|
|
# 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")
|