From 904bf3ea490c54012342a1b804f2d888bdb31102 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 17 Feb 2026 15:22:33 -0600 Subject: [PATCH] docs: Add subdirectory CLAUDE.md files for routers, stratplay, and services Provide targeted context for each app subdirectory so Claude Code understands local patterns without relying on the root CLAUDE.md. Also simplifies root CLAUDE.md dev/prod environment sections. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 35 +++++++----------------------- app/routers_v3/CLAUDE.md | 26 ++++++++++++++++++++++ app/routers_v3/stratplay/CLAUDE.md | 24 ++++++++++++++++++++ app/services/CLAUDE.md | 17 +++++++++++++++ 4 files changed, 75 insertions(+), 27 deletions(-) create mode 100644 app/routers_v3/CLAUDE.md create mode 100644 app/routers_v3/stratplay/CLAUDE.md create mode 100644 app/services/CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md index 72ee9b0..a3fec04 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,19 +2,6 @@ FastAPI backend serving SBA fantasy league data. Peewee ORM with PostgreSQL. -## Commands - -```bash -docker-compose up # Start PostgreSQL + API + Adminer -docker-compose up --build # Rebuild and start -docker-compose --profile sync up sync-prod # One-time production data sync -python migrations.py # Run migrations (SQL files in migrations/) -``` - -- **Dev server**: `10.10.0.42:814` / `ssh sba-db` → `cd container-data/dev-sba-database` | **Adminer**: `http://10.10.0.42:8080` -- **Production**: `ssh akamai` → `cd container-data/sba-database` -- **Deploy (dev/prod)**: `docker-compose pull && docker-compose down && docker-compose up -d` (on the remote server) - ## Architecture - **Routers**: Domain-based in `app/routers_v3/` under `/api/v3/` prefix @@ -24,21 +11,15 @@ python migrations.py # Run migrations (SQL files in migrat - **POST models**: Use `Optional[int] = None` for `id` fields (DB auto-generates) - **Logging**: Rotating file handler (`/tmp/sba-database.log`, 8MB max, 5 backups) -## Production Environment -- **Host**: `ssh akamai` -- **Path**: `~/container-data/sba-database` -- **Bot container**: `sba_postgres` (PostgreSQL) + `sba_db_api` (API) — check with `docker ps` -- **Other services on same host**: `major-domo-discord-app-1`, `sba_adminer`, `sba-website_sba-web_1`, `sba-ghost_sba-ghost_1` -- **Image**: `manticorum67/major-domo-database` (Docker Hub) -- **Version file**: `VERSION` — bump before merge to `main` -- **Health**: API Port 80 — `/health`, `/ready`, `/metrics`, `/diagnostics` -- **Env vars**: Set in `docker-compose.prod.yml` and passed to the container on startup (not stored in GitHub) - ## Development Environment -- **Host**: `ssh sba-db` -- **Path**: `~/container-data/dev-sba-database` -- **Bot container**: `dev_sba_postgres` (PostgreSQL) + `dev_sba_db_api` (API) — check with `docker ps` -- **Image**: `manticorum67/major-domo-database:dev` (Docker Hub) +- **Dev server location**: `10.10.0.42` +- **Start all services**: `docker-compose up` (PostgreSQL + API + Adminer) +- **Pull and start**: `docker-compose pull && docker-compose up --build` (rebuilds image with latest changes) +- **Sync from production**: `docker-compose --profile sync up sync-prod` (one-time production data sync) + +## Production Environment +- **Production deployment**: Uses same `docker-compose up` workflow +- **Production server access**: `ssh akamai` then `cd container-data/sba-database` ## Important diff --git a/app/routers_v3/CLAUDE.md b/app/routers_v3/CLAUDE.md new file mode 100644 index 0000000..be2e36e --- /dev/null +++ b/app/routers_v3/CLAUDE.md @@ -0,0 +1,26 @@ +# Routers (v3) + +Domain-based FastAPI routers under `/api/v3/` prefix. Each file = one resource. + +## Patterns + +- Every endpoint uses `@handle_db_errors` decorator (from `dependencies.py`) +- GET endpoints use `@cache_result(ttl=N, key_prefix="name")` for Redis caching +- Auth: `token: str = Depends(oauth2_scheme)` + `valid_token(token)` check on mutating endpoints +- Pydantic models for POST/PATCH bodies are defined inline in router files (not db_engine) +- POST models: `id` fields must be `Optional[int] = None` (DB auto-generates) + +## Two Styles Coexist + +- **Refactored** (players, teams): Thin HTTP layer → delegates to `services/` layer +- **Legacy** (most others): Business logic directly in router, imports models from `db_engine` + +## Adding a New Router + +1. Create `app/routers_v3/newrouter.py` with `router = APIRouter(prefix="/api/v3/name", tags=["name"])` +2. Import and register in `app/main.py`: `from .routers_v3 import newrouter` + `app.include_router(newrouter.router)` + +## IMPORTANT + +- PATCH endpoints: build update dict explicitly from parameters. Never use `locals()`. +- `stratplay/` is a sub-package with its own `__init__.py` and sub-routers (batting, pitching, fielding, plays, crud) diff --git a/app/routers_v3/stratplay/CLAUDE.md b/app/routers_v3/stratplay/CLAUDE.md new file mode 100644 index 0000000..e5bad1c --- /dev/null +++ b/app/routers_v3/stratplay/CLAUDE.md @@ -0,0 +1,24 @@ +# Stratplay Router (sub-package) + +Play-by-play data for Strat-o-Matic games. Mounted at `/api/v3/plays`. + +## Structure + +| File | Purpose | +|------|---------| +| `__init__.py` | Assembles sub-routers into parent `router` | +| `common.py` | Shared `build_season_games()` filter builder | +| `models.py` | Pydantic models (`PlayModel`, `POS_LIST` literal) | +| `plays.py` | General play queries | +| `batting.py` | Batting stats aggregated from plays | +| `pitching.py` | Pitching stats aggregated from plays | +| `fielding.py` | Fielding stats aggregated from plays | +| `crud.py` | POST/PATCH/DELETE operations (auth required) | + +## Key Patterns + +- All stat endpoints use `build_season_games()` from `common.py` for consistent filtering +- Filter params: `season`, `week`, `s_type` (regular/post), `week_start`/`week_end`, `manager_id` +- `week` and `s_type` are mutually exclusive; `week` and `week_start`/`week_end` are mutually exclusive +- Stats aggregate from `StratPlay` joined to `StratGame` via `game_id` +- CRUD writes trigger `update_season_batting_stats()` / `update_season_pitching_stats()` from `dependencies.py` diff --git a/app/services/CLAUDE.md b/app/services/CLAUDE.md new file mode 100644 index 0000000..256a4d1 --- /dev/null +++ b/app/services/CLAUDE.md @@ -0,0 +1,17 @@ +# Services Layer + +Business logic extracted from routers for testability. Uses dependency injection. + +## Architecture + +- `interfaces.py` — Protocol classes (`AbstractPlayerRepository`, `AbstractTeamRepository`, `AbstractCacheService`) +- `base.py` — `BaseService` with lazy-loaded default repos (imports from `db_engine` on first access) +- `mocks.py` — Mock implementations for testing without DB/Redis +- `player_service.py`, `team_service.py` — Domain services (class methods, no instance needed for reads) + +## Key Patterns + +- Services use `@staticmethod`/`@classmethod` for read operations (no instantiation needed) +- Default repos are created lazily inside `@property` methods to avoid circular imports with `db_engine` +- For testing: pass mock repos via `ServiceConfig` or constructor kwargs +- Only `players` and `teams` routers currently use the service layer; others call `db_engine` directly