blackshadow1 commited on
Commit
403fccb
·
verified ·
1 Parent(s): eb97f40

updated code , for new feature✅✅

Browse files
Files changed (1) hide show
  1. mediSync/app.py +489 -2
mediSync/app.py CHANGED
@@ -385,6 +385,383 @@ class MediSyncApp:
385
  return "<p>Error displaying visualization.</p>"
386
 
387
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  def create_interface():
389
  """Create and launch the Gradio interface."""
390
 
@@ -433,8 +810,18 @@ def create_interface():
433
  1. Upload a chest X-ray image
434
  2. Enter the corresponding medical report text
435
  3. Choose the analysis type: image-only, text-only, or multimodal (combined)
 
436
  """)
437
 
 
 
 
 
 
 
 
 
 
438
  with gr.Tab("Multimodal Analysis"):
439
  with gr.Row():
440
  with gr.Column():
@@ -507,6 +894,17 @@ def create_interface():
507
  label="Example Medical Report",
508
  )
509
 
 
 
 
 
 
 
 
 
 
 
 
510
  with gr.Tab("About"):
511
  gr.Markdown("""
512
  ## About MediSync
@@ -552,12 +950,101 @@ def create_interface():
552
  outputs=[text_output, text_results, text_plot],
553
  )
554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
  # Run the interface
556
  interface.launch()
557
 
558
 
559
  if __name__ == "__main__":
560
- create_interface()
561
-
562
 
563
  # Example Script
 
385
  return "<p>Error displaying visualization.</p>"
386
 
387
 
388
+ import logging
389
+ import os
390
+ import sys
391
+ import tempfile
392
+ from pathlib import Path
393
+ import requests
394
+ import gradio as gr
395
+ import matplotlib.pyplot as plt
396
+ from PIL import Image
397
+ import json
398
+
399
+ # Add parent directory to path
400
+ parent_dir = os.path.dirname(os.path.abspath(__file__))
401
+ sys.path.append(parent_dir)
402
+
403
+ # Configure logging
404
+ logging.basicConfig(
405
+ level=logging.INFO,
406
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
407
+ handlers=[logging.StreamHandler(), logging.FileHandler("mediSync.log")],
408
+ )
409
+ logger = logging.getLogger(__name__)
410
+
411
+ class MediSyncApp:
412
+ """
413
+ Main application class for the MediSync multi-modal medical analysis system.
414
+ """
415
+
416
+ def __init__(self):
417
+ """Initialize the application and load models."""
418
+ self.logger = logging.getLogger(__name__)
419
+ self.logger.info("Initializing MediSync application")
420
+ self._temp_files = [] # Track temporary files for cleanup
421
+ self.fusion_model = None
422
+ self.image_model = None
423
+ self.text_model = None
424
+
425
+ def __del__(self):
426
+ """Cleanup temporary files on object destruction."""
427
+ self.cleanup_temp_files()
428
+
429
+ def cleanup_temp_files(self):
430
+ """Clean up temporary files."""
431
+ for temp_file in self._temp_files:
432
+ try:
433
+ if os.path.exists(temp_file):
434
+ os.remove(temp_file)
435
+ self.logger.debug(f"Cleaned up temporary file: {temp_file}")
436
+ except Exception as e:
437
+ self.logger.warning(f"Failed to clean up temporary file {temp_file}: {e}")
438
+ self._temp_files = []
439
+
440
+ def load_models(self):
441
+ """
442
+ Load models if not already loaded.
443
+
444
+ Returns:
445
+ bool: True if models loaded successfully, False otherwise
446
+ """
447
+ if self.fusion_model is not None:
448
+ return True
449
+
450
+ try:
451
+ self.logger.info("Loading models...")
452
+ # For now, we'll create a simple mock implementation
453
+ # You can replace this with your actual model loading code
454
+ self.logger.info("Models loaded successfully (mock implementation)")
455
+ return True
456
+ except Exception as e:
457
+ self.logger.error(f"Error loading models: {e}")
458
+ return False
459
+
460
+ def enhance_image(self, image):
461
+ """Enhance the uploaded image."""
462
+ if image is None:
463
+ return None
464
+
465
+ try:
466
+ # Simple image enhancement (you can replace with actual enhancement logic)
467
+ enhanced_image = image
468
+ self.logger.info("Image enhanced successfully")
469
+ return enhanced_image
470
+ except Exception as e:
471
+ self.logger.error(f"Error enhancing image: {e}")
472
+ return image
473
+
474
+ def analyze_image(self, image):
475
+ """
476
+ Analyze a medical image.
477
+
478
+ Args:
479
+ image: Image file uploaded through Gradio
480
+
481
+ Returns:
482
+ tuple: (image, image_results_html, plot_as_html)
483
+ """
484
+ if image is None:
485
+ return None, "Please upload an image first.", None
486
+
487
+ if not self.load_models():
488
+ return image, "Error: Models not loaded properly.", None
489
+
490
+ try:
491
+ self.logger.info("Analyzing image")
492
+
493
+ # Mock analysis results (replace with actual model inference)
494
+ results = {
495
+ "primary_finding": "Normal chest X-ray",
496
+ "confidence": 0.85,
497
+ "has_abnormality": False,
498
+ "predictions": [
499
+ ("Normal", 0.85),
500
+ ("Pneumonia", 0.10),
501
+ ("Cardiomegaly", 0.05)
502
+ ]
503
+ }
504
+
505
+ # Create visualization
506
+ fig = self.plot_image_prediction(
507
+ image,
508
+ results.get("predictions", []),
509
+ f"Primary Finding: {results.get('primary_finding', 'Unknown')}"
510
+ )
511
+
512
+ # Convert to HTML for display
513
+ plot_html = self.fig_to_html(fig)
514
+ plt.close(fig) # Clean up matplotlib figure
515
+
516
+ # Format results as HTML
517
+ html_result = self.format_image_results(results)
518
+
519
+ return image, html_result, plot_html
520
+
521
+ except Exception as e:
522
+ self.logger.error(f"Error in image analysis: {e}")
523
+ return image, f"Error analyzing image: {str(e)}", None
524
+
525
+ def analyze_text(self, text):
526
+ """
527
+ Analyze medical report text.
528
+
529
+ Args:
530
+ text: Medical report text
531
+
532
+ Returns:
533
+ tuple: (processed_text, text_results_html, plot_as_html)
534
+ """
535
+ if not text or text.strip() == "":
536
+ return "", "Please enter medical report text.", None
537
+
538
+ if not self.load_models():
539
+ return text, "Error: Models not loaded properly.", None
540
+
541
+ try:
542
+ self.logger.info("Analyzing text")
543
+
544
+ # Mock text analysis results (replace with actual model inference)
545
+ results = {
546
+ "entities": [
547
+ {"text": "chest X-ray", "type": "PROCEDURE", "confidence": 0.95},
548
+ {"text": "55-year-old male", "type": "PATIENT", "confidence": 0.90},
549
+ {"text": "cough and fever", "type": "SYMPTOM", "confidence": 0.88}
550
+ ],
551
+ "sentiment": "neutral",
552
+ "key_findings": ["Normal heart size", "Clear lungs", "8mm nodular opacity"]
553
+ }
554
+
555
+ # Format results as HTML
556
+ html_result = self.format_text_results(results)
557
+
558
+ # Create entity visualization
559
+ plot_html = self.create_entity_visualization(results["entities"])
560
+
561
+ return text, html_result, plot_html
562
+
563
+ except Exception as e:
564
+ self.logger.error(f"Error in text analysis: {e}")
565
+ return text, f"Error analyzing text: {str(e)}", None
566
+
567
+ def analyze_multimodal(self, image, text):
568
+ """
569
+ Analyze both image and text together.
570
+
571
+ Args:
572
+ image: Medical image
573
+ text: Medical report text
574
+
575
+ Returns:
576
+ tuple: (results_html, plot_as_html)
577
+ """
578
+ if image is None and (not text or text.strip() == ""):
579
+ return "Please provide either an image or text for analysis.", None
580
+
581
+ if not self.load_models():
582
+ return "Error: Models not loaded properly.", None
583
+
584
+ try:
585
+ self.logger.info("Performing multimodal analysis")
586
+
587
+ # Mock multimodal analysis results (replace with actual model inference)
588
+ results = {
589
+ "combined_finding": "Normal chest X-ray with minor findings",
590
+ "confidence": 0.92,
591
+ "image_contribution": "Normal cardiac silhouette and clear lung fields",
592
+ "text_contribution": "Clinical history supports normal findings",
593
+ "recommendations": [
594
+ "Follow-up CT for the 8mm nodular opacity",
595
+ "Monitor for any changes in symptoms"
596
+ ]
597
+ }
598
+
599
+ # Format results as HTML
600
+ html_result = self.format_multimodal_results(results)
601
+
602
+ # Create combined visualization
603
+ plot_html = self.create_multimodal_visualization(results)
604
+
605
+ return html_result, plot_html
606
+
607
+ except Exception as e:
608
+ self.logger.error(f"Error in multimodal analysis: {e}")
609
+ return f"Error in multimodal analysis: {str(e)}", None
610
+
611
+ def format_image_results(self, results):
612
+ """Format image analysis results as HTML."""
613
+ html_result = f"""
614
+ <div style="background-color: #f8f9fa; padding: 20px; border-radius: 10px; margin: 10px 0;">
615
+ <h2 style="color: #007bff;">X-ray Analysis Results</h2>
616
+ <p><strong>Primary Finding:</strong> {results.get("primary_finding", "Unknown")}</p>
617
+ <p><strong>Confidence:</strong> {results.get("confidence", 0):.1%}</p>
618
+ <p><strong>Abnormality Detected:</strong> {"Yes" if results.get("has_abnormality", False) else "No"}</p>
619
+
620
+ <h3>Top Predictions:</h3>
621
+ <ul>
622
+ """
623
+
624
+ for label, prob in results.get("predictions", [])[:5]:
625
+ html_result += f"<li>{label}: {prob:.1%}</li>"
626
+
627
+ html_result += "</ul></div>"
628
+ return html_result
629
+
630
+ def format_text_results(self, results):
631
+ """Format text analysis results as HTML."""
632
+ html_result = f"""
633
+ <div style="background-color: #f8f9fa; padding: 20px; border-radius: 10px; margin: 10px 0;">
634
+ <h2 style="color: #28a745;">Text Analysis Results</h2>
635
+ <p><strong>Sentiment:</strong> {results.get("sentiment", "Unknown").title()}</p>
636
+
637
+ <h3>Key Findings:</h3>
638
+ <ul>
639
+ """
640
+
641
+ for finding in results.get("key_findings", []):
642
+ html_result += f"<li>{finding}</li>"
643
+
644
+ html_result += "</ul>"
645
+
646
+ html_result += "<h3>Extracted Entities:</h3><ul>"
647
+ for entity in results.get("entities", [])[:5]:
648
+ html_result += f"<li><strong>{entity['text']}</strong> ({entity['type']}) - {entity['confidence']:.1%}</li>"
649
+
650
+ html_result += "</ul></div>"
651
+ return html_result
652
+
653
+ def format_multimodal_results(self, results):
654
+ """Format multimodal analysis results as HTML."""
655
+ html_result = f"""
656
+ <div style="background-color: #f8f9fa; padding: 20px; border-radius: 10px; margin: 10px 0;">
657
+ <h2 style="color: #6f42c1;">Multimodal Analysis Results</h2>
658
+ <p><strong>Combined Finding:</strong> {results.get("combined_finding", "Unknown")}</p>
659
+ <p><strong>Overall Confidence:</strong> {results.get("confidence", 0):.1%}</p>
660
+
661
+ <h3>Image Contribution:</h3>
662
+ <p>{results.get("image_contribution", "No image analysis available")}</p>
663
+
664
+ <h3>Text Contribution:</h3>
665
+ <p>{results.get("text_contribution", "No text analysis available")}</p>
666
+
667
+ <h3>Recommendations:</h3>
668
+ <ul>
669
+ """
670
+
671
+ for rec in results.get("recommendations", []):
672
+ html_result += f"<li>{rec}</li>"
673
+
674
+ html_result += "</ul></div>"
675
+ return html_result
676
+
677
+ def plot_image_prediction(self, image, predictions, title):
678
+ """Create visualization for image predictions."""
679
+ fig, ax = plt.subplots(figsize=(10, 6))
680
+ ax.imshow(image)
681
+ ax.set_title(title, fontsize=14, fontweight='bold')
682
+ ax.axis('off')
683
+ return fig
684
+
685
+ def create_entity_visualization(self, entities):
686
+ """Create visualization for text entities."""
687
+ if not entities:
688
+ return "<p>No entities found in text.</p>"
689
+
690
+ fig, ax = plt.subplots(figsize=(10, 6))
691
+
692
+ entity_types = {}
693
+ for entity in entities:
694
+ entity_type = entity['type']
695
+ if entity_type not in entity_types:
696
+ entity_types[entity_type] = 0
697
+ entity_types[entity_type] += 1
698
+
699
+ if entity_types:
700
+ ax.bar(entity_types.keys(), entity_types.values(), color='skyblue')
701
+ ax.set_title('Entity Types Found in Text', fontsize=14, fontweight='bold')
702
+ ax.set_ylabel('Count')
703
+ plt.xticks(rotation=45)
704
+
705
+ return self.fig_to_html(fig)
706
+
707
+ def create_multimodal_visualization(self, results):
708
+ """Create visualization for multimodal results."""
709
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
710
+
711
+ # Confidence visualization
712
+ confidence = results.get("confidence", 0)
713
+ ax1.pie([confidence, 1-confidence], labels=['Confidence', 'Uncertainty'],
714
+ colors=['lightgreen', 'lightcoral'], autopct='%1.1f%%')
715
+ ax1.set_title('Analysis Confidence', fontweight='bold')
716
+
717
+ # Recommendations count
718
+ recommendations = results.get("recommendations", [])
719
+ ax2.bar(['Recommendations'], [len(recommendations)], color='lightblue')
720
+ ax2.set_title('Number of Recommendations', fontweight='bold')
721
+ ax2.set_ylabel('Count')
722
+
723
+ plt.tight_layout()
724
+ return self.fig_to_html(fig)
725
+
726
+ def fig_to_html(self, fig):
727
+ """Convert matplotlib figure to HTML."""
728
+ import io
729
+ import base64
730
+
731
+ buf = io.BytesIO()
732
+ fig.savefig(buf, format='png', bbox_inches='tight', dpi=100)
733
+ buf.seek(0)
734
+ img_str = base64.b64encode(buf.read()).decode()
735
+ buf.close()
736
+
737
+ return f'<img src="data:image/png;base64,{img_str}" style="max-width: 100%; height: auto;"/>'
738
+
739
+ def complete_appointment(appointment_id):
740
+ """
741
+ Complete an appointment by calling the Flask API.
742
+
743
+ Args:
744
+ appointment_id: The appointment ID to complete
745
+
746
+ Returns:
747
+ dict: Response from the API
748
+ """
749
+ try:
750
+ # Call the Flask API to complete the appointment
751
+ flask_api_url = "http://127.0.0.1:600/complete_appointment"
752
+ payload = {"appointment_id": appointment_id}
753
+
754
+ response = requests.post(flask_api_url, json=payload, timeout=10)
755
+
756
+ if response.status_code == 200:
757
+ return {"status": "success", "message": "Appointment completed successfully"}
758
+ else:
759
+ return {"status": "error", "message": f"Failed to complete appointment: {response.text}"}
760
+
761
+ except Exception as e:
762
+ logger.error(f"Error completing appointment: {e}")
763
+ return {"status": "error", "message": f"Error: {str(e)}"}
764
+
765
  def create_interface():
766
  """Create and launch the Gradio interface."""
767
 
 
810
  1. Upload a chest X-ray image
811
  2. Enter the corresponding medical report text
812
  3. Choose the analysis type: image-only, text-only, or multimodal (combined)
813
+ 4. Click "End Consultation" when finished to complete your appointment
814
  """)
815
 
816
+ # Add appointment ID input
817
+ with gr.Row():
818
+ appointment_id_input = gr.Textbox(
819
+ label="Appointment ID",
820
+ placeholder="Enter your appointment ID here...",
821
+ info="This will be automatically populated if you came from the doctors page",
822
+ value=gr.State("") # This will be populated via JavaScript
823
+ )
824
+
825
  with gr.Tab("Multimodal Analysis"):
826
  with gr.Row():
827
  with gr.Column():
 
894
  label="Example Medical Report",
895
  )
896
 
897
+ # End Consultation Section
898
+ with gr.Row():
899
+ with gr.Column():
900
+ end_consultation_btn = gr.Button(
901
+ "End Consultation",
902
+ variant="stop",
903
+ size="lg",
904
+ elem_classes=["end-consultation-btn"]
905
+ )
906
+ end_consultation_status = gr.HTML(label="Status")
907
+
908
  with gr.Tab("About"):
909
  gr.Markdown("""
