Files
HighCostDrugsDemo/dash_app/callbacks/trust_comparison.py
T
Andrew Charlwood 10739ca84d feat: Trust Comparison landing page + directorate selector (Task 10.7)
- Add get_directorate_summary() query for per-directorate patient/drug counts
- Create trust_comparison.py with landing grid and 6-chart dashboard layout
- Wire directorate card clicks and back button through app-state callbacks
- Add TC landing and dashboard CSS per Phase 10 design spec
- Placeholder charts for 6 dashboard graphs (filled in Task 10.8)
- Chart type toggle clears selected directorate when switching modes
2026-02-06 22:15:10 +00:00

132 lines
4.8 KiB
Python

"""Callbacks for Trust Comparison landing page and dashboard navigation."""
from dash import html, Input, Output, State, ctx, no_update
import plotly.graph_objects as go
def register_trust_comparison_callbacks(app):
"""Register Trust Comparison view callbacks."""
@app.callback(
Output("tc-landing-grid", "children"),
Output("tc-landing-grid", "className"),
Output("tc-landing-desc", "children"),
Input("app-state", "data"),
)
def populate_landing_grid(app_state):
"""Populate the landing page grid with directorate/indication cards."""
if not app_state:
return [], "tc-landing__grid", "Select a directorate to compare drug usage across trusts."
from dash_app.data.queries import get_directorate_summary
chart_type = app_state.get("chart_type", "directory")
date_filter_id = app_state.get("date_filter_id", "all_6mo")
summaries = get_directorate_summary(date_filter_id, chart_type)
# Build card buttons
cards = []
for item in summaries:
name = item["name"]
patients = item["patients"]
drugs = item["drugs"]
cards.append(
html.Button(
className="tc-card",
id={"type": "tc-selector", "index": name},
n_clicks=0,
children=[
html.Div(name, className="tc-card__name"),
html.Div(
className="tc-card__stats",
children=[
html.Span(
f"{patients:,} patients",
className="tc-card__stat",
),
html.Span("\u00b7", className="tc-card__dot"),
html.Span(
f"{drugs} drugs",
className="tc-card__stat",
),
],
),
],
)
)
# Grid class: wider for indication mode (more items)
grid_cls = "tc-landing__grid"
if chart_type == "indication":
grid_cls += " tc-landing__grid--wide"
# Description text adapts to chart type
if chart_type == "indication":
desc = "Select an indication to compare drug usage across trusts."
else:
desc = "Select a directorate to compare drug usage across trusts."
return cards, grid_cls, desc
@app.callback(
Output("trust-comparison-landing", "style"),
Output("trust-comparison-dashboard", "style"),
Output("tc-dashboard-title", "children"),
Input("app-state", "data"),
)
def toggle_tc_subviews(app_state):
"""Toggle between landing page and 6-chart dashboard."""
if not app_state:
return {}, {"display": "none"}, ""
selected = app_state.get("selected_comparison_directorate")
show = {}
hide = {"display": "none"}
if selected:
chart_type = app_state.get("chart_type", "directory")
label = "Indication" if chart_type == "indication" else "Directorate"
title = f"{selected} \u2014 Trust Comparison"
return hide, show, title
else:
return show, hide, ""
# Dashboard chart rendering will be added in Task 10.8.
# For now, register empty figure placeholders for the 6 chart IDs
# so the dcc.Graph components don't error on load.
_tc_chart_ids = [
"tc-chart-market-share",
"tc-chart-cost-waterfall",
"tc-chart-dosing",
"tc-chart-heatmap",
"tc-chart-duration",
"tc-chart-cost-effectiveness",
]
for chart_id in _tc_chart_ids:
@app.callback(
Output(chart_id, "figure"),
Input("app-state", "data"),
prevent_initial_call=True,
)
def _placeholder_chart(app_state, _cid=chart_id):
"""Placeholder — returns empty figure until Task 10.8 implements real charts."""
selected = (app_state or {}).get("selected_comparison_directorate")
if not selected:
return no_update
fig = go.Figure()
fig.update_layout(
template="plotly_white",
margin=dict(l=20, r=20, t=30, b=20),
height=300,
annotations=[
dict(
text="Chart will be implemented in Task 10.8",
xref="paper", yref="paper",
x=0.5, y=0.5, showarrow=False,
font=dict(size=14, color="#999"),
)
],
)
return fig