Rammohan0504 commited on
Commit
d8407ba
·
verified ·
1 Parent(s): 87fa542

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -161
app.py CHANGED
@@ -9,18 +9,21 @@ import base64
9
  import joblib
10
  from datetime import datetime
11
  import shutil
12
- import pdfkit
13
- import atexit
14
  import glob
 
 
 
 
 
15
 
16
  # Cleanup temporary files on exit
17
  def cleanup_temp_files():
18
- for temp_file in glob.glob("/tmp/Health_Report_*.pdf"):
19
  try:
20
  os.remove(temp_file)
21
  except Exception as e:
22
  print(f"Failed to clean up {temp_file}: {e}")
23
- for temp_file in glob.glob("/tmp/health_card*.html"):
24
  try:
25
  os.remove(temp_file)
26
  except Exception as e:
@@ -95,159 +98,105 @@ models = {
95
  def get_risk_color(value, normal_range):
96
  low, high = normal_range
97
  if value < low:
98
- return ("Low", "🔻", "#fff3cd")
99
  elif value > high:
100
- return ("High", "🔺", "#f8d7da")
101
  else:
102
- return ("Normal", "✅", "#d4edda")
103
-
104
- # Function to build table for test results
105
- def build_table(title, rows):
106
- color_map = {
107
- "Normal": "#28a745",
108
- "High": "#dc3545",
109
- "Low": "#ffc107"
110
- }
111
-
112
- html = (
113
- f'<div style="margin-bottom: 25px; border-radius: 8px; overflow: hidden; border: 1px solid #e0e0e0;">'
114
- f'<div style="background: linear-gradient(135deg, #f5f7fa, #c3cfe2); padding: 12px 16px; border-bottom: 1px solid #e0e0e0;">'
115
- f'<h4 style="margin: 0; color: #2c3e50; font-size: 16px; font-weight: 600;">{title}</h4>'
116
- f'</div>'
117
- f'<table style="width:100%; border-collapse:collapse; background: white;">'
118
- f'<thead><tr style="background:#f8f9fa;"><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:left;font-size:13px;">Test</th><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:center;font-size:13px;">Result</th><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:center;font-size:13px;">Range</th><th style="padding:12px 8px;border-bottom:2px solid #dee2e6;color:#495057;font-weight:600;text-align:center;font-size:13px;">Level</th></tr></thead><tbody>'
119
- )
120
- for i, (label, value, ref) in enumerate(rows):
121
- level, icon, bg = get_risk_color(value, ref)
122
- row_bg = "#f8f9fa" if i % 2 == 0 else "white"
123
- if level != "Normal":
124
- row_bg = bg
125
-
126
  if "Count" in label or "Platelet" in label:
127
  value_str = f"{value:.0f}"
128
  else:
129
  value_str = f"{value:.2f}"
130
-
131
- html += f'<tr style="background:{row_bg};border-bottom:1px solid #e9ecef;"><td style="padding:10px 8px;color:#2c3e50;font-weight:500;">{label}</td><td style="padding:10px 8px;text-align:center;color:#2c3e50;font-weight:600;">{value_str}</td><td style="padding:10px 8px;text-align:center;color:#6c757d;font-size:12px;">{ref[0]} - {ref[1]}</td><td style="padding:10px 8px;text-align:center;font-weight:600;color:{color_map[level]};">{icon} {level}</td></tr>'
132
- html += '</tbody></table></div>'
133
- return html
134
-
135
- # Function to save the health report to PDF
 
 
 
136
  def save_results_to_pdf(profile_image_base64, test_results, summary, patient_name, patient_age, patient_gender, patient_id, filename):
137
  try:
138
- # Generate HTML content
139
- current_date = datetime.now().strftime("%B %d, %Y %I:%M %p IST")
140
- health_card_html = f"""
141
- <!DOCTYPE html>
142
- <html>
143
- <head>
144
- <style>
145
- body {{ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 700px; margin: 20px auto; color: #1a1a1a; }}
146
- #health-card {{ border-radius: 16px; background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%); border: 2px solid #ddd; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); padding: 30px; }}
147
- .header {{ background-color: rgba(255, 255, 255, 0.9); border-radius: 12px; padding: 20px; margin-bottom: 25px; border: 1px solid #e0e0e0; display: flex; align-items: center; }}
148
- .header-title {{ background: linear-gradient(135deg, #64b5f6, #42a5f5); padding: 8px 16px; border-radius: 8px; margin-right: 20px; }}
149
- .header-title h3 {{ margin: 0; color: white; font-size: 16px; font-weight: 600; }}
150
- .header-date {{ margin-left: auto; text-align: right; color: #666; font-size: 12px; }}
151
- .profile {{ display: flex; align-items: center; }}
152
- .profile img {{ width: 90px; height: 90px; border-radius: 50%; margin-right: 20px; border: 3px solid #fff; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }}
153
- .results {{ background-color: rgba(255, 255, 255, 0.95); border-radius: 12px; padding: 25px; margin-bottom: 25px; border: 1px solid #e0e0e0; }}
154
- .summary {{ background-color: rgba(255, 255, 255, 0.95); padding: 20px; border-radius: 12px; border: 1px solid #e0e0e0; margin-bottom: 25px; }}
155
- .summary h4 {{ margin: 0 0 15px 0; color: #2c3e50; font-size: 18px; font-weight: 600; }}
156
- .buttons {{ display: flex; gap: 15px; justify-content: center; flex-wrap: wrap; }}
157
- button:disabled {{ padding: 12px 24px; background: #ccc; color: white; border: none; border-radius: 8px; cursor: not-allowed; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); }}
158
- button {{ padding: 12px 24px; background: linear-gradient(135deg, #2196f3, #1976d2); color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3); }}
159
- @media print {{ .gradio-container {{ display: block; }} #health-card {{ display: block; }} }}
160
- </style>
161
- </head>
162
- <body>
163
- <div id="health-card">
164
- <div class="header">
165
- <div class="header-title"><h3>HEALTH CARD</h3></div>
166
- <div class="header-date">
167
- <div>Report Date: {current_date}</div>
168
- {f'<div>Patient ID: {patient_id}</div>' if patient_id else ''}
169
- </div>
170
- </div>
171
- <div class="profile">
172
- <img src="data:image/png;base64,{profile_image_base64}" alt="Profile">
173
- <div>
174
- <h2>{patient_name if patient_name else 'Lab Test Results'}</h2>
175
- <p>{f'Age: {patient_age} | Gender: {patient_gender}' if patient_age and patient_gender else 'AI-Generated Health Analysis'}</p>
176
- <p>Face-Based Health Analysis Report</p>
177
- </div>
178
- </div>
179
- <div class="results">
180
- {test_results['Hematology']}
181
- {test_results['Iron Panel']}
182
- {test_results['Liver & Kidney']}
183
- {test_results['Electrolytes']}
184
- {test_results['Vitals']}
185
- </div>
186
- <div class="summary">
187
- <h4>📝 Summary & Recommendations</h4>
188
- <div>{summary}</div>
189
- </div>
190
- <div class="buttons">
191
- <button disabled>📥 Download Report</button>
192
- <button>📞 Find Labs Near Me</button>
193
- </div>
194
- </div>
195
- </body>
196
- </html>
197
- """
198
 
199
- # Debug: Print HTML length and sample
200
- print(f"HTML generated, length: {len(health_card_html)}")
201
- print(f"HTML sample: {health_card_html[:500]}...") # Print first 500 chars for inspection
202
-
203
- # Save HTML to a temporary file
204
- html_temp_path = "/tmp/health_card.html"
205
- try:
206
- os.makedirs(os.path.dirname(html_temp_path), exist_ok=True)
207
- with open(html_temp_path, "w", encoding="utf-8") as f:
208
- f.write(health_card_html)
209
- print(f"HTML saved to: {html_temp_path}")
210
- except Exception as e:
211
- print(f"Error saving HTML: {e}")
212
- return f"Error: Failed to save HTML - {str(e)}", None
213
-
214
- # Convert HTML to PDF using pdfkit with verbose logging
215
- try:
216
- pdfkit_options = {
217
- "quiet": "",
218
- "enable-local-file-access": "", # Allow local file access for images
219
- "margin-top": "0",
220
- "margin-bottom": "0",
221
- "margin-left": "0",
222
- "margin-right": "0"
223
- }
224
- pdfkit.from_file(html_temp_path, filename, options=pdfkit_options)
225
- print(f"PDF generated successfully at: {filename}")
226
- except Exception as e:
227
- print(f"Error converting to PDF: {str(e)}")
228
- # Save fallback HTML with timestamp
229
- fallback_path = f"/tmp/health_card_fallback_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html"
230
- with open(fallback_path, "w", encoding="utf-8") as f:
231
- f.write(health_card_html)
232
- print(f"Fallback HTML saved to: {fallback_path}")
233
- return f"Error: Failed to generate PDF - {str(e)}. Fallback HTML at {fallback_path}", None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
 
235
  # Move to /tmp for Gradio access
236
  temp_pdf_path = "/tmp/" + os.path.basename(filename)
237
- try:
238
- shutil.copy(filename, temp_pdf_path)
239
- if os.path.exists(temp_pdf_path) and os.access(temp_pdf_path, os.R_OK):
240
- return f"PDF saved successfully as {filename}", temp_pdf_path
241
- else:
242
- return "Error: PDF file not accessible.", None
243
- except Exception as e:
244
- print(f"Error moving PDF: {e}")
245
- return f"Error: Failed to move PDF - {str(e)}", None
246
  except Exception as e:
247
- print(f"Unexpected error in save_results_to_pdf: {str(e)}")
248
- return f"Error: Unexpected failure - {str(e)}", None
249
 
250
- # Build health card layout
251
  def build_health_card(profile_image, test_results, summary, pdf_filepath, patient_name="", patient_age="", patient_gender="", patient_id=""):
252
  current_date = datetime.now().strftime("%B %d, %Y %I:%M %p IST")
253
 
@@ -305,7 +254,7 @@ def build_health_card(profile_image, test_results, summary, pdf_filepath, patien
305
  /* Hide input sections during print */
306
  .gradio-container {{ display: block; }}
307
  /* Keep only the health card visible */
308
- #health-card {{ display: block; }}
309
  }}
