All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Adds the read+write endpoints the frontend needs to drive a
ProjectionLab-style UX on top of the existing engine.
- /networth, /networth/history — NW total + per-account from
account_snapshot (frontend chart)
- /scenarios CRUD + projection — list/get/create/patch/delete user
scenarios; cartesian read-only
- /scenarios/{id}/life-events — life event CRUD nested under scenario
- /life-events/{id} — patch + delete by id
- /scenarios/{id}/goals,
/goals/{id} — retirement goal CRUD
- /simulate, /compare — sync, no-DB-write what-if endpoints
Auth: Bearer-token dependency on writes + simulate when API_BEARER_TOKEN
is set; reads always open (lock down via Authentik-fronted ingress in
prod). Existing /recompute keeps its bearer auth.
CORS middleware reads FRONTEND_ORIGINS (comma-separated) for the dev
SPA. Lifespan now provisions the SQLAlchemy engine + session_factory
on app.state and disposes them on shutdown.
40 new tests covering happy paths and validation. 172 tests total.
mypy strict + ruff clean (B008 ignore added — Depends() in defaults
is the canonical FastAPI pattern, not a bug).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
64 lines
1.6 KiB
TOML
64 lines
1.6 KiB
TOML
[tool.poetry]
|
||
name = "fire-planner"
|
||
version = "0.1.0"
|
||
description = "Risk-adjusted, tax-minimised FIRE retirement planner — Monte Carlo simulator over jurisdictions, withdrawal strategies, and UK-departure years."
|
||
authors = ["Viktor Barzin <viktorbarzin@meta.com>"]
|
||
readme = "README.md"
|
||
packages = [{ include = "fire_planner" }]
|
||
|
||
[tool.poetry.dependencies]
|
||
python = ">=3.12,<3.13"
|
||
fastapi = "^0.115"
|
||
uvicorn = "^0.32"
|
||
httpx = "^0.27"
|
||
pydantic = "^2.9"
|
||
sqlalchemy = { extras = ["asyncio"], version = "^2.0" }
|
||
asyncpg = "^0.29"
|
||
alembic = "^1.13"
|
||
click = "^8.1"
|
||
numpy = "^2.1"
|
||
pandas = "^2.2"
|
||
prometheus-fastapi-instrumentator = "^7.0"
|
||
|
||
[tool.poetry.group.dev.dependencies]
|
||
pytest = "^8.3"
|
||
pytest-asyncio = "^0.23"
|
||
hypothesis = "^6.115"
|
||
mypy = "^1.11"
|
||
ruff = "^0.6"
|
||
yapf = "^0.43"
|
||
respx = "^0.21"
|
||
aiosqlite = "^0.20"
|
||
|
||
[build-system]
|
||
requires = ["poetry-core"]
|
||
build-backend = "poetry.core.masonry.api"
|
||
|
||
[tool.pytest.ini_options]
|
||
asyncio_mode = "auto"
|
||
testpaths = ["tests"]
|
||
|
||
[tool.mypy]
|
||
python_version = "3.12"
|
||
strict = true
|
||
files = ["fire_planner", "tests"]
|
||
|
||
[[tool.mypy.overrides]]
|
||
module = ["respx.*", "pandas.*"]
|
||
ignore_missing_imports = true
|
||
|
||
[tool.ruff]
|
||
line-length = 100
|
||
target-version = "py312"
|
||
|
||
[tool.ruff.lint]
|
||
select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"]
|
||
# RUF002 / RUF003 flag ambiguous unicode characters (×, —, etc.) in
|
||
# docstrings and comments — we use them intentionally for readability.
|
||
# B008 trips on `Depends(...)` in argument defaults — that's the
|
||
# idiomatic FastAPI pattern, not a bug.
|
||
ignore = ["RUF002", "RUF003", "B008"]
|
||
|
||
[tool.yapf]
|
||
based_on_style = "pep8"
|
||
column_limit = 100
|