Skip to content

Latest commit

 

History

History
56 lines (45 loc) · 5.83 KB

File metadata and controls

56 lines (45 loc) · 5.83 KB

Changelog — Backend & UI Upgrade

Every change below was made against the existing codebase (no rewrite, no new project) and verified with the existing + expanded pytest suite (78 tests, all passing), pyflakes (clean, no dead code/unused imports), and Streamlit's AppTest harness (every page executes with zero runtime exceptions, including the full form-submit → predict → PDF-download path).

New files

File Purpose
src/exceptions.py Typed domain error hierarchy (UnknownDiseaseError, ModelNotTrainedError, PredictionError) shared by the API and dashboard, so both surfaces agree on what "went wrong" means.
api/deps.py FastAPI dependency-injection providers for ModelRegistry/Settings, enabling app.dependency_overrides in tests without touching disk.
api/middleware.py RequestContextMiddleware — request-ID correlation + latency logging on every HTTP call.
api/routers/health.py /healthz liveness probe and /readyz readiness probe (reports per-disease model load status — the right thing for a container orchestrator to gate on).
api/routers/diseases.py /v1/diseases catalog, /v1/diseases/{key}/metrics, new /v1/diseases/{key}/schema.
api/routers/predict.py One rate-limited POST route per disease, dynamically generated from DiseaseSpec.
.env.example Documents every HDPP_-prefixed environment variable the new Settings class reads.

Modified files

File What changed and why
src/config.py Added Settings (pydantic-settings) for env-driven CORS, log level, and per-route rate limits. DiseaseSpec/DISEASES are unchanged — they were already clean.
src/prediction.py Added ModelRegistry (lazy-loading, cached, injectable) alongside the existing DiseaseModel. get_model() is kept as a back-compat wrapper so the dashboard's existing call sites didn't need touching. predict() now wraps failures in PredictionError instead of leaking raw exceptions.
api/main.py Rewritten as assembly only: mounts the three routers, adds RequestContextMiddleware, CORS, and slowapi rate limiting, and registers one global exception handler per domain error type (previously each route had its own inline try/except HTTPException).
dashboard/theme.py Full rewrite: colors moved to CSS custom properties so light/dark mode is one variable swap instead of two stylesheets; added top_nav() (sticky, real cross-page links verified against Streamlit's actual URL-slug rules), theme_toggle_control(), breadcrumb(), skeleton(), kpi_card(). Risk-rail and footer logic unchanged.
dashboard/app.py Added sticky top nav, dark-mode toggle, functional search/filter over the 5 disease cards (verified the search actually matches real description text — an earlier placeholder example didn't and was fixed), animated-looking KPI cards with icons, trained/not-trained status badges.
dashboard/common.py Added top nav, breadcrumb, a skeleton-loader placeholder shown while model.predict() runs, a success toast on completion, and proper ModelNotTrainedError/PredictionError handling instead of a bare FileNotFoundError catch.
dashboard/pages/1–5_*.py Added page icons (page_icon=) for a more polished browser tab / nav appearance. Logic unchanged — they're intentionally thin wrappers around common.render_disease_page.
dashboard/pages/6_Model_Insights.py Wired in top nav/breadcrumb/theme toggle. Added functional CSV and Excel download buttons for the metrics table (real pandas/openpyxl export, not decorative).
dashboard/pages/7_About.py Wired in top nav/breadcrumb/theme toggle. Content unchanged — it was already accurate.
tests/test_api.py Added tests for /healthz, /readyz, /v1/diseases/{key}/schema, and the request-ID response header.
tests/test_prediction.py Updated the unknown-disease test to expect the new typed UnknownDiseaseError instead of a bare KeyError.
requirements.txt Added pydantic-settings, slowapi, openpyxl.
README.md Added an "API architecture" section documenting the router split and Settings; updated the project-layout tree and endpoint list.

Bugs found and fixed during this work (not present before, or pre-existing)

  • Closure-scoped type hints under from __future__ import annotations: the dynamically-generated per-disease Pydantic request models in the predict router were silently unresolvable (FastAPI treated the request body as a missing query parameter, breaking every predict endpoint with a 422). Fixed by removing the PEP 563 future-import from that one file — necessary because dynamic create_model() closures can't be resolved as string annotations.
  • Misleading search placeholder: the home page's search box advertised "cardiology" as an example term, but that word doesn't appear anywhere in the disease descriptions it searches — it would have returned zero results. Fixed to use terms that actually match (glucose, bilirubin), and verified via AppTest.

Deliberately not done (and why)

  • Pagination was in the original request list but wasn't added: there are 5 disease models and one small metrics table — paginating either would be decorative UI with no real content behind it, which conflicts with "no placeholders."
  • Auth, model registry/experiment tracking, SHAP-dashboard-as-separate-page, migrations, full Docker hardening, and a documentation rewrite were intentionally left for a follow-up pass (as scoped in-chat) rather than attempted shallowly in the same session as the backend + UI work above.

How to verify yourself

pip install -r requirements.txt
python -m pytest -q                          # 78 passed
uvicorn api.main:app --reload --port 8000     # /docs, /healthz, /readyz
streamlit run dashboard/app.py                # http://localhost:8501