SChodavarpu commited on
Commit
d6ae56b
Β·
verified Β·
1 Parent(s): 2efdd4a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -111
app.py CHANGED
@@ -1,32 +1,37 @@
 
 
 
 
 
1
  import math
2
  from pathlib import Path
3
  import gradio as gr
4
  import matplotlib.pyplot as plt
5
 
6
  # ------------------------------
7
- # Config: MSK default scenario
8
  # ------------------------------
9
  ASSETS = Path("assets")
10
  MSK_CFG = {
11
  "description": (
12
- "AI for MSK MRI helps detect cartilage/meniscus tears, speeds reporting, "
13
- "reduces repeat scans via QA and protocol optimization."
14
  ),
15
- "sample_images": ["msk/1.png", "msk/overlay.png"],
16
  "roi_inputs": {
17
- "baseline_volume": 15000, # studies / year
18
- "avg_rev_per_study": 6000, # currency / study
19
- "mins_saved_per_study": 5, # minutes
20
- "cost_per_rad_hour": 5200, # currency / hour
21
- "baseline_repeats": 750, # repeat scans / year
22
- "cost_per_repeat": 6000, # currency / repeat scan
23
- "repeat_reduction_pct": 10, # % reduction with AI
24
- "program_cost_annual": 1200000 # currency / year
25
  },
26
  "evidence": [
27
- "Time savings on MSK reads reported in multi-center settings.",
28
- "Repeat scans reduced through automated QA / protocol selection.",
29
- "High agreement with subspecialty MSK radiologists in tear detection."
30
  ],
31
  "methodology": (
32
  "Gross benefit = Efficiency savings + Savings from reduced repeats.\n"
@@ -40,19 +45,10 @@ MSK_CFG = {
40
  # ------------------------------
41
  # ROI math
42
  # ------------------------------
43
- def compute_roi(
44
- period: str,
45
- baseline_volume: float,
46
- avg_rev_per_study: float, # kept for future extensions (e.g., throughput-driven revenue)
47
- mins_saved_per_study: float,
48
- cost_per_rad_hour: float,
49
- baseline_repeats: float,
50
- cost_per_repeat: float,
51
- repeat_reduction_pct: float,
52
- program_cost_annual: float,
53
- ):
54
- """Compute ROI metrics; normalize internally to annual, then present for selected period."""
55
- # annualized inputs
56
  eff_savings_annual = (mins_saved_per_study / 60.0) * baseline_volume * cost_per_rad_hour
57
  repeats_avoided_annual = baseline_repeats * (repeat_reduction_pct / 100.0)
58
  repeat_savings_annual = repeats_avoided_annual * cost_per_repeat
@@ -64,11 +60,9 @@ def compute_roi(
64
  roi_pct = (net_benefit_annual / program_cost * 100.0) if program_cost else 0.0
65
  payback_months = (program_cost / (gross_benefit_annual / 12.0)) if gross_benefit_annual > 0 else math.inf
66
 
67
- # simple operational metrics
68
  hours_saved_annual = (mins_saved_per_study / 60.0) * baseline_volume
69
- fte_saved_eq = hours_saved_annual / 1920.0 # ~1920 working hours/year
70
 
71
- # scale for period view
72
  factor = 1 if period == "Annual" else 1/12
73
 
74
  metrics = {
@@ -81,29 +75,24 @@ def compute_roi(
81
  "Payback (months)": (round(payback_months, 1) if payback_months != math.inf else float("inf")),
82
  "Hours saved / year": round(hours_saved_annual, 1),
83
  "FTE saved (β‰ˆ1920h/yr)": round(fte_saved_eq, 2),
84
- # Keeping avg_rev_per_study visible for future throughput revenue logic
85
  "Avg revenue per study (input)": avg_rev_per_study,
86
  }
87
  return metrics
88
 
89
 
90
  def waterfall_plot(metrics: dict):
91
- """Return a simple matplotlib waterfall figure using current metrics."""
92
  fig = plt.figure()
93
  components = ["Efficiency savings", "Repeat savings", "Program cost"]
94
  deltas = [metrics[c] for c in components]
95
- # Program cost is a negative contributor
96
  deltas[2] = -abs(deltas[2])
97
 
98
  running = 0
99
- x_positions = range(len(components) + 1)
100
  cumulative = [0]
101
  for d in deltas:
102
  running += d
103
  cumulative.append(running)
104
 
105
- # Draw bars as thick lines (simple waterfall)
106
- for i, d in enumerate(deltas):
107
  y0, y1 = cumulative[i], cumulative[i+1]
108
  plt.plot([i, i], [y0, y1], linewidth=14)
109
 
@@ -115,82 +104,48 @@ def waterfall_plot(metrics: dict):
115
  return fig
116
 
117
 
118
- # ------------------------------
119
- # UI callbacks
120
- # ------------------------------
121
-
122
  def init(period):
123
  cfg = MSK_CFG
124
  desc = cfg["description"]
125
- # only include existing asset files
126
  gallery = [str(ASSETS / p) for p in cfg["sample_images"] if (ASSETS / p).exists()]
127
 
128
  inputs = cfg["roi_inputs"]
129
- metrics = compute_roi(
130
- period,
131
- inputs["baseline_volume"],
132
- inputs["avg_rev_per_study"],
133
- inputs["mins_saved_per_study"],
134
- inputs["cost_per_rad_hour"],
135
- inputs["baseline_repeats"],
136
- inputs["cost_per_repeat"],
137
- inputs["repeat_reduction_pct"],
138
- inputs["program_cost_annual"],
139
- )
140
  fig = waterfall_plot(metrics)
141
 
142
- # Executive readout
143
  readout = (
144
- f"**MSK AI snapshot** β€” Net benefit: {metrics['Net benefit']:.0f}; "
145
  f"ROI (annualized): {metrics['ROI % (annualized)']}%; "
146
  f"Payback: {metrics['Payback (months)']} months.\n\n"
147
  f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β‰ˆ1920h/yr)']:.2f} FTE)."
148
  )
149
 
150
- ev_md = "\n".join([f"- {e}" for e in cfg.get("evidence", [])]) or "_Add citations_")
151
  meth_md = cfg.get("methodology", "")
152
 
153
- defaults = [
154
- inputs["baseline_volume"],
155
- inputs["avg_rev_per_study"],
156
- inputs["mins_saved_per_study"],
157
- inputs["cost_per_rad_hour"],
158
- inputs["baseline_repeats"],
159
- inputs["cost_per_repeat"],
160
- inputs["repeat_reduction_pct"],
161
- inputs["program_cost_annual"],
162
- ]
163
-
164
  return desc, gallery, *defaults, metrics, fig, readout, ev_md, meth_md
165
 
166
 
167
- def recalc(period,
168
- baseline_volume, avg_rev_per_study, mins_saved_per_study,
169
  cost_per_rad_hour, baseline_repeats, cost_per_repeat,
170
  repeat_reduction_pct, program_cost_annual):
171
- metrics = compute_roi(
172
- period,
173
- baseline_volume, avg_rev_per_study, mins_saved_per_study,
174
- cost_per_rad_hour, baseline_repeats, cost_per_repeat,
175
- repeat_reduction_pct, program_cost_annual,
176
- )
177
  fig = waterfall_plot(metrics)
178
  readout = (
179
- f"**MSK AI snapshot** β€” Net benefit: {metrics['Net benefit']:.0f}; "
180
  f"ROI (annualized): {metrics['ROI % (annualized)']}%; "
181
  f"Payback: {metrics['Payback (months)']} months.\n\n"
182
  f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β‰ˆ1920h/yr)']:.2f} FTE)."
183
  )
184
  return metrics, fig, readout
185
 
186
-
187
- # ------------------------------
188
- # Gradio UI
189
- # ------------------------------
190
- with gr.Blocks(title="MSK AI ROI – Interactive", fill_height=True) as demo:
191
  gr.Markdown("""
192
- # MSK AI ROI – Interactive
193
- **Clinician-first sandbox**: explore a sample MSK MRI case, tweak workflow assumptions, and see financial, operational, and clinical impact instantly.
194
  """)
195
 
196
  period = gr.Radio(["Annual", "Monthly"], value="Annual", label="Time basis")
@@ -198,17 +153,17 @@ with gr.Blocks(title="MSK AI ROI – Interactive", fill_height=True) as demo:
198
  with gr.Tabs():
199
  with gr.Tab("Explore"):
200
  desc = gr.Markdown()
201
- gallery = gr.Gallery(label="Sample MSK study", columns=3, height=320, show_label=True)
202
 
203
  with gr.Tab("ROI Simulator"):
204
  with gr.Row():
205
  with gr.Column(scale=1, min_width=360):
206
- baseline_volume = gr.Slider(0, 100000, value=15000, step=50, label="Annual MSK MRI volume")
207
  avg_rev_per_study = gr.Slider(0, 50000, value=6000, step=50, label="Avg revenue per study (info)")
208
  mins_saved_per_study = gr.Slider(0, 60, value=5, step=1, label="Minutes saved per study")
209
  cost_per_rad_hour = gr.Slider(0, 20000, value=5200, step=50, label="Radiologist cost/hour")
210
- baseline_repeats = gr.Slider(0, 100000, value=750, step=10, label="Baseline repeat scans / year")
211
- cost_per_repeat = gr.Slider(0, 50000, value=6000, step=50, label="Cost per repeat scan")
212
  repeat_reduction_pct = gr.Slider(0, 100, value=10, step=1, label="Repeat reduction with AI (%)")
213
  program_cost_annual = gr.Slider(0, 10000000, value=1200000, step=10000, label="Program cost (annual)")
214
  with gr.Column(scale=1):
@@ -222,32 +177,26 @@ with gr.Blocks(title="MSK AI ROI – Interactive", fill_height=True) as demo:
222
  with gr.Tab("Methodology"):
223
  methodology_md = gr.Markdown()
224
 
225
- # Wire events
226
- period.change(
227
- init,
228
- [period],
229
- [desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study,
230
- cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual,
231
- metrics, chart, readout, evidence_md, methodology_md]
232
- )
233
 
234
  for comp in [baseline_volume, avg_rev_per_study, mins_saved_per_study, cost_per_rad_hour,
235
  baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual]:
236
- comp.change(
237
- recalc,
238
- [period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
239
- cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual],
240
- [metrics, chart, readout]
241
- )
242
-
243
- gr.on(
244
- load=True,
245
- fn=init,
246
- inputs=[period],
247
- outputs=[desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study,
248
- cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual,
249
- metrics, chart, readout, evidence_md, methodology_md]
250
- )
251
 
252
  if __name__ == "__main__":
253
- demo.launch()
 
 
 
 
 
 
 
1
+ # Repo layout (create these files in your Space)
2
+ # β”œβ”€ app.py ← paste this whole file
3
+ # β”œβ”€ requirements.txt ← at end of this file
4
+ # └─ assets/msk/Gleamer bone view.png, assets/msk/overlay.png ← optional demo images
5
+
6
  import math
7
  from pathlib import Path
8
  import gradio as gr
9
  import matplotlib.pyplot as plt
10
 
11
  # ------------------------------
12
+ # Config: Gleamer Bone View scenario
13
  # ------------------------------
14
  ASSETS = Path("assets")
15
  MSK_CFG = {
16
  "description": (
17
+ "Gleamer Bone View AI assists in detecting fractures on X-rays, speeding reporting, "
18
+ "reducing missed diagnoses, and improving workflow efficiency."
19
  ),
20
+ "sample_images": ["msk/Gleamer Bone View.png", "msk/overlay.png"],
21
  "roi_inputs": {
22
+ "baseline_volume": 15000,
23
+ "avg_rev_per_study": 6000,
24
+ "mins_saved_per_study": 5,
25
+ "cost_per_rad_hour": 5200,
26
+ "baseline_repeats": 750,
27
+ "cost_per_repeat": 6000,
28
+ "repeat_reduction_pct": 10,
29
+ "program_cost_annual": 1200000
30
  },
31
  "evidence": [
32
+ "Time savings in fracture detection reported in multi-center settings.",
33
+ "Reduced missed fractures through AI-assisted reads.",
34
+ "High agreement with subspecialty musculoskeletal radiologists in fracture detection."
35
  ],
36
  "methodology": (
37
  "Gross benefit = Efficiency savings + Savings from reduced repeats.\n"
 
45
  # ------------------------------
46
  # ROI math
47
  # ------------------------------
48
+
49
+ def compute_roi(period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
50
+ cost_per_rad_hour, baseline_repeats, cost_per_repeat,
51
+ repeat_reduction_pct, program_cost_annual):
 
 
 
 
 
 
 
 
 
52
  eff_savings_annual = (mins_saved_per_study / 60.0) * baseline_volume * cost_per_rad_hour
53
  repeats_avoided_annual = baseline_repeats * (repeat_reduction_pct / 100.0)
54
  repeat_savings_annual = repeats_avoided_annual * cost_per_repeat
 
60
  roi_pct = (net_benefit_annual / program_cost * 100.0) if program_cost else 0.0
61
  payback_months = (program_cost / (gross_benefit_annual / 12.0)) if gross_benefit_annual > 0 else math.inf
62
 
 
63
  hours_saved_annual = (mins_saved_per_study / 60.0) * baseline_volume
64
+ fte_saved_eq = hours_saved_annual / 1920.0
65
 
 
66
  factor = 1 if period == "Annual" else 1/12
67
 
68
  metrics = {
 
75
  "Payback (months)": (round(payback_months, 1) if payback_months != math.inf else float("inf")),
76
  "Hours saved / year": round(hours_saved_annual, 1),
77
  "FTE saved (β‰ˆ1920h/yr)": round(fte_saved_eq, 2),
 
78
  "Avg revenue per study (input)": avg_rev_per_study,
79
  }
80
  return metrics
81
 
82
 
83
  def waterfall_plot(metrics: dict):
 
84
  fig = plt.figure()
85
  components = ["Efficiency savings", "Repeat savings", "Program cost"]
86
  deltas = [metrics[c] for c in components]
 
87
  deltas[2] = -abs(deltas[2])
88
 
89
  running = 0
 
90
  cumulative = [0]
91
  for d in deltas:
92
  running += d
93
  cumulative.append(running)
94
 
95
+ for i in range(len(deltas)):
 
96
  y0, y1 = cumulative[i], cumulative[i+1]
97
  plt.plot([i, i], [y0, y1], linewidth=14)
98
 
 
104
  return fig
105
 
106
 
 
 
 
 
107
  def init(period):
108
  cfg = MSK_CFG
109
  desc = cfg["description"]
 
110
  gallery = [str(ASSETS / p) for p in cfg["sample_images"] if (ASSETS / p).exists()]
111
 
112
  inputs = cfg["roi_inputs"]
113
+ metrics = compute_roi(period, **inputs)
 
 
 
 
 
 
 
 
 
 
114
  fig = waterfall_plot(metrics)
115
 
 
116
  readout = (
117
+ f"**Gleamer Bone View snapshot** β€” Net benefit: {metrics['Net benefit']:.0f}; "
118
  f"ROI (annualized): {metrics['ROI % (annualized)']}%; "
119
  f"Payback: {metrics['Payback (months)']} months.\n\n"
120
  f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β‰ˆ1920h/yr)']:.2f} FTE)."
121
  )
122
 
123
+ ev_md = "\n".join([f"- {e}" for e in cfg.get('evidence', [])]) or "_Add citations_"
124
  meth_md = cfg.get("methodology", "")
125
 
126
+ defaults = list(inputs.values())
 
 
 
 
 
 
 
 
 
 
127
  return desc, gallery, *defaults, metrics, fig, readout, ev_md, meth_md
128
 
129
 
130
+ def recalc(period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
 
131
  cost_per_rad_hour, baseline_repeats, cost_per_repeat,
132
  repeat_reduction_pct, program_cost_annual):
133
+ metrics = compute_roi(period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
134
+ cost_per_rad_hour, baseline_repeats, cost_per_repeat,
135
+ repeat_reduction_pct, program_cost_annual)
 
 
 
136
  fig = waterfall_plot(metrics)
137
  readout = (
138
+ f"**Gleamer Bone View snapshot** β€” Net benefit: {metrics['Net benefit']:.0f}; "
139
  f"ROI (annualized): {metrics['ROI % (annualized)']}%; "
140
  f"Payback: {metrics['Payback (months)']} months.\n\n"
141
  f"Operational: {metrics['Hours saved / year']:.0f} hours saved (~{metrics['FTE saved (β‰ˆ1920h/yr)']:.2f} FTE)."
142
  )
143
  return metrics, fig, readout
144
 
145
+ with gr.Blocks(title="Gleamer Bone View ROI – Interactive", fill_height=True) as demo:
 
 
 
 
146
  gr.Markdown("""
147
+ # Gleamer Bone View ROI – Interactive
148
+ **Clinician-first sandbox**: explore a sample fracture detection case, tweak workflow assumptions, and see financial, operational, and clinical impact instantly.
149
  """)
150
 
151
  period = gr.Radio(["Annual", "Monthly"], value="Annual", label="Time basis")
 
153
  with gr.Tabs():
154
  with gr.Tab("Explore"):
155
  desc = gr.Markdown()
156
+ gallery = gr.Gallery(label="Sample case", columns=3, height=320, show_label=True)
157
 
158
  with gr.Tab("ROI Simulator"):
159
  with gr.Row():
160
  with gr.Column(scale=1, min_width=360):
161
+ baseline_volume = gr.Slider(0, 100000, value=15000, step=50, label="Annual volume")
162
  avg_rev_per_study = gr.Slider(0, 50000, value=6000, step=50, label="Avg revenue per study (info)")
163
  mins_saved_per_study = gr.Slider(0, 60, value=5, step=1, label="Minutes saved per study")
164
  cost_per_rad_hour = gr.Slider(0, 20000, value=5200, step=50, label="Radiologist cost/hour")
165
+ baseline_repeats = gr.Slider(0, 100000, value=750, step=10, label="Baseline repeats/year")
166
+ cost_per_repeat = gr.Slider(0, 50000, value=6000, step=50, label="Cost per repeat")
167
  repeat_reduction_pct = gr.Slider(0, 100, value=10, step=1, label="Repeat reduction with AI (%)")
168
  program_cost_annual = gr.Slider(0, 10000000, value=1200000, step=10000, label="Program cost (annual)")
169
  with gr.Column(scale=1):
 
177
  with gr.Tab("Methodology"):
178
  methodology_md = gr.Markdown()
179
 
180
+ period.change(init, [period], [desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study,
181
+ cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual,
182
+ metrics, chart, readout, evidence_md, methodology_md])
 
 
 
 
 
183
 
184
  for comp in [baseline_volume, avg_rev_per_study, mins_saved_per_study, cost_per_rad_hour,
185
  baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual]:
186
+ comp.change(recalc, [period, baseline_volume, avg_rev_per_study, mins_saved_per_study,
187
+ cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual],
188
+ [metrics, chart, readout])
189
+
190
+ gr.on(load=True, fn=init, inputs=[period],
191
+ outputs=[desc, gallery, baseline_volume, avg_rev_per_study, mins_saved_per_study,
192
+ cost_per_rad_hour, baseline_repeats, cost_per_repeat, repeat_reduction_pct, program_cost_annual,
193
+ metrics, chart, readout, evidence_md, methodology_md])
 
 
 
 
 
 
 
194
 
195
  if __name__ == "__main__":
196
+ demo.launch()
197
+
198
+ # ------------------------------
199
+ # requirements.txt
200
+ # ------------------------------
201
+ # gradio>=4.44.0
202
+ # matplotlib