File size: 28,381 Bytes
ed48c92
7403c04
 
f5377fa
204fca4
 
 
 
 
 
 
7403c04
 
9f5b856
7403c04
 
 
 
 
 
 
 
 
 
 
 
ed48c92
 
 
 
 
 
 
 
 
 
 
 
 
 
c0531db
 
 
ed48c92
204fca4
7403c04
 
 
ed48c92
 
 
 
 
7403c04
 
 
 
 
 
 
c0531db
 
 
7403c04
c0531db
7403c04
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
# -*- coding: utf-8 

# πŸ₯ Gemma 3N SOAP Note Generator
#-*- coding: utf-8 

# πŸ₯ Gemma 3N SOAP Note Generator


# Enable widgets



# Enable widgets


import torch
from transformers import AutoProcessor, AutoModelForImageTextToText
import gradio as gr
import ipywidgets as widgets
from IPython.display import display, clear_output
import io
import base64
from datetime import datetime
from huggingface_hub import login
import getpass

# Authenticate with HuggingFace
# Replace the authentication section (lines around the getpass part) with this:

# Import libraries and authenticate
import torch
from transformers import AutoProcessor, AutoModelForImageTextToText
import gradio as gr
import ipywidgets as widgets
from IPython.display import display, clear_output
import io
import base64
from datetime import datetime
from huggingface_hub import login
import os
import easyocr
from PIL import Image



# Authenticate with HuggingFace
print("πŸ” HuggingFace Authentication Required")

# Try to get token from environment variable first (for production/HF Spaces)
hf_token = os.environ.get('HF_TOKEN') or os.environ.get('HUGGINGFACE_TOKEN')

if hf_token:
    print("βœ… Found HF token in environment variables")
try:
    login(token=hf_token)
    print("βœ… Successfully authenticated with HuggingFace!")
except Exception as e:
    print(f"❌ Authentication failed: {e}")
    print("Please check your token and try again.")
# Check GPU availability
import os
os.environ["CUDA_VISIBLE_DEVICES"] = ""   # Hide all GPUs from PyTorch

device = "cuda" if torch.cuda.is_available() else "cpu"
device = "cpu"
print(f"πŸ–₯️  Using device: {device}")
if torch.cuda.is_available():
    print(f"πŸš€ GPU: {torch.cuda.get_device_name(0)}")
    print(f"πŸ’Ύ GPU Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB")
else:
    print("⚠️  Running on CPU - this will be slower")

# Load Gemma 3N model
print("πŸ“‘ Loading Gemma 3N model...")
model_id = "google/gemma-3n-e2b-it"

print("πŸ”§ Loading processor...")
processor = AutoProcessor.from_pretrained(model_id)

print("πŸ€– Loading Gemma 3N model (this may take a few minutes)...")
model = AutoModelForImageTextToText.from_pretrained(
    model_id,
    torch_dtype=torch.float16 if device == "cuda" else torch.float32,
    low_cpu_mem_usage=True,
).to(device)

print("βœ… Gemma 3N model loaded successfully!")
print(f"πŸ“Š Model size: ~2.9GB")
print(f"🎯 Ready for SOAP note generation!")

# SOAP Note Generation Function
def generate_soap_note(doctor_notes, include_timestamp=True):
    """
    Generate a SOAP note from unstructured doctor's notes
    """
    if not doctor_notes.strip():
        return "❌ Please enter some medical notes to process."

    prompt = f"""You are a medical AI assistant. Convert the following unstructured doctor's notes into a professional SOAP note format.

Doctor's Notes:
{doctor_notes}

Please generate a structured SOAP note with the following sections:
- SUBJECTIVE: Patient's reported symptoms and history
- OBJECTIVE: Physical examination findings, vital signs, and test results
- ASSESSMENT: Clinical diagnosis and reasoning
- PLAN: Treatment plan, medications, and follow-up

Format your response as a proper medical SOAP note with specific details extracted from the notes."""

    try:
        # Process input
        inputs = processor(text=prompt, return_tensors="pt").to(device)

        # Generate response
        print("πŸ”„ Generating SOAP note with Gemma 3N...")
        with torch.no_grad():
            outputs = model.generate(
                **inputs,
                max_new_tokens=512,
                temperature=0.3,  # Lower temperature for medical precision
                do_sample=True,
                pad_token_id=processor.tokenizer.eos_token_id
            )

        # Decode response
        generated_text = processor.decode(outputs[0], skip_special_tokens=True)

        # Extract only the generated part (remove the prompt)
        soap_response = generated_text[len(prompt):].strip()

        # Add header if requested
        if include_timestamp:
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            header = f"""πŸ“‹ SOAP NOTE - Generated by Gemma 3N
πŸ• Timestamp: {timestamp}
πŸ€– Model: google/gemma-3n-e2b-it
πŸ”’ Processed locally on device: {device.upper()}

{'='*60}
"""
            return header + soap_response

        return soap_response

    except Exception as e:
        return f"❌ Error generating SOAP note: {str(e)}"

