Rammohan0504 commited on
Commit
77209a6
·
verified ·
1 Parent(s): d8407ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -127
app.py CHANGED
@@ -9,25 +9,17 @@ import base64
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:
30
- print(f"Failed to clean up {temp_file}: {e}")
31
  atexit.register(cleanup_temp_files)
32
 
33
  # Initialize the face mesh model
@@ -98,107 +90,120 @@ models = {
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
 
203
  pdf_filename = os.path.basename(pdf_filepath) if pdf_filepath else "health_report.pdf"
204
 
@@ -241,6 +246,7 @@ def build_health_card(profile_image, test_results, summary, pdf_filepath, patien
241
  </div>
242
 
243
  <div style="display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;">
 
244
  <button disabled style="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);">
245
  📥 Download Report
246
  </button>
@@ -265,7 +271,7 @@ current_patient_details = {'name': '', 'age': '', 'gender': '', 'id': ''}
265
 
266
  # Modified analyze_face function
267
  def analyze_face(input_data):
268
- if isinstance(input_data, str): # Video input (file path in Spaces)
269
  cap = cv2.VideoCapture(input_data)
270
  if not cap.isOpened():
271
  return "<div style='color:red;'>⚠️ Error: Could not open video.</div>", None
@@ -315,23 +321,23 @@ def analyze_face(input_data):
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>"
@@ -340,16 +346,7 @@ def analyze_face(input_data):
340
  profile_image_base64 = base64.b64encode(buffer).decode('utf-8')
341
 
342
  pdf_filename = f"Health_Report_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.pdf"
343
- pdf_result, pdf_filepath = save_results_to_pdf(
344
- profile_image_base64,
345
- test_results,
346
- summary,
347
- current_patient_details['name'],
348
- current_patient_details['age'],
349
- current_patient_details['gender'],
350
- current_patient_details['id'],
351
- pdf_filename
352
- )
353
 
354
  if pdf_filepath:
355
  temp_pdf_path = "/tmp/" + os.path.basename(pdf_filepath)
@@ -390,7 +387,7 @@ def route_inputs(mode, image, video, patient_name, patient_age, patient_gender,
390
 
391
  # Create Gradio interface
392
  with gr.Blocks() as demo:
393
- gr.Markdown("""# 🧠 Face-Based Lab Test AI Report""")
394
  with gr.Row():
395
  with gr.Column():
396
  gr.Markdown("### Patient Information")
@@ -408,12 +405,12 @@ with gr.Blocks() as demo:
408
  sources=["upload", "webcam"])
409
  submit_btn = gr.Button("🔍 Analyze")
410
  with gr.Column():
411
- result_html = gr.HTML(label="🧪 Health Report")
412
  result_pdf = gr.File(label="Download Health Report PDF", interactive=False)
413
 
414
  submit_btn.click(fn=route_inputs,
415
  inputs=[mode_selector, image_input, video_input, patient_name, patient_age, patient_gender, patient_id],
416
  outputs=[result_html, result_pdf])
417
 
418
- # Launch Gradio for Hugging Face Spaces
419
- demo.launch()
 
9
  import joblib
10
  from datetime import datetime
11
  import shutil
 
 
12
  from reportlab.lib.pagesizes import letter
13
  from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
14
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
15
+ from reportlab.lib import colors
16
  import atexit
17
+ import glob
18
 
19
  # Cleanup temporary files on exit
20
  def cleanup_temp_files():
21
+ for temp_file in glob.glob("/tmp/Health_Report_*.pdf"):
22
+ os.remove(temp_file)
 
 
 
 
 
 
 
 
23
  atexit.register(cleanup_temp_files)
24
 
25
  # Initialize the face mesh model
 
90
  def get_risk_color(value, normal_range):
91
  low, high = normal_range
92
  if value < low:
93
+ return ("Low", "🔻", "#fff3cd")
94
  elif value > high:
95
+ return ("High", "🔺", "#f8d7da")
96
  else:
97
+ return ("Normal", "✅", "#d4edda")
98
+
99
+ # Function to build table for test results
100
+ def build_table(title, rows):
101
+ color_map = {
102
+ "Normal": "#28a745",
103
+ "High": "#dc3545",
104
+ "Low": "#ffc107"
105
+ }
106
+
107
+ html = (
108
+ f'<div style="margin-bottom: 25px; border-radius: 8px; overflow: hidden; border: 1px solid #e0e0e0;">'
109
+ f'<div style="background: linear-gradient(135deg, #f5f7fa, #c3cfe2); padding: 12px 16px; border-bottom: 1px solid #e0e0e0;">'
110
+ f'<h4 style="margin: 0; color: #2c3e50; font-size: 16px; font-weight: 600;">{title}</h4>'
111
+ f'</div>'
112
+ f'<table style="width:100%; border-collapse:collapse; background: white;">'
113
+ 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>'
114
+ )
115
+ for i, (label, value, ref) in enumerate(rows):
116
+ level, icon, bg = get_risk_color(value, ref)
117
+ row_bg = "#f8f9fa" if i % 2 == 0 else "white"
118
+ if level != "Normal":
119
+ row_bg = bg
120
+
121
  if "Count" in label or "Platelet" in label:
122
  value_str = f"{value:.0f}"
123
  else:
124
  value_str = f"{value:.2f}"
125
+
126
+ 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>'
127
+ html += '</tbody></table></div>'
128
+ return html
 
 
 
129
 
130
+ # Function to save the health report to PDF
131
+ def save_results_to_pdf(test_results, test_values, filename):
132
  try:
133
+ doc = SimpleDocTemplate(filename, pagesize=letter)
 
 
 
 
134
  styles = getSampleStyleSheet()
135
+ title_style = ParagraphStyle(
136
+ name='Title',
137
+ fontSize=16,
138
+ leading=20,
139
+ alignment=1,
140
+ spaceAfter=20,
141
+ textColor=colors.black,
142
+ fontName='Helvetica-Bold'
143
+ )
144
+ body_style = ParagraphStyle(
145
+ name='Body',
146
+ fontSize=12,
147
+ leading=14,
148
+ spaceAfter=10,
149
+ textColor=colors.black,
150
+ fontName='Helvetica'
151
+ )
152
+
153
+ flowables = [Paragraph("Health Report", title_style), Spacer(1, 12)]
154
+
155
+ # Define ranges for all tests
156
+ test_ranges = {
157
+ "Hemoglobin": (13.5, 17.5),
158
+ "WBC Count": (4.0, 11.0),
159
+ "Platelet Count": (150, 450),
160
+ "Iron": (60, 170),
161
+ "Ferritin": (30, 300),
162
+ "TIBC": (250, 400),
163
+ "Bilirubin": (0.3, 1.2),
164
+ "Creatinine": (0.6, 1.2),
165
+ "Urea": (7, 20),
166
+ "Sodium": (135, 145),
167
+ "Potassium": (3.5, 5.1),
168
+ "SpO2": (95, 100),
169
+ "Heart Rate": (60, 100),
170
+ "Respiratory Rate": (12, 20),
171
+ "Temperature": (97, 99),
172
+ "BP Systolic": (90, 120),
173
+ "BP Diastolic": (60, 80)
174
+ }
175
+
176
+ for section_name, html in test_results.items():
177
+ flowables.append(Paragraph(section_name, styles['Heading2']))
178
+ table_data = [["Test", "Result", "Range", "Level"]]
179
+ for label in test_ranges:
180
+ if any(label in html for section_html in test_results.values()):
181
+ value = test_values.get(label, test_ranges[label][0] + random.uniform(-1, 1)) # Use actual value or fallback
182
+ level, _, _ = get_risk_color(value, test_ranges[label])
183
+ table_data.append([label, f"{value:.2f}" if label not in ["WBC Count", "Platelet Count"] else f"{value:.0f}", f"{test_ranges[label][0]} - {test_ranges[label][1]}", level])
184
+ table = Table(table_data)
185
  table.setStyle(TableStyle([
186
+ ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
187
+ ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
188
  ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
189
  ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
190
  ('FONTSIZE', (0, 0), (-1, 0), 12),
191
  ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
192
+ ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
193
+ ('GRID', (0, 0), (-1, -1), 1, colors.black)
 
 
 
 
 
194
  ]))
195
+ flowables.append(table)
196
+ flowables.append(Spacer(1, 12))
197
+
198
+ doc.build(flowables)
199
+ return f"PDF saved successfully as {filename}", filename
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  except Exception as e:
201
+ return f"Error saving PDF: {str(e)}", None
 
202
 
203
+ # Build health card layout
204
  def build_health_card(profile_image, test_results, summary, pdf_filepath, patient_name="", patient_age="", patient_gender="", patient_id=""):
205
+ from datetime import datetime
206
+ current_date = datetime.now().strftime("%B %d, %Y")
207
 
208
  pdf_filename = os.path.basename(pdf_filepath) if pdf_filepath else "health_report.pdf"
209
 
 
246
  </div>
247
 
248
  <div style="display: flex; gap: 15px; justify-content: center; flex-wrap: wrap;">
249
+ <!-- Disabled the direct download link to rely on Gradio File component -->
250
  <button disabled style="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);">
251
  📥 Download Report
252
  </button>
 
271
 
272
  # Modified analyze_face function
273
  def analyze_face(input_data):
274
+ if isinstance(input_data, str): # Video input (file path in Replit)
275
  cap = cv2.VideoCapture(input_data)
276
  if not cap.isOpened():
277
  return "<div style='color:red;'>⚠️ Error: Could not open video.</div>", None
 
321
  })
