SmartHeal commited on
Commit
c72916b
Β·
verified Β·
1 Parent(s): 39b256f

Update src/ui_components_original.py

Browse files
Files changed (1) hide show
  1. src/ui_components_original.py +357 -346
src/ui_components_original.py CHANGED
@@ -376,18 +376,87 @@ button.gr-button:hover, button.gr-button-primary:hover {
376
  """
377
 
378
  def create_interface(self):
379
- """Create the main Gradio interface with View Details + Search Specific Patient."""
380
- import html
 
 
 
 
381
  import gradio as gr
382
- import os
383
- import logging
384
- from datetime import datetime
385
 
386
- with gr.Blocks(css=self.get_custom_css(), title="SmartHeal - SmartHeal AI Wound Care Assistant") as app:
387
 
388
- # -------------------------- HEADER --------------------------
389
- logo_url = "https://scontent.fccu31-2.fna.fbcdn.net/v/t39.30808-6/275933824_102121829111657_3325198727201325354_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=6ee11a&_nc_ohc=45krrEUpcSUQ7kNvwGVdiMW&_nc_oc=AdkTdxEC_TkYGiyDkEtTJZ_DFZELW17XKFmWpswmFqGB7JSdvTyWtnrQyLS0USngEiY&_nc_zt=23&_nc_ht=scontent.fccu31-2.fna&_nc_gid=ufAA4Hj5gTRwON5POYzz0Q&oh=00_AfW1-jLEN5RGeggqOvGgEaK_gdg0EDgxf_VhKbZwFLUO0Q&oe=6897A98B"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  gr.HTML(f"""
392
  <div class="medical-header">
393
  <img src="{logo_url}" class="logo" alt="SmartHeal Logo">
@@ -400,403 +469,344 @@ button.gr-button:hover, button.gr-button-primary:hover {
400
 
401
  # Disclaimer
402
  gr.HTML("""
403
- <div style="border: 2px solid #FF6B6B; background-color: #FFE5E5; padding: 15px; border-radius: 12px; margin: 10px 0;">
404
- <h3 style="color: #D63031; margin-top: 0;">⚠️ IMPORTANT DISCLAIMER</h3>
405
- <p><strong>This model is for testing and educational purposes only and is NOT a replacement for professional medical advice.</strong></p>
406
- <p>Information generated may be inaccurate. Always consult a qualified healthcare provider for medical concerns.</p>
407
  </div>
408
  """)
409
 
410
- # -------------------------- LAYOUT --------------------------
411
  with gr.Row():
412
- # ---------------------- AUTH PANEL -----------------------
413
  with gr.Column(visible=True) as auth_panel:
414
- gr.HTML("""
415
- <div style="text-align: center; margin: 40px 0;">
416
- <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px; border-radius: 20px; box-shadow: 0 20px 40px rgba(0,0,0,0.1); max-width: 500px; margin: 0 auto;">
417
- <h2 style="color: white; font-size: 2.2rem; margin-bottom: 10px; font-weight: 700;">πŸ₯ SmartHeal Access</h2>
418
- <p style="color: rgba(255,255,255,0.95); font-size: 1rem; margin-bottom: 0;">Secure Healthcare Professional Portal</p>
419
- </div>
420
- </div>
421
- """)
422
-
423
  with gr.Tabs():
424
  with gr.Tab("πŸ” Professional Login"):
425
- login_username = gr.Textbox(label="πŸ‘€ Username", placeholder="Enter your username")
426
- login_password = gr.Textbox(label="πŸ”’ Password", type="password", placeholder="Enter your secure password")
427
- login_btn = gr.Button("πŸš€ Sign In to Dashboard", variant="primary")
428
- login_status = gr.HTML("<div style='text-align:center;color:#718096;font-size:0.9rem;margin-top:8px;'>Enter your credentials to access the system</div>")
429
 
430
  with gr.Tab("πŸ“ New Registration"):
431
- signup_username = gr.Textbox(label="πŸ‘€ Username", placeholder="Choose a unique username")
432
- signup_email = gr.Textbox(label="πŸ“§ Email Address", placeholder="Enter your professional email")
433
- signup_password = gr.Textbox(label="πŸ”’ Password", type="password", placeholder="Create a strong password")
434
- signup_name = gr.Textbox(label="πŸ‘¨β€βš•οΈ Full Name", placeholder="Enter your full professional name")
435
- signup_role = gr.Radio(["practitioner", "organization"], label="πŸ₯ Account Type", value="practitioner")
436
 
437
  with gr.Group(visible=False) as org_fields:
438
- gr.HTML("<h4 style='color:#2d3748;margin: 12px 0 8px 0;'>🏒 Organization Details</h4>")
439
- org_name = gr.Textbox(label="Organization Name", placeholder="Enter organization name")
440
- phone = gr.Textbox(label="Phone Number", placeholder="Enter contact number")
441
- country_code = gr.Textbox(label="Country Code", placeholder="e.g., +1, +44")
442
- department = gr.Textbox(label="Department", placeholder="e.g., Emergency, Surgery")
443
- location = gr.Textbox(label="Location", placeholder="City, State/Province, Country")
444
 
445
  with gr.Group(visible=True) as prac_fields:
446
- gr.HTML("<h4 style='color:#2d3748;margin: 12px 0 8px 0;'>πŸ₯ Affiliation</h4>")
447
- organization_dropdown = gr.Dropdown(choices=self.get_organizations_dropdown(), label="Select Your Organization")
448
 
449
- signup_btn = gr.Button("✨ Create Professional Account", variant="primary")
450
- signup_status = gr.HTML("<div style='text-align:center;color:#718096;font-size:0.9rem;margin-top:8px;'>Fill in your details to create an account</div>")
451
 
452
- # ------------------- PRACTITIONER PANEL -------------------
453
  with gr.Column(visible=False) as practitioner_panel:
454
- gr.HTML('<div class="medical-card-title" style="font-weight:800;font-size:20px;">πŸ‘©β€βš•οΈ Practitioner Dashboard</div>')
455
  user_info = gr.HTML("")
456
  logout_btn_prac = gr.Button("πŸšͺ Logout", variant="secondary")
457
 
458
  with gr.Tabs():
459
- # ------------- WOUND ANALYSIS TAB -------------
460
  with gr.Tab("πŸ”¬ Wound Analysis"):
461
  with gr.Row():
462
  with gr.Column(scale=1):
463
- gr.HTML("<h3>πŸ“‹ Patient Information</h3>")
464
- patient_name = gr.Textbox(label="Patient Name", placeholder="Enter patient's full name")
465
- patient_age = gr.Number(label="Age", value=30, minimum=0, maximum=120)
466
- patient_gender = gr.Dropdown(choices=["Male", "Female", "Other"], label="Gender", value="Male")
 
 
 
 
 
 
 
 
 
 
 
467
 
468
  gr.HTML("<h3>🩹 Wound Information</h3>")
469
- wound_location = gr.Textbox(label="Wound Location", placeholder="e.g., Left ankle, Right arm")
470
- wound_duration = gr.Textbox(label="Wound Duration", placeholder="e.g., 2 weeks, 1 month")
471
- pain_level = gr.Slider(minimum=0, maximum=10, value=5, step=1, label="Pain Level (0-10)")
472
 
473
  gr.HTML("<h3>βš•οΈ Clinical Assessment</h3>")
474
- moisture_level = gr.Dropdown(choices=["Dry", "Moist", "Wet", "Saturated"], label="Moisture Level", value="Moist")
475
- infection_signs = gr.Dropdown(choices=["None", "Mild", "Moderate", "Severe"], label="Signs of Infection", value="None")
476
- diabetic_status = gr.Dropdown(choices=["Non-diabetic", "Type 1", "Type 2", "Gestational"], label="Diabetic Status", value="Non-diabetic")
477
 
478
  with gr.Column(scale=1):
479
- gr.HTML("<h3>πŸ“Έ Wound Image Upload</h3>")
480
  wound_image = gr.Image(label="Upload Wound Image", type="filepath")
481
-
482
  gr.HTML("<h3>πŸ“ Medical History</h3>")
483
- previous_treatment = gr.Textbox(label="Previous Treatment", placeholder="Describe any previous treatments...", lines=3)
484
- medical_history = gr.Textbox(label="Medical History", placeholder="Relevant medical conditions, surgeries, etc...", lines=3)
485
- medications = gr.Textbox(label="Current Medications", placeholder="List current medications...", lines=2)
486
- allergies = gr.Textbox(label="Known Allergies", placeholder="List any known allergies...", lines=2)
487
- additional_notes = gr.Textbox(label="Additional Notes", placeholder="Any additional clinical observations...", lines=3)
488
 
489
- analyze_btn = gr.Button("πŸ”¬ Analyze Wound", variant="primary", size="lg", elem_id="analyze-btn")
490
  analysis_output = gr.HTML("")
491
 
492
- # ------------- PATIENT HISTORY TAB -------------
493
  with gr.Tab("πŸ“‹ Patient History"):
494
  with gr.Row():
495
  with gr.Column(scale=2):
496
- gr.HTML("<h3>πŸ“Š Patient History Dashboard</h3>")
497
- history_btn = gr.Button("πŸ“‹ Load Patient History", variant="primary")
498
  patient_history_output = gr.HTML("")
499
-
500
  with gr.Column(scale=1):
501
- # View Details (dropdown)
502
- gr.HTML("<h3>πŸ”Ž View Details</h3>")
503
- patient_selector = gr.Dropdown(
504
- choices=[],
505
- label="Select Patient",
506
- interactive=True,
507
- allow_custom_value=False,
508
- info="Pick a patient to view wound progression"
509
- )
510
- reload_patients_btn = gr.Button("πŸ”„ Refresh Patient List", variant="secondary")
511
- view_details_btn = gr.Button("πŸ” View Details", variant="primary")
512
- patient_details_output = gr.HTML("")
513
-
514
- # Search Specific Patient (restored)
515
- gr.HTML("<h3 style='margin-top:20px;'>πŸ” Search Specific Patient</h3>")
516
- search_patient_name = gr.Textbox(label="Patient Name", placeholder="Enter patient name to search…")
517
- search_patient_btn = gr.Button("πŸ”Ž Search Patient History", variant="secondary")
518
  specific_patient_output = gr.HTML("")
519
 
520
- # --------------------- HELPERS & HANDLERS ---------------------
521
-
522
- def _risk_chip_ui(level: str) -> str:
523
- rl = (level or "Unknown").strip().lower()
524
- bg, fg = "#f0f0f0", "#333333"
525
- if rl.startswith("low"):
526
- bg, fg = "#d4edda", "#155724"
527
- elif rl.startswith("moderate"):
528
- bg, fg = "#fff3cd", "#856404"
529
- elif rl.startswith("high"):
530
- bg, fg = "#f8d7da", "#721c24"
531
- return f"<span style='background:{bg};color:{fg};padding:6px 10px;border-radius:999px;font-weight:700;font-size:12px;letter-spacing:.4px;text-transform:uppercase;'>{html.escape(level or 'Unknown')}</span>"
532
-
533
- def _render_progression_timeline(rows):
534
- if not rows:
535
- return "<div class='status-warning'>No wound progression data found for this patient.</div>"
536
-
537
- head = """
538
- <div style="border:1px solid #e2e8f0;border-radius:16px;overflow:hidden;background:white;box-shadow:0 8px 24px rgba(0,0,0,.06);">
539
- <div style="background:linear-gradient(135deg,#2563eb 0%,#1e3a8a 100%);color:white;padding:24px 28px;">
540
- <h2 style="margin:0;font-size:22px;font-weight:800;letter-spacing:.2px;">🩺 Wound Progression</h2>
541
- <p style="margin:6px 0 0 0;opacity:.95;">Chronological clinical snapshots with AI risk assessment</p>
542
- </div>
543
- <div style="padding:22px 22px 6px 22px">
544
- """
545
- items = []
546
- for i, r in enumerate(rows, start=1):
547
- dt = r.get("visit_date")
548
- try:
549
- dt_str = dt.strftime('%b %d, %Y β€’ %I:%M %p') if hasattr(dt, "strftime") else str(dt)
550
- except Exception:
551
- dt_str = str(dt)
552
-
553
- risk = _risk_chip_ui(r.get("risk_level"))
554
- wound_loc = r.get("wound_location") or "N/A"
555
- moisture = r.get("moisture") or "β€”"
556
- infection = r.get("infection") or "β€”"
557
- pain = r.get("pain_level", "N/A")
558
- summary = r.get("summary")
559
- img = r.get("image_url")
560
-
561
- summary_block = (
562
- f"<div style='margin-top:12px;color:#0f172a;background:white;border:1px solid #e2e8f0;border-radius:10px;padding:12px;'><strong>Summary:</strong> {html.escape(str(summary))}</div>"
563
- if summary else ""
564
- )
565
- img_block = (
566
- f"<div style='margin-top:12px;'><img src='{html.escape(str(img))}' style='max-width:360px;border-radius:12px;border:1px solid #e2e8f0;box-shadow:0 6px 18px rgba(0,0,0,.06)'></div>"
567
- if img else ""
568
- )
569
 
570
- card = f"""
571
- <div style="display:grid;grid-template-columns:32px 1fr;gap:16px;position:relative;margin-bottom:22px;">
572
- <div style="display:flex;align-items:center;justify-content:center;">
573
- <div style="width:12px;height:12px;background:#2563eb;border:2px solid white;border-radius:999px;box-shadow:0 0 0 3px rgba(37,99,235,.15)"></div>
574
- <div style="position:absolute;left:6px;top:24px;bottom:-10px;width:2px;background:linear-gradient(180deg, rgba(203,213,225,1), rgba(203,213,225,0));"></div>
575
- </div>
576
- <div style="border:1px solid #e2e8f0;border-radius:12px;padding:16px;background:#f9fafb;">
577
- <div style="display:flex;flex-wrap:wrap;align-items:center;gap:10px;justify-content:space-between;">
578
- <div style="font-weight:800;color:#0f172a;letter-spacing:.2px;">Visit #{i}</div>
579
- <div style="color:#475569;font-weight:600;">{html.escape(dt_str)}</div>
580
- </div>
581
- <div style="margin-top:10px;display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;">
582
- <div style="background:white;border:1px solid #e2e8f0;border-radius:10px;padding:12px;">
583
- <div style="font-size:12px;color:#64748b;font-weight:700;letter-spacing:.4px;text-transform:uppercase;">Location</div>
584
- <div style="font-weight:700;color:#0f172a;">{html.escape(str(wound_loc))}</div>
585
- </div>
586
- <div style="background:white;border:1px solid #e2e8f0;border-radius:10px;padding:12px;">
587
- <div style="font-size:12px;color:#64748b;font-weight:700;letter-spacing:.4px;text-transform:uppercase;">Pain</div>
588
- <div style="font-weight:700;color:#0f172a;">{html.escape(str(pain))} / 10</div>
589
- </div>
590
- <div style="background:white;border:1px solid #e2e8f0;border-radius:10px;padding:12px;">
591
- <div style="font-size:12px;color:#64748b;font-weight:700;letter-spacing:.4px;text-transform:uppercase;">Moisture</div>
592
- <div style="font-weight:700;color:#0f172a;">{html.escape(str(moisture))}</div>
593
- </div>
594
- <div style="background:white;border:1px solid #e2e8f0;border-radius:10px;padding:12px;">
595
- <div style="font-size:12px;color:#64748b;font-weight:700;letter-spacing:.4px;text-transform:uppercase;">Infection</div>
596
- <div style="font-weight:700;color:#0f172a;">{html.escape(str(infection))}</div>
597
- </div>
598
- <div style="background:white;border:1px solid #e2e8f0;border-radius:10px;padding:12px;">
599
- <div style="font-size:12px;color:#64748b;font-weight:700;letter-spacing:.4px;text-transform:uppercase;">AI Risk</div>
600
- <div>{risk}</div>
601
- </div>
602
- </div>
603
- {summary_block}
604
- {img_block}
605
- </div>
606
- </div>
607
- """
608
- items.append(card)
609
 
610
- tail = "</div></div>"
611
- return head + "".join(items) + tail
612
 
613
- # ---------- AUTH HANDLERS ----------
614
- def handle_login(username, password):
615
- user_data = self.auth_manager.authenticate_user(username, password)
616
- if user_data:
617
- self.current_user = user_data
618
- patient_update = _load_patient_names()
619
- return {
620
- auth_panel: gr.update(visible=False),
621
- practitioner_panel: gr.update(visible=True),
622
- login_status: "<div class='status-success'>βœ… Login successful! Welcome to SmartHeal</div>",
623
- patient_selector: patient_update
624
- }
625
- else:
626
- return {login_status: "<div class='status-error'>❌ Invalid credentials. Please try again.</div>"}
627
 
628
- def handle_signup(username, email, password, name, role, org_name_val, phone_val, cc_val, dept_val, loc_val, org_dropdown_val):
629
  try:
630
  if role == "organization":
631
  org_data = {
632
- 'org_name': org_name_val,
633
  'email': email,
634
- 'phone': phone_val,
635
- 'country_code': cc_val,
636
- 'department': dept_val,
637
- 'location': loc_val
638
  }
639
  org_id = self.database_manager.create_organization(org_data)
640
- user_data = {'username': username, 'email': email, 'password': password, 'name': name, 'role': role, 'org_id': org_id}
641
  else:
 
642
  org_id = 1
643
- user_data = {'username': username, 'email': email, 'password': password, 'name': name, 'role': role, 'org_id': org_id}
644
 
645
- if self.auth_manager.create_user(user_data):
646
- return {signup_status: "<div class='status-success'>βœ… Account created successfully! Please login.</div>"}
647
- else:
648
- return {signup_status: "<div class='status-error'>❌ Failed to create account. Username or email may already exist.</div>"}
 
 
 
 
649
  except Exception as e:
650
- return {signup_status: f"<div class='status-error'>❌ Error: {html.escape(str(e))}</div>"}
651
 
652
- def toggle_role_fields(role):
653
- if role == "organization":
654
- return {org_fields: gr.update(visible=True), prac_fields: gr.update(visible=False)}
655
- return {org_fields: gr.update(visible=False), prac_fields: gr.update(visible=True)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656
 
657
  def handle_logout():
658
  self.current_user = {}
659
- return {auth_panel: gr.update(visible=True), practitioner_panel: gr.update(visible=False)}
660
-
661
- # ---------- ANALYSIS HANDLER ----------
662
- def handle_analysis(
663
- _patient_name, _patient_age, _patient_gender, _wound_location, _wound_duration,
664
- _pain_level, _moisture_level, _infection_signs, _diabetic_status, _previous_treatment,
665
- _medical_history, _medications, _allergies, _additional_notes, _wound_image
666
- ):
 
 
 
 
 
 
 
 
667
  try:
668
- if not _wound_image:
669
- return "<div class='status-error'>❌ Please upload a wound image for analysis.</div>"
670
-
671
- questionnaire_data_for_db = {
672
- 'user_id': self.current_user.get('id', 1),
673
- 'patient_name': _patient_name,
674
- 'patient_age': _patient_age,
675
- 'patient_gender': _patient_gender,
676
- 'wound_location': _wound_location,
677
- 'wound_duration': _wound_duration,
678
- 'pain_level': _pain_level,
679
- 'moisture_level': _moisture_level,
680
- 'infection_signs': _infection_signs,
681
- 'diabetic_status': _diabetic_status,
682
- 'previous_treatment': _previous_treatment,
683
- 'medical_history': _medical_history,
684
- 'medications': _medications,
685
- 'allergies': _allergies,
686
- 'additional_notes': _additional_notes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
687
  }
688
 
689
- response_id = self.database_manager.save_questionnaire(questionnaire_data_for_db)
 
690
  if not response_id:
691
- return "<div class='status-error'>❌ Could not save questionnaire (patient/profile). Check logs.</div>"
 
 
 
 
 
692
 
693
- questionnaire_data_for_ai = {
694
- 'age': _patient_age,
695
- 'diabetic': 'Yes' if _diabetic_status != 'Non-diabetic' else 'No',
696
- 'allergies': _allergies,
 
 
 
 
 
 
 
 
 
 
 
697
  'date_of_injury': 'Unknown',
698
  'professional_care': 'Yes',
699
- 'oozing_bleeding': 'Minor Oozing' if _infection_signs != 'None' else 'None',
700
- 'infection': 'Yes' if _infection_signs != 'None' else 'No',
701
- 'moisture': _moisture_level,
702
- 'patient_name': _patient_name,
703
- 'patient_gender': _patient_gender,
704
- 'wound_location': _wound_location,
705
- 'wound_duration': _wound_duration,
706
- 'pain_level': _pain_level,
707
- 'previous_treatment': _previous_treatment,
708
- 'medical_history': _medical_history,
709
- 'medications': _medications,
710
- 'additional_notes': _additional_notes
711
  }
712
 
713
- analysis_result = self.wound_analyzer.analyze_wound(_wound_image, questionnaire_data_for_ai)
714
- if not analysis_result.get('success'):
715
- err = analysis_result.get('error', 'Analysis failed')
716
- return f"<div class='status-error'>❌ AI Analysis Error: {html.escape(str(err))}</div>"
 
717
 
 
718
  try:
719
- self.database_manager.save_analysis_result(response_id, analysis_result)
720
- except Exception as db_error:
721
- logging.error(f"DB save (analysis_result) error: {db_error}")
722
 
723
- formatted = self._format_comprehensive_analysis_results(analysis_result, _wound_image, questionnaire_data_for_ai)
724
- return formatted
 
725
 
 
 
 
 
726
  except Exception as e:
727
- logging.error(f"❌ Analysis handler error: {e}", exc_info=True)
728
- return f"<div class='status-error'>❌ System Error: {html.escape(str(e))}</div>"
729
 
730
- # ---------- HISTORY HELPERS ----------
731
- def load_patient_history():
732
  try:
733
- user_id = self.current_user.get('id', 1)
734
- if not user_id:
735
  return "<div class='status-error'>❌ Please login first.</div>"
736
- history_data = self.patient_history_manager.get_user_patient_history(user_id)
737
- formatted = self.patient_history_manager.format_history_for_display(history_data)
738
- return formatted
739
- except Exception as e:
740
- logging.error(f"Error loading patient history: {e}")
741
- return f"<div class='status-error'>❌ Error loading history: {html.escape(str(e))}</div>"
742
-
743
- def _load_patient_names():
744
- try:
745
- uid = self.current_user.get('id', 1)
746
- plist = self.patient_history_manager.get_patient_list(uid) or []
747
- names, seen = [], set()
748
- for row in plist:
749
- nm = row.get("patient_name")
750
- if nm and nm not in seen:
751
- names.append(nm); seen.add(nm)
752
- if not names:
753
- names = ["β€” No patients yet β€”"]
754
- return gr.update(choices=names, value=(names[0] if names else None))
755
  except Exception as e:
756
- logging.error(f"load patients error: {e}")
757
- return gr.update(choices=["β€” Error loading β€”"], value=None)
758
 
759
- def view_details_action(selected_name):
760
  try:
761
- uid = self.current_user.get('id', 1)
762
- if not selected_name or selected_name.startswith("β€”"):
763
- return "<div class='status-warning'>Please select a valid patient.</div>"
764
- rows = self.patient_history_manager.get_wound_progression(uid, selected_name)
765
- return _render_progression_timeline(rows)
 
 
 
766
  except Exception as e:
767
- logging.error(f"view details error: {e}")
768
- return f"<div class='status-error'>Error loading details: {html.escape(str(e))}</div>"
769
 
770
- def search_specific_patient_action(name_text):
771
  try:
772
- uid = self.current_user.get('id', 1)
773
  if not uid:
774
  return "<div class='status-error'>❌ Please login first.</div>"
775
- if not name_text or not name_text.strip():
776
- return "<div class='status-warning'>⚠️ Please enter a patient name to search.</div>"
777
- rows = self.patient_history_manager.search_patient_by_name(uid, name_text.strip())
778
- if not rows:
779
- return f"<div class='status-warning'>⚠️ No records found for patient: {html.escape(name_text.strip())}</div>"
780
- return self.patient_history_manager.format_patient_data_for_display(rows)
781
  except Exception as e:
782
- logging.error(f"Error searching patient: {e}")
783
- return f"<div class='status-error'>❌ Error searching patient: {html.escape(str(e))}</div>"
784
 
785
- # --------------------------- BINDINGS ---------------------------
786
- login_btn.click(
787
- handle_login,
788
- inputs=[login_username, login_password],
789
- outputs=[auth_panel, practitioner_panel, login_status, patient_selector]
790
- )
791
-
792
- signup_btn.click(
793
- handle_signup,
794
- inputs=[
795
- signup_username, signup_email, signup_password, signup_name, signup_role,
796
- org_name, phone, country_code, department, location, organization_dropdown
797
- ],
798
- outputs=[signup_status]
799
- )
800
 
801
  signup_role.change(
802
  toggle_role_fields,
@@ -804,42 +814,43 @@ button.gr-button:hover, button.gr-button-primary:hover {
804
  outputs=[org_fields, prac_fields]
805
  )
806
 
807
- analyze_btn.click(
808
- handle_analysis,
809
- inputs=[
810
- patient_name, patient_age, patient_gender, wound_location, wound_duration,
811
- pain_level, moisture_level, infection_signs, diabetic_status, previous_treatment,
812
- medical_history, medications, allergies, additional_notes, wound_image
813
- ],
814
- outputs=[analysis_output]
815
  )
816
 
817
- logout_btn_prac.click(
818
- handle_logout,
819
- outputs=[auth_panel, practitioner_panel]
 
 
820
  )
821
 
822
- history_btn.click(
823
- load_patient_history,
824
- outputs=[patient_history_output]
825
- )
826
 
827
- reload_patients_btn.click(
828
- _load_patient_names,
829
- outputs=[patient_selector]
 
830
  )
831
 
832
- view_details_btn.click(
833
- view_details_action,
834
- inputs=[patient_selector],
835
- outputs=[patient_details_output]
 
 
 
 
 
836
  )
837
 
838
- search_patient_btn.click(
839
- search_specific_patient_action,
840
- inputs=[search_patient_name],
841
- outputs=[specific_patient_output]
842
- )
843
 
844
  return app
845
 
 
376
  """
377
 
378
  def create_interface(self):
379
+ """
380
+ SmartHeal UI – aligned with current DB + history manager:
381
+ β€’ Login (practitioner / organization)
382
+ β€’ Practitioner: Wound Analysis (existing vs new patient), Patient History, View Details
383
+ β€’ Images from disk are shown via data URLs for reliable rendering
384
+ """
385
  import gradio as gr
386
+ from PIL import Image
 
 
387
 
388
+ # ----------------------- helpers (inner) -----------------------
389
 
390
+ self._patient_choices = [] # list[str] rendered in dropdown
391
+ self._patient_map = {} # label -> patient_id (int)
392
+
393
+ def _to_data_url_if_local(path_or_url: str) -> str:
394
+ if not path_or_url:
395
+ return ""
396
+ try:
397
+ if os.path.exists(path_or_url):
398
+ return self.image_to_base64(path_or_url) or ""
399
+ return path_or_url # already a URL
400
+ except Exception:
401
+ return ""
402
+
403
+ def _refresh_patient_dropdown(user_id: int):
404
+ """Query patient's list and prepare dropdown choices."""
405
+ self._patient_choices = []
406
+ self._patient_map = {}
407
+ try:
408
+ rows = self.patient_history_manager.get_patient_list(user_id) or []
409
+ # label starts with id -> stable parse
410
+ for r in rows:
411
+ pid = int(r.get("id") or 0)
412
+ nm = r.get("patient_name") or "Unknown"
413
+ age = r.get("patient_age") or ""
414
+ gen = r.get("patient_gender") or ""
415
+ v = int(r.get("total_visits") or 0)
416
+ label = f"{pid} β€’ {nm} ({age}y {gen}) β€” visits: {v}"
417
+ self._patient_choices.append(label)
418
+ self._patient_map[label] = pid
419
+ except Exception as e:
420
+ logging.error(f"refresh dropdown error: {e}")
421
+
422
+ def _label_to_id(label: str) -> Optional[int]:
423
+ if not label: return None
424
+ try:
425
+ return int(str(label).split("β€’", 1)[0].strip())
426
+ except Exception:
427
+ return None
428
 
429
+ def _fetch_patient_core(pid: int):
430
+ """Get name/age/gender for an existing patient id."""
431
+ row = self.database_manager.execute_query_one(
432
+ "SELECT id, name, age, gender FROM patients WHERE id=%s LIMIT 1", (pid,)
433
+ )
434
+ return row or {}
435
+
436
+ def _response_to_patient_id(resp_id: int) -> Optional[int]:
437
+ row = self.database_manager.execute_query_one(
438
+ "SELECT patient_id FROM questionnaire_responses WHERE id=%s LIMIT 1", (resp_id,)
439
+ )
440
+ try:
441
+ return int(row["patient_id"]) if row and "patient_id" in row else None
442
+ except Exception:
443
+ return None
444
+
445
+ def _rows_with_inline_images(rows: list[dict]) -> list[dict]:
446
+ """Convert local file paths to data URLs so HTML displays them anywhere."""
447
+ out = []
448
+ for r in rows or []:
449
+ r = dict(r)
450
+ if r.get("image_url"):
451
+ r["image_url"] = _to_data_url_if_local(r["image_url"])
452
+ out.append(r)
453
+ return out
454
+
455
+ # ----------------------- Blocks UI -----------------------
456
+
457
+ with gr.Blocks(css=self.get_custom_css(), title="SmartHeal - AI Wound Care Assistant") as app:
458
+ # Header
459
+ logo_url = "https://scontent.fccu31-2.fna.fbcdn.net/v/t39.30808-6/275933824_102121829111657_3325198727201325354_n.jpg?_nc_cat=104&ccb=1-7&_nc_sid=6ee11a&_nc_ohc=45krrEUpcSUQ7kNvwGVdiMW&_nc_oc=AdkTdxEC_TkYGiyDkEtTJZ_DFZELW17XKFmWpswmFqGB7JSdvTyWtnrQyLS0USngEiY&_nc_zt=23&_nc_ht=scontent.fccu31-2.fna&_nc_gid=ufAA4Hj5gTRwON5POYzz0Q&oh=00_AfW1-jLEN5RGeggqOvGgEaK_gdg0EDgxf_VhKbZwFLUO0Q&oe=6897A98B"
460
  gr.HTML(f"""
461
  <div class="medical-header">
462
  <img src="{logo_url}" class="logo" alt="SmartHeal Logo">
 
469
 
470
  # Disclaimer
471
  gr.HTML("""
472
+ <div style="border:2px solid #FF6B6B;background:#FFE5E5;padding:15px;border-radius:12px;margin:10px 0;">
473
+ <h3 style="color:#D63031;margin:0 0 8px 0;">⚠️ IMPORTANT DISCLAIMER</h3>
474
+ <p><strong>This system is for testing/education and not a substitute for clinical judgment.</strong></p>
 
475
  </div>
476
  """)
477
 
478
+ # Panels: auth vs practitioner vs organization
479
  with gr.Row():
 
480
  with gr.Column(visible=True) as auth_panel:
 
 
 
 
 
 
 
 
 
481
  with gr.Tabs():
482
  with gr.Tab("πŸ” Professional Login"):
483
+ login_username = gr.Textbox(label="πŸ‘€ Username")
484
+ login_password = gr.Textbox(label="πŸ”’ Password", type="password")
485
+ login_btn = gr.Button("πŸš€ Sign In", variant="primary")
486
+ login_status = gr.HTML("<div class='status-warning'>Please sign in.</div>")
487
 
488
  with gr.Tab("πŸ“ New Registration"):
489
+ signup_username = gr.Textbox(label="πŸ‘€ Username")
490
+ signup_email = gr.Textbox(label="πŸ“§ Email")
491
+ signup_password = gr.Textbox(label="πŸ”’ Password", type="password")
492
+ signup_name = gr.Textbox(label="πŸ‘¨β€βš•οΈ Full Name")
493
+ signup_role = gr.Radio(["practitioner", "organization"], label="Account Type", value="practitioner")
494
 
495
  with gr.Group(visible=False) as org_fields:
496
+ org_name = gr.Textbox(label="Organization Name")
497
+ phone = gr.Textbox(label="Phone")
498
+ country_code = gr.Textbox(label="Country Code")
499
+ department = gr.Textbox(label="Department")
500
+ location = gr.Textbox(label="Location")
 
501
 
502
  with gr.Group(visible=True) as prac_fields:
503
+ organization_dropdown = gr.Dropdown(choices=self.get_organizations_dropdown(), label="Select Organization")
 
504
 
505
+ signup_btn = gr.Button("✨ Create Account", variant="primary")
506
+ signup_status = gr.HTML()
507
 
 
508
  with gr.Column(visible=False) as practitioner_panel:
 
509
  user_info = gr.HTML("")
510
  logout_btn_prac = gr.Button("πŸšͺ Logout", variant="secondary")
511
 
512
  with gr.Tabs():
513
+ # ------------------- WOUND ANALYSIS -------------------
514
  with gr.Tab("πŸ”¬ Wound Analysis"):
515
  with gr.Row():
516
  with gr.Column(scale=1):
517
+ gr.HTML("<h3>πŸ“‹ Patient Selection</h3>")
518
+ patient_mode = gr.Radio(
519
+ ["Existing patient", "New patient"],
520
+ label="Patient mode",
521
+ value="Existing patient"
522
+ )
523
+ existing_patient_dd = gr.Dropdown(
524
+ choices=[],
525
+ label="Select existing patient (ID β€’ Name)",
526
+ interactive=True
527
+ )
528
+ with gr.Group(visible=False) as new_patient_group:
529
+ new_patient_name = gr.Textbox(label="Patient Name")
530
+ new_patient_age = gr.Number(label="Age", value=30, minimum=0, maximum=120)
531
+ new_patient_gender = gr.Dropdown(choices=["Male", "Female", "Other"], value="Male", label="Gender")
532
 
533
  gr.HTML("<h3>🩹 Wound Information</h3>")
534
+ wound_location = gr.Textbox(label="Wound Location", placeholder="e.g., Left ankle")
535
+ wound_duration = gr.Textbox(label="Wound Duration", placeholder="e.g., 2 weeks")
536
+ pain_level = gr.Slider(0, 10, value=5, step=1, label="Pain Level (0-10)")
537
 
538
  gr.HTML("<h3>βš•οΈ Clinical Assessment</h3>")
539
+ moisture_level = gr.Dropdown(["Dry", "Moist", "Wet", "Saturated"], value="Moist", label="Moisture Level")
540
+ infection_signs = gr.Dropdown(["None", "Mild", "Moderate", "Severe"], value="None", label="Signs of Infection")
541
+ diabetic_status = gr.Dropdown(["Non-diabetic", "Type 1", "Type 2", "Gestational"], value="Non-diabetic", label="Diabetic Status")
542
 
543
  with gr.Column(scale=1):
544
+ gr.HTML("<h3>πŸ“Έ Wound Image</h3>")
545
  wound_image = gr.Image(label="Upload Wound Image", type="filepath")
 
546
  gr.HTML("<h3>πŸ“ Medical History</h3>")
547
+ previous_treatment = gr.Textbox(label="Previous Treatment", lines=3)
548
+ medical_history = gr.Textbox(label="Medical History", lines=3)
549
+ medications = gr.Textbox(label="Current Medications", lines=2)
550
+ allergies = gr.Textbox(label="Known Allergies", lines=2)
551
+ additional_notes = gr.Textbox(label="Additional Notes", lines=3)
552
 
553
+ analyze_btn = gr.Button("πŸ”¬ Analyze Wound", variant="primary", elem_id="analyze-btn")
554
  analysis_output = gr.HTML("")
555
 
556
+ # ------------------- PATIENT HISTORY -------------------
557
  with gr.Tab("πŸ“‹ Patient History"):
558
  with gr.Row():
559
  with gr.Column(scale=2):
560
+ history_btn = gr.Button("πŸ“„ Load Patient History", variant="primary")
 
561
  patient_history_output = gr.HTML("")
 
562
  with gr.Column(scale=1):
563
+ search_patient_name = gr.Textbox(label="Search patient by name")
564
+ search_patient_btn = gr.Button("πŸ” Search", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
  specific_patient_output = gr.HTML("")
566
 
567
+ gr.HTML("<hr style='margin:10px 0 6px 0;border:none;border-top:1px solid #e2e8f0'>")
568
+ with gr.Row():
569
+ view_details_dd = gr.Dropdown(choices=[], label="Select patient to view details")
570
+ view_details_btn = gr.Button("πŸ“ˆ View Details (Timeline)", variant="primary")
571
+ view_details_output = gr.HTML("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
 
573
+ with gr.Column(visible=False) as organization_panel:
574
+ gr.HTML("<div class='status-warning'>Organization dashboard coming soon.</div>")
575
+ logout_btn_org = gr.Button("πŸšͺ Logout", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
576
 
577
+ # ----------------------- handlers -----------------------
 
578
 
579
+ def toggle_role_fields(role):
580
+ return {
581
+ org_fields: gr.update(visible=(role == "organization")),
582
+ prac_fields: gr.update(visible=(role != "organization"))
583
+ }
 
 
 
 
 
 
 
 
 
584
 
585
+ def handle_signup(username, email, password, name, role, org_name_v, phone_v, cc_v, dept_v, loc_v, org_dropdown):
586
  try:
587
  if role == "organization":
588
  org_data = {
589
+ 'org_name': org_name_v,
590
  'email': email,
591
+ 'phone': phone_v,
592
+ 'country_code': cc_v,
593
+ 'department': dept_v,
594
+ 'location': loc_v
595
  }
596
  org_id = self.database_manager.create_organization(org_data)
 
597
  else:
598
+ # For now pick first org (or default)
599
  org_id = 1
 
600
 
601
+ user_data = {
602
+ 'username': username, 'email': email, 'password': password,
603
+ 'name': name, 'role': role, 'org_id': org_id
604
+ }
605
+ ok = self.auth_manager.create_user(user_data)
606
+ if ok:
607
+ return "<div class='status-success'>βœ… Account created. Please log in.</div>"
608
+ return "<div class='status-error'>❌ Could not create account. Username/email may exist.</div>"
609
  except Exception as e:
610
+ return f"<div class='status-error'>❌ Error: {html.escape(str(e))}</div>"
611
 
612
+ def handle_login(username, password):
613
+ user = self.auth_manager.authenticate_user(username, password)
614
+ if not user:
615
+ return {
616
+ login_status: "<div class='status-error'>❌ Invalid credentials.</div>"
617
+ }
618
+ self.current_user = user
619
+ uid = int(user.get("id"))
620
+ role = user.get("role")
621
+
622
+ # Preload patient dropdowns for practitioners
623
+ if role == "practitioner":
624
+ _refresh_patient_dropdown(uid)
625
+
626
+ info = f"<div class='status-success'>Welcome, <strong>{html.escape(user.get('name','User'))}</strong> β€” {html.escape(role)}</div>"
627
+ updates = {login_status: info}
628
+
629
+ if role == "practitioner":
630
+ updates.update({
631
+ auth_panel: gr.update(visible=False),
632
+ practitioner_panel: gr.update(visible=True),
633
+ user_info: info,
634
+ existing_patient_dd: gr.update(choices=self._patient_choices),
635
+ view_details_dd: gr.update(choices=self._patient_choices),
636
+ })
637
+ else:
638
+ updates.update({
639
+ auth_panel: gr.update(visible=False),
640
+ organization_panel: gr.update(visible=True),
641
+ })
642
+ return updates
643
 
644
  def handle_logout():
645
  self.current_user = {}
646
+ return {
647
+ auth_panel: gr.update(visible=True),
648
+ practitioner_panel: gr.update(visible=False),
649
+ organization_panel: gr.update(visible=False)
650
+ }
651
+
652
+ def on_patient_mode_change(mode):
653
+ return {
654
+ new_patient_group: gr.update(visible=(mode == "New patient")),
655
+ existing_patient_dd: gr.update(interactive=(mode == "Existing patient"))
656
+ }
657
+
658
+ def run_analysis(mode, existing_label,
659
+ np_name, np_age, np_gender,
660
+ w_loc, w_dur, pain, moist, infect, diabetic,
661
+ prev_tx, med_hist, meds, alls, notes, img_path):
662
  try:
663
+ if not img_path:
664
+ return "<div class='status-error'>❌ Please upload a wound image.</div>"
665
+
666
+ user_id = int(self.current_user.get("id", 0) or 0)
667
+ if not user_id:
668
+ return "<div class='status-error'>❌ Please login first.</div>"
669
+
670
+ # Determine patient core fields (ensures same patient_id for existing)
671
+ if mode == "Existing patient":
672
+ pid = _label_to_id(existing_label)
673
+ if not pid:
674
+ return "<div class='status-warning'>⚠️ Select an existing patient.</div>"
675
+ pcore = _fetch_patient_core(pid)
676
+ patient_name_v = pcore.get("name")
677
+ patient_age_v = pcore.get("age")
678
+ patient_gender_v = pcore.get("gender")
679
+ else:
680
+ patient_name_v = np_name
681
+ patient_age_v = np_age
682
+ patient_gender_v = np_gender
683
+
684
+ # Build questionnaire payload
685
+ q_payload = {
686
+ 'user_id': user_id,
687
+ 'patient_name': patient_name_v,
688
+ 'patient_age': patient_age_v,
689
+ 'patient_gender': patient_gender_v,
690
+ 'wound_location': w_loc,
691
+ 'wound_duration': w_dur,
692
+ 'pain_level': pain,
693
+ 'moisture_level': moist,
694
+ 'infection_signs': infect,
695
+ 'diabetic_status': diabetic,
696
+ 'previous_treatment': prev_tx,
697
+ 'medical_history': med_hist,
698
+ 'medications': meds,
699
+ 'allergies': alls,
700
+ 'additional_notes': notes
701
  }
702
 
703
+ # Save questionnaire -> response_id
704
+ response_id = self.database_manager.save_questionnaire(q_payload)
705
  if not response_id:
706
+ return "<div class='status-error'>❌ Could not save questionnaire.</div>"
707
+
708
+ # Resolve patient_id from response (works for new or existing)
709
+ patient_id = _response_to_patient_id(response_id)
710
+ if not patient_id:
711
+ return "<div class='status-error'>❌ Could not resolve patient ID.</div>"
712
 
713
+ # Save wound image to DB
714
+ try:
715
+ with Image.open(img_path) as pil:
716
+ pil = pil.convert("RGB")
717
+ img_meta = self.database_manager.save_wound_image(patient_id, pil)
718
+ image_db_id = img_meta["id"] if img_meta else None
719
+ except Exception as e:
720
+ logging.error(f"save_wound_image error: {e}")
721
+ image_db_id = None
722
+
723
+ # Prepare AI analyzer questionnaire dict
724
+ q_for_ai = {
725
+ 'age': patient_age_v,
726
+ 'diabetic': 'Yes' if diabetic != 'Non-diabetic' else 'No',
727
+ 'allergies': alls,
728
  'date_of_injury': 'Unknown',
729
  'professional_care': 'Yes',
730
+ 'oozing_bleeding': 'Minor Oozing' if infect != 'None' else 'None',
731
+ 'infection': 'Yes' if infect != 'None' else 'No',
732
+ 'moisture': moist,
733
+ 'patient_name': patient_name_v,
734
+ 'patient_gender': patient_gender_v,
735
+ 'wound_location': w_loc,
736
+ 'wound_duration': w_dur,
737
+ 'pain_level': pain,
738
+ 'previous_treatment': prev_tx,
739
+ 'medical_history': med_hist,
740
+ 'medications': meds,
741
+ 'additional_notes': notes
742
  }
743
 
744
+ # Run AI
745
+ analysis_result = self.wound_analyzer.analyze_wound(img_path, q_for_ai)
746
+ if not analysis_result or not analysis_result.get("success"):
747
+ err = (analysis_result or {}).get("error", "Unknown analysis error")
748
+ return f"<div class='status-error'>❌ AI Analysis failed: {html.escape(str(err))}</div>"
749
 
750
+ # Persist AI analysis (ties back to template via response->questionnaire_id)
751
  try:
752
+ self.database_manager.save_analysis(response_id, image_db_id, analysis_result)
753
+ except Exception as e:
754
+ logging.error(f"save_analysis error: {e}")
755
 
756
+ # If a new patient was created, refresh dropdowns
757
+ if mode == "New patient":
758
+ _refresh_patient_dropdown(user_id)
759
 
760
+ # Render fancy results (this method already converts file paths to data URLs)
761
+ return self._format_comprehensive_analysis_results(
762
+ analysis_result, img_path, q_for_ai
763
+ )
764
  except Exception as e:
765
+ logging.exception("run_analysis exception")
766
+ return f"<div class='status-error'>❌ System error: {html.escape(str(e))}</div>"
767
 
768
+ def load_history():
 
769
  try:
770
+ uid = int(self.current_user.get("id", 0) or 0)
771
+ if not uid:
772
  return "<div class='status-error'>❌ Please login first.</div>"
773
+ rows = self.patient_history_manager.get_user_patient_history(uid) or []
774
+ rows = _rows_with_inline_images(rows)
775
+ return self.patient_history_manager.format_history_for_display(rows)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
776
  except Exception as e:
777
+ logging.error(f"load_history error: {e}")
778
+ return f"<div class='status-error'>❌ Error: {html.escape(str(e))}</div>"
779
 
780
+ def do_search(name):
781
  try:
782
+ uid = int(self.current_user.get("id", 0) or 0)
783
+ if not uid:
784
+ return "<div class='status-error'>❌ Please login first.</div>"
785
+ if not (name or "").strip():
786
+ return "<div class='status-warning'>⚠️ Enter a name to search.</div>"
787
+ rows = self.patient_history_manager.search_patient_by_name(uid, name.strip()) or []
788
+ rows = _rows_with_inline_images(rows)
789
+ return self.patient_history_manager.format_patient_data_for_display(rows)
790
  except Exception as e:
791
+ logging.error(f"search error: {e}")
792
+ return f"<div class='status-error'>❌ Error: {html.escape(str(e))}</div>"
793
 
794
+ def view_details(existing_label):
795
  try:
796
+ uid = int(self.current_user.get("id", 0) or 0)
797
  if not uid:
798
  return "<div class='status-error'>❌ Please login first.</div>"
799
+ pid = _label_to_id(existing_label)
800
+ if not pid:
801
+ return "<div class='status-warning'>⚠️ Select a patient.</div>"
802
+ rows = self.patient_history_manager.get_wound_progression_by_id(uid, pid) or []
803
+ rows = _rows_with_inline_images(rows)
804
+ return self.patient_history_manager.format_patient_progress_for_display(rows)
805
  except Exception as e:
806
+ logging.error(f"view_details error: {e}")
807
+ return f"<div class='status-error'>❌ Error: {html.escape(str(e))}</div>"
808
 
809
+ # ----------------------- wiring -----------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
810
 
811
  signup_role.change(
812
  toggle_role_fields,
 
814
  outputs=[org_fields, prac_fields]
815
  )
816
 
817
+ signup_btn.click(
818
+ handle_signup,
819
+ inputs=[signup_username, signup_email, signup_password, signup_name, signup_role,
820
+ org_name, phone, country_code, department, location, organization_dropdown],
821
+ outputs=[signup_status]
 
 
 
822
  )
823
 
824
+ login_btn.click(
825
+ handle_login,
826
+ inputs=[login_username, login_password],
827
+ outputs=[login_status, auth_panel, practitioner_panel, organization_panel,
828
+ user_info, existing_patient_dd, view_details_dd]
829
  )
830
 
831
+ logout_btn_prac.click(handle_logout, outputs=[auth_panel, practitioner_panel, organization_panel])
832
+ logout_btn_org.click(handle_logout, outputs=[auth_panel, practitioner_panel, organization_panel])
 
 
833
 
834
+ patient_mode.change(
835
+ on_patient_mode_change,
836
+ inputs=[patient_mode],
837
+ outputs=[new_patient_group, existing_patient_dd]
838
  )
839
 
840
+ analyze_btn.click(
841
+ run_analysis,
842
+ inputs=[
843
+ patient_mode, existing_patient_dd,
844
+ new_patient_name, new_patient_age, new_patient_gender,
845
+ wound_location, wound_duration, pain_level, moisture_level, infection_signs, diabetic_status,
846
+ previous_treatment, medical_history, medications, allergies, additional_notes, wound_image
847
+ ],
848
+ outputs=[analysis_output]
849
  )
850
 
851
+ history_btn.click(load_history, outputs=[patient_history_output])
852
+ search_patient_btn.click(do_search, inputs=[search_patient_name], outputs=[specific_patient_output])
853
+ view_details_btn.click(view_details, inputs=[view_details_dd], outputs=[view_details_output])
 
 
854
 
855
  return app
856