Lars Masanneck commited on
Commit
523afc6
·
1 Parent(s): f50b9a8

Small bug fixes and enabling flie uploads

Browse files
.streamlit/config.toml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [server]
2
+ # Enable file uploads
3
+ enableXsrfProtection = false
4
+ enableCORS = false
5
+ maxUploadSize = 200
6
+
7
+ [browser]
8
+ # Gather usage stats
9
+ gatherUsageStats = false
10
+
11
+ [theme]
12
+ # Orange theme to match the app
13
+ primaryColor = "#e67e22"
14
+ backgroundColor = "#ffffff"
15
+ secondaryBackgroundColor = "#f8f9fa"
16
+ textColor = "#262730"
17
+
Dockerfile CHANGED
@@ -14,4 +14,4 @@ COPY . ./
14
  EXPOSE 8501
15
 
16
  # Run Streamlit app
17
- CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0", "--server.port=8501"]
 
14
  EXPOSE 8501
15
 
16
  # Run Streamlit app
17
+ CMD ["streamlit", "run", "Z-Score_Calculator.py", "--server.address=0.0.0.0", "--server.port=8501"]
app.py → Z-Score_Calculator.py RENAMED
File without changes
batch_utils.py CHANGED
@@ -31,6 +31,27 @@ BIOMARKER_LABELS = {
31
  "pwv": "Pulse Wave Velocity",
32
  }
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  # Biomarkers available for batch processing (excluding disabled ones)
35
  AVAILABLE_BIOMARKERS = [
36
  "nb_steps",
@@ -100,18 +121,34 @@ def process_batch_data(df: pd.DataFrame, normative_df: pd.DataFrame,
100
  result[f'{biomarker}_z'] = round(res['z_score'], 2)
101
  result[f'{biomarker}_percentile'] = round(res['percentile'], 1)
102
 
103
- # Add interpretation
104
  z = res['z_score']
105
- if z < -2:
106
- result[f'{biomarker}_interpretation'] = 'Very Low'
107
- elif z < -1:
108
- result[f'{biomarker}_interpretation'] = 'Below Average'
109
- elif z < 1:
110
- result[f'{biomarker}_interpretation'] = 'Average'
111
- elif z < 2:
112
- result[f'{biomarker}_interpretation'] = 'Above Average'
 
 
 
 
 
 
113
  else:
114
- result[f'{biomarker}_interpretation'] = 'Very High'
 
 
 
 
 
 
 
 
 
 
115
 
116
  except Exception as e:
117
  result[f'{biomarker}_z'] = 'N/A'
@@ -127,8 +164,9 @@ def process_batch_data(df: pd.DataFrame, normative_df: pd.DataFrame,
127
  return pd.DataFrame(results)
128
 
129
 
130
- def create_z_score_gauge(z_score: float, label: str, width: float = 350, height: float = 100) -> Drawing:
131
- """Create a horizontal gauge showing z-score position with orange theme."""
 
132
  d = Drawing(width, height)
133
 
134
  gauge_y = 35
@@ -136,15 +174,29 @@ def create_z_score_gauge(z_score: float, label: str, width: float = 350, height:
136
  gauge_left = 50
137
  gauge_width = width - 100
138
 
139
- # Color zones - orange themed
140
- zone_colors = [
141
- (colors.HexColor('#2ecc71'), -2), # Green - very low (good for some metrics)
142
- (colors.HexColor('#27ae60'), -1), # Darker green
143
- (colors.HexColor('#f39c12'), 0), # Orange - average
144
- (colors.HexColor('#e67e22'), 1), # Darker orange
145
- (colors.HexColor('#d35400'), 2), # Deep orange
146
- (colors.HexColor('#c0392b'), 3), # Red - extreme
147
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  zone_width = gauge_width / 6
150
  for i, (color, _) in enumerate(zone_colors):
@@ -282,17 +334,33 @@ def generate_pdf_report(patient_info: dict, measurements: dict, z_scores: dict =
282
  value = measurements.get(biomarker, 'N/A')
283
  label = BIOMARKER_LABELS.get(biomarker, biomarker.replace('_', ' ').title())
284
 
285
- # Interpretation
286
- if z < -2:
287
- interp = "Very Low"
288
- elif z < -1:
289
- interp = "Below Average"
290
- elif z < 1:
291
- interp = "Average"
292
- elif z < 2:
293
- interp = "Above Average"
 
 
 
 
 
 
294
  else:
295
- interp = "Very High"
 
 
 
 
 
 
 
 
 
 
296
 
297
  z_data.append([label, str(value), f"{z:.2f}", f"{pct:.1f}%", interp])
298
 
@@ -312,11 +380,11 @@ def generate_pdf_report(patient_info: dict, measurements: dict, z_scores: dict =
312
  elements.append(z_table)
313
  elements.append(Spacer(1, 0.15*inch))
314
 
315
- # Add Z-score gauges
316
  for biomarker, data in z_scores.items():
317
  if isinstance(data, dict) and 'z_score' in data:
318
  label = BIOMARKER_LABELS.get(biomarker, biomarker.replace('_', ' ').title())
319
- gauge = create_z_score_gauge(data['z_score'], label)
320
  elements.append(gauge)
321
  elements.append(Spacer(1, 0.1*inch))
322
 
 
31
  "pwv": "Pulse Wave Velocity",
32
  }
33
 
34
+ # Biomarkers where HIGHER values are BETTER (more is good)
35
+ # These get green for high z-scores, concerning colors for low
36
+ HIGHER_IS_BETTER = {
37
+ "nb_steps",
38
+ "max_steps",
39
+ "mean_active_time",
40
+ "sleep_duration",
41
+ "nb_moderate_active_minutes",
42
+ "nb_vigorous_active_minutes",
43
+ }
44
+
45
+ # Biomarkers where LOWER values are BETTER (less is good)
46
+ # These get green for low z-scores, concerning colors for high
47
+ LOWER_IS_BETTER = {
48
+ "sbp",
49
+ "dbp",
50
+ "pwv",
51
+ "avg_night_hr",
52
+ "weight",
53
+ }
54
+
55
  # Biomarkers available for batch processing (excluding disabled ones)
56
  AVAILABLE_BIOMARKERS = [
57
  "nb_steps",
 
121
  result[f'{biomarker}_z'] = round(res['z_score'], 2)
122
  result[f'{biomarker}_percentile'] = round(res['percentile'], 1)
123
 
124
+ # Context-aware interpretation
125
  z = res['z_score']
126
+ higher_is_better = biomarker in HIGHER_IS_BETTER
127
+
128
+ if higher_is_better:
129
+ # For steps, sleep, activity: high is good
130
+ if z < -2:
131
+ result[f'{biomarker}_interpretation'] = 'Very Low ⚠️'
132
+ elif z < -1:
133
+ result[f'{biomarker}_interpretation'] = 'Below Average'
134
+ elif z < 1:
135
+ result[f'{biomarker}_interpretation'] = 'Average'
136
+ elif z < 2:
137
+ result[f'{biomarker}_interpretation'] = 'Above Average ✓'
138
+ else:
139
+ result[f'{biomarker}_interpretation'] = 'Excellent ✓✓'
140
  else:
141
+ # For HR, BP, PWV: low is good
142
+ if z < -2:
143
+ result[f'{biomarker}_interpretation'] = 'Very Low ✓✓'
144
+ elif z < -1:
145
+ result[f'{biomarker}_interpretation'] = 'Below Average ✓'
146
+ elif z < 1:
147
+ result[f'{biomarker}_interpretation'] = 'Average'
148
+ elif z < 2:
149
+ result[f'{biomarker}_interpretation'] = 'Above Average'
150
+ else:
151
+ result[f'{biomarker}_interpretation'] = 'Elevated ⚠️'
152
 
153
  except Exception as e:
154
  result[f'{biomarker}_z'] = 'N/A'
 
164
  return pd.DataFrame(results)
165
 
166
 
167
+ def create_z_score_gauge(z_score: float, label: str, biomarker: str = None,
168
+ width: float = 350, height: float = 100) -> Drawing:
169
+ """Create a horizontal gauge showing z-score position with context-aware coloring."""
170
  d = Drawing(width, height)
171
 
172
  gauge_y = 35
 
174
  gauge_left = 50
175
  gauge_width = width - 100
176
 
177
+ # Determine if higher is better for this biomarker
178
+ higher_is_better = biomarker in HIGHER_IS_BETTER if biomarker else False
179
+
180
+ if higher_is_better:
181
+ # For steps, sleep, activity: LOW is bad (red), HIGH is good (green)
182
+ zone_colors = [
183
+ (colors.HexColor('#c0392b'), -3), # Red - very low (bad)
184
+ (colors.HexColor('#e74c3c'), -2), # Lighter red
185
+ (colors.HexColor('#f39c12'), -1), # Orange - below average
186
+ (colors.HexColor('#f1c40f'), 0), # Yellow - average
187
+ (colors.HexColor('#2ecc71'), 1), # Light green - above average
188
+ (colors.HexColor('#27ae60'), 2), # Green - high (good)
189
+ ]
190
+ else:
191
+ # For BP, HR, PWV: HIGH is bad (red), LOW is good (green)
192
+ zone_colors = [
193
+ (colors.HexColor('#27ae60'), -3), # Green - very low (good)
194
+ (colors.HexColor('#2ecc71'), -2), # Light green
195
+ (colors.HexColor('#f1c40f'), -1), # Yellow - average
196
+ (colors.HexColor('#f39c12'), 0), # Orange
197
+ (colors.HexColor('#e74c3c'), 1), # Lighter red - elevated
198
+ (colors.HexColor('#c0392b'), 2), # Red - high (bad)
199
+ ]
200
 
201
  zone_width = gauge_width / 6
202
  for i, (color, _) in enumerate(zone_colors):
 
334
  value = measurements.get(biomarker, 'N/A')
335
  label = BIOMARKER_LABELS.get(biomarker, biomarker.replace('_', ' ').title())
336
 
337
+ # Context-aware interpretation
338
+ higher_is_better = biomarker in HIGHER_IS_BETTER
339
+
340
+ if higher_is_better:
341
+ # For steps, sleep, activity: high is good
342
+ if z < -2:
343
+ interp = "Very Low ⚠️"
344
+ elif z < -1:
345
+ interp = "Below Average"
346
+ elif z < 1:
347
+ interp = "Average"
348
+ elif z < 2:
349
+ interp = "Above Average ✓"
350
+ else:
351
+ interp = "Excellent ✓✓"
352
  else:
353
+ # For HR, BP, PWV: low is good
354
+ if z < -2:
355
+ interp = "Very Low ✓✓"
356
+ elif z < -1:
357
+ interp = "Below Average ✓"
358
+ elif z < 1:
359
+ interp = "Average"
360
+ elif z < 2:
361
+ interp = "Above Average"
362
+ else:
363
+ interp = "Elevated ⚠️"
364
 
365
  z_data.append([label, str(value), f"{z:.2f}", f"{pct:.1f}%", interp])
366
 
 
380
  elements.append(z_table)
381
  elements.append(Spacer(1, 0.15*inch))
382
 
383
+ # Add Z-score gauges with context-aware coloring
384
  for biomarker, data in z_scores.items():
385
  if isinstance(data, dict) and 'z_score' in data:
386
  label = BIOMARKER_LABELS.get(biomarker, biomarker.replace('_', ' ').title())
387
+ gauge = create_z_score_gauge(data['z_score'], label, biomarker=biomarker)
388
  elements.append(gauge)
389
  elements.append(Spacer(1, 0.1*inch))
390
 
pages/2_PDF_Report.py CHANGED
@@ -9,7 +9,7 @@ import os
9
 
10
  # Add parent directory to path for imports
11
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
12
- from batch_utils import generate_pdf_report, BIOMARKER_LABELS, AVAILABLE_BIOMARKERS
13
  import normalizer_model
14
 
15
  st.set_page_config(
@@ -218,17 +218,33 @@ if st.button("📄 Generate PDF Report", type="primary"):
218
  pct = data['percentile']
219
  value = measurements[biomarker]
220
 
221
- # Determine interpretation
222
- if z < -2:
223
- interp = "Very Low"
224
- elif z < -1:
225
- interp = "Below Average"
226
- elif z < 1:
227
- interp = "Average"
228
- elif z < 2:
229
- interp = "Above Average"
 
 
 
 
 
 
230
  else:
231
- interp = "Very High"
 
 
 
 
 
 
 
 
 
 
232
 
233
  st.metric(
234
  label,
 
9
 
10
  # Add parent directory to path for imports
11
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
12
+ from batch_utils import generate_pdf_report, BIOMARKER_LABELS, AVAILABLE_BIOMARKERS, HIGHER_IS_BETTER
13
  import normalizer_model
14
 
15
  st.set_page_config(
 
218
  pct = data['percentile']
219
  value = measurements[biomarker]
220
 
221
+ # Context-aware interpretation
222
+ higher_is_better = biomarker in HIGHER_IS_BETTER
223
+
224
+ if higher_is_better:
225
+ # For steps, sleep, activity: high is good
226
+ if z < -2:
227
+ interp = "Very Low ⚠️"
228
+ elif z < -1:
229
+ interp = "Below Average"
230
+ elif z < 1:
231
+ interp = "Average"
232
+ elif z < 2:
233
+ interp = "Above Average ✓"
234
+ else:
235
+ interp = "Excellent ✓✓"
236
  else:
237
+ # For HR: low is good
238
+ if z < -2:
239
+ interp = "Very Low ✓✓"
240
+ elif z < -1:
241
+ interp = "Below Average ✓"
242
+ elif z < 1:
243
+ interp = "Average"
244
+ elif z < 2:
245
+ interp = "Above Average"
246
+ else:
247
+ interp = "Elevated ⚠️"
248
 
249
  st.metric(
250
  label,