From b9fe89a4ee350d78f3c138e18b13a1d68ae13d74 Mon Sep 17 00:00:00 2001 From: Andrew Charlwood Date: Tue, 10 Feb 2026 16:27:36 +0000 Subject: [PATCH] Cleaned up old reflex files/legacy code etc --- README.md | 106 ++++++++++++++++++++++++++-------------- app.spec | 4 +- app_desktop.py | 8 +-- cli/compute_trends.py | 2 +- cli/refresh_pathways.py | 2 +- core/resource_path.py | 6 +-- pyproject.toml | 4 +- requirements.txt | Bin 356 -> 0 bytes run_dash.py | 8 +-- setup_dev.py | 13 ----- 10 files changed, 86 insertions(+), 67 deletions(-) delete mode 100644 requirements.txt delete mode 100644 setup_dev.py diff --git a/README.md b/README.md index 9d5163b..f1dacea 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,13 @@ A web-based application for analyzing secondary care patient treatment pathways. ## Features +- **Desktop App**: Native window experience via pywebview (no browser needed) - **Interactive Visualization**: Plotly icicle charts showing patient treatment hierarchies with cost and frequency statistics - **Dual Chart Types**: Directory-based (Trust → Directorate → Drug → Pathway) and Indication-based (Trust → GP Diagnosis → Drug → Pathway) views - **Pre-computed Pathways**: Treatment pathways pre-processed and stored in SQLite for sub-50ms filter response times - **GP Diagnosis Matching**: Patient indications matched from GP records using SNOMED cluster codes (~93% match rate) -- **Modern Web Interface**: Browser-based UI using Dash (Plotly) + Dash Mantine Components with NHS branding -- **Drug Browser**: Drawer-based card browser organized by clinical directorate for drug/indication selection +- **Trend Analysis**: Historical trend views showing how drug usage and costs change over time +- **Modern Web Interface**: Dash (Plotly) + Dash Mantine Components with NHS branding - **Flexible Filtering**: Filter by date range, NHS trusts, drugs, and medical directories ## Requirements @@ -29,20 +30,21 @@ cd patient-pathway-analysis # Install dependencies uv sync - -# One-time dev setup: adds src/ to Python path via .pth file -uv run python setup_dev.py ``` ## Quick Start -### Run the Web Application +### Run the Application ```bash +# Run as desktop app (recommended) +python app_desktop.py + +# Run in browser (development) python run_dash.py ``` -Open http://localhost:8050 in your browser. +The desktop app opens automatically in a native window. For browser mode, open http://localhost:8050. The application loads pre-computed pathway data from SQLite on startup. No additional configuration is needed for viewing existing data. @@ -65,6 +67,19 @@ python -m cli.refresh_pathways --chart-type indication python -m cli.refresh_pathways --chart-type all --dry-run -v ``` +**Compute Trends (for Trends view):** + +```bash +# Compute historical trend snapshots +python -m cli.compute_trends + +# Custom date range +python -m cli.compute_trends --start 2022-01-01 --end 2025-06-30 + +# Help +python -m cli.compute_trends --help +``` + ## Usage ### Interface Overview @@ -73,21 +88,28 @@ The application has a single-page layout with: | Component | Purpose | |-----------|---------| -| **Header** | NHS branding, data freshness indicator (patient count + relative time) | -| **Sidebar** | Navigation items with drawer triggers for Drug Selection, Trust Selection, Indications | -| **KPI Row** | 4 cards: Unique Patients, Drug Types, Total Cost, Indication Match Rate | -| **Filter Bar** | Chart type toggle (By Directory / By Indication) + date filter dropdowns | -| **Chart Card** | Interactive Plotly icicle chart with loading spinner | -| **Drawer** | Right-side panel with drug chips, trust chips, and directorate card browser | +| **Header** | NHS branding, fraction KPIs (patients, drugs, cost), data freshness indicator | +| **Sidebar** | Navigation: Patient Pathways, Trust Comparison, Trends | +| **Sub-Header** | Chart type toggle (By Directory / By Indication) + date filter dropdowns | +| **Filter Bar** | Patient Pathways drug/trust/directorate filter buttons with modals | +| **Chart Card** | 9-tab chart area (Icicle, Sankey, Heatmap, Funnel, Depth, Scatter, Network, Timeline, Doses) | +| **Trust Comparison** | Per-directorate 6-chart dashboard comparing drugs across trusts | +| **Trends** | Historical trend analysis with directorate overview + drug drill-down | ### Filtering Data -1. **Chart Type**: Toggle between "By Directory" and "By Indication" views -2. **Date Filters**: Select treatment initiation period and last-seen window -3. **Drug Selection**: Open the drawer to select specific drugs via chips -4. **Trust Selection**: Open the drawer to filter by NHS trusts -5. **Directorate Browser**: Navigate directorates → indications → drug fragments in the drawer -6. **Clear Filters**: Reset all selections to show full dataset +The application has three analytical views: + +1. **Patient Pathways**: Icicle chart + 8 additional analytics tabs with drug/trust/directorate filtering +2. **Trust Comparison**: Per-directorate analysis comparing drugs across trusts +3. **Trends**: Historical trend analysis showing directorate and drug-level changes over time + +Common controls across all views: + +- **Chart Type**: Toggle between "By Directory" and "By Indication" views +- **Date Filters**: Select treatment initiation period and last-seen window +- **Drug/Trust/Directorate Selection**: Open modals to filter by specific drugs, trusts, or directorates (Patient Pathways) +- **Clear Filters**: Reset all selections to show full dataset ### Understanding the Pathway Chart @@ -121,22 +143,23 @@ Root (Regional Total) ``` . -├── src/ # All application library code -│ ├── core/ # Foundation: paths, models, logging -│ ├── config/ # Snowflake connection settings -│ ├── data_processing/ # Data layer (SQLite, Snowflake, transforms) -│ ├── analysis/ # Analysis pipeline -│ ├── visualization/ # Plotly chart generation -│ └── cli/ # CLI tools (refresh_pathways) +├── core/ # Foundation: paths, models, logging +├── config/ # Snowflake connection settings +├── data_processing/ # Data layer (SQLite, Snowflake, transforms) +├── analysis/ # Analysis pipeline +├── visualization/ # Plotly chart generation +├── cli/ # CLI tools (refresh_pathways, compute_trends) ├── dash_app/ # Dash web application │ ├── app.py # App entry point, layout, stores │ ├── assets/nhs.css # NHS design system CSS │ ├── data/ # Query wrappers + card browser data -│ ├── components/ # UI components (header, sidebar, etc.) -│ └── callbacks/ # Dash callbacks (filters, chart, KPI, drawer) -├── run_dash.py # Entry point: python run_dash.py +│ ├── components/ # UI components (header, sidebar, chart_card, trends, etc.) +│ └── callbacks/ # Dash callbacks (filters, chart, KPI, trends, etc.) +├── app_desktop.py # Desktop entry point (pywebview native window) +├── run_dash.py # Browser entry point +├── app.spec # PyInstaller packaging spec ├── data/ # Reference data + SQLite DB (pathways.db) -├── tests/ # Test suite (113 tests) +├── tests/ # Test suite (114 tests) ├── docs/ # Documentation └── archive/ # Historical/deprecated code ``` @@ -149,7 +172,7 @@ See `CLAUDE.md` for detailed architecture documentation. # Run all tests python -m pytest tests/ -v -# Run with coverage +# Run with coverage (114 tests) python -m pytest tests/ -v --cov=core --cov=data_processing --cov=analysis # Run only fast tests @@ -158,7 +181,16 @@ python -m pytest tests/ -v -m "not slow" ## Configuration -### Snowflake Connection (`src/config/snowflake.toml`) +### Desktop Packaging + +```bash +# Build standalone executable (Windows) +pyinstaller app.spec + +# Output: dist/NHS_Pathway_Analysis/NHS_Pathway_Analysis.exe +``` + +### Snowflake Connection (`config/snowflake.toml`) ```toml [snowflake] @@ -177,11 +209,11 @@ authenticator = "externalbrowser" # Required for NHS SSO # Ensure dependencies are installed uv sync -# Ensure src/ is on Python path -uv run python setup_dev.py +# Try desktop mode +python app_desktop.py -# Try running with uv -uv run python run_dash.py +# Or browser mode +python run_dash.py ``` ### Database not found @@ -193,7 +225,7 @@ python -m data_processing.migrate ### Snowflake connection issues -1. Ensure `src/config/snowflake.toml` has the correct account identifier +1. Ensure `config/snowflake.toml` has the correct account identifier 2. A browser window will open for SSO authentication 3. Verify your network allows Snowflake connections diff --git a/app.spec b/app.spec index 52770ff..6d99600 100644 --- a/app.spec +++ b/app.spec @@ -10,7 +10,7 @@ ROOT = os.path.abspath(os.path.dirname(SPECPATH)) if 'SPECPATH' in dir() else os a = Analysis( ['app_desktop.py'], - pathex=[os.path.join(ROOT, 'src')], + pathex=[ROOT], binaries=[], datas=[ ('data/pathways.db', 'data'), @@ -39,7 +39,7 @@ a = Analysis( 'pandas._libs.tslibs', 'numpy', 'sqlite3', - # App packages (src/ on pathex) + # App packages (project root on pathex) 'core', 'core.config', 'core.models', diff --git a/app_desktop.py b/app_desktop.py index 34cca9c..7ce448e 100644 --- a/app_desktop.py +++ b/app_desktop.py @@ -6,10 +6,10 @@ import threading import time from pathlib import Path -# Ensure src/ is on sys.path so that core/, data_processing/, etc. are importable -_src_dir = str(Path(__file__).resolve().parent / "src") -if _src_dir not in sys.path: - sys.path.insert(0, _src_dir) +# Ensure project root is on sys.path so that core/, data_processing/, etc. are importable +_project_root = str(Path(__file__).resolve().parent) +if _project_root not in sys.path: + sys.path.insert(0, _project_root) import webview from dash_app.app import app diff --git a/cli/compute_trends.py b/cli/compute_trends.py index 91b1ddf..316917e 100644 --- a/cli/compute_trends.py +++ b/cli/compute_trends.py @@ -26,7 +26,7 @@ from datetime import date, timedelta from pathlib import Path from typing import Optional -# Ensure src/ is on sys.path when run as `python -m cli.compute_trends` +# Ensure project root is on sys.path when run as `python -m cli.compute_trends` _src_dir = str(Path(__file__).resolve().parent.parent) if _src_dir not in sys.path: sys.path.insert(0, _src_dir) diff --git a/cli/refresh_pathways.py b/cli/refresh_pathways.py index b0b1bf3..81c2f1c 100644 --- a/cli/refresh_pathways.py +++ b/cli/refresh_pathways.py @@ -28,7 +28,7 @@ from datetime import datetime from pathlib import Path from typing import Optional -# Ensure src/ is on sys.path when run as `python -m cli.refresh_pathways` +# Ensure project root is on sys.path when run as `python -m cli.refresh_pathways` _src_dir = str(Path(__file__).resolve().parent.parent) if _src_dir not in sys.path: sys.path.insert(0, _src_dir) diff --git a/core/resource_path.py b/core/resource_path.py index d5fbb09..1afa87a 100644 --- a/core/resource_path.py +++ b/core/resource_path.py @@ -8,11 +8,11 @@ def get_resource_path(relative_path: str) -> Path: """Return absolute path to a bundled resource. In frozen mode (PyInstaller), resolves from sys._MEIPASS. - In dev mode, resolves from the project root (3 parents up from this file: - src/core/resource_path.py → src/core → src → project root). + In dev mode, resolves from the project root (2 parents up from this file: + core/resource_path.py → core → project root). """ if getattr(sys, "frozen", False): base = Path(sys._MEIPASS) else: - base = Path(__file__).resolve().parents[2] + base = Path(__file__).resolve().parents[1] return base / relative_path diff --git a/pyproject.toml b/pyproject.toml index 405f449..b419e7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ test = [ ] [tool.pytest.ini_options] -pythonpath = ["src"] +pythonpath = ["."] testpaths = ["tests"] python_files = ["test_*.py"] python_classes = ["Test*"] @@ -43,7 +43,7 @@ markers = [ ] [tool.coverage.run] -source = ["src/core", "src/data_processing", "src/analysis", "src/visualization"] +source = ["core", "data_processing", "analysis", "visualization"] branch = true omit = [ "*/tests/*", diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d85c4f49bbc1f352ac72680cb40338e4aaf913d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 356 zcmX|+Q4Yc&5Jcy@iARx8s>Yw*!7DVCsh#~BQEkK(31e}Jr~W&4I8l*~SIM=-n{y{Rk5mJenxEjwk<=v| zGHJ`}bkanE$TO!o(g|Uv9F#99ZN!~9o=G{yGGWEOS~ru~vQ5O9uXK6zrB~8q;=Fgy lo(=~R1=gI_Xi}xVK((iu%Kn*Fh!<7#!|2~_Yt7cU%@5CCIS&8; diff --git a/run_dash.py b/run_dash.py index 77a5fbd..46fe822 100644 --- a/run_dash.py +++ b/run_dash.py @@ -2,10 +2,10 @@ import sys from pathlib import Path -# Ensure src/ is on sys.path so that core/, data_processing/, etc. are importable -_src_dir = str(Path(__file__).resolve().parent / "src") -if _src_dir not in sys.path: - sys.path.insert(0, _src_dir) +# Ensure project root is on sys.path so that core/, data_processing/, etc. are importable +_project_root = str(Path(__file__).resolve().parent) +if _project_root not in sys.path: + sys.path.insert(0, _project_root) from dash_app.app import app diff --git a/setup_dev.py b/setup_dev.py deleted file mode 100644 index 158f090..0000000 --- a/setup_dev.py +++ /dev/null @@ -1,13 +0,0 @@ -"""One-time dev setup: adds src/ to the venv's Python path via a .pth file.""" - -import site -import sys -from pathlib import Path - -src_dir = Path(__file__).resolve().parent / "src" -site_packages = Path(site.getsitepackages()[-1]) -pth_file = site_packages / "patient_pathways.pth" - -pth_file.write_text(str(src_dir) + "\n") -print(f"Created {pth_file}") -print(f" -> {src_dir}")