| import gradio as gr |
| import logging |
| from datetime import datetime |
| import time |
| from typing import Dict, Any, Optional, Tuple |
| import json |
| import os |
| from PIL import Image |
|
|
| from .enhanced_ai_processor import EnhancedAIProcessor |
| from .dashboard_database_manager import DashboardDatabaseManager |
| from .dashboard_api import DashboardIntegrationManager |
| from .auth import AuthManager |
|
|
| class EnhancedUIComponents: |
| """Enhanced UI components with dashboard integration and analytics tracking""" |
| |
| def __init__(self, auth_manager: AuthManager, database_manager: DashboardDatabaseManager, |
| ai_processor: EnhancedAIProcessor): |
| """Initialize enhanced UI components""" |
| self.auth_manager = auth_manager |
| self.database_manager = database_manager |
| self.ai_processor = ai_processor |
| self.dashboard_integration = DashboardIntegrationManager(database_manager) |
| |
| |
| self.dashboard_integration.start_integration() |
| |
| |
| self.theme = gr.themes.Soft() |
| self.custom_css = self._load_custom_css() |
| |
| |
| self.current_session = {} |
| |
| logging.info("β
Enhanced UI Components initialized with dashboard integration") |
| |
| def _load_custom_css(self): |
| """Load custom CSS for the application""" |
| return """ |
| /* =================== SMARTHEAL CSS =================== */ |
| /* Global Styling */ |
| body, html { |
| margin: 0 !important; |
| padding: 0 !important; |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif !important; |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important; |
| color: #1A202C !important; |
| line-height: 1.6 !important; |
| } |
| /* Professional Header with Logo */ |
| .medical-header { |
| background: linear-gradient(135deg, #3182ce 0%, #2c5aa0 100%) !important; |
| color: white !important; |
| padding: 32px 40px !important; |
| border-radius: 20px 20px 0 0 !important; |
| display: flex !important; |
| align-items: center !important; |
| justify-content: center !important; |
| margin-bottom: 0 !important; |
| box-shadow: 0 10px 40px rgba(49, 130, 206, 0.3) !important; |
| border: none !important; |
| position: relative !important; |
| overflow: hidden !important; |
| } |
| .logo { |
| width: 80px !important; |
| height: 80px !important; |
| border-radius: 50% !important; |
| margin-right: 24px !important; |
| border: 4px solid rgba(255, 255, 255, 0.3) !important; |
| box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2) !important; |
| background: white !important; |
| padding: 4px !important; |
| } |
| .medical-header h1 { |
| font-size: 3.5rem !important; |
| font-weight: 800 !important; |
| margin: 0 !important; |
| text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3) !important; |
| background: linear-gradient(45deg, #ffffff, #f8f9fa) !important; |
| -webkit-background-clip: text !important; |
| -webkit-text-fill-color: transparent !important; |
| background-clip: text !important; |
| filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.3)) !important; |
| } |
| .medical-header p { |
| font-size: 1.3rem !important; |
| margin: 8px 0 0 0 !important; |
| opacity: 0.95 !important; |
| font-weight: 500 !important; |
| text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2) !important; |
| } |
| /* Enhanced Form Styling */ |
| .gr-form { |
| background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%) !important; |
| border-radius: 20px !important; |
| padding: 32px !important; |
| margin: 24px 0 !important; |
| box-shadow: 0 16px 48px rgba(0, 0, 0, 0.1) !important; |
| border: 1px solid rgba(229, 62, 62, 0.1) !important; |
| backdrop-filter: blur(10px) !important; |
| position: relative !important; |
| overflow: hidden !important; |
| } |
| /* Professional Input Fields */ |
| .gr-textbox, .gr-number { |
| border-radius: 12px !important; |
| border: 2px solid #E2E8F0 !important; |
| background: #FFFFFF !important; |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05) !important; |
| font-size: 1rem !important; |
| color: #1A202C !important; |
| padding: 16px 20px !important; |
| } |
| .gr-textbox:focus, .gr-number:focus, .gr-textbox input:focus, .gr-number input:focus { |
| border-color: #E53E3E !important; |
| box-shadow: 0 0 0 4px rgba(229, 62, 62, 0.1) !important; |
| background: #FFFFFF !important; |
| outline: none !important; |
| transform: translateY(-1px) !important; |
| } |
| /* Enhanced Button Styling */ |
| button.gr-button, button.gr-button-primary { |
| background: linear-gradient(135deg, #E53E3E 0%, #C53030 100%) !important; |
| color: #FFFFFF !important; |
| border: none !important; |
| border-radius: 12px !important; |
| font-weight: 700 !important; |
| padding: 16px 32px !important; |
| font-size: 1.1rem !important; |
| letter-spacing: 0.5px !important; |
| text-align: center !important; |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; |
| box-shadow: 0 4px 16px rgba(229, 62, 62, 0.3) !important; |
| position: relative !important; |
| overflow: hidden !important; |
| text-transform: uppercase !important; |
| cursor: pointer !important; |
| } |
| button.gr-button:hover, button.gr-button-primary:hover { |
| background: linear-gradient(135deg, #C53030 0%, #9C2A2A 100%) !important; |
| box-shadow: 0 8px 32px rgba(229, 62, 62, 0.4) !important; |
| transform: translateY(-3px) !important; |
| } |
| /* Professional Status Messages */ |
| .status-success { |
| background: linear-gradient(135deg, #F0FFF4 0%, #E6FFFA 100%) !important; |
| border: 2px solid #38A169 !important; |
| color: #22543D !important; |
| padding: 20px 24px !important; |
| border-radius: 16px !important; |
| font-weight: 600 !important; |
| margin: 16px 0 !important; |
| box-shadow: 0 8px 24px rgba(56, 161, 105, 0.2) !important; |
| backdrop-filter: blur(10px) !important; |
| } |
| .status-error { |
| background: linear-gradient(135deg, #FFF5F5 0%, #FED7D7 100%) !important; |
| border: 2px solid #E53E3E !important; |
| color: #742A2A !important; |
| padding: 20px 24px !important; |
| border-radius: 16px !important; |
| font-weight: 600 !important; |
| margin: 16px 0 !important; |
| box-shadow: 0 8px 24px rgba(229, 62, 62, 0.2) !important; |
| backdrop-filter: blur(10px) !important; |
| } |
| .status-warning { |
| background: linear-gradient(135deg, #FFFAF0 0%, #FEEBC8 100%) !important; |
| border: 2px solid #DD6B20 !important; |
| color: #9C4221 !important; |
| padding: 20px 24px !important; |
| border-radius: 16px !important; |
| font-weight: 600 !important; |
| margin: 16px 0 !important; |
| box-shadow: 0 8px 24px rgba(221, 107, 32, 0.2) !important; |
| backdrop-filter: blur(10px) !important; |
| } |
| /* Image gallery styling for better visualization */ |
| .image-gallery { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
| gap: 20px; |
| margin: 20px 0; |
| } |
| .image-item { |
| background: #f8f9fa; |
| border-radius: 12px; |
| padding: 15px; |
| box-shadow: 0 4px 12px rgba(0,0,0,0.1); |
| text-align: center; |
| } |
| .image-item img { |
| max-width: 100%; |
| height: auto; |
| border-radius: 8px; |
| box-shadow: 0 2px 8px rgba(0,0,0,0.15); |
| } |
| .image-item h4 { |
| margin: 15px 0 5px 0; |
| color: #2d3748; |
| font-weight: 600; |
| } |
| .image-item p { |
| margin: 0; |
| color: #666; |
| font-size: 0.9em; |
| } |
| /* Analyze button special styling */ |
| #analyze-btn { |
| background: linear-gradient(135deg, #1B5CF3 0%, #1E3A8A 100%) !important; |
| color: #FFFFFF !important; |
| border: none !important; |
| border-radius: 8px !important; |
| font-weight: 700 !important; |
| padding: 14px 28px !important; |
| font-size: 1.1rem !important; |
| letter-spacing: 0.5px !important; |
| text-align: center !important; |
| transition: all 0.2s ease-in-out !important; |
| } |
| #analyze-btn:hover { |
| background: linear-gradient(135deg, #174ea6 0%, #123b82 100%) !important; |
| box-shadow: 0 4px 14px rgba(27, 95, 193, 0.4) !important; |
| transform: translateY(-2px) !important; |
| } |
| /* Responsive design */ |
| @media (max-width: 768px) { |
| .medical-header { |
| padding: 16px !important; |
| text-align: center !important; |
| } |
| |
| .medical-header h1 { |
| font-size: 2rem !important; |
| } |
| |
| .logo { |
| width: 48px !important; |
| height: 48px !important; |
| margin-right: 16px !important; |
| } |
| |
| .gr-form { |
| padding: 16px !important; |
| margin: 8px 0 !important; |
| } |
| |
| .image-gallery { |
| grid-template-columns: 1fr; |
| } |
| } |
| """ |
| |
| def create_interface(self): |
| """Create the enhanced Gradio interface with dashboard integration""" |
| |
| with gr.Blocks(theme=self.theme, css=self.custom_css, title="SmartHeal AI - Enhanced") as interface: |
| |
| |
| gr.HTML(""" |
| <div class="main-header"> |
| <h1>π₯ SmartHeal AI - Enhanced Edition</h1> |
| <p>Advanced Wound Care Analysis with Real-time Dashboard Integration</p> |
| </div> |
| """) |
| |
| |
| integration_status = gr.HTML(self._get_integration_status_html()) |
| |
| |
| session_info = gr.HTML(self._get_session_info_html()) |
| |
| with gr.Tabs(): |
| |
| |
| with gr.Tab("π Authentication"): |
| with gr.Row(): |
| with gr.Column(): |
| gr.HTML(""" |
| <div class="section-header"> |
| <h2>User Authentication</h2> |
| <h3>Login to access SmartHeal AI analysis features</h3> |
| </div> |
| """) |
| |
| username_input = gr.Textbox( |
| label="Username", |
| placeholder="Enter your username", |
| interactive=True |
| ) |
| password_input = gr.Textbox( |
| label="Password", |
| type="password", |
| placeholder="Enter your password", |
| interactive=True |
| ) |
| login_btn = gr.Button("Login", variant="primary") |
| logout_btn = gr.Button("Logout", variant="secondary") |
| |
| auth_status = gr.HTML(value="<div class='warning-box'>Please login to continue</div>") |
| |
| |
| with gr.Tab("π¬ Wound Analysis"): |
| with gr.Row(): |
| with gr.Column(scale=1): |
| gr.HTML(""" |
| <div class="section-header"> |
| <h2>Patient Information</h2> |
| <h3>Complete patient details for comprehensive analysis</h3> |
| </div> |
| """) |
| |
| |
| patient_name = gr.Textbox(label="Patient Name", placeholder="Enter patient name") |
| patient_age = gr.Number(label="Patient Age", value=0, minimum=0, maximum=120) |
| patient_gender = gr.Dropdown( |
| label="Gender", |
| choices=["Male", "Female", "Other"], |
| value="Male" |
| ) |
| |
| |
| gr.HTML(""" |
| <div class="section-header"> |
| <h2>Wound Information</h2> |
| </div> |
| """) |
| |
| wound_location = gr.Textbox(label="Wound Location", placeholder="e.g., Left heel, Right forearm") |
| wound_duration = gr.Textbox(label="Wound Duration", placeholder="e.g., 2 weeks, 1 month") |
| pain_level = gr.Slider(label="Pain Level (0-10)", minimum=0, maximum=10, value=0, step=1) |
| |
| |
| moisture_level = gr.Dropdown( |
| label="Moisture Level", |
| choices=["Dry", "Moist", "Wet", "Macerated"], |
| value="Moist" |
| ) |
| infection_signs = gr.Dropdown( |
| label="Signs of Infection", |
| choices=["None", "Mild", "Moderate", "Severe"], |
| value="None" |
| ) |
| diabetic_status = gr.Dropdown( |
| label="Diabetic Status", |
| choices=["No", "Type 1", "Type 2", "Unknown"], |
| value="No" |
| ) |
| |
| |
| gr.HTML(""" |
| <div class="section-header"> |
| <h2>Medical History</h2> |
| </div> |
| """) |
| |
| previous_treatment = gr.Textbox( |
| label="Previous Treatment", |
| placeholder="Describe any previous treatments", |
| lines=2 |
| ) |
| medical_history = gr.Textbox( |
| label="Medical History", |
| placeholder="Relevant medical conditions", |
| lines=2 |
| ) |
| medications = gr.Textbox( |
| label="Current Medications", |
| placeholder="List current medications", |
| lines=2 |
| ) |
| allergies = gr.Textbox( |
| label="Known Allergies", |
| placeholder="List any known allergies", |
| lines=2 |
| ) |
| additional_notes = gr.Textbox( |
| label="Additional Notes", |
| placeholder="Any additional relevant information", |
| lines=3 |
| ) |
| |
| with gr.Column(scale=1): |
| gr.HTML(""" |
| <div class="section-header"> |
| <h2>Wound Image Analysis</h2> |
| <h3>Upload wound image for AI analysis</h3> |
| </div> |
| """) |
| |
| |
| wound_image = gr.Image( |
| label="Wound Image", |
| type="pil", |
| height=400 |
| ) |
| |
| |
| analyze_btn = gr.Button("π Analyze Wound", variant="primary", size="lg") |
| |
| |
| processing_status = gr.HTML(visible=False) |
| |
| |
| analysis_metrics = gr.HTML(visible=False) |
| |
| |
| with gr.Row(): |
| with gr.Column(): |
| gr.HTML(""" |
| <div class="section-header"> |
| <h2>Analysis Results</h2> |
| <h3>Comprehensive AI-powered wound assessment</h3> |
| </div> |
| """) |
| |
| |
| with gr.Row(): |
| detection_image = gr.Image(label="Wound Detection", visible=False) |
| segmentation_image = gr.Image(label="Wound Segmentation", visible=False) |
| |
| |
| analysis_report = gr.Markdown(visible=False) |
| |
| |
| with gr.Row(): |
| download_report = gr.File(label="Download Report", visible=False) |
| download_images = gr.File(label="Download Analysis Images", visible=False) |
| |
| |
| with gr.Tab("π Dashboard Integration"): |
| gr.HTML(""" |
| <div class="section-header"> |
| <h2>Dashboard Integration Status</h2> |
| <h3>Real-time connection to SmartHeal Dashboard</h3> |
| </div> |
| """) |
| |
| dashboard_status = gr.HTML() |
| |
| with gr.Row(): |
| refresh_status_btn = gr.Button("π Refresh Status", variant="secondary") |
| view_analytics_btn = gr.Button("π View Analytics", variant="primary") |
| |
| |
| analytics_summary = gr.HTML() |
| |
| |
| recent_activity = gr.HTML() |
| |
| |
| |
| |
| login_btn.click( |
| fn=self._handle_login, |
| inputs=[username_input, password_input], |
| outputs=[auth_status, session_info] |
| ) |
| |
| logout_btn.click( |
| fn=self._handle_logout, |
| outputs=[auth_status, session_info] |
| ) |
| |
| |
| analyze_btn.click( |
| fn=self._start_analysis, |
| inputs=[], |
| outputs=[processing_status, analysis_metrics] |
| ).then( |
| fn=self._perform_enhanced_analysis, |
| inputs=[ |
| patient_name, patient_age, patient_gender, wound_location, wound_duration, |
| pain_level, moisture_level, infection_signs, diabetic_status, |
| previous_treatment, medical_history, medications, allergies, |
| additional_notes, wound_image |
| ], |
| outputs=[ |
| analysis_report, detection_image, segmentation_image, |
| download_report, download_images, processing_status, |
| analysis_metrics, session_info |
| ] |
| ) |
| |
| |
| refresh_status_btn.click( |
| fn=self._refresh_dashboard_status, |
| outputs=[dashboard_status, analytics_summary] |
| ) |
| |
| view_analytics_btn.click( |
| fn=self._get_analytics_summary, |
| outputs=[analytics_summary, recent_activity] |
| ) |
| |
| |
| interface.load( |
| fn=self._refresh_dashboard_status, |
| outputs=[dashboard_status, analytics_summary] |
| ) |
| |
| return interface |
| |
| def _get_integration_status_html(self) -> str: |
| """Get HTML for integration status display""" |
| status = self.dashboard_integration.get_integration_status() |
| |
| if status['api_running'] and status['database_connected']: |
| return """ |
| <div class="integration-status"> |
| β
<strong>Dashboard Integration Active</strong><br> |
| API Server: Running | Database: Connected | Real-time Analytics: Enabled |
| </div> |
| """ |
| else: |
| return """ |
| <div class="error-box"> |
| β <strong>Dashboard Integration Issues</strong><br> |
| Please check API server and database connection |
| </div> |
| """ |
| |
| def _get_session_info_html(self) -> str: |
| """Get HTML for session information display""" |
| if self.current_session: |
| user_info = self.current_session.get('user_info', {}) |
| return f""" |
| <div class="session-info"> |
| π€ <strong>Active Session</strong><br> |
| User: {user_info.get('name', 'Unknown')} | |
| Role: {user_info.get('role', 'Unknown')} | |
| Session Started: {self.current_session.get('start_time', 'Unknown')} |
| </div> |
| """ |
| else: |
| return """ |
| <div class="warning-box"> |
| β οΈ <strong>No Active Session</strong><br> |
| Please login to start tracking your analysis session |
| </div> |
| """ |
| |
| def _handle_login(self, username: str, password: str) -> Tuple[str, str]: |
| """Handle user login with session tracking""" |
| try: |
| if not username or not password: |
| return ( |
| "<div class='error-box'>β Please enter both username and password</div>", |
| self._get_session_info_html() |
| ) |
| |
| |
| user_info = self.auth_manager.authenticate_user(username, password) |
| |
| if user_info: |
| |
| self.current_session = { |
| 'user_info': user_info, |
| 'start_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), |
| 'session_id': f"session_{int(time.time())}", |
| 'analyses_count': 0 |
| } |
| |
| return ( |
| f"<div class='success-box'>β
Welcome, {user_info.get('name', username)}! You are now logged in.</div>", |
| self._get_session_info_html() |
| ) |
| else: |
| return ( |
| "<div class='error-box'>β Invalid username or password</div>", |
| self._get_session_info_html() |
| ) |
| |
| except Exception as e: |
| logging.error(f"Login error: {e}") |
| return ( |
| f"<div class='error-box'>β Login failed: {str(e)}</div>", |
| self._get_session_info_html() |
| ) |
| |
| def _handle_logout(self) -> Tuple[str, str]: |
| """Handle user logout""" |
| try: |
| if self.current_session: |
| |
| session_duration = time.time() - datetime.strptime( |
| self.current_session['start_time'], '%Y-%m-%d %H:%M:%S' |
| ).timestamp() |
| |
| session_data = { |
| 'user_id': self.current_session['user_info'].get('id'), |
| 'session_duration': round(session_duration / 60, 2), |
| 'analyses_count': self.current_session.get('analyses_count', 0) |
| } |
| |
| |
| self.current_session = {} |
| |
| return ( |
| "<div class='warning-box'>π You have been logged out successfully</div>", |
| self._get_session_info_html() |
| ) |
| else: |
| return ( |
| "<div class='warning-box'>β οΈ No active session to logout</div>", |
| self._get_session_info_html() |
| ) |
| |
| except Exception as e: |
| logging.error(f"Logout error: {e}") |
| return ( |
| f"<div class='error-box'>β Logout error: {str(e)}</div>", |
| self._get_session_info_html() |
| ) |
| |
| def _start_analysis(self) -> Tuple[str, str]: |
| """Start analysis process with status indicators""" |
| return ( |
| """ |
| <div class="processing-indicator" style="display: block;"> |
| π <strong>Analysis in Progress...</strong><br> |
| Please wait while we process your wound image and patient data |
| </div> |
| """, |
| """ |
| <div class="metrics-display"> |
| <strong>Analysis Metrics:</strong><br> |
| Status: Initializing...<br> |
| Processing Time: 0.0s<br> |
| Models Loading: β³ |
| </div> |
| """ |
| ) |
| |
| def _perform_enhanced_analysis(self, patient_name: str, patient_age: int, patient_gender: str, |
| wound_location: str, wound_duration: str, pain_level: int, |
| moisture_level: str, infection_signs: str, diabetic_status: str, |
| previous_treatment: str, medical_history: str, medications: str, |
| allergies: str, additional_notes: str, wound_image) -> Tuple: |
| """Perform enhanced analysis with dashboard integration""" |
| |
| start_time = time.time() |
| |
| try: |
| |
| if not self.current_session: |
| return ( |
| "β **Authentication Required**\n\nPlease login before performing analysis.", |
| None, None, None, None, |
| "<div class='error-box'>β Authentication required</div>", |
| "<div class='error-box'>Please login to continue</div>", |
| self._get_session_info_html() |
| ) |
| |
| |
| if not wound_image: |
| return ( |
| "β **Image Required**\n\nPlease upload a wound image for analysis.", |
| None, None, None, None, |
| "<div class='error-box'>β Wound image required</div>", |
| "<div class='error-box'>Please upload an image</div>", |
| self._get_session_info_html() |
| ) |
| |
| if not patient_name.strip(): |
| return ( |
| "β **Patient Name Required**\n\nPlease enter the patient's name.", |
| None, None, None, None, |
| "<div class='error-box'>β Patient name required</div>", |
| "<div class='error-box'>Please enter patient name</div>", |
| self._get_session_info_html() |
| ) |
| |
| |
| patient_info = { |
| 'patient_name': patient_name, |
| 'patient_age': patient_age, |
| 'patient_gender': patient_gender, |
| 'wound_location': wound_location, |
| 'wound_duration': wound_duration, |
| 'pain_level': pain_level, |
| 'moisture_level': moisture_level, |
| 'infection_signs': infection_signs, |
| 'diabetic_status': diabetic_status, |
| 'previous_treatment': previous_treatment, |
| 'medical_history': medical_history, |
| 'medications': medications, |
| 'allergies': allergies, |
| 'additional_notes': additional_notes |
| } |
| |
| |
| user_id = self.current_session['user_info'].get('id') |
| questionnaire_id = self.database_manager.save_questionnaire_response(patient_info, user_id) |
| |
| if not questionnaire_id: |
| logging.warning("Failed to save questionnaire response") |
| |
| |
| image_id = None |
| if questionnaire_id: |
| image_id = self.database_manager.save_wound_image(questionnaire_id, wound_image, "wound_analysis.jpg") |
| |
| |
| analysis_results = self.ai_processor.perform_comprehensive_analysis(wound_image, patient_info) |
| |
| processing_time = analysis_results.get('processing_time', 0) |
| |
| |
| analysis_data = { |
| 'questionnaire_id': questionnaire_id, |
| 'image_id': image_id, |
| 'analysis_data': analysis_results, |
| 'summary': analysis_results.get('report', '')[:1000], |
| 'recommendations': analysis_results.get('report', ''), |
| 'risk_score': analysis_results.get('risk_score', 0), |
| 'processing_time': processing_time, |
| 'model_version': analysis_results.get('model_version', 'v1.0'), |
| 'visual_results': analysis_results.get('visual_results', {}) |
| } |
| |
| analysis_id = self.database_manager.save_ai_analysis(analysis_data) |
| |
| |
| session_data = { |
| 'user_id': user_id, |
| 'questionnaire_id': questionnaire_id, |
| 'image_id': image_id, |
| 'analysis_id': analysis_id, |
| 'session_duration': processing_time |
| } |
| |
| self.dashboard_integration.log_analysis_session(session_data) |
| |
| |
| interaction_data = { |
| 'patient_id': None, |
| 'practitioner_id': user_id, |
| 'input_text': f"Wound analysis for {patient_name}", |
| 'output_text': analysis_results.get('report', '')[:500], |
| 'wound_image_url': f"uploads/wound_analysis_{int(time.time())}.jpg", |
| 'interaction_type': 'wound_analysis' |
| } |
| |
| self.dashboard_integration.log_bot_interaction(interaction_data) |
| |
| |
| self.current_session['analyses_count'] = self.current_session.get('analyses_count', 0) + 1 |
| |
| |
| visual_results = analysis_results.get('visual_results', {}) |
| report = analysis_results.get('report', 'Analysis completed but no report generated.') |
| |
| |
| detection_image = visual_results.get('detection_image_pil') |
| segmentation_image = visual_results.get('segmentation_image_pil') |
| |
| |
| report_file = self._create_report_file(analysis_results, patient_info) |
| |
| |
| metrics_html = f""" |
| <div class="metrics-display"> |
| <strong>Analysis Completed Successfully!</strong><br> |
| Processing Time: {processing_time}s<br> |
| Risk Score: {analysis_results.get('risk_score', 0)}/100<br> |
| Wound Type: {visual_results.get('wound_type', 'Unknown')}<br> |
| Surface Area: {visual_results.get('surface_area_cm2', 0)} cmΒ²<br> |
| Model Version: {analysis_results.get('model_version', 'v1.0')}<br> |
| Dashboard Integration: β
Active |
| </div> |
| """ |
| |
| success_status = f""" |
| <div class="success-box"> |
| β
<strong>Analysis Completed Successfully!</strong><br> |
| Processing Time: {processing_time}s | Risk Score: {analysis_results.get('risk_score', 0)}/100<br> |
| Results saved to dashboard for real-time analytics |
| </div> |
| """ |
| |
| return ( |
| report, |
| detection_image, |
| segmentation_image, |
| report_file, |
| None, |
| success_status, |
| metrics_html, |
| self._get_session_info_html() |
| ) |
| |
| except Exception as e: |
| processing_time = time.time() - start_time |
| error_message = str(e) |
| logging.error(f"Analysis error: {error_message}") |
| |
| error_status = f""" |
| <div class="error-box"> |
| β <strong>Analysis Failed</strong><br> |
| Error: {error_message}<br> |
| Processing Time: {processing_time:.2f}s |
| </div> |
| """ |
| |
| error_metrics = f""" |
| <div class="error-box"> |
| <strong>Analysis Error:</strong><br> |
| Status: Failed<br> |
| Processing Time: {processing_time:.2f}s<br> |
| Error: {error_message} |
| </div> |
| """ |
| |
| return ( |
| f"β **Analysis Failed**\n\n**Error:** {error_message}\n\nPlease check your inputs and try again.", |
| None, None, None, None, |
| error_status, |
| error_metrics, |
| self._get_session_info_html() |
| ) |
| |
| def _create_report_file(self, analysis_results: Dict[str, Any], patient_info: Dict[str, Any]) -> str: |
| """Create downloadable report file""" |
| try: |
| timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') |
| filename = f"wound_analysis_report_{timestamp}.md" |
| filepath = os.path.join("uploads", filename) |
| |
| |
| os.makedirs("uploads", exist_ok=True) |
| |
| |
| report_content = f"""# SmartHeal AI Wound Analysis Report |
| |
| **Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} |
| **Patient:** {patient_info.get('patient_name', 'N/A')} |
| **Analysis ID:** {timestamp} |
| |
| ## Patient Information |
| - **Name:** {patient_info.get('patient_name', 'N/A')} |
| - **Age:** {patient_info.get('patient_age', 'N/A')} years |
| - **Gender:** {patient_info.get('patient_gender', 'N/A')} |
| - **Wound Location:** {patient_info.get('wound_location', 'N/A')} |
| - **Wound Duration:** {patient_info.get('wound_duration', 'N/A')} |
| - **Pain Level:** {patient_info.get('pain_level', 'N/A')}/10 |
| |
| ## Analysis Results |
| {analysis_results.get('report', 'No report generated')} |
| |
| ## Technical Details |
| - **Processing Time:** {analysis_results.get('processing_time', 0)}s |
| - **Risk Score:** {analysis_results.get('risk_score', 0)}/100 |
| - **Model Version:** {analysis_results.get('model_version', 'Unknown')} |
| - **Analysis Timestamp:** {analysis_results.get('analysis_timestamp', 'Unknown')} |
| |
| --- |
| *Generated by SmartHeal AI Enhanced Edition with Dashboard Integration* |
| """ |
| |
| with open(filepath, 'w', encoding='utf-8') as f: |
| f.write(report_content) |
| |
| return filepath |
| |
| except Exception as e: |
| logging.error(f"Error creating report file: {e}") |
| return None |
| |
| def _refresh_dashboard_status(self) -> Tuple[str, str]: |
| """Refresh dashboard integration status""" |
| try: |
| status = self.dashboard_integration.get_integration_status() |
| analytics_data = self.database_manager.get_analytics_data() |
| |
| if status['api_running'] and status['database_connected']: |
| status_html = f""" |
| <div class="integration-status"> |
| β
<strong>Dashboard Integration Active</strong><br> |
| API Server: Running on port 5001<br> |
| Database: Connected<br> |
| Last Updated: {status['timestamp']}<br> |
| <a href="http://localhost:5001/api/health" target="_blank">π Test API Health</a> |
| </div> |
| """ |
| else: |
| status_html = f""" |
| <div class="error-box"> |
| β <strong>Dashboard Integration Issues</strong><br> |
| API Running: {status['api_running']}<br> |
| Database Connected: {status['database_connected']}<br> |
| Last Checked: {status['timestamp']} |
| </div> |
| """ |
| |
| analytics_html = f""" |
| <div class="analytics-info"> |
| π <strong>Analytics Summary</strong><br> |
| Total Analyses: {analytics_data.get('total_analyses', 0)}<br> |
| Average Processing Time: {analytics_data.get('avg_processing_time', 0)}s<br> |
| High Risk Cases: {analytics_data.get('high_risk_count', 0)}<br> |
| Average Risk Score: {analytics_data.get('avg_risk_score', 0)}<br> |
| Analyses Today: {analytics_data.get('analyses_today', 0)} |
| </div> |
| """ |
| |
| return status_html, analytics_html |
| |
| except Exception as e: |
| logging.error(f"Error refreshing dashboard status: {e}") |
| return ( |
| f"<div class='error-box'>β Error refreshing status: {str(e)}</div>", |
| "<div class='error-box'>β Unable to load analytics</div>" |
| ) |
| |
| def _get_analytics_summary(self) -> Tuple[str, str]: |
| """Get comprehensive analytics summary""" |
| try: |
| analytics_data = self.database_manager.get_analytics_data() |
| interaction_history = self.database_manager.get_interaction_history(10) |
| |
| |
| analytics_html = f""" |
| <div class="analytics-info"> |
| <h3>π Comprehensive Analytics</h3> |
| <strong>Analysis Statistics:</strong><br> |
| β’ Total Analyses: {analytics_data.get('total_analyses', 0)}<br> |
| β’ Analyses Today: {analytics_data.get('analyses_today', 0)}<br> |
| β’ Analyses This Week: {analytics_data.get('analyses_this_week', 0)}<br> |
| β’ Average Processing Time: {analytics_data.get('avg_processing_time', 0)}s<br> |
| β’ Average Risk Score: {analytics_data.get('avg_risk_score', 0)}/100<br> |
| <br> |
| <strong>Risk Distribution:</strong><br> |
| β’ High Risk Cases: {analytics_data.get('high_risk_count', 0)}<br> |
| β’ Unique Questionnaires: {analytics_data.get('unique_questionnaires', 0)}<br> |
| β’ Analyses with Images: {analytics_data.get('analyses_with_images', 0)}<br> |
| </div> |
| """ |
| |
| |
| activity_html = "<div class='result-box'><h3>π Recent Activity</h3>" |
| |
| if interaction_history: |
| activity_html += "<ul>" |
| for interaction in interaction_history[:5]: |
| timestamp = interaction.get('interacted_at', 'Unknown') |
| if isinstance(timestamp, str): |
| try: |
| timestamp = datetime.fromisoformat(timestamp.replace('Z', '+00:00')).strftime('%Y-%m-%d %H:%M') |
| except: |
| pass |
| |
| activity_html += f""" |
| <li><strong>{timestamp}</strong> - {interaction.get('interaction_type', 'Unknown')} |
| (Patient: {interaction.get('patient_name', 'Unknown')})</li> |
| """ |
| activity_html += "</ul>" |
| else: |
| activity_html += "<p>No recent activity found.</p>" |
| |
| activity_html += "</div>" |
| |
| return analytics_html, activity_html |
| |
| except Exception as e: |
| logging.error(f"Error getting analytics summary: {e}") |
| return ( |
| f"<div class='error-box'>β Error loading analytics: {str(e)}</div>", |
| "<div class='error-box'>β Unable to load recent activity</div>" |
| ) |
|
|
|
|