RHTP / src /pages /0_Overview.py
rmbielski's picture
stuff
b7f7cc7
Raw
History Blame Contribute Delete
11.2 kB
"""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")
# --------------------------------------------------------------------------
# Brand strip + headline
# --------------------------------------------------------------------------
st.markdown(
"<div class='rhtp-brandbar'>"
"<div>"
"<div class='kicker'>Montana DPHHS &middot; 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,
)
# --------------------------------------------------------------------------
# Top-line stats
# --------------------------------------------------------------------------
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}")
# --------------------------------------------------------------------------
# Evaluation framing
# --------------------------------------------------------------------------
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,
)
# --------------------------------------------------------------------------
# Map
# --------------------------------------------------------------------------
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:,}")
# --------------------------------------------------------------------------
# Hospital roster
# --------------------------------------------------------------------------
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,
)
# --------------------------------------------------------------------------
# Latest workforce snapshot
# --------------------------------------------------------------------------
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)
# --------------------------------------------------------------------------
# Reading guide
# --------------------------------------------------------------------------
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",
)