nihalaninihal commited on
Commit
f254709
Β·
verified Β·
1 Parent(s): f66f04d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +740 -60
app.py CHANGED
@@ -1,64 +1,744 @@
 
 
 
 
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
- """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
6
- """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
-
9
-
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
-
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
-
26
- messages.append({"role": "user", "content": message})
27
-
28
- response = ""
29
-
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
38
-
39
- response += token
40
- yield response
41
-
42
-
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
60
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
 
 
62
 
 
63
  if __name__ == "__main__":
64
- demo.launch()
 
 
1
+ # Complete Blood Report Analyzer for Hugging Face
2
+ # ==============================================
3
+
4
+ # Import Libraries
5
+ import os
6
  import gradio as gr
7
+ import google.generativeai as genai
8
+ import fitz # PyMuPDF
9
+ from PIL import Image, ImageEnhance
10
+ import io
11
+ import re
12
+ import json
13
+ import numpy as np
14
+ import pandas as pd
15
+ from datetime import datetime
16
+ import base64
17
+
18
+ # Blood Report Analyzer Implementation
19
+
20
+ # Configure Google Gemini API
21
+ def configure_genai(api_key):
22
+ genai.configure(api_key=api_key)
23
+ # Use Gemini Pro Vision for image analysis
24
+ vision_model = genai.GenerativeModel('gemini-pro-vision')
25
+ # Use Gemini Pro for text analysis (better for structured text)
26
+ text_model = genai.GenerativeModel('gemini-pro')
27
+ return vision_model, text_model
28
+
29
+ # Image preprocessing to improve OCR
30
+ def preprocess_image(image):
31
+ # Convert to grayscale
32
+ img_gray = image.convert('L')
33
+
34
+ # Enhance contrast
35
+ enhancer = ImageEnhance.Contrast(img_gray)
36
+ img_enhanced = enhancer.enhance(2.0)
37
+
38
+ # Increase sharpness
39
+ sharpness = ImageEnhance.Sharpness(img_enhanced)
40
+ img_sharp = sharpness.enhance(2.0)
41
+
42
+ return img_sharp
43
+
44
+ # Extract text from PDF with advanced techniques
45
+ def extract_text_from_pdf(pdf_file):
46
+ doc = fitz.open(stream=pdf_file, filetype="pdf")
47
+ complete_text = ""
48
+ images = []
49
+ tables = []
50
+
51
+ for page_num in range(len(doc)):
52
+ page = doc.load_page(page_num)
53
+
54
+ # Get text with improved layout preservation
55
+ text = page.get_text("dict")
56
+ blocks = text.get("blocks", [])
57
+
58
+ # Process text blocks to preserve table-like structures
59
+ page_text = ""
60
+ for block in blocks:
61
+ if block.get("type") == 0: # Text block
62
+ for line in block.get("lines", []):
63
+ line_text = " ".join([span.get("text", "") for span in line.get("spans", [])])
64
+ page_text += line_text + "\n"
65
+
66
+ complete_text += page_text + "\n\n"
67
+
68
+ # Extract tables using heuristics
69
+ # Look for grid-like structures in the text
70
+ table_candidates = re.findall(r'(?:\w+[\t ]+){2,}(?:\d+\.?\d*[\t ]+){2,}', page_text)
71
+ if table_candidates:
72
+ tables.extend(table_candidates)
73
+
74
+ # Extract images for visual analysis
75
+ image_list = page.get_images(full=True)
76
+ for img_index, img in enumerate(image_list):
77
+ xref = img[0]
78
+ base_image = doc.extract_image(xref)
79
+ image_bytes = base_image["image"]
80
+ image = Image.open(io.BytesIO(image_bytes))
81
+
82
+ # Only keep images that might be charts or reports
83
+ # (filter out logos and decorative elements)
84
+ if image.width > 200 and image.height > 200:
85
+ # Preprocess image to improve readability
86
+ processed_image = preprocess_image(image)
87
+ images.append(processed_image)
88
+
89
+ return complete_text, images, tables
90
+
91
+ # Blood markers dictionary for reference
92
+ BLOOD_MARKERS = {
93
+ "Vitamin D": ["25-OH Vitamin D", "Vitamin D, 25-Hydroxy", "25(OH)D", "Calcidiol"],
94
+ "Vitamin B12": ["Cobalamin", "Cyanocobalamin", "Methylcobalamin", "B-12"],
95
+ "Folate": ["Vitamin B9", "Folic Acid"],
96
+ "Vitamin A": ["Retinol", "Beta-carotene"],
97
+ "Vitamin E": ["Tocopherol", "Alpha-tocopherol"],
98
+ "Vitamin K": ["Phylloquinone", "Menaquinone"],
99
+ "Vitamin C": ["Ascorbic Acid", "L-ascorbic acid"],
100
+ "Vitamin B1": ["Thiamine", "Thiamin"],
101
+ "Vitamin B2": ["Riboflavin"],
102
+ "Vitamin B3": ["Niacin", "Nicotinic acid"],
103
+ "Vitamin B5": ["Pantothenic acid"],
104
+ "Vitamin B6": ["Pyridoxine", "Pyridoxal", "Pyridoxamine"],
105
+ "Vitamin B7": ["Biotin"],
106
+ "Iron": ["Ferritin", "Transferrin", "TIBC", "UIBC", "Serum Iron"],
107
+ "Calcium": ["Ca", "Serum Calcium", "Ionized Calcium"],
108
+ "Magnesium": ["Mg", "Serum Magnesium"],
109
+ "Zinc": ["Zn", "Serum Zinc"],
110
+ "Selenium": ["Se", "Serum Selenium"],
111
+ "Iodine": ["I", "Urinary Iodine"]
112
+ }
113
+
114
+ # Normal ranges reference (based on Indian standards)
115
+ REFERENCE_RANGES = {
116
+ "Vitamin D": {"unit": "ng/mL", "min": 30, "max": 100,
117
+ "deficiency": "<20", "insufficiency": "20-29"},
118
+ "Vitamin B12": {"unit": "pg/mL", "min": 211, "max": 911,
119
+ "deficiency": "<200", "insufficiency": "200-300"},
120
+ "Folate": {"unit": "ng/mL", "min": 5.9, "max": 24.8,
121
+ "deficiency": "<5.9"},
122
+ "Ferritin": {"unit": "ng/mL", "min_male": 30, "max_male": 400,
123
+ "min_female": 13, "max_female": 150,
124
+ "deficiency_male": "<30", "deficiency_female": "<13"},
125
+ "Hemoglobin": {"unit": "g/dL",
126
+ "min_male": 13.5, "max_male": 17.5,
127
+ "min_female": 12.0, "max_female": 15.5,
128
+ "deficiency_male": "<13.5", "deficiency_female": "<12.0"},
129
+ "Calcium": {"unit": "mg/dL", "min": 8.6, "max": 10.3,
130
+ "deficiency": "<8.6"},
131
+ "Magnesium": {"unit": "mg/dL", "min": 1.7, "max": 2.2,
132
+ "deficiency": "<1.7"},
133
+ "Zinc": {"unit": "ΞΌg/dL", "min": 70, "max": 120,
134
+ "deficiency": "<70"}
135
+ }
136
+
137
+ # Extract blood markers and values from text
138
+ def extract_blood_markers(text):
139
+ extracted_markers = {}
140
+
141
+ # Iterate through all known markers and their aliases
142
+ for vitamin, aliases in BLOOD_MARKERS.items():
143
+ all_terms = aliases + [vitamin]
144
+ for term in all_terms:
145
+ # Look for the marker and its value
146
+ # Pattern matches: Marker name: value unit
147
+ # Or: Marker name value unit
148
+ pattern = r'(?i)(%s)\s*[:=-]?\s*(\d+\.?\d*)' % re.escape(term)
149
+ matches = re.findall(pattern, text)
150
+
151
+ if matches:
152
+ for match in matches:
153
+ marker, value = match
154
+ # Convert to float if possible
155
+ try:
156
+ value = float(value)
157
+ extracted_markers[vitamin] = value
158
+ break # Found a value for this vitamin, move to next
159
+ except ValueError:
160
+ continue
161
+
162
+ return extracted_markers
163
+
164
+ # Analyze report with Gemini using structured approach
165
+ def analyze_report(vision_model, text_model, content, extracted_markers, is_text=False):
166
+ # Create structured input for better analysis
167
+ analysis_prompt = f"""
168
+ I need a detailed analysis of this blood test report. Focus specifically on vitamin, mineral and nutritional deficiencies.
169
+
170
+ The report is from India, so provide recommendations relevant to Indian context, diet, and healthcare practices.
171
+
172
+ For each identified deficiency:
173
+ 1. Specify the exact deficiency (vitamin/mineral name)
174
+ 2. Current level from report and normal reference range
175
+ 3. Severity (mild/moderate/severe)
176
+ 4. Recommended daily dosage in appropriate units (mg, mcg, IU) for supplementation
177
+ 5. Duration of recommended supplementation
178
+ 6. Specific health impacts this deficiency is causing or may cause
179
+ 7. Recommended foods available in India that address this deficiency (include both vegetarian and non-vegetarian options)
180
+ 8. Any additional blood tests that should be considered for confirmation
181
+
182
+ Also provide:
183
+ - A comprehensive summary of all nutritional findings
184
+ - Lifestyle modifications specific to Indian context
185
+ - Any concerning values that require immediate medical attention
186
+ - Follow-up testing recommendations with timeline
187
+
188
+ If you cannot confidently determine specific deficiencies, explain why and suggest further tests.
189
+
190
+ The extracted markers I've identified include: {json.dumps(extracted_markers)}
191
+
192
+ Format your response as structured JSON with the following schema:
193
+ {{
194
+ "deficiencies": [
195
+ {{
196
+ "nutrient": "string",
197
+ "current_level": "string",
198
+ "reference_range": "string",
199
+ "severity": "string",
200
+ "recommended_dosage": "string",
201
+ "supplementation_duration": "string",
202
+ "health_impacts": ["string"],
203
+ "recommended_foods": {{
204
+ "vegetarian": ["string"],
205
+ "non_vegetarian": ["string"]
206
+ }},
207
+ "confirmation_tests": ["string"]
208
+ }}
209
+ ],
210
+ "summary": "string",
211
+ "lifestyle_modifications": ["string"],
212
+ "urgent_concerns": ["string"] or null,
213
+ "followup_recommendations": {{
214
+ "tests": ["string"],
215
+ "timeline": "string"
216
+ }}
217
+ }}
218
+ """
219
+
220
+ try:
221
+ if is_text:
222
+ full_content = content + "\n\nExtracted markers: " + json.dumps(extracted_markers)
223
+ response = text_model.generate_content([analysis_prompt, full_content])
224
+ else:
225
+ # For image, combine extracted markers with the image
226
+ response = vision_model.generate_content([analysis_prompt, content])
227
+
228
+ # Extract JSON from response
229
+ response_text = response.text
230
+ # Find JSON object in the response
231
+ json_match = re.search(r'```json\s*([\s\S]*?)\s*```', response_text)
232
+ if json_match:
233
+ json_str = json_match.group(1)
234
+ else:
235
+ # Try to find JSON without code blocks
236
+ json_match = re.search(r'({[\s\S]*})', response_text)
237
+ if json_match:
238
+ json_str = json_match.group(1)
239
+ else:
240
+ return {"error": "Failed to parse JSON response", "raw_response": response_text}
241
+
242
+ # Parse JSON
243
+ try:
244
+ result = json.loads(json_str)
245
+ return result
246
+ except json.JSONDecodeError:
247
+ return {"error": "Invalid JSON response", "raw_response": response_text}
248
+
249
+ except Exception as e:
250
+ return {"error": f"Analysis failed: {str(e)}"}
251
+
252
+ # Generate personalized recommendation report
253
+ def generate_recommendation_html(analysis_result, patient_info=None):
254
+ if "error" in analysis_result:
255
+ return f"<div class='error'>Error in analysis: {analysis_result['error']}</div>"
256
+
257
+ # Current date for the report
258
+ current_date = datetime.now().strftime("%d %B, %Y")
259
+
260
+ # Start building HTML
261
+ html = f"""
262
+ <div style="font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; line-height: 1.6;">
263
+ <div style="text-align: center; border-bottom: 2px solid #2c3e50; padding-bottom: 10px; margin-bottom: 20px;">
264
+ <h1 style="color: #2c3e50;">Nutritional Analysis Report</h1>
265
+ <p>Generated on: {current_date}</p>
266
+ {f"<p>Patient: {patient_info['name']} | Age: {patient_info['age']} | Gender: {patient_info['gender']}</p>" if patient_info else ""}
267
+ </div>
268
+
269
+ <div style="background-color: #f9f9f9; border-left: 4px solid #3498db; padding: 15px; margin-bottom: 25px;">
270
+ <h2 style="color: #3498db; margin-top: 0;">Summary</h2>
271
+ <p>{analysis_result.get('summary', 'No summary available')}</p>
272
+ </div>
273
+ """
274
+
275
+ # Add deficiencies section
276
+ deficiencies = analysis_result.get('deficiencies', [])
277
+ if deficiencies:
278
+ html += '<h2 style="color: #2c3e50; border-bottom: 1px solid #ddd; padding-bottom: 8px;">Detected Deficiencies</h2>'
279
+
280
+ for deficiency in deficiencies:
281
+ severity_color = {
282
+ "mild": "#f39c12",
283
+ "moderate": "#e67e22",
284
+ "severe": "#c0392b"
285
+ }.get(deficiency.get('severity', '').lower(), "#7f8c8d")
286
+
287
+ html += f"""
288
+ <div style="margin-bottom: 30px; background-color: #f8f9fa; border-radius: 5px; padding: 15px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
289
+ <h3 style="color: {severity_color}; margin-top: 0;">
290
+ {deficiency.get('nutrient', 'Unknown')}
291
+ <span style="font-size: 0.8em; background-color: {severity_color}; color: white; padding: 3px 8px; border-radius: 3px; margin-left: 10px;">
292
+ {deficiency.get('severity', 'Unknown')} deficiency
293
+ </span>
294
+ </h3>
295
+
296
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-bottom: 15px;">
297
+ <div>
298
+ <p><strong>Current Level:</strong> {deficiency.get('current_level', 'N/A')}</p>
299
+ <p><strong>Reference Range:</strong> {deficiency.get('reference_range', 'N/A')}</p>
300
+ <p><strong>Recommended Dosage:</strong> {deficiency.get('recommended_dosage', 'N/A')}</p>
301
+ <p><strong>Duration:</strong> {deficiency.get('supplementation_duration', 'N/A')}</p>
302
+ </div>
303
+ <div>
304
+ <p><strong>Health Impacts:</strong></p>
305
+ <ul style="margin-top: 5px; padding-left: 20px;">
306
+ """
307
+
308
+ # Add health impacts
309
+ for impact in deficiency.get('health_impacts', ['N/A']):
310
+ html += f"<li>{impact}</li>"
311
+
312
+ html += """
313
+ </ul>
314
+ </div>
315
+ </div>
316
+
317
+ <div style="margin-top: 15px;">
318
+ <h4 style="color: #2c3e50; margin-bottom: 8px;">Recommended Foods</h4>
319
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
320
+ <div>
321
+ <h5 style="color: #27ae60; margin-bottom: 5px;">Vegetarian Options</h5>
322
+ <ul style="margin-top: 5px; padding-left: 20px;">
323
+ """
324
+
325
+ # Add vegetarian foods
326
+ veg_foods = deficiency.get('recommended_foods', {}).get('vegetarian', ['N/A'])
327
+ for food in veg_foods:
328
+ html += f"<li>{food}</li>"
329
+
330
+ html += """
331
+ </ul>
332
+ </div>
333
+ <div>
334
+ <h5 style="color: #c0392b; margin-bottom: 5px;">Non-Vegetarian Options</h5>
335
+ <ul style="margin-top: 5px; padding-left: 20px;">
336
+ """
337
+
338
+ # Add non-vegetarian foods
339
+ non_veg_foods = deficiency.get('recommended_foods', {}).get('non_vegetarian', ['N/A'])
340
+ for food in non_veg_foods:
341
+ html += f"<li>{food}</li>"
342
+
343
+ html += """
344
+ </ul>
345
+ </div>
346
+ </div>
347
+ </div>
348
+
349
+ <div style="margin-top: 15px; background-color: #eaf2f8; padding: 10px; border-radius: 4px;">
350
+ <h4 style="color: #2980b9; margin-top: 0; margin-bottom: 8px;">Additional Tests</h4>
351
+ <ul style="margin-top: 5px; padding-left: 20px;">
352
+ """
353
+
354
+ # Add confirmation tests
355
+ tests = deficiency.get('confirmation_tests', ['None recommended'])
356
+ for test in tests:
357
+ html += f"<li>{test}</li>"
358
+
359
+ html += """
360
+ </ul>
361
+ </div>
362
+ </div>
363
+ """
364
+ else:
365
+ html += '<div style="padding: 15px; background-color: #e8f8f5; border-radius: 5px; margin-bottom: 25px;"><p>No specific deficiencies detected.</p></div>'
366
+
367
+ # Add lifestyle modifications
368
+ lifestyle = analysis_result.get('lifestyle_modifications', [])
369
+ if lifestyle:
370
+ html += """
371
+ <h2 style="color: #2c3e50; border-bottom: 1px solid #ddd; padding-bottom: 8px;">Lifestyle Recommendations</h2>
372
+ <div style="background-color: #f2f6fc; padding: 15px; border-radius: 5px; margin-bottom: 25px;">
373
+ <ul style="padding-left: 20px;">
374
+ """
375
+
376
+ for item in lifestyle:
377
+ html += f"<li>{item}</li>"
378
+
379
+ html += """
380
+ </ul>
381
+ </div>
382
+ """
383
+
384
+ # Add urgent concerns
385
+ urgent = analysis_result.get('urgent_concerns', [])
386
+ if urgent and urgent != [None]:
387
+ html += """
388
+ <h2 style="color: #c0392b; border-bottom: 1px solid #ddd; padding-bottom: 8px;">⚠️ Urgent Considerations</h2>
389
+ <div style="background-color: #fdf2f0; padding: 15px; border-radius: 5px; border-left: 4px solid #c0392b; margin-bottom: 25px;">
390
+ <ul style="padding-left: 20px;">
391
+ """
392
+
393
+ for item in urgent:
394
+ html += f"<li>{item}</li>"
395
+
396
+ html += """
397
+ </ul>
398
+ <p style="margin-top: 10px; font-weight: bold;">Please consult with a healthcare provider promptly regarding these concerns.</p>
399
+ </div>
400
+ """
401
+
402
+ # Add follow-up recommendations
403
+ followup = analysis_result.get('followup_recommendations', {})
404
+ if followup and followup.get('tests'):
405
+ html += f"""
406
+ <h2 style="color: #2c3e50; border-bottom: 1px solid #ddd; padding-bottom: 8px;">Follow-up Recommendations</h2>
407
+ <div style="background-color: #f9f9f9; padding: 15px; border-radius: 5px; margin-bottom: 25px;">
408
+ <p><strong>Timeline:</strong> {followup.get('timeline', 'As advised by your healthcare provider')}</p>
409
+ <p><strong>Recommended Tests:</strong></p>
410
+ <ul style="padding-left: 20px;">
411
+ """
412
+
413
+ for test in followup.get('tests', []):
414
+ html += f"<li>{test}</li>"
415
+
416
+ html += """
417
+ </ul>
418
+ </div>
419
+ """
420
+
421
+ # Disclaimer
422
+ html += """
423
+ <div style="border-top: 1px solid #ddd; margin-top: 30px; padding-top: 15px; font-size: 0.9em; color: #7f8c8d;">
424
+ <p><strong>Disclaimer:</strong> This analysis is generated by an AI system and should not replace professional medical advice.
425
+ Always consult with a healthcare provider before making any changes to your diet, lifestyle, or supplementation regimen.</p>
426
+ </div>
427
+ </div>
428
+ """
429
+
430
+ return html
431
+
432
+ # Calculate nutritional recommendations based on deficiencies
433
+ def calculate_recommendations(analysis_result, weight_kg=70, height_cm=165, activity_level="moderate"):
434
+ if not analysis_result or "deficiencies" not in analysis_result:
435
+ return None
436
+
437
+ # Basic calculations
438
+ bmi = weight_kg / ((height_cm/100) ** 2)
439
+
440
+ # Activity level multipliers
441
+ activity_multipliers = {
442
+ "sedentary": 1.2,
443
+ "light": 1.375,
444
+ "moderate": 1.55,
445
+ "active": 1.725,
446
+ "very active": 1.9
447
+ }
448
+
449
+ # Calculate basal metabolic rate (BMR) using Mifflin-St Jeor equation
450
+ bmr = 10 * weight_kg + 6.25 * height_cm - 5 * 30 + 5 # Assuming age 30 for example
451
+
452
+ # Calculate total daily energy expenditure
453
+ tdee = bmr * activity_multipliers.get(activity_level.lower(), 1.55)
454
+
455
+ # Create recommendation dictionary
456
+ recommendations = {
457
+ "anthropometrics": {
458
+ "bmi": round(bmi, 1),
459
+ "bmi_category": get_bmi_category(bmi),
460
+ "estimated_energy_needs": round(tdee)
461
+ },
462
+ "supplements": []
463
+ }
464
+
465
+ # Process each deficiency
466
+ for deficiency in analysis_result["deficiencies"]:
467
+ nutrient = deficiency["nutrient"]
468
+ severity = deficiency["severity"].lower()
469
+
470
+ # Extract dosage value and unit
471
+ dosage_match = re.search(r'(\d+[\.\d]*)\s*([a-zA-Z]+)', deficiency["recommended_dosage"])
472
+ if dosage_match:
473
+ amount = float(dosage_match.group(1))
474
+ unit = dosage_match.group(2)
475
+
476
+ # Adjust based on severity
477
+ if severity == "severe":
478
+ adjusted_amount = amount * 1.2 # 20% higher for severe
479
+ elif severity == "mild":
480
+ adjusted_amount = amount * 0.9 # 10% lower for mild
481
+ else:
482
+ adjusted_amount = amount
483
+
484
+ recommendations["supplements"].append({
485
+ "nutrient": nutrient,
486
+ "dosage": f"{round(adjusted_amount, 2)} {unit}",
487
+ "original_dosage": f"{amount} {unit}",
488
+ "severity": severity,
489
+ "duration": deficiency["supplementation_duration"],
490
+ "frequency": "Daily",
491
+ "best_time": get_best_time_for_supplement(nutrient),
492
+ "interactions": get_supplement_interactions(nutrient)
493
+ })
494
+
495
+ return recommendations
496
+
497
+ # Helper functions for recommendations
498
+ def get_bmi_category(bmi):
499
+ if bmi < 18.5:
500
+ return "Underweight"
501
+ elif bmi < 25:
502
+ return "Normal weight"
503
+ elif bmi < 30:
504
+ return "Overweight"
505
+ else:
506
+ return "Obese"
507
+
508
+ def get_best_time_for_supplement(nutrient):
509
+ # Time recommendations based on Indian context
510
+ nutrient_lower = nutrient.lower()
511
+
512
+ if any(term in nutrient_lower for term in ["d", "a", "e", "k"]):
513
+ return "With meals containing some fat (lunch or dinner)"
514
+ elif "b12" in nutrient_lower:
515
+ return "Morning, with breakfast"
516
+ elif "iron" in nutrient_lower:
517
+ return "On empty stomach, 1 hour before meals with Vitamin C"
518
+ elif "calcium" in nutrient_lower:
519
+ return "Between meals, avoid taking with iron supplements"
520
+ elif "zinc" in nutrient_lower:
521
+ return "1-2 hours after meals, not with calcium supplements"
522
+ else:
523
+ return "As directed by healthcare provider"
524
+
525
+ def get_supplement_interactions(nutrient):
526
+ # Common interactions for Indian medications and supplements
527
+ nutrient_lower = nutrient.lower()
528
+
529
+ if "iron" in nutrient_lower:
530
+ return ["Calcium supplements", "Tea/coffee", "Antacids", "Certain antibiotics"]
531
+ elif "calcium" in nutrient_lower:
532
+ return ["Iron supplements", "Certain antibiotics", "Thyroid medications"]
533
+ elif "b12" in nutrient_lower:
534
+ return ["Metformin", "Acid-reducing medications", "Colchicine"]
535
+ elif "d" in nutrient_lower:
536
+ return ["Steroids", "Weight loss medications", "Certain cholesterol medications"]
537
+ else:
538
+ return []
539
+
540
+ # File upload handler for Hugging Face
541
+ def upload_and_process_file(file, api_key, name, age, gender):
542
+ if not api_key:
543
+ return "Please enter a valid Google API key", None
544
+
545
+ try:
546
+ if file is None:
547
+ return "No file was uploaded", None
548
+
549
+ # Get file extension
550
+ file_extension = file.name.split('.')[-1].lower()
551
+ file_content = file.read()
552
+
553
+ # Process based on file type
554
+ if file_extension == 'pdf':
555
+ report_text, extracted_images, tables = extract_text_from_pdf(file_content)
556
+ extracted_markers = extract_blood_markers(report_text)
557
+
558
+ vision_model, text_model = configure_genai(api_key)
559
+
560
+ # If text extraction worked well and we found markers
561
+ if len(extracted_markers) > 0:
562
+ analysis_result = analyze_report(vision_model, text_model, report_text, extracted_markers, is_text=True)
563
+ # If text extraction didn't yield much, use the images
564
+ elif extracted_images:
565
+ # Use the first image as primary, but include text context
566
+ analysis_result = analyze_report(vision_model, text_model,
567
+ [report_text, extracted_images[0]],
568
+ extracted_markers)
569
+ else:
570
+ return "Could not extract sufficient data from the PDF. Please try uploading a clearer document.", None
571
+
572
+ elif file_extension in ['jpg', 'jpeg', 'png']:
573
+ img = Image.open(io.BytesIO(file_content))
574
+ processed_img = preprocess_image(img)
575
+
576
+ vision_model, text_model = configure_genai(api_key)
577
+ analysis_result = analyze_report(vision_model, text_model, processed_img, {})
578
+
579
+ else:
580
+ return f"Unsupported file format: {file_extension}. Please upload a PDF or image (JPG, PNG).", None
581
+
582
+ # Create patient info dictionary if provided
583
+ patient_info = None
584
+ if name or age or gender:
585
+ patient_info = {
586
+ "name": name,
587
+ "age": age,
588
+ "gender": gender
589
+ }
590
+
591
+ # Generate HTML report
592
+ html_report = generate_recommendation_html(analysis_result, patient_info)
593
+
594
+ return html_report, analysis_result
595
+
596
+ except Exception as e:
597
+ return f"An error occurred: {str(e)}", None
598
+
599
+ # Create the Gradio Interface for Hugging Face
600
+ def create_interface():
601
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="indigo")) as app:
602
+ gr.Markdown(
603
+ """
604
+ # 🩸 Blood Report Analyzer
605
+
606
+ ## Analyze blood test reports for vitamin deficiencies and get personalized recommendations
607
+
608
+ This application uses Gemini AI to analyze your blood test results and provide detailed insights
609
+ on nutritional deficiencies with recommendations tailored to Indian health needs.
610
+ """
611
+ )
612
+
613
+ with gr.Tab("πŸ“Š Report Analysis"):
614
+ with gr.Row():
615
+ with gr.Column(scale=1):
616
+ api_key = gr.Textbox(
617
+ label="Google Gemini API Key",
618
+ placeholder="Enter your Gemini API key",
619
+ type="password"
620
+ )
621
+
622
+ with gr.Accordion("Instructions for Using This Tool", open=False):
623
+ gr.Markdown(
624
+ """
625
+ ## How to Use This Tool
626
+
627
+ ### 1. Prepare Your Report
628
+ - Ensure your blood report is clear and readable
629
+ - PDF format is preferred
630
+ - If using images, ensure good lighting and focus
631
+
632
+ ### 2. Get a Gemini API Key
633
+ - Visit [Google AI Studio](https://ai.google.dev/)
634
+ - Create an account or sign in
635
+ - Navigate to API keys and create a new key
636
+
637
+ ### 3. Upload and Analyze
638
+ - Enter your API key in the designated field
639
+ - (Optional) Enter patient information for personalized results
640
+ - Upload your blood report file
641
+ - Click "Analyze Report"
642
+
643
+ ### 4. Review Results
644
+ - The analysis will display deficiencies found, their severity, and recommendations
645
+ - For personalized supplementation, enter weight, height, and activity level
646
+ - Click "Generate Supplement Plan" for customized dosage recommendations
647
+
648
+ ### 5. Share Results
649
+ - You can save the HTML report by right-clicking and selecting "Save as"
650
+ - Share the results with your healthcare provider
651
+
652
+ ### Important Notes
653
+ - This tool is for informational purposes only
654
+ - Always consult with healthcare professionals before making health decisions
655
+ - Your data is not stored and is only used for analysis
656
+ """
657
+ )
658
+
659
+ with gr.Row():
660
+ with gr.Column(scale=1):
661
+ with gr.Group():
662
+ gr.Markdown("### Patient Information (Optional)")
663
+ name = gr.Textbox(label="Name", placeholder="Enter patient name")
664
+ with gr.Row():
665
+ age = gr.Textbox(label="Age", placeholder="e.g., 35")
666
+ gender = gr.Dropdown(label="Gender", choices=["Male", "Female", "Other"], value="Male")
667
+
668
+ upload_file = gr.File(label="Upload Blood Report")
669
+ analyze_button = gr.Button("πŸ“Š Analyze Report", variant="primary")
670
+
671
+ with gr.Column(scale=2):
672
+ output = gr.HTML(label="Analysis Results")
673
+ raw_output = gr.JSON(label="Raw Analysis Data", visible=False)
674
+
675
+ with gr.Row():
676
+ with gr.Column():
677
+ with gr.Group():
678
+ gr.Markdown("### Supplement Recommendations")
679
+ with gr.Row():
680
+ weight = gr.Number(label="Weight (kg)", value=70)
681
+ height = gr.Number(label="Height (cm)", value=165)
682
+ activity = gr.Dropdown(
683
+ label="Activity Level",
684
+ choices=["Sedentary", "Light", "Moderate", "Active", "Very Active"],
685
+ value="Moderate"
686
+ )
687
+ supplement_button = gr.Button("πŸ’Š Generate Supplement Plan")
688
+
689
+ supplement_output = gr.JSON(label="Personalized Supplement Plan")
690
+
691
+ # Connect the buttons to functions
692
+ analyze_button.click(
693
+ fn=upload_and_process_file,
694
+ inputs=[upload_file, api_key, name, age, gender],
695
+ outputs=[output, raw_output]
696
+ )
697
+
698
+ supplement_button.click(
699
+ fn=calculate_recommendations,
700
+ inputs=[raw_output, weight, height, activity],
701
+ outputs=[supplement_output]
702
+ )
703
+
704
+ with gr.Tab("πŸ“‹ About"):
705
+ gr.Markdown(
706
+ """
707
+ ## About Blood Report Analyzer
708
+
709
+ This tool was developed to help people in India better understand their blood test results,
710
+ with a focus on identifying nutritional deficiencies that are common in the Indian population.
711
+
712
+ ### How it Works
713
+ 1. The tool uses advanced OCR and AI to extract relevant information from your blood report
714
+ 2. Google's Gemini AI models analyze the data to identify deficiencies
715
+ 3. Recommendations are tailored to the Indian context, including:
716
+ - Locally available foods
717
+ - Cultural dietary considerations
718
+ - Regional supplementation guidelines
719
+
720
+ ### Privacy & Security
721
+ - Your data remains private and is not stored
722
+ - Analysis happens in real-time
723
+ - API keys are only used for processing and are not saved
724
+
725
+ ### Limitations
726
+ - This tool is for informational purposes only
727
+ - It does not replace medical advice from healthcare professionals
728
+ - Accuracy depends on the quality of the uploaded report
729
+ - Some rare deficiencies may not be detected
730
+
731
+ ### Acknowledgements
732
+ This application uses Google's Gemini AI models and is built with Gradio for Hugging Face Spaces.
733
+ """
734
+ )
735
+
736
+ return app
737
 
738
+ # Export the interface
739
+ app = create_interface()
740
 
741
+ # Launch the app
742
  if __name__ == "__main__":
743
+ app.launch()
744
+ app.launch(share=True)