print("βœ… SOAP generation function ready!")

"""## πŸ“ Interactive SOAP Note Generator
### Enter medical notes below and generate professional SOAP documentation
"""

# Create interactive widgets
print("🎨 Creating interactive interface...")

# Text input area
notes_input = widgets.Textarea(
    value='',
    placeholder='Enter unstructured doctor notes here...\n\nExample:\nPatient John Smith, 45yo male, came in complaining of chest pain for 2 days. Pain is sharp, 7/10 intensity, worse with movement. Vital signs: BP 140/90, HR 88, Temp 98.6F...',
    description='Medical Notes:',
    layout=widgets.Layout(width='100%', height='200px')
)

# File upload widget
file_upload = widgets.FileUpload(
    accept='.txt,.doc,.docx,.pdf',
    multiple=False,
    description='Or upload file:',
    layout=widgets.Layout(width='300px')
)

# Generate button
generate_btn = widgets.Button(
    description='πŸ€– Generate SOAP Note',
    button_style='primary',
    layout=widgets.Layout(width='200px', height='40px')
)

# Output area
output_area = widgets.HTML(
    value='<p style="color: #666;">πŸ“‹ Ready to generate SOAP notes! Enter medical notes above or upload a file.</p>',
    layout=widgets.Layout(width='100%', height='400px', overflow='auto', border='1px solid #ddd', padding='10px')
)

# Example buttons
example1_btn = widgets.Button(description='πŸ“‹ Chest Pain Example', button_style='info', layout=widgets.Layout(width='180px'))
example2_btn = widgets.Button(description='🩺 Diabetes Follow-up', button_style='info', layout=widgets.Layout(width='180px'))
example3_btn = widgets.Button(description='πŸ‘Ά Pediatric Visit', button_style='info', layout=widgets.Layout(width='180px'))

# Clear button
clear_btn = widgets.Button(description='πŸ—‘οΈ Clear', button_style='warning', layout=widgets.Layout(width='100px'))

print("βœ… Interface widgets created!")

# Example medical notes
examples = {
    'chest_pain': """Patient John Smith, 45yo male, came in complaining of chest pain for 2 days. Pain is sharp, 7/10 intensity, worse with movement. No radiation to arms. Vital signs: BP 140/90, HR 88, Temp 98.6F, RR 16, O2 sat 98%. Physical exam shows tenderness over left chest wall, no murmurs. EKG normal sinus rhythm. Chest X-ray clear. Diagnosed with costochondritis. Prescribed ibuprofen 600mg TID and advised rest. Follow up in 1 week if symptoms persist.""",

    'diabetes': """Sarah Johnson, 62yo female with Type 2 diabetes, here for routine follow-up. Says blood sugars have been running high lately, 180-220 mg/dL. Taking metformin 1000mg BID. Diet has been poor due to holiday stress. Weight increased 5 lbs since last visit. BP 150/85, BMI 32. HbA1c 8.2% (was 7.1% 3 months ago). Feet exam normal, no neuropathy. Plan to increase metformin to 1000mg TID, refer to nutritionist, recheck labs in 3 months.""",

    'pediatric': """Tommy Rodriguez, 8yo male, brought by mother for fever and cough x3 days. Fever up to 102F, productive cough with yellow sputum. Decreased appetite, no vomiting or diarrhea. Vital signs: Temp 101.2F, HR 110, RR 24, BP 95/60. Exam shows bilateral crackles in lower lobes, no wheeze. Throat clear. Diagnosed with bacterial pneumonia. Prescribed amoxicillin 500mg BID x10 days. Return if fever persists >48 hours on antibiotics."""
}

