| """Overview / dashboard home page.""" |
| from __future__ import annotations |
|
|
| import sys |
| from pathlib import Path |
|
|
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) |
|
|
| import pandas as pd |
| import streamlit as st |
|
|
| from utils import data_loader as dl |
| from utils.plotting import montana_with_hospitals |
| from utils.styling import callout, page_setup, section, INITIATIVE_COLOR |
|
|
|
|
| page_setup("Overview") |
|
|
| |
| |
| |
| st.markdown( |
| "<div class='rhtp-brandbar'>" |
| "<div>" |
| "<div class='kicker'>Montana DPHHS · Rural Health Transformation Program</div>" |
| "<h1>RHTP Evaluation Dashboard</h1>" |
| "<div class='subtitle'>A fixed research plan, layered, causal evaluation of all five " |
| "RHTP initiatives across Montana's 56 counties and 65 hospitals.</div>" |
| "</div></div>", |
| unsafe_allow_html=True, |
| ) |
|
|
| |
| |
| |
| counties = dl.counties() |
| hosp = dl.hospitals() |
| fq = dl.facility_quarter_ops() |
| coe = dl.coe_implementation() |
| wf = dl.workforce_panel() |
|
|
| c1, c2, c3, c4, c5 = st.columns(5) |
| with c1: |
| st.metric("Hospitals tracked", f"{len(hosp):,}", |
| help="Critical Access, PPS, Tertiary, Sole Community, and Tribal/IHS facilities.") |
| with c2: |
| rural = (counties["rurality"] != "Urban").sum() |
| st.metric("Rural / tribal counties", f"{rural} / {len(counties)}") |
| with c3: |
| treated = (coe["coe_cohort"] != "never").sum() |
| st.metric("CoE-enrolled facilities", f"{treated} / {len(hosp)}", |
| delta=f"{treated/len(hosp):.0%}", delta_color="off") |
| with c4: |
| latest = fq.sort_values(["year", "quarter"]).groupby("facility_id").tail(1) |
| neg_margin = (latest["operating_margin_pct"] < 0).sum() |
| st.metric("Negative-margin hospitals (latest Q)", |
| f"{neg_margin} / {len(latest)}", |
| delta=f"{neg_margin/len(latest):.0%}", delta_color="off") |
| with c5: |
| pop = counties["population_2020"].sum() |
| st.metric("Population covered", f"{pop:,.0f}") |
|
|
| |
| |
| |
| section("Evaluation framing", |
| "How this dashboard is structured and what each page answers.") |
|
|
| c1, c2 = st.columns([3, 2]) |
| with c1: |
| st.markdown( |
| "Every initiative page follows the same three-section pattern:\n\n" |
| "1. **Initiative scope** — KPOs (with baseline + FY2031 target + level) " |
| "and treatment / intervention inputs, so you know exactly what each " |
| "page is trying to move and which levers it pulls.\n" |
| "2. **Data Explorer** — interactive panel of the underlying outcomes " |
| "and treatments.\n" |
| "3. **Models** — the exact econometric specification(s) for that " |
| "initiative, with adjustable knobs so a stakeholder can see *how* an " |
| "estimate is constructed.\n" |
| "4. **Was the initiative successful?** — direction, magnitude, " |
| "precision, timing, consistency, and dose-response, applied to the " |
| "model's outputs.\n\n" |
| "This structure mirrors the **RHTP_models_full** modeling playbook." |
| ) |
| callout( |
| "<strong>About the data.</strong> Every file in <code>/data/</code> is " |
| "<strong>synthetic</strong>, generated by <code>scripts/generate_data.py</code> " |
| "to mirror the schema and units of the real source feeds — CMS Hospital " |
| "Cost Reports, Montana Medicaid claims, HRSA workforce data, BRFSS, " |
| "Big Sky Care Connect HIE rosters, and the MT DPHHS facility / county " |
| "files. Treatment effects are baked in deterministically (with realistic " |
| "noise), so the models <em>should</em> recover positive effects on the " |
| "outcomes the RHTP plan is designed to move. To switch the dashboard to " |
| "live data, drop a real file with the same name and column schema into " |
| "<code>/data/</code> — no code changes needed.", |
| kind="info", |
| ) |
|
|
| with c2: |
| st.markdown("##### Five initiatives") |
| initiatives = [ |
| ("01", "Workforce"), |
| ("02", "Facility Sustainability"), |
| ("03", "Innovative Care & Payment"), |
| ("04", "Community Prevention"), |
| ("05", "Technology & Data"), |
| ] |
| for num, name in initiatives: |
| color = INITIATIVE_COLOR[int(num)] |
| st.markdown( |
| f"<div class='rhtp-card' style='border-left:5px solid {color};'>" |
| f"<div style='display:flex; align-items:baseline; gap:10px;'>" |
| f"<span style='color:{color}; font-weight:700; " |
| f"letter-spacing:0.06em; font-size:0.78rem;'>INITIATIVE {num}</span>" |
| f"<h4 style='margin:0;'>{name}</h4></div>" |
| f"<div style='color:#9CA3AF; font-size:0.85rem; margin-top:4px;'>" |
| f"open via the sidebar</div></div>", |
| unsafe_allow_html=True, |
| ) |
|
|
| |
| |
| |
| section("Montana hospitals by county", |
| "Hover any county for population and rurality; hover any marker for the " |
| "hospital name, facility type, staffed beds, ownership, and CCN.") |
|
|
| ctrl_col, _ = st.columns([3, 1]) |
| with ctrl_col: |
| facility_types = sorted(hosp["facility_type"].unique().tolist()) |
| selected_types = st.multiselect( |
| "Facility types to display", |
| options=facility_types, |
| default=facility_types, |
| help="Filter the map markers. The choropleth always shows all 56 counties.", |
| ) |
|
|
| if not selected_types: |
| selected_types = facility_types |
|
|
| fig = montana_with_hospitals(counties, hosp, facility_type_filter=selected_types, |
| height=620) |
| st.plotly_chart(fig, use_container_width=True, config={"displaylogo": False}) |
|
|
| fcount = hosp[hosp["facility_type"].isin(selected_types)] |
| beds_total = int(fcount["staffed_beds"].sum()) |
| m1, m2, m3, m4 = st.columns(4) |
| with m1: |
| st.metric("Facilities shown", f"{len(fcount):,}") |
| with m2: |
| st.metric("Staffed beds", f"{beds_total:,}") |
| with m3: |
| cah = (fcount["facility_type"] == "CAH").sum() |
| st.metric("Critical Access (CAH)", f"{cah:,}") |
| with m4: |
| tribal = (fcount["facility_type"] == "Tribal/IHS").sum() |
| st.metric("Tribal / IHS", f"{tribal:,}") |
|
|
| |
| |
| |
| section("Hospital roster", |
| "Schema mirrors the CMS Provider of Services file plus the MT DPHHS " |
| "facility roster — drop in real data with the same columns to switch " |
| "from synthetic to live.") |
|
|
| with st.expander("Show the full roster"): |
| st.dataframe( |
| hosp[[ |
| "facility_id", "facility_name", "county_name", "facility_type", |
| "ownership", "staffed_beds", "ccn", "rurality", "region", |
| ]].sort_values(["facility_type", "facility_name"]), |
| hide_index=True, use_container_width=True, |
| ) |
|
|
| |
| |
| |
| section("County baseline panel — workforce snapshot (latest year)", |
| "Where Montana's rural workforce stood at the close of the most recent " |
| "year of available data. Use this view alongside the Workforce page to " |
| "judge what 'meaningfully improved' means relative to baseline.") |
|
|
| latest_year = int(wf["year"].max()) |
| wf_latest = wf[wf["year"] == latest_year].merge( |
| counties[["fips", "county_name", "rurality", "region"]], on="fips") |
|
|
| c1, c2 = st.columns(2) |
| with c1: |
| metric_choice = st.selectbox( |
| "Metric to display", |
| options=[ |
| ("np_per_100k", "Nurse practitioners per 100k"), |
| ("md_per_100k", "Physicians per 100k"), |
| ("rn_per_100k", "Registered nurses per 100k"), |
| ("turnover_rate", "Annual provider turnover rate"), |
| ("provider_mh_score", "Provider mental health score (1-10)"), |
| ], |
| format_func=lambda x: x[1], |
| ) |
| metric_col, metric_label = metric_choice |
| with c2: |
| rurality_filter = st.multiselect( |
| "Show counties by rurality", |
| options=["Urban", "Rural", "Tribal"], |
| default=["Urban", "Rural", "Tribal"], |
| ) |
|
|
| display_df = wf_latest[wf_latest["rurality"].isin(rurality_filter)].copy() |
| display_df = display_df[["county_name", "rurality", "region", metric_col, |
| "population", "medicaid_share", "unemployment_pct"]] |
| display_df = display_df.rename(columns={ |
| "county_name": "County", "rurality": "Type", "region": "Region", |
| metric_col: metric_label, "population": "Population", |
| "medicaid_share": "Medicaid share", "unemployment_pct": "Unemployment %", |
| }) |
| display_df = display_df.sort_values(metric_label, |
| ascending=metric_col == "turnover_rate") |
| st.dataframe(display_df, hide_index=True, use_container_width=True, height=320) |
|
|
| |
| |
| |
| section("How a stakeholder should read this evaluation") |
|
|
| c1, c2, c3 = st.columns(3) |
| with c1: |
| st.markdown( |
| "**Step 1 — Implementation.** Did the intervention actually roll out? " |
| "Each initiative page begins by showing the cumulative reach of the " |
| "intervention in question. Weak downstream effects are hard to " |
| "interpret if the intervention barely reached anyone." |
| ) |
| with c2: |
| st.markdown( |
| "**Step 2 — Effect.** Did treated units improve more than comparable " |
| "untreated ones? The models on each page are causal: they use county " |
| "or facility fixed effects to absorb stable differences, year fixed " |
| "effects to absorb statewide trends, and event-study structure to test " |
| "*when* effects appear." |
| ) |
| with c3: |
| st.markdown( |
| "**Step 3 — Synthesis.** Across all five initiatives, did Montana move " |
| "materially closer to its FY2031 rural health goals on workforce, " |
| "access, quality, financial sustainability, and tech/data capacity? " |
| "**That is the program-level success question — and it is a synthesis " |
| "of many imperfect estimates, not a single p-value.**" |
| ) |
|
|
| st.markdown("<br/>", unsafe_allow_html=True) |
| callout( |
| "Built as a working draft of the RHTP evaluation tool. Every visualization, model, " |
| "and statistical test is fully reproducible from the file at " |
| "<code>/scripts/generate_data.py</code>. Replace files in <code>/data/</code> " |
| "with real source data of the same schema to switch this dashboard from " |
| "synthetic to live.", |
| kind="info", title="About this dashboard", |
| ) |
|
|