docs: update all documentation for Dash migration (Phase 6)
Rewrote README.md, USER_GUIDE.md, and DEPLOYMENT.md to reflect the Dash application. Updated RALPH_PROMPT.md, guardrails.md, and DESIGN_SYSTEM.md to remove Reflex references. All non-archive documentation now reflects the current Dash + DMC architecture.
This commit is contained in:
+89
-289
@@ -1,10 +1,10 @@
|
||||
# Reflex Deployment Guide
|
||||
# Deployment Guide
|
||||
|
||||
This guide covers deployment options for the Patient Pathway Analysis web application built with Reflex.
|
||||
This guide covers deployment options for the Patient Pathway Analysis web application built with Dash.
|
||||
|
||||
## Overview
|
||||
|
||||
Reflex applications compile to a FastAPI backend and Next.js frontend. This creates two deployment artifacts that can be deployed together or separately depending on your infrastructure requirements.
|
||||
The application is a single-process Python Dash app that serves both the frontend and API from one server. It reads pre-computed data from a local SQLite database.
|
||||
|
||||
## Development Mode
|
||||
|
||||
@@ -12,9 +12,9 @@ For local development:
|
||||
|
||||
```bash
|
||||
# Start development server with hot reload
|
||||
reflex run
|
||||
python run_dash.py
|
||||
|
||||
# Access the application at http://localhost:3000
|
||||
# Access the application at http://localhost:8050
|
||||
```
|
||||
|
||||
## Production Deployment Options
|
||||
@@ -24,84 +24,55 @@ reflex run
|
||||
The simplest approach for internal deployments:
|
||||
|
||||
```bash
|
||||
# Run in production mode (optimized build)
|
||||
reflex run --env prod
|
||||
```
|
||||
# Run with Gunicorn (Linux/macOS)
|
||||
gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4
|
||||
|
||||
This starts:
|
||||
- FastAPI backend on port 8000
|
||||
- Next.js frontend on port 3000
|
||||
# Or directly with Python
|
||||
python run_dash.py
|
||||
```
|
||||
|
||||
For background execution:
|
||||
|
||||
```bash
|
||||
# Using nohup (Linux/macOS)
|
||||
nohup reflex run --env prod > reflex.log 2>&1 &
|
||||
nohup gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4 > dash.log 2>&1 &
|
||||
|
||||
# Using PowerShell (Windows)
|
||||
Start-Process -NoNewWindow -FilePath "reflex" -ArgumentList "run --env prod"
|
||||
Start-Process -NoNewWindow -FilePath "python" -ArgumentList "run_dash.py"
|
||||
```
|
||||
|
||||
### Option 2: Separate Backend and Frontend
|
||||
|
||||
For more control, run backend and frontend separately:
|
||||
|
||||
```bash
|
||||
# Terminal 1: Start backend only
|
||||
reflex run --env prod --backend-only
|
||||
|
||||
# Terminal 2: Start frontend only
|
||||
reflex run --env prod --frontend-only
|
||||
```
|
||||
|
||||
### Option 3: Static Export
|
||||
|
||||
Export the frontend as static files for deployment on static hosting or CDN:
|
||||
|
||||
```bash
|
||||
# Export application
|
||||
reflex export
|
||||
|
||||
# This creates:
|
||||
# - frontend.zip (static Next.js build)
|
||||
# - backend.zip (Python application source)
|
||||
```
|
||||
|
||||
Then:
|
||||
1. Unzip `frontend.zip` and serve via nginx, Apache, or any static file server
|
||||
2. Run the backend separately using uvicorn/gunicorn
|
||||
|
||||
### Option 4: Docker Deployment
|
||||
### Option 2: Docker Deployment
|
||||
|
||||
Create a `Dockerfile` for containerized deployment:
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install Node.js for Reflex frontend build
|
||||
RUN apt-get update && apt-get install -y curl && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
|
||||
apt-get install -y nodejs && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
# Install uv for fast dependency management
|
||||
RUN pip install uv
|
||||
|
||||
# Copy requirements and install dependencies
|
||||
COPY requirements.txt pyproject.toml ./
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
# Copy dependency files
|
||||
COPY pyproject.toml uv.lock ./
|
||||
|
||||
# Install dependencies
|
||||
RUN uv sync --no-dev
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
COPY src/ src/
|
||||
COPY dash_app/ dash_app/
|
||||
COPY data/ data/
|
||||
COPY run_dash.py setup_dev.py ./
|
||||
|
||||
# Initialize Reflex (downloads frontend dependencies)
|
||||
RUN reflex init --loglevel debug
|
||||
# Set up Python path
|
||||
RUN uv run python setup_dev.py
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 3000 8000
|
||||
# Expose port
|
||||
EXPOSE 8050
|
||||
|
||||
# Start in production mode
|
||||
CMD ["reflex", "run", "--env", "prod"]
|
||||
# Start the application
|
||||
CMD ["uv", "run", "gunicorn", "dash_app.app:server", "-b", "0.0.0.0:8050", "--workers", "4"]
|
||||
```
|
||||
|
||||
Build and run:
|
||||
@@ -111,41 +82,24 @@ Build and run:
|
||||
docker build -t pathway-analysis .
|
||||
|
||||
# Run the container
|
||||
docker run -p 3000:3000 -p 8000:8000 \
|
||||
docker run -p 8050:8050 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
-v $(pwd)/config:/app/config \
|
||||
pathway-analysis
|
||||
```
|
||||
|
||||
### Option 5: Docker Compose (Recommended for Production)
|
||||
|
||||
Create `docker-compose.yml` for multi-container deployment:
|
||||
### Option 3: Docker Compose
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
app:
|
||||
build: .
|
||||
command: reflex run --env prod --backend-only
|
||||
ports:
|
||||
- "8000:8000"
|
||||
- "8050:8050"
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./config:/app/config
|
||||
environment:
|
||||
- REFLEX_ENV=prod
|
||||
restart: unless-stopped
|
||||
|
||||
frontend:
|
||||
build: .
|
||||
command: reflex run --env prod --frontend-only
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
- backend
|
||||
environment:
|
||||
- REFLEX_ENV=prod
|
||||
- ./src/config:/app/src/config
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
@@ -162,42 +116,16 @@ docker-compose up -d
|
||||
For production deployments behind nginx:
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/pathway-analysis
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-server.nhs.uk;
|
||||
|
||||
# Backend API endpoints
|
||||
location /admin {
|
||||
proxy_pass http://localhost:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
|
||||
location /ping {
|
||||
proxy_pass http://localhost:8000;
|
||||
}
|
||||
|
||||
location /upload {
|
||||
proxy_pass http://localhost:8000;
|
||||
client_max_body_size 100M; # For large data file uploads
|
||||
}
|
||||
|
||||
# WebSocket connections (required for Reflex state sync)
|
||||
location /_event/ {
|
||||
proxy_pass http://localhost:8000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_read_timeout 86400; # 24 hours for long-running connections
|
||||
}
|
||||
|
||||
# Frontend (all other requests)
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_pass http://localhost:8050;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -209,69 +137,21 @@ sudo ln -s /etc/nginx/sites-available/pathway-analysis /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### Caddy (Alternative)
|
||||
|
||||
Caddy provides automatic HTTPS:
|
||||
|
||||
```caddyfile
|
||||
# Caddyfile
|
||||
your-server.nhs.uk {
|
||||
# Backend API
|
||||
handle /admin/* {
|
||||
reverse_proxy localhost:8000
|
||||
}
|
||||
handle /ping {
|
||||
reverse_proxy localhost:8000
|
||||
}
|
||||
handle /upload {
|
||||
reverse_proxy localhost:8000
|
||||
}
|
||||
handle /_event/* {
|
||||
reverse_proxy localhost:8000
|
||||
}
|
||||
|
||||
# Frontend
|
||||
handle {
|
||||
reverse_proxy localhost:3000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Process Management
|
||||
|
||||
### Systemd (Linux)
|
||||
|
||||
Create service files for automatic startup:
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/pathway-backend.service
|
||||
# /etc/systemd/system/pathway-analysis.service
|
||||
[Unit]
|
||||
Description=Pathway Analysis Backend
|
||||
Description=Pathway Analysis Dash App
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/opt/pathway-analysis
|
||||
ExecStart=/usr/bin/reflex run --env prod --backend-only
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/pathway-frontend.service
|
||||
[Unit]
|
||||
Description=Pathway Analysis Frontend
|
||||
After=network.target pathway-backend.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/opt/pathway-analysis
|
||||
ExecStart=/usr/bin/reflex run --env prod --frontend-only
|
||||
ExecStart=/opt/pathway-analysis/.venv/bin/gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
@@ -283,8 +163,8 @@ Enable and start:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable pathway-backend pathway-frontend
|
||||
sudo systemctl start pathway-backend pathway-frontend
|
||||
sudo systemctl enable pathway-analysis
|
||||
sudo systemctl start pathway-analysis
|
||||
```
|
||||
|
||||
### Windows Service
|
||||
@@ -296,8 +176,8 @@ Use NSSM (Non-Sucking Service Manager) on Windows:
|
||||
choco install nssm
|
||||
|
||||
# Create service
|
||||
nssm install PathwayAnalysis "C:\Path\To\reflex.exe" "run --env prod"
|
||||
nssm set PathwayAnalysis AppDirectory "C:\Path\To\Patient pathway analysis"
|
||||
nssm install PathwayAnalysis "C:\Path\To\python.exe" "run_dash.py"
|
||||
nssm set PathwayAnalysis AppDirectory "C:\Path\To\pathway-analysis"
|
||||
nssm start PathwayAnalysis
|
||||
```
|
||||
|
||||
@@ -305,192 +185,112 @@ nssm start PathwayAnalysis
|
||||
|
||||
### Production Environment Variables
|
||||
|
||||
Set these environment variables for production:
|
||||
|
||||
```bash
|
||||
# Reflex configuration
|
||||
export REFLEX_ENV=prod
|
||||
|
||||
# Database paths (if using custom locations)
|
||||
# Database path (if using custom location)
|
||||
export PATHWAY_DB_PATH=/var/data/pathways.db
|
||||
export PATHWAY_CACHE_DIR=/var/cache/pathway-analysis
|
||||
|
||||
# Snowflake (if using)
|
||||
# Snowflake (for data refresh only — not needed for the web app)
|
||||
export SNOWFLAKE_ACCOUNT=your-account
|
||||
export SNOWFLAKE_WAREHOUSE=your-warehouse
|
||||
```
|
||||
|
||||
### Snowflake Configuration
|
||||
|
||||
Ensure `config/snowflake.toml` is properly configured for production:
|
||||
Snowflake is only needed for the data refresh CLI command, not for running the web application. Ensure `src/config/snowflake.toml` is configured:
|
||||
|
||||
```toml
|
||||
[connection]
|
||||
[snowflake]
|
||||
account = "your-production-account"
|
||||
warehouse = "ANALYTICS_WH"
|
||||
database = "DATA_HUB"
|
||||
schema = "CDM"
|
||||
authenticator = "externalbrowser" # or "oauth" for service accounts
|
||||
|
||||
[cache]
|
||||
enabled = true
|
||||
directory = "/var/cache/pathway-analysis"
|
||||
ttl_seconds = 86400 # 24 hours
|
||||
authenticator = "externalbrowser"
|
||||
```
|
||||
|
||||
## Reflex Cloud
|
||||
## Data Refresh
|
||||
|
||||
For managed hosting, consider [Reflex Cloud](https://reflex.dev/cloud/):
|
||||
The web application reads pre-computed data from SQLite. To update the data:
|
||||
|
||||
```bash
|
||||
# Deploy to Reflex Cloud
|
||||
reflex deploy
|
||||
# Full refresh (both chart types, all date filters)
|
||||
python -m cli.refresh_pathways --chart-type all
|
||||
|
||||
# The app will serve new data immediately — no restart needed
|
||||
```
|
||||
|
||||
Benefits:
|
||||
- Zero configuration deployment
|
||||
- Automatic scaling
|
||||
- Built-in SSL certificates
|
||||
- Managed state management with Redis
|
||||
Schedule this as a cron job or Windows Task Scheduler task for periodic updates.
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Network Security
|
||||
|
||||
1. **Firewall Rules**: Only expose necessary ports (typically just 80/443)
|
||||
2. **HTTPS**: Use TLS certificates (Let's Encrypt or organizational certs)
|
||||
1. **Firewall Rules**: Only expose port 8050 (or 80/443 behind reverse proxy)
|
||||
2. **HTTPS**: Use TLS certificates via reverse proxy (nginx, Caddy)
|
||||
3. **VPN**: Consider restricting access to NHS network only
|
||||
|
||||
### Data Security
|
||||
|
||||
1. **Database Access**: Ensure SQLite database permissions are restricted
|
||||
2. **File Uploads**: Validate file types and scan for malware
|
||||
3. **Snowflake**: Use least-privilege service accounts
|
||||
|
||||
### Authentication
|
||||
|
||||
For NHS deployments, consider adding authentication:
|
||||
|
||||
```python
|
||||
# Example: Add basic auth middleware
|
||||
import reflex as rx
|
||||
from starlette.middleware import Middleware
|
||||
from starlette.middleware.authentication import AuthenticationMiddleware
|
||||
|
||||
# In rxconfig.py
|
||||
config = rx.Config(
|
||||
app_name="pathways_app",
|
||||
# Add authentication middleware
|
||||
)
|
||||
```
|
||||
1. **Database Access**: The app uses read-only SQLite access
|
||||
2. **No file uploads**: The Dash app does not accept file uploads
|
||||
3. **No authentication built in**: Add authentication via reverse proxy or middleware if needed
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Health Checks
|
||||
|
||||
The application provides endpoints for monitoring:
|
||||
|
||||
- `/ping` - Basic health check
|
||||
- Backend port 8000 - FastAPI health
|
||||
The application serves at `/` — a 200 response indicates the app is running.
|
||||
|
||||
### Logging
|
||||
|
||||
Configure logging for production:
|
||||
Dash outputs request logs to stdout. Configure log aggregation as needed:
|
||||
|
||||
```python
|
||||
# In pathways_app/pathways_app.py
|
||||
import logging
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('/var/log/pathway-analysis/app.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
```bash
|
||||
# Redirect logs to file
|
||||
gunicorn dash_app.app:server -b 0.0.0.0:8050 --access-logfile /var/log/pathway-analysis/access.log --error-logfile /var/log/pathway-analysis/error.log
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
### Port already in use
|
||||
|
||||
**Port already in use:**
|
||||
```bash
|
||||
# Find and kill process using port 3000
|
||||
lsof -i :3000
|
||||
kill -9 <PID>
|
||||
# Find process using port 8050
|
||||
lsof -i :8050 # Linux/macOS
|
||||
netstat -ano | findstr :8050 # Windows
|
||||
```
|
||||
|
||||
**Build cache issues:**
|
||||
```bash
|
||||
# Clear Reflex build cache
|
||||
rm -rf .web
|
||||
reflex run --env prod
|
||||
```
|
||||
### Database not found
|
||||
|
||||
**Database connection errors:**
|
||||
```bash
|
||||
# Verify database exists and has correct permissions
|
||||
# Verify database exists
|
||||
ls -la data/pathways.db
|
||||
sqlite3 data/pathways.db ".tables"
|
||||
|
||||
# Recreate if needed
|
||||
python -m data_processing.migrate
|
||||
python -m cli.refresh_pathways --chart-type all
|
||||
```
|
||||
|
||||
**Snowflake authentication:**
|
||||
- Ensure browser is available for SSO popup
|
||||
- Check firewall allows connections to Snowflake endpoints
|
||||
- Verify account identifier is correct
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### Backend (FastAPI/Uvicorn)
|
||||
|
||||
For high-traffic deployments:
|
||||
### Import errors
|
||||
|
||||
```bash
|
||||
# Run with multiple workers
|
||||
uvicorn pathways_app:app --workers 4 --host 0.0.0.0 --port 8000
|
||||
```
|
||||
# Ensure src/ is on Python path
|
||||
uv run python setup_dev.py
|
||||
|
||||
### State Management
|
||||
|
||||
For multi-instance deployments, configure Redis for state management:
|
||||
|
||||
```python
|
||||
# rxconfig.py
|
||||
config = rx.Config(
|
||||
app_name="pathways_app",
|
||||
state_manager_mode="redis",
|
||||
redis_url="redis://localhost:6379/0",
|
||||
)
|
||||
```
|
||||
|
||||
### Caching
|
||||
|
||||
Enable aggressive caching for Snowflake queries in `config/snowflake.toml`:
|
||||
|
||||
```toml
|
||||
[cache]
|
||||
enabled = true
|
||||
ttl_seconds = 86400 # 24 hours for historical data
|
||||
ttl_current_data_seconds = 3600 # 1 hour for recent data
|
||||
max_size_mb = 1000 # 1GB cache
|
||||
# Verify imports
|
||||
uv run python -c "from dash_app.app import app; print('OK')"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Environment | Command | Ports |
|
||||
|-------------|---------|-------|
|
||||
| Development | `reflex run` | 3000, 8000 |
|
||||
| Production | `reflex run --env prod` | 3000, 8000 |
|
||||
| Backend only | `reflex run --backend-only` | 8000 |
|
||||
| Frontend only | `reflex run --frontend-only` | 3000 |
|
||||
| Export | `reflex export` | Static files |
|
||||
| Cloud | `reflex deploy` | Managed |
|
||||
| Environment | Command | Port |
|
||||
|-------------|---------|------|
|
||||
| Development | `python run_dash.py` | 8050 |
|
||||
| Production | `gunicorn dash_app.app:server -b 0.0.0.0:8050 --workers 4` | 8050 |
|
||||
| Docker | `docker run -p 8050:8050 pathway-analysis` | 8050 |
|
||||
|
||||
For more information, see:
|
||||
- [Reflex Documentation](https://reflex.dev/docs/)
|
||||
- [Reflex Cloud](https://reflex.dev/cloud/)
|
||||
- [FastAPI Deployment](https://fastapi.tiangolo.com/deployment/)
|
||||
- [Dash Documentation](https://dash.plotly.com/)
|
||||
- [Gunicorn Deployment](https://docs.gunicorn.org/en/stable/deploy.html)
|
||||
|
||||
@@ -187,8 +187,8 @@ All transitions: 150ms ease-out (faster than before)
|
||||
}
|
||||
```
|
||||
|
||||
### Reflex Implementation
|
||||
- Use `height="calc(100vh - 96px)"` for chart container
|
||||
- Use `width="100%"` with `padding_x="16px"` for full-width
|
||||
- Use `flex="1"` to let chart grow
|
||||
- Keep `min_height="500px"` as fallback
|
||||
### Dash Implementation
|
||||
- Chart container uses `dcc.Loading` wrapper around `dcc.Graph`
|
||||
- Full-width layout via CSS class `.chart-card` in `dash_app/assets/nhs.css`
|
||||
- Minimum height set via CSS: `min-height: 500px`
|
||||
- Margins controlled in `create_icicle_from_nodes()`: `t:40, l:8, r:8, b:24`
|
||||
|
||||
+145
-291
@@ -6,15 +6,11 @@ This guide explains how to use the NHS High-Cost Drug Patient Pathway Analysis T
|
||||
|
||||
1. [Getting Started](#getting-started)
|
||||
2. [Interface Overview](#interface-overview)
|
||||
3. [Selecting Your Data Source](#selecting-your-data-source)
|
||||
4. [Configuring Analysis Filters](#configuring-analysis-filters)
|
||||
5. [Selecting Drugs, Trusts, and Directories](#selecting-drugs-trusts-and-directories)
|
||||
6. [Running the Analysis](#running-the-analysis)
|
||||
7. [Understanding the Pathway Chart](#understanding-the-pathway-chart)
|
||||
8. [Exporting Results](#exporting-results)
|
||||
9. [GP Indication Validation](#gp-indication-validation)
|
||||
10. [Keyboard Navigation and Accessibility](#keyboard-navigation-and-accessibility)
|
||||
11. [Troubleshooting](#troubleshooting)
|
||||
3. [Filtering Data](#filtering-data)
|
||||
4. [Using the Drug Browser](#using-the-drug-browser)
|
||||
5. [Understanding the Pathway Chart](#understanding-the-pathway-chart)
|
||||
6. [GP Indication Matching](#gp-indication-matching)
|
||||
7. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
@@ -25,371 +21,229 @@ This guide explains how to use the NHS High-Cost Drug Patient Pathway Analysis T
|
||||
Start the application by running:
|
||||
|
||||
```bash
|
||||
reflex run
|
||||
python run_dash.py
|
||||
```
|
||||
|
||||
Then open your browser to **http://localhost:3000**
|
||||
Then open your browser to **http://localhost:8050**
|
||||
|
||||
The application will automatically load reference data (drugs, trusts, directories) when you first access it.
|
||||
The application automatically loads pre-computed pathway data from SQLite on startup. No additional setup is needed to view existing data.
|
||||
|
||||
### First-Time Setup
|
||||
### Data Freshness
|
||||
|
||||
1. Click **Load Reference Data** on the Home page to populate the filter options
|
||||
2. Select your preferred data source (SQLite, File Upload, or Snowflake)
|
||||
3. Configure your date range and other filters
|
||||
4. Click **Run Analysis** to generate your first pathway chart
|
||||
The header bar shows when data was last refreshed:
|
||||
- **Patient count**: Total patients in the dataset (e.g., "11,118 patients")
|
||||
- **Last updated**: Relative time since the last data refresh (e.g., "2h ago")
|
||||
|
||||
To refresh the data, run the CLI command (requires Snowflake access):
|
||||
|
||||
```bash
|
||||
python -m cli.refresh_pathways --chart-type all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Interface Overview
|
||||
|
||||
The application has four main pages, accessible from the sidebar navigation:
|
||||
The application is a single-page layout with the following components:
|
||||
|
||||
| Page | Purpose |
|
||||
|------|---------|
|
||||
| **Home** | Main analysis dashboard with data source selection, filters, and chart display |
|
||||
| **Drug Selection** | Select which high-cost drugs to include in the analysis |
|
||||
| **Trust Selection** | Filter by specific NHS trusts |
|
||||
| **Directory Selection** | Filter by medical directories/specialties |
|
||||
### Header
|
||||
- NHS branding and application title ("HCD Analysis")
|
||||
- Green status dot with patient count and last-updated time
|
||||
|
||||
### Navigation
|
||||
### Sidebar (Left)
|
||||
Navigation items including:
|
||||
- **Pathway Overview** — main view (always active)
|
||||
- **Drug Selection** — opens the drug browser drawer
|
||||
- **Trust Selection** — opens the drawer with trust chips
|
||||
- **Indications** — opens the drawer with directorate browser
|
||||
|
||||
- **Desktop**: Use the sidebar on the left to switch between pages
|
||||
- **Mobile**: Use the top navigation bar
|
||||
- **Keyboard**: Press Tab to navigate, Enter to select
|
||||
### KPI Row
|
||||
Four summary cards that update dynamically:
|
||||
- **Unique Patients** — number of distinct patients matching current filters
|
||||
- **Drug Types** — number of distinct drugs in filtered data
|
||||
- **Total Cost** — total cost of treatments in the filtered dataset
|
||||
- **Indication Match** — GP diagnosis match rate (~93% for indication charts, shown as "—" for directory charts)
|
||||
|
||||
### Filter Bar
|
||||
- **Chart type toggle**: "By Directory" / "By Indication" pills
|
||||
- **Treatment Initiated**: All years, Last 2 years, or Last 1 year
|
||||
- **Last Seen**: Last 6 months or Last 12 months
|
||||
|
||||
### Chart Card
|
||||
- Dynamic subtitle showing the current hierarchy (e.g., "Trust → Directorate → Drug → Pathway")
|
||||
- Interactive Plotly icicle chart
|
||||
- Loading spinner during data fetch
|
||||
|
||||
---
|
||||
|
||||
## Selecting Your Data Source
|
||||
## Filtering Data
|
||||
|
||||
The application supports three data sources:
|
||||
### Chart Type
|
||||
|
||||
### 1. SQLite Database (Recommended)
|
||||
Toggle between two views using the pills in the filter bar:
|
||||
|
||||
Pre-loaded patient data stored locally for fast performance.
|
||||
| View | Hierarchy | Best For |
|
||||
|------|-----------|----------|
|
||||
| **By Directory** | Trust → Directorate → Drug → Pathway | Understanding treatment by medical specialty |
|
||||
| **By Indication** | Trust → GP Diagnosis → Drug → Pathway | Understanding treatment by patient condition |
|
||||
|
||||
**Advantages:**
|
||||
- Fastest analysis performance
|
||||
- Works offline
|
||||
- No authentication required
|
||||
### Date Filters
|
||||
|
||||
**To use:** Click "Use SQLite" in the Data Source section
|
||||
Two dropdowns control the time window:
|
||||
|
||||
### 2. File Upload
|
||||
| Filter | Options | Effect |
|
||||
|--------|---------|--------|
|
||||
| **Treatment Initiated** | All years, Last 2 years, Last 1 year | When patients started treatment |
|
||||
| **Last Seen** | Last 6 months, Last 12 months | Most recent activity window |
|
||||
|
||||
Upload CSV or Parquet files directly.
|
||||
The default is "All years / Last 6 months" — showing all patients who have been active in the last 6 months.
|
||||
|
||||
**Supported formats:**
|
||||
- CSV files (.csv)
|
||||
- Apache Parquet files (.parquet, .pq)
|
||||
### Drug and Trust Selection
|
||||
|
||||
**To use:**
|
||||
1. Drag and drop a file, or click the upload area
|
||||
2. Wait for the file to process
|
||||
3. Click "Use File" to select it as your data source
|
||||
Open the drawer (right panel) by clicking "Drug Selection" or "Trust Selection" in the sidebar:
|
||||
|
||||
### 3. Snowflake
|
||||
- **Drug chips**: Click to select/deselect specific drugs. Selected drugs filter the chart.
|
||||
- **Trust chips**: Click to select/deselect specific NHS trusts.
|
||||
- **Clear All Filters**: Button at the bottom resets all drug and trust selections.
|
||||
|
||||
Query live data from the NHS data warehouse.
|
||||
|
||||
**Requirements:**
|
||||
- Snowflake must be configured (see `config/snowflake.toml`)
|
||||
- Browser-based NHS SSO authentication
|
||||
|
||||
**To use:** Click "Use Snowflake" - you'll be prompted to authenticate via your browser
|
||||
**No selections = show everything.** Leaving chips unselected is the same as selecting all.
|
||||
|
||||
---
|
||||
|
||||
## Configuring Analysis Filters
|
||||
## Using the Drug Browser
|
||||
|
||||
The Home page provides several filter options:
|
||||
The drawer contains three sections:
|
||||
|
||||
### Date Range
|
||||
### All Drugs
|
||||
A flat list of all 42 available drugs as selectable chips. Click one or more to filter the chart to those drugs only.
|
||||
|
||||
| Field | Description |
|
||||
|-------|-------------|
|
||||
| **Start Date** | Include patients initiated from this date onwards |
|
||||
| **End Date** | Include patients initiated until this date |
|
||||
| **Last Seen After** | Only include patients with activity after this date (excludes patients who haven't been seen recently) |
|
||||
### Trusts
|
||||
A list of 7 NHS trusts as selectable chips. Click to filter by specific organizations.
|
||||
|
||||
**Tip:** The default range is the last 12 months.
|
||||
### By Directorate
|
||||
An accordion browser organized by clinical directorate:
|
||||
|
||||
### Minimum Patients
|
||||
1. Click a **directorate** (e.g., "CARDIOLOGY") to expand it
|
||||
2. Inside, click an **indication** (e.g., "heart failure") to expand further
|
||||
3. Each indication shows **drug fragment badges** (e.g., "SACUBITRIL", "IVABRADINE")
|
||||
4. Clicking a drug fragment badge selects all full drug names that contain that fragment
|
||||
|
||||
Filter out pathways with fewer patients than the threshold you set.
|
||||
For example, clicking the "ADALIMUMAB" badge would select "ADALIMUMAB" in the drug chips above.
|
||||
|
||||
- Use the slider for quick adjustment (0-100)
|
||||
- Or type a specific number in the text field
|
||||
- Set to 0 to show all pathways regardless of patient count
|
||||
### Fragment Matching
|
||||
|
||||
### Custom Title
|
||||
Drug fragments are substrings, not exact matches. The fragment "INHALED" would match drugs like "INHALED BECLOMETASONE" and "INHALED FLUTICASONE".
|
||||
|
||||
Override the automatically generated chart title with your own text.
|
||||
|
||||
- Leave empty to use the default title: "Patients initiated [start date] to [end date]"
|
||||
- Useful for specific reports or presentations
|
||||
|
||||
---
|
||||
|
||||
## Selecting Drugs, Trusts, and Directories
|
||||
|
||||
Each selection page works the same way:
|
||||
|
||||
### Navigation
|
||||
|
||||
1. Click "Drug Selection", "Trust Selection", or "Directory Selection" in the sidebar
|
||||
2. The page shows all available options with checkboxes
|
||||
|
||||
### Search
|
||||
|
||||
Type in the search box to filter the list. The list updates as you type.
|
||||
|
||||
### Selection Actions
|
||||
|
||||
| Button | Action |
|
||||
|--------|--------|
|
||||
| **Select All** | Check all visible items |
|
||||
| **Clear All** | Uncheck all items |
|
||||
| **Select Defaults** | (Drugs only) Select pre-configured default drugs (Include=1 in include.csv) |
|
||||
|
||||
### Selection Behavior
|
||||
|
||||
- **No items selected** = Include ALL items in analysis
|
||||
- **Some items selected** = Include ONLY the selected items
|
||||
|
||||
This means leaving a filter empty is equivalent to "select all".
|
||||
|
||||
---
|
||||
|
||||
## Running the Analysis
|
||||
|
||||
### Steps
|
||||
|
||||
1. Ensure your data source is selected and configured
|
||||
2. Set your date range and other filters
|
||||
3. Select desired drugs, trusts, and directories (or leave empty for all)
|
||||
4. Click the green **Run Analysis** button
|
||||
|
||||
### During Analysis
|
||||
|
||||
- The button shows a spinner while analysis is running
|
||||
- Status messages appear below the button
|
||||
- The interface remains responsive - you can review settings
|
||||
|
||||
### After Analysis
|
||||
|
||||
- The pathway chart appears in the chart section
|
||||
- Export buttons become available
|
||||
- GP indication validation results appear (if Snowflake is connected)
|
||||
Clicking a fragment toggles its matching drugs:
|
||||
- **First click**: Selects all matching drugs
|
||||
- **Second click**: Deselects all matching drugs (if all were already selected)
|
||||
|
||||
---
|
||||
|
||||
## Understanding the Pathway Chart
|
||||
|
||||
The analysis generates an interactive **icicle chart** showing patient treatment pathways.
|
||||
|
||||
### Hierarchy Structure
|
||||
|
||||
The chart displays a hierarchical structure:
|
||||
The icicle chart displays a hierarchical breakdown:
|
||||
|
||||
**Directory view:**
|
||||
```
|
||||
N&WICS (Regional Total)
|
||||
└─ Trust Name (e.g., "Norfolk and Norwich University Hospitals")
|
||||
└─ Directory (e.g., "Rheumatology", "Gastroenterology")
|
||||
└─ Drug Name (e.g., "ADALIMUMAB", "INFLIXIMAB")
|
||||
Root (Regional Total)
|
||||
└─ Trust (e.g., "Norfolk and Norwich University Hospitals")
|
||||
└─ Directorate (e.g., "RHEUMATOLOGY")
|
||||
└─ Drug (e.g., "ADALIMUMAB")
|
||||
└─ Pathway (e.g., "ADALIMUMAB → INFLIXIMAB")
|
||||
```
|
||||
|
||||
**Indication view:**
|
||||
```
|
||||
Root (Regional Total)
|
||||
└─ Trust
|
||||
└─ GP Diagnosis (e.g., "rheumatoid arthritis")
|
||||
└─ Drug
|
||||
└─ Pathway
|
||||
```
|
||||
|
||||
### Reading the Chart
|
||||
|
||||
- **Width** of each section indicates relative patient count
|
||||
- **Color intensity** indicates proportion of patients at that level
|
||||
- **Labels** show the category name and patient count
|
||||
- **Color intensity** (NHS blue gradient) indicates proportion of parent group
|
||||
- **Labels** show the name and patient count
|
||||
|
||||
### Interacting with the Chart
|
||||
|
||||
| Action | Effect |
|
||||
|--------|--------|
|
||||
| **Click** a section | Zoom in to show details for that branch |
|
||||
| **Click** the root | Zoom out to show full hierarchy |
|
||||
| **Hover** over a section | See tooltip with patient count |
|
||||
| Use the **toolbar** | Reset, download image, pan, zoom |
|
||||
| **Click** the parent/root | Zoom back out |
|
||||
| **Hover** over a section | See tooltip with patient count, cost, dosing frequency, dates |
|
||||
|
||||
### Plotly Toolbar
|
||||
### Hover Tooltip Information
|
||||
|
||||
The chart includes a Plotly toolbar (top right) with:
|
||||
|
||||
- **Download as PNG** - Save static image
|
||||
- **Zoom controls** - Zoom in/out
|
||||
- **Pan** - Click and drag to move
|
||||
- **Reset** - Return to original view
|
||||
When hovering over a chart section, you'll see:
|
||||
- Patient count and percentage of parent
|
||||
- Total cost and cost per patient
|
||||
- First and last seen dates
|
||||
- Treatment dosing frequency (for drug nodes)
|
||||
- Cost per patient per annum
|
||||
|
||||
---
|
||||
|
||||
## Exporting Results
|
||||
## GP Indication Matching
|
||||
|
||||
Two export options are available after running an analysis:
|
||||
When viewing "By Indication" charts, the application uses pre-computed GP diagnosis matches:
|
||||
|
||||
### Export HTML
|
||||
### How It Works
|
||||
|
||||
Creates an interactive HTML file that can be opened in any browser.
|
||||
1. During data refresh, each patient's NHS pseudonym is queried against GP primary care records
|
||||
2. SNOMED cluster codes map clinical conditions to drug indications
|
||||
3. The most recent GP diagnosis match is used for each patient
|
||||
4. ~93% of patients are matched to a GP diagnosis
|
||||
|
||||
- **Output**: `data/exports/pathway_chart_[timestamp].html`
|
||||
- **Use case**: Sharing interactive charts via email or file share
|
||||
- **Features**: Full interactivity, no software required to view
|
||||
### Unmatched Patients
|
||||
|
||||
### Export CSV
|
||||
Patients without a GP diagnosis match appear under their directorate with a "(no GP dx)" suffix (e.g., "RHEUMATOLOGY (no GP dx)").
|
||||
|
||||
Exports the underlying data as a spreadsheet.
|
||||
|
||||
- **Output**: `data/exports/pathway_data_[timestamp].csv`
|
||||
- **Use case**: Further analysis in Excel, importing to other tools
|
||||
- **Includes**: Patient IDs, drugs, dates, costs, directories, indication validation status
|
||||
|
||||
### Export Location
|
||||
|
||||
All exports are saved to the `data/exports/` directory with timestamped filenames to prevent overwriting.
|
||||
|
||||
---
|
||||
|
||||
## GP Indication Validation
|
||||
|
||||
When connected to Snowflake, the application validates whether patients have appropriate GP diagnoses for their prescribed drugs.
|
||||
|
||||
### What It Does
|
||||
|
||||
1. Looks up the drug's licensed indications (e.g., ADALIMUMAB for rheumatoid arthritis)
|
||||
2. Finds corresponding SNOMED codes for those indications
|
||||
3. Checks each patient's GP records for matching diagnoses
|
||||
4. Reports the match rate per drug
|
||||
|
||||
### Understanding Results
|
||||
|
||||
After analysis, a table shows:
|
||||
|
||||
| Column | Meaning |
|
||||
|--------|---------|
|
||||
| **Drug Name** | The high-cost drug |
|
||||
| **Total Patients** | Number of patients prescribed this drug |
|
||||
| **With GP Indication** | Patients with matching GP diagnosis |
|
||||
| **Match Rate** | Percentage with valid indication |
|
||||
|
||||
### Match Rate Interpretation
|
||||
|
||||
| Rate | Meaning | Color |
|
||||
|------|---------|-------|
|
||||
| **80%+** | Good coverage - most patients have GP diagnoses | Green |
|
||||
| **50-79%** | Moderate coverage - investigate missing cases | Orange |
|
||||
| **<50%** | Low coverage - may indicate data quality issues or off-label use | Red |
|
||||
|
||||
### Why Rates May Be Low
|
||||
|
||||
Low match rates don't necessarily indicate problems:
|
||||
|
||||
- **Cross-provider treatment**: Patient's GP is outside the data coverage
|
||||
- **Recent diagnoses**: Diagnosis not yet recorded in GP system
|
||||
- **Specialist-only conditions**: Some conditions are only managed in secondary care
|
||||
- **Off-label prescribing**: Legitimate use for indications not in the mapping
|
||||
|
||||
### Enabling/Disabling
|
||||
|
||||
Indication validation is enabled by default when Snowflake is connected. It requires:
|
||||
- Active Snowflake connection
|
||||
- Drug-to-cluster mappings in the database
|
||||
|
||||
---
|
||||
|
||||
## Keyboard Navigation and Accessibility
|
||||
|
||||
The application is designed to be accessible:
|
||||
|
||||
### Skip Link
|
||||
|
||||
Press **Tab** when the page loads to reveal a "Skip to main content" link that bypasses navigation.
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| **Tab** | Move to next interactive element |
|
||||
| **Shift+Tab** | Move to previous element |
|
||||
| **Enter** | Activate buttons, links, checkboxes |
|
||||
| **Space** | Toggle checkboxes |
|
||||
| **Arrow keys** | Adjust sliders |
|
||||
|
||||
### Screen Reader Support
|
||||
|
||||
- All buttons and inputs have descriptive labels
|
||||
- Status messages announce via ARIA live regions
|
||||
- Charts include figure descriptions
|
||||
|
||||
### Theme Toggle
|
||||
|
||||
A dark/light mode toggle is available at the bottom of the sidebar for visual preference.
|
||||
Reasons for unmatched patients:
|
||||
- GP is outside the data coverage area
|
||||
- Diagnosis not yet recorded in GP system
|
||||
- Condition managed only in secondary care
|
||||
- Off-label prescribing
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "No data available" Error
|
||||
### No data showing
|
||||
|
||||
**Cause**: No data matches your current filter settings
|
||||
1. Check the filter bar — are filters too restrictive?
|
||||
2. Try clearing all drug/trust selections in the drawer
|
||||
3. Widen the date range (e.g., "All years / Last 12 months")
|
||||
|
||||
**Solutions:**
|
||||
1. Check your date range - is it too narrow?
|
||||
2. Verify your data source has data loaded
|
||||
3. Check if selected trusts/drugs have any matching records
|
||||
4. Try clearing all selections (to include everything)
|
||||
### Chart shows "No matching pathways found"
|
||||
|
||||
### Chart Not Displaying
|
||||
The current filter combination matches zero patients. Adjust filters or click "Clear All Filters" in the drawer.
|
||||
|
||||
**Cause**: Analysis completed but no data met the minimum patients threshold
|
||||
### App won't start
|
||||
|
||||
**Solutions:**
|
||||
1. Lower the minimum patients threshold
|
||||
2. Expand your date range
|
||||
3. Select more drugs or trusts
|
||||
```bash
|
||||
# Ensure dependencies are installed
|
||||
uv sync
|
||||
|
||||
### Snowflake Connection Failed
|
||||
# Ensure src/ is on Python path
|
||||
uv run python setup_dev.py
|
||||
|
||||
**Cause**: Unable to connect to Snowflake
|
||||
# Run with uv
|
||||
uv run python run_dash.py
|
||||
```
|
||||
|
||||
**Solutions:**
|
||||
1. Check that `config/snowflake.toml` exists and is configured
|
||||
2. Complete browser authentication when prompted
|
||||
3. Verify your network allows Snowflake connections
|
||||
4. Try using SQLite as an alternative data source
|
||||
### Stale data
|
||||
|
||||
### File Upload Failed
|
||||
Data is as fresh as the last CLI refresh. Check the header's "Last updated" indicator. To refresh:
|
||||
|
||||
**Cause**: File format or content issue
|
||||
|
||||
**Solutions:**
|
||||
1. Ensure file is CSV or Parquet format
|
||||
2. Check file isn't corrupted or empty
|
||||
3. Verify file contains required columns
|
||||
4. Try a smaller file to test
|
||||
|
||||
### Slow Performance
|
||||
|
||||
**Cause**: Large data volume or complex filtering
|
||||
|
||||
**Solutions:**
|
||||
1. Use SQLite instead of file upload for large datasets
|
||||
2. Narrow your date range
|
||||
3. Select fewer drugs/trusts to analyze
|
||||
4. Increase minimum patients threshold to reduce chart complexity
|
||||
|
||||
### Reference Data Not Loading
|
||||
|
||||
**Cause**: Missing or corrupted reference files
|
||||
|
||||
**Solutions:**
|
||||
1. Click "Load Reference Data" to retry
|
||||
2. Check that `data/` directory contains required CSV files:
|
||||
- `include.csv`
|
||||
- `defaultTrusts.csv`
|
||||
- `directory_list.csv`
|
||||
3. Verify files aren't empty or malformed
|
||||
```bash
|
||||
python -m cli.refresh_pathways --chart-type all
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
@@ -397,7 +251,7 @@ A dark/light mode toggle is available at the bottom of the sidebar for visual pr
|
||||
|
||||
If you encounter issues not covered in this guide:
|
||||
|
||||
1. Check the [README](../README.md) for installation and setup information
|
||||
1. Check the [README](../README.md) for installation and setup
|
||||
2. Review [DEPLOYMENT.md](./DEPLOYMENT.md) for server configuration
|
||||
3. Consult [CLAUDE.md](../CLAUDE.md) for technical architecture details
|
||||
4. Contact your local support team for NHS-specific questions
|
||||
4. Contact the Medicines Intelligence team for NHS-specific questions
|
||||
|
||||
Reference in New Issue
Block a user