310
  </style>
311
  """
@@ -366,23 +315,23 @@ def analyze_face(input_data):
366
  })
367
 
368
  test_results = {
369
- "Hematology": build_table("🩸 Hematology", [("Hemoglobin", test_values["Hemoglobin"], (13.5, 17.5)),
370
- ("WBC Count", test_values["WBC Count"], (4.0, 11.0)),
371
- ("Platelet Count", test_values["Platelet Count"], (150, 450))]),
372
- "Iron Panel": build_table("🧬 Iron Panel", [("Iron", test_values["Iron"], (60, 170)),
373
- ("Ferritin", test_values["Ferritin"], (30, 300)),
374
- ("TIBC", test_values["TIBC"], (250, 400))]),
375
- "Liver & Kidney": build_table("🧬 Liver & Kidney", [("Bilirubin", test_values["Bilirubin"], (0.3, 1.2)),
376
- ("Creatinine", test_values["Creatinine"], (0.6, 1.2)),
377
- ("Urea", test_values["Urea"], (7, 20))]),
378
- "Electrolytes": build_table("🧪 Electrolytes", [("Sodium", test_values["Sodium"], (135, 145)),
379
- ("Potassium", test_values["Potassium"], (3.5, 5.1))]),
380
- "Vitals": build_table("❤️ Vitals", [("SpO2", test_values["SpO2"], (95, 100)),
381
- ("Heart Rate", test_values["Heart Rate"], (60, 100)),
382
- ("Respiratory Rate", test_values["Respiratory Rate"], (12, 20)),
383
- ("Temperature", test_values["Temperature"], (97, 99)),
384
- ("BP Systolic", test_values["BP Systolic"], (90, 120)),
385
- ("BP Diastolic", test_values["BP Diastolic"], (60, 80))])
386
  }
387
 
388
  summary = "<ul><li>Your hemoglobin is a bit low — this could mean mild anemia.</li><li>Low iron storage detected — consider an iron profile test.</li><li>Elevated bilirubin — possible jaundice. Recommend LFT.</li><li>High HbA1c — prediabetes indication. Recommend glucose check.</li><li>Low SpO₂ — suggest retesting with a pulse oximeter.</li></ul>"
 
9
  import joblib
10
  from datetime import datetime
11
  import shutil
 
 
12
  import glob
13
+ from reportlab.lib import colors
14
+ from reportlab.lib.pagesizes import letter
15
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
16
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
17
+ import atexit
18
 
19
  # Cleanup temporary files on exit
20
  def cleanup_temp_files():
21
+ for temp_file in glob.glob("Health_Report_*.pdf"):
22
  try:
23
  os.remove(temp_file)
24
  except Exception as e:
25
  print(f"Failed to clean up {temp_file}: {e}")
26
+ for temp_file in glob.glob("health_card*.html"):
27
  try:
28
  os.remove(temp_file)
29
  except Exception as e:
 
98
  def get_risk_color(value, normal_range):
99
  low, high = normal_range
100
  if value < low:
101
+ return ("Low", "🔻", colors.yellow)
102
  elif value > high:
103
+ return ("High", "🔺", colors.red)
104
  else:
105
+ return ("Normal", "✅", colors.green)
106
+
107
+ # Function to build table data for PDF
108
+ def build_table_data(title, rows):
109
+ data = [[Paragraph(title, getSampleStyleSheet()['Heading2'])]]
110
+ data.append(["Test", "Result", "Range", "Level"])
111
+ for label, value, ref in rows:
112
+ level, icon, color = get_risk_color(value, ref)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  if "Count" in label or "Platelet" in label:
114
  value_str = f"{value:.0f}"
115
  else:
116
  value_str = f"{value:.2f}"
117
+ data.append([
118
+ label,
119
+ value_str,
120
+ f"{ref[0]} - {ref[1]}",
121
+ Paragraph(f"{icon} {level}", ParagraphStyle('Custom', textColor=color))
122
+ ])
123
+ return data
124
+
125
+ # Function to save the health report to PDF using reportlab
126
  def save_results_to_pdf(profile_image_base64, test_results, summary, patient_name, patient_age, patient_gender, patient_id, filename):
127
  try:
128
+ # Create PDF document
129
+ pdf = SimpleDocTemplate(filename, pagesize=letter)
130
+ elements = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
+ # Header
133
+ styles = getSampleStyleSheet()
134
+ current_date = datetime.now().strftime("%B %d, %Y %I:%M %p IST")
135
+ header_text = f"HEALTH CARD - Report Date: {current_date}"
136
+ if patient_id:
137
+ header_text += f" | Patient ID: {patient_id}"
138
+ elements.append(Paragraph(header_text, styles['Title']))
139
+ elements.append(Spacer(1, 12))
140
+
141
+ # Profile Section
142
+ profile_text = f"{patient_name if patient_name else 'Lab Test Results'}"
143
+ if patient_age and patient_gender:
144
+ profile_text += f" | Age: {patient_age} | Gender: {patient_gender}"
145
+ else:
146
+ profile_text += " | AI-Generated Health Analysis"
147
+ profile_text += " | Face-Based Health Analysis Report"
148
+ elements.append(Paragraph(profile_text, styles['Heading1']))
149
+ elements.append(Spacer(1, 12))
150
+
151
+ # Test Results Tables
152
+ for title, rows in [
153
+ ("Hematology", test_results['Hematology']),
154
+ ("Iron Panel", test_results['Iron Panel']),
155
+ ("Liver & Kidney", test_results['Liver & Kidney']),
156
+ ("Electrolytes", test_results['Electrolytes']),
157
+ ("Vitals", test_results['Vitals'])
158
+ ]:
159
+ table_data = build_table_data(title, rows)
160
+ table = Table(table_data, colWidths=[100, 70, 70, 70])
161
+ table.setStyle(TableStyle([
162
+ ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),
163
+ ('TEXTCOLOR', (0, 0), (-1, 0), colors.black),
164
+ ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
165
+ ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
166
+ ('FONTSIZE', (0, 0), (-1, 0), 12),
167
+ ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
168
+ ('BACKGROUND', (0, 1), (-1, -1), colors.white),
169
+ ('TEXTCOLOR', (0, 1), (-1, -1), colors.black),
170
+ ('ALIGN', (0, 1), (-1, -1), 'LEFT'),
171
+ ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
172
+ ('FONTSIZE', (0, 1), (-1, -1), 10),
173
+ ('GRID', (0, 0), (-1, -1), 1, colors.black),
174
+ ('VALIGN', (0, 0), (-1, -1), 'MIDDLE')
175
+ ]))
176
+ elements.append(table)
177
+ elements.append(Spacer(1, 12))
178
+
179
+ # Summary
180
+ elements.append(Paragraph("Summary & Recommendations", styles['Heading2']))
181
+ elements.append(Paragraph(summary, styles['Normal']))
182
+ elements.append(Spacer(1, 12))
183
+
184
+ # Build PDF
185
+ pdf.build(elements)
186
+ print(f"PDF generated successfully at: {filename}")
187
 
188
  # Move to /tmp for Gradio access
189
  temp_pdf_path = "/tmp/" + os.path.basename(filename)
190
+ shutil.copy(filename, temp_pdf_path)
191
+ if os.path.exists(temp_pdf_path) and os.access(temp_pdf_path, os.R_OK):
192
+ return f"PDF saved successfully as {filename}", temp_pdf_path
193
+ else:
194
+ return "Error: PDF file not accessible.", None
 
 
 
 
195
  except Exception as e:
196
+ print(f"Error generating PDF: {str(e)}")
197
+ return f"Error: Failed to generate PDF - {str(e)}", None
198
 
199
+ # Build health card layout (unchanged, using HTML for display)
200
  def build_health_card(profile_image, test_results, summary, pdf_filepath, patient_name="", patient_age="", patient_gender="", patient_id=""):
201
  current_date = datetime.now().strftime("%B %d, %Y %I:%M %p IST")
202
 
 
254
  /* Hide input sections during print */
255
  .gradio-container {{ display: block; }}
256
  /* Keep only the health card visible */
257
+ #health-card {{ display: block; }}
258
  }}
259
  </style>
260
  """
 