# Event handlers
def on_generate_click(b):
    with output_area:
        output_area.value = '<p style="color: #007bff;">πŸ”„ Processing with Gemma 3N... Please wait...</p>'

        # Get text from input or uploaded file
        text_to_process = notes_input.value

        # Check if file was uploaded
        if file_upload.value and len(file_upload.value) > 0:
            try:
                uploaded_file = list(file_upload.value.values())[0]
                file_content = uploaded_file['content'].decode('utf-8')
                text_to_process = file_content
                notes_input.value = file_content  # Show in text area
            except Exception as e:
                output_area.value = f'<p style="color: #dc3545;">❌ Error reading file: {str(e)}</p>'
                return

        if not text_to_process.strip():
            output_area.value = '<p style="color: #dc3545;">❌ Please enter medical notes or upload a file!</p>'
            return

        # Generate SOAP note
        soap_note = generate_soap_note(text_to_process)

        # Format output as HTML
        formatted_output = f'<pre style="font-family: monospace; font-size: 12px; line-height: 1.4; white-space: pre-wrap;">{soap_note}</pre>'
        output_area.value = formatted_output

def on_example1_click(b):
    notes_input.value = examples['chest_pain']
    output_area.value = '<p style="color: #28a745;">βœ… Chest pain example loaded! Click "Generate SOAP Note" to process.</p>'

def on_example2_click(b):
    notes_input.value = examples['diabetes']
    output_area.value = '<p style="color: #28a745;">βœ… Diabetes follow-up example loaded! Click "Generate SOAP Note" to process.</p>'

def on_example3_click(b):
    notes_input.value = examples['pediatric']
    output_area.value = '<p style="color: #28a745;">βœ… Pediatric example loaded! Click "Generate SOAP Note" to process.</p>'

def on_clear_click(b):
    notes_input.value = ''
    file_upload.value = ()
    output_area.value = '<p style="color: #666;">πŸ“‹ Ready to generate SOAP notes! Enter medical notes above or upload a file.</p>'

# Bind event handlers
generate_btn.on_click(on_generate_click)
example1_btn.on_click(on_example1_click)
example2_btn.on_click(on_example2_click)
example3_btn.on_click(on_example3_click)
clear_btn.on_click(on_clear_click)

print("βœ… Event handlers configured!")

# Define example medical notes first
example_notes_1 = """
Patient: John Smith, 45-year-old male
Chief Complaint: Chest pain for 2 hours
History: Patient reports sudden onset of sharp chest pain while at work. Pain is 7/10 intensity, located substernal, radiating to left arm. Associated with shortness of breath and diaphoresis. No previous cardiac history. Denies nausea or vomiting.
Physical Exam: VS: BP 150/90, HR 110, RR 22, O2 Sat 96% on RA. Patient appears anxious and diaphoretic. Heart: Regular rhythm, no murmurs. Lungs: Clear bilaterally. Extremities: No edema.
Assessment: Acute chest pain, rule out myocardial infarction
Plan: EKG, cardiac enzymes, chest X-ray, aspirin 325mg, continuous cardiac monitoring
"""

example_notes_2 = """
Patient: Sarah Johnson, 28-year-old female
Chief Complaint: Severe headache and fever
History: 3-day history of progressive headache, fever up to 101.5Β°F, photophobia, and neck stiffness. Patient reports this is the worst headache of her life. No recent travel or sick contacts. No rash noted.
Physical Exam: VS: T 101.2Β°F, BP 130/80, HR 95, RR 18. Patient appears ill and photophobic. HEENT: Pupils equal and reactive. Neck: Stiff with positive Kernig's sign. Neurologic: Alert and oriented x3, no focal deficits.
Assessment: Suspected meningitis
Plan: Lumbar puncture, blood cultures, empiric antibiotics, supportive care
"""