910
  ## About MediSync
 
950
  outputs=[text_output, text_results, text_plot],
951
  )
952
 
953
+ # End consultation handler
954
+ def handle_end_consultation(appointment_id):
955
+ if not appointment_id or appointment_id.strip() == "":
956
+ return "<div style='color: red; padding: 10px; background-color: #ffe6e6; border-radius: 5px;'>Please enter your appointment ID first.</div>"
957
+
958
+ result = complete_appointment(appointment_id.strip())
959
+
960
+ if result["status"] == "success":
961
+ # Create success message with redirect button
962
+ html_response = f"""
963
+ <div style='color: green; padding: 15px; background-color: #e6ffe6; border-radius: 5px; margin: 10px 0;'>
964
+ <h3>✅ Consultation Completed Successfully!</h3>
965
+ <p>{result['message']}</p>
966
+ <p>Your appointment has been marked as completed.</p>
967
+ <button onclick="window.open('http://127.0.0.1:600/doctors', '_blank')"
968
+ style="background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; margin-top: 10px;">
969
+ Return to Doctors Page
970
+ </button>
971
+ </div>
972
+ """
973
+ else:
974
+ html_response = f"""
975
+ <div style='color: red; padding: 15px; background-color: #ffe6e6; border-radius: 5px; margin: 10px 0;'>
976
+ <h3>❌ Error Completing Consultation</h3>
977
+ <p>{result['message']}</p>
978
+ <p>Please try again or contact support if the problem persists.</p>
979
+ </div>
980
+ """
981
+
982
+ return html_response
983
+
984
+ end_consultation_btn.click(
985
+ handle_end_consultation,
986
+ inputs=[appointment_id_input],
987
+ outputs=[end_consultation_status]
988
+ )
989
+
990
+ # Add custom CSS and JavaScript for better styling and functionality
991
+ gr.HTML("""
992
+ <style>
993
+ .end-consultation-btn {
994
+ background-color: #dc3545 !important;
995
+ border-color: #dc3545 !important;
996
+ color: white !important;
997
+ font-weight: bold !important;
998
+ }
999
+ .end-consultation-btn:hover {
1000
+ background-color: #c82333 !important;
1001
+ border-color: #bd2130 !important;
1002
+ }
1003
+ </style>
1004
+
1005
+ <script>
1006
+ // Function to get URL parameters
1007
+ function getUrlParameter(name) {
1008
+ name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
1009
+ var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
1010
+ var results = regex.exec(location.search);
1011
+ return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
1012
+ }
1013
+
1014
+ // Function to populate appointment ID from URL
1015
+ function populateAppointmentId() {
1016
+ var appointmentId = getUrlParameter('appointment_id');
1017
+ if (appointmentId) {
1018
+ // Find the appointment ID input field and populate it
1019
+ var inputs = document.querySelectorAll('input[type="text"]');
1020
+ for (var i = 0; i < inputs.length; i++) {
1021
+ if (inputs[i].placeholder && inputs[i].placeholder.includes('appointment ID')) {
1022
+ inputs[i].value = appointmentId;
1023
+ break;
1024
+ }
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ // Run when page loads
1030
+ document.addEventListener('DOMContentLoaded', function() {
1031
+ populateAppointmentId();
1032
+ });
1033
+
1034
+ // Also run after Gradio loads (in case of dynamic loading)
1035
+ if (typeof gradio !== 'undefined') {
1036
+ gradio.addEventListener('load', function() {
1037
+ setTimeout(populateAppointmentId, 1000);
1038
+ });
1039
+ }
1040
+ </script>
1041
+ """)
1042
+
1043
  # Run the interface
1044
  interface.launch()
1045
 
1046
 
1047
  if __name__ == "__main__":
1048
+ create_interface()
 
1049
 
1050
  # Example Script