322
 
323
  test_results = {
324
+ "Hematology": build_table("🩸 Hematology", [("Hemoglobin", test_values["Hemoglobin"], (13.5, 17.5)),
325
+ ("WBC Count", test_values["WBC Count"], (4.0, 11.0)),
326
+ ("Platelet Count", test_values["Platelet Count"], (150, 450))]),
327
+ "Iron Panel": build_table("🧬 Iron Panel", [("Iron", test_values["Iron"], (60, 170)),
328
+ ("Ferritin", test_values["Ferritin"], (30, 300)),
329
+ ("TIBC", test_values["TIBC"], (250, 400))]),
330
+ "Liver & Kidney": build_table("🧬 Liver & Kidney", [("Bilirubin", test_values["Bilirubin"], (0.3, 1.2)),
331
+ ("Creatinine", test_values["Creatinine"], (0.6, 1.2)),
332
+ ("Urea", test_values["Urea"], (7, 20))]),
333
+ "Electrolytes": build_table("🧪 Electrolytes", [("Sodium", test_values["Sodium"], (135, 145)),
334
+ ("Potassium", test_values["Potassium"], (3.5, 5.1))]),
335
+ "Vitals": build_table("❤️ Vitals", [("SpO2", test_values["SpO2"], (95, 100)),
336
+ ("Heart Rate", test_values["Heart Rate"], (60, 100)),
337
+ ("Respiratory Rate", test_values["Respiratory Rate"], (12, 20)),
338
+ ("Temperature", test_values["Temperature"], (97, 99)),
339
+ ("BP Systolic", test_values["BP Systolic"], (90, 120)),
340
+ ("BP Diastolic", test_values["BP Diastolic"], (60, 80))])
341
  }
