| """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) |
|
|
| |
| 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", |
| ]) |
|
|
| |
| 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']}%") |
|
|
| |
| 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 |
| ]) |
|
|
| |
| 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() |
|
|
| |
| 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 = [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="🚨", |
| ) |
|
|
| |
| 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() |
|
|
| |
| 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}%") |
|
|
| |
| 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) |
|
|
| |
| 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() |
|
|
| |
| 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"]) |
|
|
| |
| 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() |
|
|
| |
| 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() |
|
|
| |
| 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_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']}") |
|
|
| |
| 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() |
|
|
| |
| 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) |
|
|
| |
| 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") |
|
|
| |
| 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) |
|
|
| |
| 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") |
|
|