kenmandal's picture
feat: add polypharmacy, PCP collaboration, ED avoidance, age-in-place, concierge timeline, medication intelligence
e1134c5
"""Facility Executive persona.
Features:
- KPI dashboard (occupancy, ER avoidance, falls, compliance)
- Risk heatmap across residents
- Financial impact metrics
- Staff efficiency analytics
"""
import streamlit as st
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
from data.synthetic import (
get_residents, get_facility_kpis, get_sensor_data, get_staff_metrics,
get_ed_avoidance_data, get_age_in_place_scores,
)
from components.chatbot import render_chatbot
def render():
"""Render the Facility Executive dashboard."""
st.title("Executive Intelligence Dashboard")
kpis = get_facility_kpis()
residents = get_residents()
staff = get_staff_metrics()
sensors = get_sensor_data(hours=48)
# Top KPI row
c1, c2, c3, c4, c5 = st.columns(5)
with c1:
st.metric("Occupancy", f"{kpis['occupancy_rate']}%", kpis["occupancy_delta"])
with c2:
st.metric("ER Transfers", kpis["er_transfers_month"], kpis["er_transfers_delta"], delta_color="inverse")
with c3:
st.metric("Falls (Month)", kpis["falls_this_month"], kpis["falls_delta"], delta_color="inverse")
with c4:
st.metric("Family Satisfaction", f"{kpis['family_satisfaction']}/5.0", "+0.2")
with c5:
st.metric("Compliance", f"{kpis['regulatory_compliance_pct']}%", "+0.3%")
st.divider()
tab_ops, tab_risk, tab_ed, tab_aip, tab_finance, tab_staff, tab_chat = st.tabs([
"Operations", "Risk Overview", "🚑 ED Avoidance",
"🏠 Age-in-Place", "Financial Impact", "Staff Analytics", "📊 Strategic Advisor",
])
# --- TAB: OPERATIONS ---
with tab_ops:
col1, col2 = st.columns(2)
with col1:
st.subheader("Mobility Trends (All Residents)")
fig = px.area(
sensors, x="Timestamp", y="Mobility_Index",
title="Aggregate Mobility Index (48h)",
template="plotly_white",
)
fig.update_traces(line_color="#8b5cf6", fillcolor="rgba(139,92,246,0.1)")
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("Social Engagement Trend")
fig2 = px.bar(
sensors.tail(24), x="Timestamp", y="Social_Engagement_Hrs",
title="Communal Hours (Last 24h)",
template="plotly_white",
color="Social_Engagement_Hrs",
color_continuous_scale="Teal",
)
st.plotly_chart(fig2, use_container_width=True)
st.subheader("Key Operational Metrics")
op_col1, op_col2, op_col3, op_col4 = st.columns(4)
with op_col1:
st.metric("Avg Length of Stay", f"{kpis['avg_los_days']} days")
with op_col2:
st.metric("Infection Rate", f"{kpis['infection_rate_per_1000']}/1000")
with op_col3:
st.metric("Readmission Rate", f"{kpis['readmission_rate_pct']}%")
with op_col4:
st.metric("Staff Turnover", f"{kpis['staff_turnover_pct']}%")
# --- TAB: RISK OVERVIEW ---
with tab_risk:
st.subheader("Resident Risk Heatmap")
risk_data = pd.DataFrame([
{
"Resident": r["name"],
"Room": r["room"],
"Unit": r["unit"],
"Risk Level": r["risk_level"],
"Wellness Score": r["wellness_score"],
"Mobility": r["mobility_baseline"],
"Diagnoses": ", ".join(r["diagnoses"][:2]),
}
for r in residents
])
# Color-coded risk table
def highlight_risk(val):
colors = {"High": "background-color: #fef2f2; color: #dc2626;",
"Moderate": "background-color: #fffbeb; color: #d97706;",
"Low": "background-color: #f0fdf4; color: #16a34a;"}
return colors.get(val, "")
styled = risk_data.style.applymap(highlight_risk, subset=["Risk Level"])
st.dataframe(styled, use_container_width=True, hide_index=True)
st.divider()
# Risk distribution
col_r1, col_r2 = st.columns(2)
with col_r1:
risk_counts = risk_data["Risk Level"].value_counts().reset_index()
risk_counts.columns = ["Risk Level", "Count"]
fig_risk = px.pie(
risk_counts, values="Count", names="Risk Level",
title="Risk Distribution",
color="Risk Level",
color_discrete_map={"High": "#ef4444", "Moderate": "#f59e0b", "Low": "#10b981"},
template="plotly_white",
)
st.plotly_chart(fig_risk, use_container_width=True)
with col_r2:
fig_well = px.bar(
risk_data, x="Resident", y="Wellness Score",
title="Wellness Scores by Resident",
color="Wellness Score",
color_continuous_scale="RdYlGn",
template="plotly_white",
)
fig_well.add_hline(y=85, line_dash="dash", line_color="green", annotation_text="Target")
st.plotly_chart(fig_well, use_container_width=True)
# High risk alerts
high_risk = [r for r in residents if r["risk_level"] == "High"]
if high_risk:
st.error(
f"**{len(high_risk)} High-Risk Resident(s):** "
+ ", ".join(f"{r['name']} (Room {r['room']})" for r in high_risk),
icon="🚨",
)
# --- TAB: ED AVOIDANCE INTELLIGENCE ---
with tab_ed:
st.subheader("ED Avoidance Intelligence")
st.caption(
"Track emergency department transfers avoided through proactive AI-powered interventions. "
"Relief from acuity burden with predictive clinical care."
)
ed_data = get_ed_avoidance_data()
# Top metrics
ed1, ed2, ed3, ed4 = st.columns(4)
with ed1:
st.metric("ED Transfers (Month)", ed_data["ed_transfers"][-1], "-5 vs 6mo ago", delta_color="inverse")
with ed2:
st.metric("ED Avoidances (Month)", ed_data["ed_avoidances"][-1], "+6 vs 6mo ago")
with ed3:
total_savings = sum(ed_data["savings_per_month"])
st.metric("Total Savings (6mo)", f"${total_savings:,}")
with ed4:
avoidance_rate = ed_data["ed_avoidances"][-1] / (ed_data["ed_transfers"][-1] + ed_data["ed_avoidances"][-1]) * 100
st.metric("Avoidance Rate", f"{avoidance_rate:.0f}%")
# Charts
ed_col1, ed_col2 = st.columns(2)
with ed_col1:
ed_df = pd.DataFrame({
"Month": ed_data["months"],
"ED Transfers": ed_data["ed_transfers"],
"Avoided": ed_data["ed_avoidances"],
})
fig_ed = px.bar(
ed_df, x="Month", y=["ED Transfers", "Avoided"],
title="ED Transfers vs. AI-Prevented Transfers",
barmode="group",
color_discrete_map={"ED Transfers": "#ef4444", "Avoided": "#10b981"},
template="plotly_white",
)
st.plotly_chart(fig_ed, use_container_width=True)
with ed_col2:
fig_sav = px.area(
x=ed_data["months"], y=ed_data["savings_per_month"],
title="Monthly ED Avoidance Savings ($)",
labels={"x": "Month", "y": "Savings ($)"},
template="plotly_white",
)
fig_sav.update_traces(line_color="#10b981", fillcolor="rgba(16,185,129,0.15)")
st.plotly_chart(fig_sav, use_container_width=True)
# Top avoidance reasons
st.markdown("**Top ED Avoidance Categories (6 Months)**")
for item in ed_data["top_avoidance_reasons"]:
r_col1, r_col2, r_col3 = st.columns([4, 1, 1])
with r_col1:
st.markdown(f"**{item['reason']}**")
with r_col2:
st.metric("Count", item["count"], label_visibility="collapsed")
with r_col3:
st.metric("Saved", f"${item['savings']:,}", label_visibility="collapsed")
st.divider()
# Recent interventions
st.markdown("**This Month's Interventions**")
for intervention in ed_data["interventions_this_month"]:
with st.expander(f"✅ {intervention['resident']}{intervention['type']} (Saved {intervention['savings']})"):
st.markdown(intervention["action"])
# --- TAB: AGE-IN-PLACE ---
with tab_aip:
st.subheader("Age-in-Place Stability Index")
st.caption(
"Measure how effectively each resident is aging in place. "
"Higher scores indicate greater stability and longer projected stays."
)
aip_scores = get_age_in_place_scores()
# Summary metrics
avg_score = sum(s["score"] for s in aip_scores) / len(aip_scores)
high_risk = [s for s in aip_scores if s["risk_of_transfer"] in ("Moderate", "High")]
aip_c1, aip_c2, aip_c3 = st.columns(3)
with aip_c1:
st.metric("Avg Age-in-Place Score", f"{avg_score:.0f}/100")
with aip_c2:
st.metric("At-Risk Residents", len(high_risk), delta_color="inverse")
with aip_c3:
avg_los = sum(s["projected_los_months"] for s in aip_scores) / len(aip_scores)
st.metric("Avg Projected Stay", f"{avg_los:.0f} months")
st.divider()
# Resident-by-resident breakdown
for aip in aip_scores:
score_color = "#10b981" if aip["score"] >= 75 else "#f59e0b" if aip["score"] >= 50 else "#ef4444"
trend_icon = {"stable": "➡️", "improving": "📈", "declining": "📉"}.get(aip["trend"], "")
with st.expander(
f"{trend_icon} {aip['resident']} (Room {aip['room']}) — "
f"Score: {aip['score']}/100 | Transfer Risk: {aip['risk_of_transfer']}",
expanded=(aip["risk_of_transfer"] != "Low"),
):
aip_left, aip_right = st.columns([1, 2])
with aip_left:
st.markdown(
f"""<div style="background:white; padding:16px; border-radius:12px;
text-align:center; border:2px solid {score_color};">
<p style="font-size:2.5rem; font-weight:800; color:{score_color};
margin:0; line-height:1;">{aip['score']}</p>
<p style="font-size:0.8rem; color:#64748b; margin:4px 0 0;">
Age-in-Place Score</p>
</div>""",
unsafe_allow_html=True,
)
st.caption(f"Projected Stay: {aip['projected_los_months']} months")
with aip_right:
# Factor bar chart
factor_df = pd.DataFrame(
list(aip["factors"].items()),
columns=["Factor", "Score"],
)
fig_f = px.bar(
factor_df, x="Score", y="Factor", orientation="h",
color="Score", color_continuous_scale="RdYlGn",
range_x=[0, 100], template="plotly_white",
)
fig_f.update_layout(height=250, showlegend=False, margin=dict(l=0, r=0, t=10, b=10))
st.plotly_chart(fig_f, use_container_width=True)
st.info(f"**Recommendation:** {aip['recommendation']}")
# --- TAB: FINANCIAL IMPACT ---
with tab_finance:
st.subheader("AI-Driven Financial Impact")
fc1, fc2, fc3 = st.columns(3)
with fc1:
st.metric("ER Avoidance Savings", f"${kpis['er_avoidance_savings']:,}", "This Month")
with fc2:
st.metric("Projected Annual Savings", f"${kpis['er_avoidance_savings'] * 12:,}")
with fc3:
st.metric("Cost per Resident/Day Reduction", "$12.40", "-8%")
st.divider()
# Monthly savings trend
months = ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar"]
savings = [12200, 14500, 15800, 16900, 17600, kpis["er_avoidance_savings"]]
fig_sav = px.bar(
x=months, y=savings,
title="Monthly ER Avoidance Savings ($)",
labels={"x": "Month", "y": "Savings ($)"},
template="plotly_white",
color=savings,
color_continuous_scale="Greens",
)
st.plotly_chart(fig_sav, use_container_width=True)
# --- TAB: STAFF ANALYTICS ---
with tab_staff:
st.subheader("Staff Efficiency & Burnout Metrics")
sc1, sc2, sc3, sc4 = st.columns(4)
with sc1:
st.metric("Staff on Shift", staff["total_staff_on_shift"])
with sc2:
st.metric("Avg Doc Time", f"{staff['avg_documentation_time_min']} min", staff["avg_documentation_time_delta"])
with sc3:
st.metric("Paperwork Saved (Week)", f"{staff['paperwork_mins_saved_week']} min")
with sc4:
st.metric("Shift Satisfaction", f"{staff['shift_satisfaction_avg']}/5.0")
# Task automation gauge
completion_pct = staff["tasks_auto_completed"] / staff["tasks_total"] * 100
fig_gauge = go.Figure(go.Indicator(
mode="gauge+number+delta",
value=completion_pct,
title={"text": "Task Automation Rate"},
delta={"reference": 65},
gauge={
"axis": {"range": [0, 100]},
"bar": {"color": "#3b82f6"},
"steps": [
{"range": [0, 50], "color": "#fee2e2"},
{"range": [50, 75], "color": "#fef3c7"},
{"range": [75, 100], "color": "#dcfce7"},
],
"threshold": {"line": {"color": "red", "width": 3}, "thickness": 0.75, "value": 90},
},
))
fig_gauge.update_layout(height=300, template="plotly_white")
st.plotly_chart(fig_gauge, use_container_width=True)
# --- TAB: STRATEGIC ADVISOR CHATBOT ---
with tab_chat:
st.subheader("Strategic Advisor")
st.caption(
"AI-powered executive intelligence. Ask about occupancy, financial impact, "
"staffing efficiency, fall prevention ROI, compliance, and family satisfaction."
)
render_chatbot(persona="executive")