315
  })
316
 
317
  test_results = {
318
+ "Hematology": build_table_data("🩸 Hematology", [("Hemoglobin", test_values["Hemoglobin"], (13.5, 17.5)),
319
+ ("WBC Count", test_values["WBC Count"], (4.0, 11.0)),
320
+ ("Platelet Count", test_values["Platelet Count"], (150, 450))]),
321
+ "Iron Panel": build_table_data("🧬 Iron Panel", [("Iron", test_values["Iron"], (60, 170)),
322
+ ("Ferritin", test_values["Ferritin"], (30, 300)),
323
+ ("TIBC", test_values["TIBC"], (250, 400))]),
324
+ "Liver & Kidney": build_table_data("🧬 Liver & Kidney", [("Bilirubin", test_values["Bilirubin"], (0.3, 1.2)),
325
+ ("Creatinine", test_values["Creatinine"], (0.6, 1.2)),
326
+ ("Urea", test_values["Urea"], (7, 20))]),
327
+ "Electrolytes": build_table_data("🧪 Electrolytes", [("Sodium", test_values["Sodium"], (135, 145)),
328
+ ("Potassium", test_values["Potassium"], (3.5, 5.1))]),
329
+ "Vitals": build_table_data("❤️ Vitals", [("SpO2", test_values["SpO2"], (95, 100)),
330
+ ("Heart Rate", test_values["Heart Rate"], (60, 100)),
331
+ ("Respiratory Rate", test_values["Respiratory Rate"], (12, 20)),
332
+ ("Temperature", test_values["Temperature"], (97, 99)),
333
+ ("BP Systolic", test_values["BP Systolic"], (90, 120)),
334
+ ("BP Diastolic", test_values["BP Diastolic"], (60, 80))])
335
  }
336
 
337
  summary = "<ul><li>Your hemoglobin is a bit low — this could mean mild anemia.</li><li>Low iron storage detected — consider an iron profile test.</li><li>Elevated bilirubin — possible jaundice. Recommend LFT.</li><li>High HbA1c — prediabetes indication. Recommend glucose check.</li><li>Low SpO₂ — suggest retesting with a pulse oximeter.</li></ul>"