Files
HighCostDrugsDemo/dash_app/callbacks/drawer.py
T
Andrew Charlwood 7aa49b0d6b refactor: restructure sidebar with chart views, remove placeholder items (Task 7.3)
- Remove non-functional sidebar items: Cost Analysis, Export Data
- Remove filter trigger items: Drug/Trust/Directory Selection, Indications
- Add Chart Views section: Icicle Chart (active), Sankey Diagram (disabled), Timeline (disabled)
- Remove tab row from chart_card.py (chart view selection now in sidebar)
- Remove open_drawer callback (sidebar no longer has filter triggers)
- Add .sidebar__item--disabled CSS class
2026-02-06 15:29:53 +00:00

69 lines
2.8 KiB
Python

"""Callbacks for drug selection: fragment matching and clear filters.
The open_drawer callback was removed in Task 7.3 (sidebar restructure) because
the sidebar no longer has filter trigger items. Task 7.4 will replace the drawer
with modals opened from the filter bar.
"""
from dash import Input, Output, State, ctx, no_update, ALL
def register_drawer_callbacks(app):
"""Register drug/trust selection callbacks (fragment matching + clear)."""
@app.callback(
Output("all-drugs-chips", "value"),
Output("trust-chips", "value"),
Input({"type": "drug-fragment", "index": ALL}, "n_clicks"),
Input("clear-drug-filters", "n_clicks"),
State("all-drugs-chips", "value"),
State("reference-data", "data"),
prevent_initial_call=True,
)
def handle_fragment_or_clear(fragment_clicks, _clear_clicks, current_chips, ref_data):
"""Handle drug fragment badge click (substring match) or clear all filters.
Fragment click: find all full drug names containing the fragment substring,
toggle them in the chip selection. Trust chips unchanged.
Clear click: reset both drug and trust chip selections.
"""
triggered = ctx.triggered_id
# Clear button — reset both drug and trust chips
if triggered == "clear-drug-filters":
return [], []
# Fragment badge click — triggered_id is a dict like {"type": "drug-fragment", "index": "DIR|FRAG"}
if isinstance(triggered, dict) and triggered.get("type") == "drug-fragment":
# Check if any fragment was actually clicked (not just initial render)
if not any(n for n in (fragment_clicks or []) if n):
return no_update, no_update
fragment_key = triggered["index"] # e.g. "CARDIOLOGY|acute coronary syndrome|RIVAROXABAN"
fragment = fragment_key.rsplit("|", 1)[-1] if "|" in fragment_key else fragment_key
# Get all available drugs from reference data
all_drugs = (ref_data or {}).get("available_drugs", [])
# Find drugs whose names contain this fragment (case-insensitive substring)
matching_drugs = [
drug for drug in all_drugs
if fragment.upper() in drug.upper()
]
if not matching_drugs:
return no_update, no_update
# Toggle: if all matching drugs are already selected, deselect them;
# otherwise, add them to selection
current = set(current_chips or [])
all_selected = all(d in current for d in matching_drugs)
if all_selected:
updated = current - set(matching_drugs)
else:
updated = current | set(matching_drugs)
return sorted(updated), no_update
return no_update, no_update