Interactive_MSK / app.py
SChodavarpu's picture
Update app.py
160e1c6 verified
# Repo layout (create these files in your Space)
# β”œβ”€ app.py ← paste this whole file
# β”œβ”€ requirements.txt ← at end of this file
# └─ assets/msk/Gleamer bone view.png, assets/msk/overlay.png ← optional demo images
import math
from pathlib import Path
import gradio as gr
import matplotlib.pyplot as plt
# ------------------------------
# Config: Gleamer Bone View scenario
# ------------------------------
ASSETS = Path("assets")
MSK_CFG = {
"description": (
"Gleamer Bone View AI assists in detecting fractures on X-rays, speeding reporting, "
"reducing missed diagnoses, and improving workflow efficiency."
),
"sample_images": ["msk/Gleamer Bone View.png", "msk/overlay.png"],
"roi_inputs": {
"baseline_volume": 15000,
"avg_rev_per_study": 6000,
"mins_saved_per_study": 5,
"cost_per_rad_hour": 5200,
"baseline_repeats": 750,
"cost_per_repeat": 6000,
"repeat_reduction_pct": 10,
"program_cost_annual": 1200000
},
"evidence": [
"Time savings in fracture detection reported in multi-center settings.",
"Reduced missed fractures through AI-assisted reads.",
"High agreement with subspecialty musculoskeletal radiologists in fracture detection."
],
"methodology": (
"Gross benefit = Efficiency savings + Savings from reduced repeats.\n"
"Efficiency savings = (Minutes saved per study / 60) Γ— Volume Γ— Cost/hour.\n"
"Repeat savings = (Baseline repeats Γ— Reduction% Γ— Cost per repeat).\n"
"ROI = (Gross benefit βˆ’ Program cost) / Program cost.\n"
"Payback (months) = Program cost / (Gross benefit / 12), if benefit > 0."
),
}
# ------------------------------
# ROI math
# ------------------------------
def compute_roi(period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
cost_per_rad_hour, baseline_repeats, cost_per_repeat,
repeat_reduction_pct, program_cost_annual):
eff_savings_annual = (mins_saved_per_study / 60.0) * baseline_volume * cost_per_rad_hour
repeats_avoided_annual = baseline_repeats * (repeat_reduction_pct / 100.0)
repeat_savings_annual = repeats_avoided_annual * cost_per_repeat
gross_benefit_annual = eff_savings_annual + repeat_savings_annual
program_cost = program_cost_annual
net_benefit_annual = gross_benefit_annual - program_cost
roi_pct = (net_benefit_annual / program_cost * 100.0) if program_cost else 0.0
payback_months = (program_cost / (gross_benefit_annual / 12.0)) if gross_benefit_annual > 0 else math.inf
hours_saved_annual = (mins_saved_per_study / 60.0) * baseline_volume
fte_saved_eq = hours_saved_annual / 1920.0
factor = 1 if period == "Annual" else 1/12
metrics = {
"Program cost": round(program_cost * factor, 2),
"Efficiency savings": round(eff_savings_annual * factor, 2),
"Repeat savings": round(repeat_savings_annual * factor, 2),
"Gross benefit": round(gross_benefit_annual * factor, 2),
"Net benefit": round(net_benefit_annual * factor, 2),
"ROI % (annualized)": round(roi_pct, 1),
"Payback (months)": (round(payback_months, 1) if payback_months != math.inf else float("inf")),
"Hours saved / year": round(hours_saved_annual, 1),
"FTE saved (β‰ˆ1920h/yr)": round(fte_saved_eq, 2),
"Avg revenue per study (input)": avg_rev_per_study,
}
return metrics
def waterfall_plot(metrics: dict):
fig = plt.figure()
components = ["Efficiency savings", "Repeat savings", "Program cost"]
deltas = [metrics[c] for c in components]
deltas[2] = -abs(deltas[2])
running = 0
cumulative = [0]
for d in deltas:
running += d
cumulative.append(running)
for i in range(len(deltas)):
y0, y1 = cumulative[i], cumulative[i+1]
plt.plot([i, i], [y0, y1], linewidth=14)
plt.axhline(0, linewidth=1)
plt.title("Benefit/Cost Waterfall (selected period)")
plt.xlabel("Components")
plt.ylabel("Value")
plt.xticks(range(len(components)), components, rotation=15)
return fig
def init(period):
cfg = MSK_CFG
desc = cfg["description"]
gallery = [str(ASSETS / p) for p in cfg["sample_images"] if (ASSETS / p).exists()]
inputs = cfg["roi_inputs"]
metrics = compute_roi(period, **inputs)
fig = waterfall_plot(metrics)
readout = (
f"**Gleamer Bone View snapshot** β€” Net benefit: {metrics['Net benefit']:.0f}; "
f"ROI (annualized): {metrics['ROI % (annualized)']}%; "
f"Payback: {metrics['Payback (months)']} months.\n\n"
f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β‰ˆ1920h/yr)']:.2f} FTE)."
)
ev_md = "\n".join([f"- {e}" for e in cfg.get('evidence', [])]) or "_Add citations_"
meth_md = cfg.get("methodology", "")
defaults = list(inputs.values())
return desc, gallery, *defaults, metrics, fig, readout, ev_md, meth_md
def recalc(period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
cost_per_rad_hour, baseline_repeats, cost_per_repeat,
repeat_reduction_pct, program_cost_annual):
metrics = compute_roi(period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
cost_per_rad_hour, baseline_repeats, cost_per_repeat,
repeat_reduction_pct, program_cost_annual)
fig = waterfall_plot(metrics)
readout = (
f"**Gleamer Bone View snapshot** β€” Net benefit: {metrics['Net benefit']:.0f}; "
f"ROI (annualized): {metrics['ROI % (annualized)']}%; "
f"Payback: {metrics['Payback (months)']} months.\n\n"
f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β‰ˆ1920h/yr)']:.2f} FTE)."
)
return metrics, fig, readout
with gr.Blocks(title="Gleamer Bone View ROI – Interactive", fill_height=True) as demo:
gr.Markdown("""
# Gleamer Bone View ROI – Interactive
**Clinician-first sandbox**: explore a sample fracture detection case, tweak workflow assumptions, and see financial, operational, and clinical impact instantly.
""")
period = gr.Radio(["Annual", "Monthly"], value="Annual", label="Time basis")
with gr.Tabs():
with gr.Tab("Explore"):
desc = gr.Markdown()
gallery = gr.Gallery(label="Sample case", columns=3, height=320, show_label=True)
with gr.Tab("ROI Simulator"):
with gr.Row():
with gr.Column(scale=1, min_width=360):
baseline_volume = gr.Slider(0, 100000, value=15000, step=50, label="Annual volume")
avg_rev_per_study = gr.Slider(0, 50000, value=6000, step=50, label="Avg revenue per study (info)")
mins_saved_per_study = gr.Slider(0, 60, value=5, step=1, label="Minutes saved per study")
cost_per_rad_hour = gr.Slider(0, 20000, value=5200, step=50, label="Radiologist cost/hour")
baseline_repeats = gr.Slider(0, 100000, value=750, step=10, label="Baseline repeats/year")
cost_per_repeat = gr.Slider(0, 50000, value=6000, step=50, label="Cost per repeat")
repeat_reduction_pct = gr.Slider(0, 100, value=10, step=1, label="Repeat reduction with AI (%)")
program_cost_annual = gr.Slider(0, 10000000, value=1200000, step=10000, label="Program cost (annual)")
with gr.Column(scale=1):
metrics = gr.JSON(label="Outputs")
chart = gr.Plot(label="Waterfall")
readout = gr.Markdown(label="Executive readout")
with gr.Tab("Evidence"):
evidence_md = gr.Markdown()
with gr.Tab("Methodology"):
methodology_md = gr.Markdown()
period.change(init, [period], [desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study,
cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual,
metrics, chart, readout, evidence_md, methodology_md])
for comp in [baseline_volume, avg_rev_per_study, mins_saved_per_study, cost_per_rad_hour,
baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual]:
comp.change(recalc, [period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual],
[metrics, chart, readout])
demo.load(init, [period], [desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study,
cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual,
metrics, chart, readout, evidence_md, methodology_md])
if __name__ == "__main__":
demo.launch()
# ------------------------------
# requirements.txt
# ------------------------------
# gradio>=4.44.0
# matplotlib