Skip to main content

Dash — NHS Quickstart

📊 Python · Plotly · Dash · SQL Server (optional) · NHS-styled dashboards
Why Dash in the NHS

Dash turns Python dataframes into interactive, NHS‑branded dashboards without learning React. Ideal for analyst/scientist‑led apps, prototyping operational KPIs, or sharing model outputs.

Great for: BI Analyst · Data Scientist · Clinician‑Researcher.


⚙️ 10‑minute install

python -m venv .venv && . .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install dash plotly pandas python-dotenv
# Optional SQL Server access
pip install sqlalchemy pyodbc

🚀 “Hello NHS” dashboard

Folder layout

dash-nhs/
.env
app.py
assets/nhs.css # optional branding (see Styling below)

.env

# Optional if using SQL Server
SQLSERVER_SERVER=YOURSERVER
SQLSERVER_DATABASE=NHS_Analytics

app.py (pick a data source below)

app.py
import dash
from dash import html, dcc, Input, Output
import plotly.express as px
import pandas as pd

# Example small dataset created on first run
try:
df = pd.read_parquet("out/kpi.parquet")
except Exception:
df = pd.DataFrame({
"practice_id": ["A","B","C","A","B","C"],
"total_appointments": [120, 95, 60, 130, 100, 70],
"month": ["2025-06","2025-06","2025-06","2025-07","2025-07","2025-07"]
})

app = dash.Dash(__name__)
app.title = "NHS KPI (Demo)"

app.layout = html.Div([
html.H2("NHS KPI Dashboard"),
dcc.Dropdown(sorted(df["month"].unique()), df["month"].iloc[0], id="month"),
dcc.Graph(id="chart")
], style={"maxWidth":"900px","margin":"40px auto"})

@app.callback(Output("chart","figure"), Input("month","value"))
def update(month):
d = df[df["month"]==month]
return px.bar(d, x="practice_id", y="total_appointments",
title=f"Appointments by Practice — {month}",
labels={"total_appointments":"Appointments","practice_id":"Practice"})

server = app.server # for production WSGI servers
if __name__ == "__main__":
app.run_server(debug=True)

Run

python app.py
# open http://127.0.0.1:8050

🎨 Styling (NHS brand)

Place a CSS file in assets/nhs.css (Dash auto-loads from assets/). For example:

assets/nhs.css
body { font-family: "Segoe UI", Roboto, Arial, sans-serif; }
h2 { color: #005EB8; } /* NHS blue */
.card { border: 1px solid #D8DDE0; border-radius: 8px; padding: 12px; }

For full NHS.UK styling, you can vend your own minimal CSS tokens or embed the NHS Frontend CSS compiled by your web team and include it as assets/nhs-frontend.css.


🧪 Health check (production)

Expose a simple health endpoint using the underlying Flask server:

# add after app = dash.Dash(...)
server = app.server

@server.get("/health")
def health():
return {"status":"ok"}

🐳 Containerise (production)

Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir dash plotly pandas python-dotenv gunicorn
ENV PORT=8050
EXPOSE 8050
CMD ["gunicorn","-w","2","-b","0.0.0.0:8050","app:server"]

Deploy to AWS App Runner or Azure App Service / Container Apps (see platform pages).


🔒 NHS‑ready patterns

  • Secrets: keep DB creds out of code; use env vars and a secret store (Key Vault / Secrets Manager).
  • Caching: cache heavy queries to Parquet on a schedule; refresh via a CI job or ETL.
  • RBAC: put Dash behind a reverse proxy with Entra ID/NHS Login; restrict by group.
  • Small-number suppression: apply in SQL views or in export steps before display.
  • Logging: avoid PHI; add request IDs and basic usage metrics.

🗓️ Week‑one build (repeatable, safe)

  • Day 1 — Data contract: one authoritative view per KPI; document definitions.
  • Day 2 — Automation: scheduled extract → Parquet; write to read-only share.
  • Day 3 — Dashboard: dropdown + trend + “data last updated”; definitions popovers.
  • Day 4 — Versioning: repo with app + SQL; PR reviews; simple CI.
  • Day 5 — IG checks: secrets via store; small-number suppression; synthetic dev data.

📏 Measuring impact

  • Latency: source load → dashboard refresh time.
  • Reliability: % successful refreshes; uptime.
  • Adoption: weekly active users; stakeholders using the dashboard.
  • Quality: validation pass rate; number of rework cycles.

🔗 See also

What’s next?

You’ve completed the Learn — Dash stage. Keep momentum: