feat: implement reactive Plotly icicle chart generation (Task 4.2)
- Add plotly.graph_objects import - Create icicle_figure computed property (@rx.var) - NHS-inspired blue gradient colorscale (Heritage Blue → Pale Blue) - Custom hover template with patient count, percentage, and cost - Responsive height (600px), transparent background - Maintain hierarchy order with sort=False
This commit is contained in:
@@ -147,10 +147,10 @@ cd pathways_app && timeout 60 python -m reflex run 2>&1 | head -30
|
||||
- [x] Call prepare_chart_data() from apply_filters() for reactive updates
|
||||
|
||||
### 4.2 Reactive Plotly Integration
|
||||
- [ ] Create `generate_icicle_chart()` computed property that returns Plotly figure
|
||||
- [ ] Configure chart colors using design system palette
|
||||
- [ ] Configure chart interactivity (zoom, pan, click, hover)
|
||||
- [ ] Set responsive sizing
|
||||
- [x] Create `generate_icicle_chart()` computed property that returns Plotly figure
|
||||
- [x] Configure chart colors using design system palette
|
||||
- [x] Configure chart interactivity (zoom, pan, click, hover)
|
||||
- [x] Set responsive sizing
|
||||
|
||||
### 4.3 Chart Component
|
||||
- [ ] Integrate `rx.plotly()` component in chart_section
|
||||
|
||||
@@ -9,6 +9,7 @@ from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import plotly.graph_objects as go
|
||||
import reflex as rx
|
||||
|
||||
from pathways_app.styles import (
|
||||
@@ -903,6 +904,114 @@ class AppState(rx.State):
|
||||
return " | ".join(parts)
|
||||
return "All Patients"
|
||||
|
||||
# =========================================================================
|
||||
# Plotly Chart Generation
|
||||
# =========================================================================
|
||||
|
||||
@rx.var
|
||||
def icicle_figure(self) -> go.Figure:
|
||||
"""
|
||||
Generate Plotly icicle chart from chart_data.
|
||||
|
||||
This computed property creates a go.Figure with the hierarchical icicle chart
|
||||
using data from prepare_chart_data(). The chart displays patient pathways:
|
||||
Root → Trust → Directory → Drug
|
||||
|
||||
Colors use a custom NHS-inspired blue gradient colorscale.
|
||||
Hover displays patient count, cost, and percentage of parent.
|
||||
|
||||
Returns:
|
||||
Plotly Figure object ready for rx.plotly() component
|
||||
"""
|
||||
# Return empty figure if no data
|
||||
if not self.chart_data:
|
||||
return go.Figure()
|
||||
|
||||
# Extract lists from chart_data
|
||||
parents = [d.get("parents", "") for d in self.chart_data]
|
||||
ids = [d.get("ids", "") for d in self.chart_data]
|
||||
labels = [d.get("labels", "") for d in self.chart_data]
|
||||
values = [d.get("value", 0) for d in self.chart_data]
|
||||
costs = [d.get("cost", 0.0) for d in self.chart_data]
|
||||
colours = [d.get("colour", 0.0) for d in self.chart_data]
|
||||
|
||||
# NHS-inspired blue gradient colorscale (from design system)
|
||||
# Heritage Blue → Primary Blue → Vibrant Blue → Sky Blue → Pale Blue
|
||||
colorscale = [
|
||||
[0.0, "#003087"], # Heritage Blue
|
||||
[0.25, "#0066CC"], # Primary Blue
|
||||
[0.5, "#1E88E5"], # Vibrant Blue
|
||||
[0.75, "#4FC3F7"], # Sky Blue
|
||||
[1.0, "#E3F2FD"], # Pale Blue
|
||||
]
|
||||
|
||||
# Create the icicle chart
|
||||
fig = go.Figure(
|
||||
go.Icicle(
|
||||
labels=labels,
|
||||
ids=ids,
|
||||
parents=parents,
|
||||
values=values,
|
||||
branchvalues="total",
|
||||
marker=dict(
|
||||
colors=colours,
|
||||
colorscale=colorscale,
|
||||
line=dict(width=1, color="#FFFFFF"),
|
||||
),
|
||||
maxdepth=3,
|
||||
# Custom data for hover template
|
||||
customdata=list(zip(values, colours, costs)),
|
||||
# Text shown on chart segments
|
||||
texttemplate="<b>%{label}</b><br>%{value:,} patients",
|
||||
# Hover text with full details
|
||||
hovertemplate=(
|
||||
"<b>%{label}</b><br>"
|
||||
"Patients: %{customdata[0]:,} (%{customdata[1]:.1%} of parent)<br>"
|
||||
"Total Cost: £%{customdata[2]:,.0f}"
|
||||
"<extra></extra>"
|
||||
),
|
||||
textfont=dict(
|
||||
family="Inter, system-ui, sans-serif",
|
||||
size=12,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
# Configure layout
|
||||
fig.update_layout(
|
||||
title=dict(
|
||||
text=f"Patient Pathways — {self.chart_title}",
|
||||
font=dict(
|
||||
family="Inter, system-ui, sans-serif",
|
||||
size=18,
|
||||
color="#1E293B", # Slate 900
|
||||
),
|
||||
x=0.5,
|
||||
xanchor="center",
|
||||
),
|
||||
margin=dict(t=60, l=10, r=10, b=30),
|
||||
hoverlabel=dict(
|
||||
bgcolor="#FFFFFF",
|
||||
bordercolor="#CBD5E1", # Slate 300
|
||||
font=dict(
|
||||
family="Inter, system-ui, sans-serif",
|
||||
size=13,
|
||||
color="#1E293B", # Slate 900
|
||||
),
|
||||
),
|
||||
paper_bgcolor="rgba(0,0,0,0)", # Transparent background
|
||||
plot_bgcolor="rgba(0,0,0,0)",
|
||||
# Responsive sizing - height set but width auto
|
||||
height=600,
|
||||
# Enable interactivity
|
||||
clickmode="event+select",
|
||||
)
|
||||
|
||||
# Disable sort to maintain hierarchy order
|
||||
fig.update_traces(sort=False)
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Layout Components
|
||||
|
||||
Reference in New Issue
Block a user