342
 
343
  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>"
 
346
  profile_image_base64 = base64.b64encode(buffer).decode('utf-8')
347
 
348
  pdf_filename = f"Health_Report_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.pdf"
349
+ pdf_result, pdf_filepath = save_results_to_pdf(test_results, test_values, pdf_filename)
 
 
 
 
 
 
 
 
 
350
 
351
  if pdf_filepath:
352
  temp_pdf_path = "/tmp/" + os.path.basename(pdf_filepath)
 
387
 
388
  # Create Gradio interface
389
  with gr.Blocks() as demo:
390
+ gr.Markdown("""# 🧠 Face-Based Lab Test AI Report (Video Mode)""")
391
  with gr.Row():
392
  with gr.Column():
393
  gr.Markdown("### Patient Information")
 
405
  sources=["upload", "webcam"])
406
  submit_btn = gr.Button("🔍 Analyze")
407
  with gr.Column():
408
+ result_html = gr.HTML(label="🧪 Health Report Table")
409
  result_pdf = gr.File(label="Download Health Report PDF", interactive=False)
410
 
411
  submit_btn.click(fn=route_inputs,
412
  inputs=[mode_selector, image_input, video_input, patient_name, patient_age, patient_gender, patient_id],
413
  outputs=[result_html, result_pdf])
414
 
415
+ # Launch Gradio for Replit
416
+ demo.launch(server_name="0.0.0.0", server_port=7860)