example_notes_3 = """
Patient: Robert Davis, 62-year-old male
Chief Complaint: Shortness of breath and leg swelling
History: 2-week history of progressive dyspnea on exertion, orthopnea, and bilateral lower extremity edema. Patient has history of hypertension and diabetes. Reports sleeping on 3 pillows due to breathing difficulty.
Physical Exam: VS: BP 140/85, HR 88, RR 24, O2 Sat 92% on RA. Heart: S3 gallop present, JVD elevated. Lungs: Bilateral rales in lower fields. Extremities: 2+ pitting edema bilaterally.
Assessment: Congestive heart failure exacerbation
Plan: Chest X-ray, BNP, echocardiogram, furosemide, ACE inhibitor, daily weights
"""

# Event handlers
def on_generate_click(b):
    try:
        # Update the HTML widget directly
        output_area.value = '<p style="color: #007bff;">πŸ”„ Processing with Gemma 3N... Please wait...</p>'

        # Get input text
        input_text = notes_input.value.strip()

        # Check if file was uploaded
        if file_upload.value:
            try:
                # Process uploaded file
                uploaded_file = list(file_upload.value.values())[0]
                file_content = uploaded_file['content'].decode('utf-8')
                input_text = file_content
            except Exception as upload_error:
                output_area.value = f'<p style="color: #ff6b6b;">❌ File upload error: {str(upload_error)}</p>'
                return

        if not input_text:
            output_area.value = '<p style="color: #ff6b6b;">⚠️ Please enter medical notes or upload a file first!</p>'
            return

        # Check if generate_soap_note function exists
        if 'generate_soap_note' not in globals():
            output_area.value = '<p style="color: #ff6b6b;">❌ Error: generate_soap_note function not found. Please define it first.</p>'
            return

        # Generate SOAP note using Gemma
        soap_note = generate_soap_note(input_text)

        # Escape HTML in soap_note to prevent rendering issues
        import html
        escaped_soap_note = html.escape(soap_note)

        # Display result
        output_area.value = f'''
        <div style="background: #f8f9fa; padding: 15px; border-radius: 8px; border-left: 4px solid #28a745;">
            <h4 style="color: #28a745; margin-top: 0;">βœ… Generated SOAP Note:</h4>
            <pre style="white-space: pre-wrap; font-family: 'Courier New', monospace; background: white; padding: 15px; border-radius: 5px; border: 1px solid #ddd;">{escaped_soap_note}</pre>
        </div>
        '''

    except Exception as e:
        import traceback
        error_details = traceback.format_exc()
        output_area.value = f'''
        <div style="color: #ff6b6b; background: #ffe6e6; padding: 15px; border-radius: 5px;">
            <h4>❌ Error Details:</h4>
            <p><strong>Error:</strong> {str(e)}</p>
            <details>
                <summary>Click for full traceback</summary>
                <pre style="font-size: 12px; background: #fff; padding: 10px; border-radius: 3px; margin-top: 10px;">{error_details}</pre>
            </details>
        </div>
        '''

def on_clear_click(b):
    try:
        notes_input.value = ""
        file_upload.value = ()
        output_area.value = '<p>πŸ“‹ Ready to generate SOAP notes! Enter medical notes above or upload a file.</p>'
    except Exception as e:
        output_area.value = f'<p style="color: #ff6b6b;">❌ Clear error: {str(e)}</p>'

def on_example_click(example_text):
    def handler(b):
        try:
            notes_input.value = example_text
            output_area.value = '<p style="color: #28a745;">πŸ“‹ Example loaded! Click "Generate SOAP Note" to process.</p>'
        except Exception as e:
            output_area.value = f'<p style="color: #ff6b6b;">❌ Example load error: {str(e)}</p>'
    return handler

# Connect event handlers to buttons
try:
    generate_btn.on_click(on_generate_click)
    clear_btn.on_click(on_clear_click)
    example1_btn.on_click(on_example_click(example_notes_1))
    example2_btn.on_click(on_example_click(example_notes_2))
    example3_btn.on_click(on_example_click(example_notes_3))

    print("βœ… Event handlers connected successfully!")
    print("πŸ“‹ Example notes loaded:")
    print("   - Example 1: Chest pain case")
    print("   - Example 2: Suspected meningitis")
    print("   - Example 3: Heart failure")

except Exception as e:
    print(f"❌ Error connecting event handlers: {str(e)}")
    import traceback
    traceback.print_exc()

"""## 🌐 Alternative: Gradio Web Interface
### Run this cell for a shareable web interface
"""

# Install required packages for image processing and OCR

import gradio as gr
import torch
from PIL import Image
import pytesseract
import cv2
import numpy as np
import easyocr
import io

# First, make sure you have the examples dictionary defined
examples = {
    'chest_pain': """Patient: John Smith, 45-year-old male
Chief Complaint: Chest pain for 2 hours
History: Patient reports sudden onset of sharp chest pain while at work. Pain is 7/10 intensity, located substernal, radiating to left arm. Associated with shortness of breath and diaphoresis. No previous cardiac history. Denies nausea or vomiting.
Physical Exam: VS: BP 150/90, HR 110, RR 22, O2 Sat 96% on RA. Patient appears anxious and diaphoretic. Heart: Regular rhythm, no murmurs. Lungs: Clear bilaterally. Extremities: No edema.
Assessment: Acute chest pain, rule out myocardial infarction
Plan: EKG, cardiac enzymes, chest X-ray, aspirin 325mg, continuous cardiac monitoring""",

    'diabetes': """Patient: Maria Garcia, 52-year-old female
Chief Complaint: Increased thirst and frequent urination for 3 weeks
History: Patient reports polyuria, polydipsia, and unintentional weight loss of 10 lbs over past month. Family history of diabetes. Denies fever, abdominal pain, or vision changes.
Physical Exam: VS: BP 140/85, HR 88, RR 16, BMI 28. Patient appears well but slightly dehydrated. HEENT: Dry mucous membranes. Cardiovascular: Regular rate and rhythm. Extremities: No diabetic foot changes noted.
Assessment: New onset diabetes mellitus, likely Type 2
Plan: HbA1c, fasting glucose, comprehensive metabolic panel, diabetic education, metformin initiation""",

    'pediatric': """Patient: Emma Thompson, 8-year-old female
Chief Complaint: Fever and sore throat for 2 days
History: Mother reports fever up to 102Β°F, sore throat, difficulty swallowing, and decreased appetite. No cough or runny nose. Several classmates have been sick with similar symptoms.
Physical Exam: VS: T 101.8Β°F, HR 110, RR 20, O2 Sat 99%. Patient appears mildly ill but alert. HEENT: Throat erythematous with tonsillar exudate, anterior cervical lymphadenopathy. Heart and lungs: Normal.
Assessment: Streptococcal pharyngitis (probable)
Plan: Rapid strep test, throat culture, amoxicillin if positive, supportive care, return if worsening"""
}

# Initialize EasyOCR reader (better for handwritten text)
try:
    ocr_reader = easyocr.Reader(['en'])
    print("βœ… EasyOCR initialized successfully")
except:
    ocr_reader = None
    print("⚠️ EasyOCR not available, using Tesseract only")

def preprocess_image_for_ocr(image):
    """
    Preprocess image to improve OCR accuracy
    """
    # Convert PIL Image to numpy array
    img_array = np.array(image)

    # Convert to grayscale if needed
    if len(img_array.shape) == 3:
        gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
    else:
        gray = img_array

    # Apply image preprocessing for better OCR
    # 1. Resize image if too small
    height, width = gray.shape
    if height < 300 or width < 300:
        scale_factor = max(300/height, 300/width)
        new_width = int(width * scale_factor)
        new_height = int(height * scale_factor)
        gray = cv2.resize(gray, (new_width, new_height), interpolation=cv2.INTER_CUBIC)

    # 2. Noise removal
    denoised = cv2.medianBlur(gray, 3)

    # 3. Contrast enhancement
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    enhanced = clahe.apply(denoised)

    # 4. Thresholding
    _, thresh = cv2.threshold(enhanced, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    return thresh

def extract_text_from_image(image):
    """
    Extract text from image using multiple OCR methods
    """
    if image is None:
        return "❌ No image provided"

    try:
        # Preprocess image
        processed_img = preprocess_image_for_ocr(image)

        # Method 1: Try EasyOCR (better for handwritten text)
        if ocr_reader is not None:
            try:
                # Convert back to PIL Image for EasyOCR
                pil_img = Image.fromarray(processed_img)
                results = ocr_reader.readtext(np.array(pil_img))

                # Extract text from EasyOCR results
                easyocr_text = ' '.join([result[1] for result in results])

                if len(easyocr_text.strip()) > 20:  # If we got good results
                    return clean_extracted_text(easyocr_text)

            except Exception as e:
                print(f"EasyOCR failed: {e}")

        # Method 2: Tesseract OCR (fallback)
        try:
            # Configure Tesseract for medical text
            custom_config = r'--oem 3 --psm 6'
            tesseract_text = pytesseract.image_to_string(processed_img, config=custom_config)

            if len(tesseract_text.strip()) > 10:
                return clean_extracted_text(tesseract_text)

        except Exception as e:
            print(f"Tesseract failed: {e}")

        return "❌ Could not extract text from image. Please try a clearer image or enter text manually."

    except Exception as e:
        return f"❌ Error processing image: {str(e)}"

def clean_extracted_text(text):
    """
    Clean up extracted text
    """
    # Remove excessive whitespace and empty lines
    lines = [line.strip() for line in text.split('\n') if line.strip()]
    cleaned_text = '\n'.join(lines)

    # Remove special characters that might interfere
    cleaned_text = cleaned_text.replace('|', '').replace('_', ' ')

    return cleaned_text.strip()

def gradio_generate_soap(medical_notes, uploaded_image):
    """
    Modified Gradio interface function for SOAP generation from images
    """
    text_to_process = medical_notes.strip() if medical_notes else ""

    # If image is uploaded, extract text using OCR
    if uploaded_image is not None:
        try:
            print("πŸ” Extracting text from uploaded image...")
            extracted_text = extract_text_from_image(uploaded_image)

            # Check if OCR was successful
            if extracted_text.startswith("❌"):
                return extracted_text

            # Use extracted text if manual text is empty or append to manual text
            if not text_to_process:
                text_to_process = extracted_text
            else:
                text_to_process = f"{text_to_process}\n\n--- Extracted from image ---\n{extracted_text}"

        except Exception as e:
            return f"❌ Error processing image: {str(e)}"

    if not text_to_process:
        return "❌ Please enter medical notes manually or upload a PNG/JPG image with medical text"

    # Check if generate_soap_note function exists
    if 'generate_soap_note' not in globals():
        return "❌ Error: generate_soap_note function not found. Please define it first."

    try:
        return generate_soap_note(text_to_process)
    except Exception as e:
        return f"❌ Error generating SOAP note: {str(e)}"

# Create example images (you can replace these with actual medical note images)
def create_example_image(text, filename):
    """
    Create example images from text (for demonstration)
    """
    from PIL import Image, ImageDraw, ImageFont

    # Create a white image
    img = Image.new('RGB', (800, 600), color='white')
    draw = ImageDraw.Draw(img)

    try:
        # Try to use a default font
        font = ImageFont.load_default()
    except:
        font = None

    # Add text to image
    lines = text.split('\n')
    y_offset = 20
    for line in lines[:15]:  # Limit to first 15 lines
        draw.text((20, y_offset), line, fill='black', font=font)
        y_offset += 25

    return img

# Create Gradio interface
gradio_interface = gr.Interface(
    fn=gradio_generate_soap,
    inputs=[
        gr.Textbox(
            lines=6,
            placeholder="Enter medical notes manually (optional)...\n\nOr upload an image below and text will be extracted automatically.",
            label="πŸ“ Medical Notes (Manual Entry)"
        ),
        gr.Image(
            type="pil",
            label="πŸ“· Upload Medical Image (PNG/JPG only)",
            sources=["upload", "webcam"],  # FIXED: Changed "camera" to "webcam"
            image_mode="RGB"
        )
    ],
    outputs=[
        gr.Textbox(
            lines=15,
            label="πŸ“‹ Generated SOAP Note",
            show_copy_button=True
        )
    ],
    title="πŸ₯ Medical Image SOAP Note Generator",
    description="""
    Transform medical images (PNG/JPG) into professional SOAP documentation using OCR + Gemma 3N model.

    πŸ“Έ **How to use:**
    1. Upload a PNG or JPG image of medical notes (typed or handwritten)
    2. Or enter text manually in the text box above
    3. The system will extract text from images using OCR
    4. Generate structured SOAP notes automatically

    πŸ’‘ **Tips for better OCR results:**
    - Use clear, high-resolution images
    - Ensure good lighting and contrast
    - Keep text horizontal (not tilted)
    - Handwritten text works best when clearly written
    """,
    examples=[
        [examples['chest_pain'], None],
        [examples['diabetes'], None],
        [examples['pediatric'], None]
    ],
    theme=gr.themes.Soft(),
    flagging_mode="never"
)

# Launch Gradio interface with flexible port selection
print("πŸš€ Launching Medical Image SOAP Generator...")

try:
    # Try different ports if 7860 is busy
    for port in [7860, 7861, 7862, 7863, 7864]:
        try:
            gradio_interface.launch(
                share=True,  # Creates a public shareable link
                server_port=port,
                show_error=True,
                quiet=False
            )
            print(f"βœ… Interface launched successfully on port {port}")
            break
        except OSError as port_error:
            print(f"⚠️ Port {port} is busy, trying next port...")
            continue
    else:
        # If all ports are busy, let Gradio choose automatically
        print("πŸ”„ All preferred ports busy, letting Gradio choose automatically...")
        gradio_interface.launch(
            share=True,
            show_error=True,
            quiet=False
        )

except Exception as e:
    print(f"❌ Error launching Gradio interface: {str(e)}")
    print("πŸ’‘ Alternative: Try running without share=True:")
    print("gradio_interface.launch(show_error=True)")

print("🎯 Medical Image SOAP Generator ready!")
print("πŸ“Έ Upload PNG/JPG images of medical notes for automatic text extraction and SOAP generation")

"""## πŸ“Š Usage Statistics & Model Info"""

# Display model and system information
import psutil
import GPUtil

def show_system_info():
    print("πŸ”§ SYSTEM INFORMATION")
    print("="*50)
    print(f"πŸ–₯️  Device: {device.upper()}")
    print(f"🧠 CPU Usage: {psutil.cpu_percent(interval=1):.1f}%")
    print(f"πŸ’Ύ RAM Usage: {psutil.virtual_memory().percent:.1f}%")

    if torch.cuda.is_available():
        try:
            gpus = GPUtil.getGPUs()
            if gpus:
                gpu = gpus[0]
                print(f"πŸš€ GPU: {gpu.name}")
                print(f"πŸ“Š GPU Usage: {gpu.load*100:.1f}%")
                print(f"πŸ”₯ GPU Memory: {gpu.memoryUsed}/{gpu.memoryTotal} MB ({gpu.memoryPercent:.1f}%)")
                print(f"🌑️  GPU Temp: {gpu.temperature}°C")
        except:
            print(f"πŸš€ GPU Memory: {torch.cuda.memory_allocated()/1e9:.1f}GB / {torch.cuda.memory_reserved()/1e9:.1f}GB")

    print("\nπŸ€– MODEL INFORMATION")
    print("="*50)
    print(f"πŸ“‘ Model ID: {model_id}")
    print(f"🎯 Model Type: Multimodal (Text, Image, Audio)")
    print(f"πŸ“Š Model Size: ~2.9GB")
    print(f"πŸ”’ Parameters: ~2.9B")
    print(f"🌍 Languages: 140 text + 35 multimodal")
    print(f"πŸ’½ Precision: {model.dtype}")

    print("\nβœ… Ready for SOAP note generation!")

show_system_info()

"""---
## πŸ“‹ SOAP Note Format Reference

**S - SUBJECTIVE**: Patient's reported symptoms and history  
**O - OBJECTIVE**: Observable clinical findings  
**A - ASSESSMENT**: Clinical diagnosis/impression  
**P - PLAN**: Treatment and follow-up plan  

---
*πŸ€– Powered by Google's Gemma 3N Model | πŸ”’ All processing performed locally*
"""