feat: add reference data loading and filter state callbacks (Task 3.1)

This commit is contained in:
Andrew Charlwood
2026-02-06 13:29:30 +00:00
parent d896c63dc5
commit eda35c7168
4 changed files with 93 additions and 3 deletions
+3 -3
View File
@@ -163,12 +163,12 @@ Drawer selection → update_drug_selection → app-state store → load_pathway_
## Phase 3: Core Callbacks ## Phase 3: Core Callbacks
### 3.1 Reference data loading + filter state management ### 3.1 Reference data loading + filter state management
- [ ] Create `dash_app/callbacks/filters.py`: - [x] Create `dash_app/callbacks/filters.py`:
- `load_reference_data` callback: fires on page load, calls `queries.load_initial_data()`, populates `reference-data` store + header indicators - `load_reference_data` callback: fires on page load, calls `queries.load_initial_data()`, populates `reference-data` store + header indicators
- `update_app_state` callback: fires when chart-type toggle or date dropdowns change, computes `date_filter_id` (e.g., `"all_6mo"`), updates `app-state` store - `update_app_state` callback: fires when chart-type toggle or date dropdowns change, computes `date_filter_id` (e.g., `"all_6mo"`), updates `app-state` store
- Chart type toggle: use `callback_context` to determine which button was clicked, set active class via `className` - Chart type toggle: use `callback_context` to determine which button was clicked, set active class via `className`
- [ ] Create `dash_app/callbacks/__init__.py` with `register_callbacks(app)` that imports and registers all callback modules - [x] Create `dash_app/callbacks/__init__.py` with `register_callbacks(app)` that imports and registers all callback modules
- [ ] Wire `register_callbacks(app)` in `app.py` - [x] Wire `register_callbacks(app)` in `app.py`
- **Checkpoint**: Page loads reference data, filter dropdowns update app-state store (verify via browser dev tools → dcc.Store) - **Checkpoint**: Page loads reference data, filter dropdowns update app-state store (verify via browser dev tools → dcc.Store)
### 3.2 Pathway data loading callback ### 3.2 Pathway data loading callback
+5
View File
@@ -27,6 +27,7 @@ app.layout = dmc.MantineProvider(
}), }),
dcc.Store(id="chart-data", storage_type="memory"), dcc.Store(id="chart-data", storage_type="memory"),
dcc.Store(id="reference-data", storage_type="session"), dcc.Store(id="reference-data", storage_type="session"),
dcc.Location(id="url", refresh=False),
# Page structure # Page structure
make_header(), make_header(),
@@ -43,4 +44,8 @@ app.layout = dmc.MantineProvider(
], ],
) )
from dash_app.callbacks import register_callbacks
register_callbacks(app)
server = app.server server = app.server
+8
View File
@@ -0,0 +1,8 @@
"""Callback registration — imports all callback modules and wires them to the app."""
def register_callbacks(app):
"""Register all Dash callbacks with the app instance."""
from dash_app.callbacks.filters import register_filter_callbacks
register_filter_callbacks(app)
+77
View File
@@ -0,0 +1,77 @@
"""Callbacks for reference data loading and filter state management."""
from dash import Input, Output, State, callback, ctx, no_update
def register_filter_callbacks(app):
"""Register reference data loading and filter state callbacks."""
@app.callback(
Output("reference-data", "data"),
Output("header-record-count", "children"),
Output("header-last-updated", "children"),
Input("url", "pathname"), # fires once on page load
)
def load_reference_data(_pathname):
"""Load reference data once on page load."""
from dash_app.data.queries import load_initial_data
ref = load_initial_data()
total = ref.get("total_records", 0)
record_text = f"{total:,} records" if total else "Data loaded"
last_updated = ref.get("last_updated", "")
updated_text = last_updated[:10] if last_updated else "Unknown"
return ref, record_text, updated_text
@app.callback(
Output("app-state", "data"),
Output("chart-type-directory", "className"),
Output("chart-type-indication", "className"),
Input("chart-type-directory", "n_clicks"),
Input("chart-type-indication", "n_clicks"),
Input("filter-initiated", "value"),
Input("filter-last-seen", "value"),
State("app-state", "data"),
)
def update_app_state(
_dir_clicks, _ind_clicks, initiated, last_seen, current_state
):
"""Update app-state when chart type toggle or date filters change."""
if not current_state:
current_state = {
"chart_type": "directory",
"initiated": "all",
"last_seen": "6mo",
"date_filter_id": "all_6mo",
"selected_drugs": [],
"selected_directorates": [],
}
triggered_id = ctx.triggered_id
# Determine chart type from toggle pills
chart_type = current_state.get("chart_type", "directory")
if triggered_id == "chart-type-directory":
chart_type = "directory"
elif triggered_id == "chart-type-indication":
chart_type = "indication"
# Compute date_filter_id from dropdown values
date_filter_id = f"{initiated}_{last_seen}"
# Build updated state
updated_state = {
**current_state,
"chart_type": chart_type,
"initiated": initiated,
"last_seen": last_seen,
"date_filter_id": date_filter_id,
}
# Toggle pill CSS classes
base = "toggle-pill"
active = f"{base} toggle-pill--active"
dir_class = active if chart_type == "directory" else base
ind_class = active if chart_type == "indication" else base
return updated_state, dir_class, ind_class