- Replace non-linear 7-stop colorscale with linear 5-stop in both
create_heatmap_figure() and create_trust_heatmap_figure()
- Add cell text annotations formatted per metric (patients: N, cost: £Nk, cost_pp_pa: £N)
- Set zmin=0 explicitly for correct color mapping
- Remove fixed width, use autosize=True via _base_layout()
- Replace l=200 fixed margin with l=8 + yaxis automargin=True
- Add subtitle annotation when 25-drug cap is reached
- Reduce xgap/ygap from 2 to 1 when >15 drug columns
- Apply _base_layout() to both heatmap functions for consistent styling
Add module-level constants (CHART_FONT_FAMILY, CHART_TITLE_SIZE,
CHART_TITLE_COLOR, GRID_COLOR, ANNOTATION_COLOR, TRUST_PALETTE,
DRUG_PALETTE) and _base_layout() helper to DRY shared layout
properties across all chart functions. Apply to create_icicle_from_nodes
as proof-of-concept.
- Add 3 new visualization functions to plotly_generator.py:
create_trust_market_share_figure, create_trust_heatmap_figure,
create_trust_duration_figure
- Replace 6 placeholder callbacks in trust_comparison.py with real
implementations using trust-comparison queries + figure builders
- Cost Waterfall reuses existing figure function via key mapping
- Dosing reuses existing create_dosing_figure with group_by="trust"
- Cost Effectiveness reuses existing function scoped to directorate
- All 6 charts respond to date filter and chart type toggle
- Validated with both directory (RHEUMATOLOGY) and indication (asthma)
New per-trust-within-directorate queries:
- get_trust_market_share: drugs by trust within a directorate
- get_trust_cost_waterfall: cost per patient by trust
- get_trust_dosing: drug dosing intervals by trust
- get_trust_heatmap: trust x drug matrix for one directorate
- get_trust_durations: drug durations by trust
Also verified existing get_pathway_costs(directory=X) works for
directorate-scoped Cost Effectiveness (no new function needed).
Thin wrappers added in dash_app/data/queries.py.
- Create create_cost_effectiveness_figure() in plotly_generator.py
Horizontal lollipop chart with dot size by patient count,
colour gradient green→amber→red by cost, retention annotations
- Fix calculate_retention_rate() to accept both 'value' and 'patients' keys
- Add _render_cost_effectiveness() dispatch in chart.py callbacks
- Wire into tab switching for active_tab='cost-effectiveness'
- create_market_share_figure() in src/visualization/plotly_generator.py
- Horizontal stacked bar chart: directorates × drugs with patient %
- Wire into tab dispatch via _render_market_share() helper in chart.py
- Responds to date, chart type, trust, and directorate filters
New query functions in src/data_processing/pathway_queries.py:
- get_drug_market_share: Level 3 drug nodes grouped by directory
- get_pathway_costs: Level 4+ pathway nodes with cost_pp_pa
- get_cost_waterfall: Directorate cost per patient from level 3 aggregation
- get_drug_transitions: Sankey source/target drug transitions with ordinal line labels
- get_dosing_intervals: Parsed average_spacing by trust/directory
- get_drug_directory_matrix: Directory x drug pivot with patient/cost metrics
- get_treatment_durations: Weighted avg_days by drug within directorates
Thin wrappers added in dash_app/data/queries.py for all 7 functions.
- Create src/data_processing/parsing.py with parse_average_spacing(),
parse_pathway_drugs(), and calculate_retention_rate()
- Add 8-tab bar to chart_card.py (Icicle, Market Share, Cost Effectiveness,
Cost Waterfall, Sankey, Dosing, Heatmap, Duration)
- Add active-tab dcc.Store and tab switching callback in chart.py
- Remove Chart Views section from sidebar (now in tab bar)
- Lazy rendering: only active tab's chart is computed
- Add _prune_empty_ancestors() to remove directorate/trust nodes with no
matching children when drug or directorate filters are active (e.g.,
filtering by Immunoglobulin no longer shows empty Ophthalmology box)
- Sum level-3 drug nodes for KPI values when entity filters are active
instead of using the root node's pre-computed unfiltered totals
Drug filter WHERE clause used `drug_sequence IS NULL` to keep ancestor nodes,
but levels 0-2 have empty string '' not NULL. Changed to level-based gating:
- Drug filter: `(level < 3 OR drug_sequence LIKE ...)`
- Directorate filter: `(level < 2 OR directory IN (...) OR directory IS NULL OR directory = '')`
- Trust filter was already correct (had `OR trust_name = ''`)
- Add create_icicle_from_nodes() to src/visualization/plotly_generator.py
accepting list-of-dicts from dcc.Store with NHS blue gradient colorscale,
10-field customdata, and matching text/hover templates from Reflex version
- Add update_chart callback to dash_app/callbacks/chart.py rendering
go.Icicle figure from chart-data store with dynamic subtitle
- Title generation helper mirrors Reflex _generate_pathway_chart_title()
Extract load_data() and load_pathway_data() logic from Reflex AppState
into standalone functions in src/data_processing/pathway_queries.py.
Create thin dash_app/data/queries.py wrapper with DB_PATH resolution.