import gradio as gr import os import re import logging import base64 from datetime import datetime from PIL import Image import html from .patient_history import PatientHistoryManager, ReportGenerator def pil_to_base64(pil_image): """Convert PIL Image to base64 data URL""" import io import base64 from PIL import Image if pil_image is None: return None try: # Convert image to RGB if it's not already if pil_image.mode != 'RGB': pil_image = pil_image.convert('RGB') buffer = io.BytesIO() pil_image.save(buffer, format='PNG') img_str = base64.b64encode(buffer.getvalue()).decode() return f"data:image/png;base64,{img_str}" except Exception as e: logging.error(f"Error converting PIL image to base64: {e}") return None class UIComponents: def __init__(self, auth_manager, database_manager, wound_analyzer): self.auth_manager = auth_manager self.database_manager = database_manager self.wound_analyzer = wound_analyzer self.current_user = {} self.patient_history_manager = PatientHistoryManager(database_manager) self.report_generator = ReportGenerator() # Ensure uploads directory exists if not os.path.exists("uploads"): os.makedirs("uploads", exist_ok=True) def image_to_base64(self, image_path): """Convert image to base64 data URL for embedding in HTML""" if not image_path or not os.path.exists(image_path): return None try: with open(image_path, "rb") as image_file: encoded_string = base64.b64encode(image_file.read()).decode() # Determine image format image_ext = os.path.splitext(image_path)[1].lower() if image_ext in [".jpg", ".jpeg"]: mime_type = "image/jpeg" elif image_ext == ".png": mime_type = "image/png" elif image_ext == ".gif": mime_type = "image/gif" else: mime_type = "image/png" # Default to PNG return f"data:{mime_type};base64,{encoded_string}" except Exception as e: logging.error(f"Error converting image to base64: {e}") return None def markdown_to_html(self, markdown_text): """Convert markdown text to proper HTML format with enhanced support""" if not markdown_text: return "" # Escape HTML entities first to prevent issues with special characters html_text = html.escape(markdown_text) # Convert headers html_text = re.sub(r"^### (.*?)$", r"

\1

", html_text, flags=re.MULTILINE) html_text = re.sub(r"^## (.*?)$", r"

\1

", html_text, flags=re.MULTILINE) html_text = re.sub(r"^# (.*?)$", r"

\1

", html_text, flags=re.MULTILINE) # Convert bold text html_text = re.sub(r"\*\*(.*?)\*\*", r"\1", html_text) # Convert italic text html_text = re.sub(r"\*(.*?)\*", r"\1", html_text) # Convert code blocks (triple backticks) html_text = re.sub(r"```(.*?)```", r"
\1
", html_text, flags=re.DOTALL) # Convert inline code (single backticks) html_text = re.sub(r"`(.*?)`", r"\1", html_text) # Convert blockquotes html_text = re.sub(r"^> (.*?)$", r"
\1
", html_text, flags=re.MULTILINE) # Convert links html_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"\1", html_text) # Convert horizontal rules html_text = re.sub(r"^\s*[-*_]{3,}\s*$", r"
", html_text, flags=re.MULTILINE) # Convert bullet points lines = html_text.split("\n") in_list = False result_lines = [] for line in lines: stripped = line.strip() if stripped.startswith("- "): if not in_list: result_lines.append("") in_list = False if stripped: result_lines.append(f"

{stripped}

") else: result_lines.append("
") if in_list: result_lines.append("") return "\n".join(result_lines) def get_organizations_dropdown(self): """Get list of organizations for dropdown""" try: organizations = self.database_manager.get_organizations() return [f"{org['org_name']} - {org['location']}" for org in organizations] except Exception as e: logging.error(f"Error getting organizations: {e}") return ["Default Hospital - Location"] def get_custom_css(self): 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 main Gradio interface with comprehensive analysis display""" with gr.Blocks(css=self.get_custom_css(), title="SmartHeal - AI Wound Care Assistant") as app: # Header with SmartHeal logo 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" gr.HTML(f"""

SmartHeal AI

Advanced Wound Care Analysis & Clinical Support System

""") # Professional disclaimer gr.HTML("""

⚠️ IMPORTANT DISCLAIMER

This model is for testing and educational purposes only and is NOT a replacement for professional medical advice.

Information generated may be inaccurate. Always consult a qualified healthcare provider for medical concerns. This AI system uses chain-of-thought reasoning to show its decision-making process, but should never be used as the sole basis for clinical decisions.

Uploaded images may be stored and used for testing and model improvement purposes.

""") # Main interface with conditional visibility with gr.Row(): # Authentication Panel (visible when not logged in) with gr.Column(visible=True) as auth_panel: gr.HTML("""

🏥 SmartHeal Access

Secure Healthcare Professional Portal

""") with gr.Tabs(): with gr.Tab("🔐 Professional Login") as login_tab: gr.HTML("""

Welcome Back

Access your professional dashboard

""") login_username = gr.Textbox( label="👤 Username", placeholder="Enter your username" ) login_password = gr.Textbox( label="🔒 Password", type="password", placeholder="Enter your secure password" ) login_btn = gr.Button( "🚀 Sign In to Dashboard", variant="primary", size="lg" ) login_status = gr.HTML( value="
Enter your credentials to access the system
" ) with gr.Tab("📝 New Registration") as signup_tab: gr.HTML("""

Create Account

Join the SmartHeal healthcare network

""") signup_username = gr.Textbox( label="👤 Username", placeholder="Choose a unique username" ) signup_email = gr.Textbox( label="📧 Email Address", placeholder="Enter your professional email" ) signup_password = gr.Textbox( label="🔒 Password", type="password", placeholder="Create a strong password" ) signup_name = gr.Textbox( label="👨‍⚕️ Full Name", placeholder="Enter your full professional name" ) signup_role = gr.Radio( ["practitioner", "organization"], label="🏥 Account Type", value="practitioner" ) # Organization-specific fields with gr.Group(visible=False) as org_fields: gr.HTML("

🏢 Organization Details

") org_name = gr.Textbox(label="Organization Name", placeholder="Enter organization name") phone = gr.Textbox(label="Phone Number", placeholder="Enter contact number") country_code = gr.Textbox(label="Country Code", placeholder="e.g., +1, +44") department = gr.Textbox(label="Department", placeholder="e.g., Emergency, Surgery") location = gr.Textbox(label="Location", placeholder="City, State/Province, Country") # Practitioner-specific fields with gr.Group(visible=True) as prac_fields: gr.HTML("

🏥 Affiliation

") organization_dropdown = gr.Dropdown( choices=self.get_organizations_dropdown(), label="Select Your Organization" ) signup_btn = gr.Button( "✨ Create Professional Account", variant="primary", size="lg" ) signup_status = gr.HTML( value="
Fill in your details to create an account
" ) # Practitioner Interface (hidden initially) with gr.Column(visible=False) as practitioner_panel: gr.HTML('
👩‍⚕️ Practitioner Dashboard
') user_info = gr.HTML("") logout_btn_prac = gr.Button("🚪 Logout", variant="secondary") # Main tabs for different functions with gr.Tabs(): # WOUND ANALYSIS TAB with gr.Tab("🔬 Wound Analysis"): with gr.Row(): with gr.Column(scale=1): gr.HTML("

📋 Patient Information

") patient_name = gr.Textbox(label="Patient Name", placeholder="Enter patient's full name") patient_age = gr.Number(label="Age", value=30, minimum=0, maximum=120) patient_gender = gr.Dropdown( choices=["Male", "Female", "Other"], label="Gender", value="Male" ) gr.HTML("

🩹 Wound Information

") wound_location = gr.Textbox(label="Wound Location", placeholder="e.g., Left ankle, Right arm") wound_duration = gr.Textbox(label="Wound Duration", placeholder="e.g., 2 weeks, 1 month") pain_level = gr.Slider( minimum=0, maximum=10, value=5, step=1, label="Pain Level (0-10)" ) gr.HTML("

⚕️ Clinical Assessment

") moisture_level = gr.Dropdown( choices=["Dry", "Moist", "Wet", "Saturated"], label="Moisture Level", value="Moist" ) infection_signs = gr.Dropdown( choices=["None", "Mild", "Moderate", "Severe"], label="Signs of Infection", value="None" ) diabetic_status = gr.Dropdown( choices=["Non-diabetic", "Type 1", "Type 2", "Gestational"], label="Diabetic Status", value="Non-diabetic" ) with gr.Column(scale=1): gr.HTML("

📸 Wound Image Upload

") wound_image = gr.Image( label="Upload Wound Image", type="filepath" ) gr.HTML("

📝 Medical History

") previous_treatment = gr.Textbox( label="Previous Treatment", placeholder="Describe any previous treatments...", lines=3 ) medical_history = gr.Textbox( label="Medical History", placeholder="Relevant medical conditions, surgeries, etc...", lines=3 ) 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 clinical observations...", lines=3 ) analyze_btn = gr.Button("🔬 Analyze Wound", variant="primary", size="lg", elem_id="analyze-btn") analysis_output = gr.HTML("") # PATIENT HISTORY TAB with gr.Tab("📋 Patient History"): with gr.Row(): with gr.Column(scale=2): gr.HTML("

📊 Patient History Dashboard

") history_btn = gr.Button("📋 Load Patient History", variant="primary") patient_history_output = gr.HTML("") with gr.Column(scale=1): gr.HTML("

🔍 Search Specific Patient

") search_patient_name = gr.Textbox( label="Patient Name", placeholder="Enter patient name to search..." ) search_patient_btn = gr.Button("🔍 Search Patient History", variant="secondary") specific_patient_output = gr.HTML("") # Event handlers def handle_login(username, password): user_data = self.auth_manager.authenticate_user(username, password) if user_data: self.current_user = user_data return { auth_panel: gr.update(visible=False), practitioner_panel: gr.update(visible=True), login_status: "
✅ Login successful! Welcome to SmartHeal
" } else: return { login_status: "
❌ Invalid credentials. Please try again.
" } def handle_signup(username, email, password, name, role, org_name, phone, country_code, department, location, organization_dropdown): try: if role == "organization": org_data = { 'org_name': org_name, 'phone': phone, 'country_code': country_code, 'department': department, 'location': location } org_id = self.database_manager.create_organization(org_data) user_data = { 'username': username, 'email': email, 'password': password, 'name': name, 'role': role, 'org_id': org_id } else: # Extract org_id from dropdown selection org_id = 1 # Default organization for now user_data = { 'username': username, 'email': email, 'password': password, 'name': name, 'role': role, 'org_id': org_id } if self.auth_manager.create_user(user_data): return { signup_status: "
✅ Account created successfully! Please login.
" } else: return { signup_status: "
❌ Failed to create account. Username or email may already exist.
" } except Exception as e: return { signup_status: f"
❌ Error: {str(e)}
" } def handle_analysis(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): try: if not wound_image: return "
❌ Please upload a wound image for analysis.
" # Show loading state first loading_html = """

🔬 SmartHeal AI Processing

Analyzing wound image with advanced computer vision...

⚡ Detection → 📏 Segmentation → 🤖 AI Report

""" # 1. Save questionnaire FIRST to get valid ID questionnaire_data_for_db = { 'user_id': self.current_user.get('id', 1), # Default to 1 if no user logged in '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 } # Save questionnaire FIRST to get the ID questionnaire_response_id = self.database_manager.save_questionnaire(questionnaire_data_for_db) logging.info(f"✅ Questionnaire saved with response ID: {questionnaire_response_id}") # 2. Create AIProcessor questionnaire format questionnaire_data_for_ai = { 'age': patient_age, 'diabetic': 'Yes' if diabetic_status != 'Non-diabetic' else 'No', 'allergies': allergies, 'date_of_injury': 'Unknown', # Not collected in this form 'professional_care': 'Yes', # Assumed since using professional interface 'oozing_bleeding': 'Minor Oozing' if infection_signs != 'None' else 'None', 'infection': 'Yes' if infection_signs != 'None' else 'No', 'moisture': moisture_level, # Additional comprehensive fields 'patient_name': patient_name, 'patient_gender': patient_gender, 'wound_location': wound_location, 'wound_duration': wound_duration, 'pain_level': pain_level, 'previous_treatment': previous_treatment, 'medical_history': medical_history, 'medications': medications, 'additional_notes': additional_notes } # 3. Run AI analysis using the AIProcessor class try: logging.info(f"🔬 Starting AI analysis for image: {wound_image}") # Use the AIProcessor analyze_wound method analysis_result = self.wound_analyzer.analyze_wound(wound_image, questionnaire_data_for_ai) if not analysis_result.get('success', False): error_msg = analysis_result.get('error', 'Analysis failed for unknown reason') logging.error(f"❌ AI Analysis failed: {error_msg}") return f"""

❌ AI Analysis Error

Error Details:

{error_msg}

Please try again with a different image or contact technical support.

Troubleshooting Tips:
""" logging.info("✅ AI Analysis completed successfully") # 4. Save analysis result with valid questionnaire_response_id try: if questionnaire_response_id: self.database_manager.save_analysis_result(questionnaire_response_id, analysis_result) logging.info("✅ Analysis result saved to database") except Exception as db_error: logging.error(f"❌ Database save error: {db_error}") # Continue with display even if DB save fails # 5. Format comprehensive analysis results with all images formatted_analysis = self._format_comprehensive_analysis_results( analysis_result, wound_image, questionnaire_data_for_ai ) return formatted_analysis except Exception as analysis_error: logging.error(f"❌ AI analysis exception: {analysis_error}", exc_info=True) return f"""

❌ Analysis Processing Error

There was an unexpected error during wound analysis:

{str(analysis_error)}

Next Steps:

""" except Exception as e: logging.error(f"❌ Analysis handler error: {e}", exc_info=True) return f"""

❌ System Error

A system error occurred while processing your request:

{str(e)}

Please contact technical support if this issue continues.

""" def handle_logout(): self.current_user = {} return { auth_panel: gr.update(visible=True), practitioner_panel: gr.update(visible=False) } def toggle_role_fields(role): if role == "organization": return { org_fields: gr.update(visible=True), prac_fields: gr.update(visible=False) } else: return { org_fields: gr.update(visible=False), prac_fields: gr.update(visible=True) } def load_patient_history(): try: user_id = self.current_user.get('id', 1) if not user_id: return "
❌ Please login first.
" history_data = self.patient_history_manager.get_user_patient_history(user_id) formatted_history = self.patient_history_manager.format_history_for_display(history_data) return formatted_history except Exception as e: logging.error(f"Error loading patient history: {e}") return f"
❌ Error loading history: {str(e)}
" def search_specific_patient(patient_name): try: user_id = self.current_user.get('id', 1) if not user_id: return "
❌ Please login first.
" if not patient_name.strip(): return "
⚠️ Please enter a patient name to search.
" patient_data = self.patient_history_manager.search_patient_by_name(user_id, patient_name.strip()) if patient_data: formatted_data = self.patient_history_manager.format_patient_data_for_display(patient_data) return formatted_data else: return f"
⚠️ No records found for patient: {patient_name}
" except Exception as e: logging.error(f"Error searching patient: {e}") return f"
❌ Error searching patient: {str(e)}
" # Bind event handlers login_btn.click( handle_login, inputs=[login_username, login_password], outputs=[auth_panel, practitioner_panel, login_status] ) signup_btn.click( handle_signup, inputs=[signup_username, signup_email, signup_password, signup_name, signup_role, org_name, phone, country_code, department, location, organization_dropdown], outputs=[signup_status] ) signup_role.change( toggle_role_fields, inputs=[signup_role], outputs=[org_fields, prac_fields] ) analyze_btn.click( handle_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_output] ) logout_btn_prac.click( handle_logout, outputs=[auth_panel, practitioner_panel] ) history_btn.click( load_patient_history, outputs=[patient_history_output] ) search_patient_btn.click( search_specific_patient, inputs=[search_patient_name], outputs=[specific_patient_output] ) return app def _format_comprehensive_analysis_results(self, analysis_result, image_url=None, questionnaire_data=None): """Format comprehensive analysis results with all visualization images from AIProcessor.""" try: # Extract the core analysis results from AIProcessor success = analysis_result.get('success', False) if not success: error_msg = analysis_result.get('error', 'Unknown error') return f"
❌ Analysis failed: {error_msg}
" visual_analysis = analysis_result.get('visual_analysis', {}) report = analysis_result.get('report', '') saved_image_path = analysis_result.get('saved_image_path', '') # Extract wound metrics wound_type = visual_analysis.get('wound_type', 'Unknown') length_cm = visual_analysis.get('length_cm', 0) breadth_cm = visual_analysis.get('breadth_cm', 0) area_cm2 = visual_analysis.get('surface_area_cm2', 0) detection_confidence = visual_analysis.get('detection_confidence', 0) # Get image paths for visualizations detection_image_path = visual_analysis.get('detection_image_path', '') segmentation_image_path = visual_analysis.get('segmentation_image_path', '') original_image_path = visual_analysis.get('original_image_path', '') # Convert images to base64 for embedding original_image_base64 = None detection_image_base64 = None segmentation_image_base64 = None # Original uploaded image if image_url and os.path.exists(image_url): original_image_base64 = self.image_to_base64(image_url) elif original_image_path and os.path.exists(original_image_path): original_image_base64 = self.image_to_base64(original_image_path) elif saved_image_path and os.path.exists(saved_image_path): original_image_base64 = self.image_to_base64(saved_image_path) # Detection visualization if detection_image_path and os.path.exists(detection_image_path): detection_image_base64 = self.image_to_base64(detection_image_path) # Segmentation visualization if segmentation_image_path and os.path.exists(segmentation_image_path): segmentation_image_base64 = self.image_to_base64(segmentation_image_path) # Generate risk assessment from questionnaire data risk_assessment = self._generate_risk_assessment(questionnaire_data) risk_level = risk_assessment['risk_level'] risk_score = risk_assessment['risk_score'] risk_factors = risk_assessment['risk_factors'] # Set risk class for styling risk_class = "low" if risk_level.lower() == "moderate": risk_class = "moderate" elif risk_level.lower() in ["high", "very high"]: risk_class = "high" # Format risk factors risk_factors_html = "" if risk_factors else "

No specific risk factors identified.

" # Create image gallery image_gallery_html = "" if original_image_base64 or detection_image_base64 or segmentation_image_base64: image_gallery_html = '' # Convert markdown report to HTML report_html = "" if report: report_html = self.markdown_to_html(report) # Final comprehensive HTML output html_output = f"""

🔬 SmartHeal AI Comprehensive Analysis

Advanced Computer Vision & Medical AI Assessment

Patient: {questionnaire_data.get('patient_name', 'Unknown')} | Analysis Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

✅ Analysis Status: Analysis completed successfully with comprehensive wound assessment

🖼️ Visual Analysis Gallery

{image_gallery_html}

🔍 Wound Detection & Classification

Wound Type

{wound_type}

Detection Confidence

{detection_confidence:.1%}

Location

{questionnaire_data.get('wound_location', 'Not specified')}

📏 Wound Measurements

Length

{length_cm:.2f} cm

Width

{breadth_cm:.2f} cm

Surface Area

{area_cm2:.2f} cm²

⚠️ Risk Assessment

{risk_level} RISK
Risk Score: {risk_score}/10

Identified Risk Factors:

{risk_factors_html}

👤 Patient Information Summary

Age: {questionnaire_data.get('age', 'Not specified')} years
Gender: {questionnaire_data.get('patient_gender', 'Not specified')}
Diabetic Status: {questionnaire_data.get('diabetic', 'Unknown')}
Pain Level: {questionnaire_data.get('pain_level', 'Not assessed')}/10
Wound Duration: {questionnaire_data.get('wound_duration', 'Not specified')}
Moisture Level: {questionnaire_data.get('moisture', 'Not assessed')}
{f"
Medical History: {questionnaire_data.get('medical_history', 'None provided')}
" if questionnaire_data.get('medical_history') else ""} {f"
Current Medications: {questionnaire_data.get('medications', 'None listed')}
" if questionnaire_data.get('medications') else ""} {f"
Known Allergies: {questionnaire_data.get('allergies', 'None listed')}
" if questionnaire_data.get('allergies') else ""}
{f'

🤖 AI-Generated Clinical Report

{report_html}
' if report_html else ''}

⚠️ Important Medical Disclaimers

  • Not a Medical Diagnosis: This AI analysis is for informational purposes only and does not constitute medical advice, diagnosis, or treatment.
  • Professional Consultation Required: Always consult with qualified healthcare professionals for proper clinical assessment and treatment decisions.
  • Measurement Accuracy: All measurements are estimates based on computer vision algorithms and should be verified with clinical tools.
  • Risk Assessment Limitations: Risk factors are based on provided information and may not reflect the complete clinical picture.

🏥 Analysis completed by SmartHeal AI - Advanced Wound Care Assistant
Report generated on {datetime.now().strftime('%B %d, %Y at %I:%M %p')}

""" return html_output except Exception as e: logging.error(f"Error formatting comprehensive results: {e}") return f"
❌ Error displaying results: {str(e)}
" def _generate_risk_assessment(self, questionnaire_data): """Generate risk assessment based on questionnaire data""" if not questionnaire_data: return {'risk_level': 'Unknown', 'risk_score': 0, 'risk_factors': []} risk_factors = [] risk_score = 0 try: # Age assessment age = questionnaire_data.get('age', 0) if isinstance(age, str): try: age = int(age) except ValueError: age = 0 if age > 65: risk_factors.append("Advanced age (>65 years)") risk_score += 2 elif age > 50: risk_factors.append("Older adult (50-65 years)") risk_score += 1 # Diabetic status diabetic_status = str(questionnaire_data.get('diabetic', '')).lower() if 'yes' in diabetic_status: risk_factors.append("Diabetes mellitus") risk_score += 3 # Infection signs infection = str(questionnaire_data.get('infection', '')).lower() if 'yes' in infection: risk_factors.append("Signs of infection present") risk_score += 3 # Pain level pain_level = questionnaire_data.get('pain_level', 0) if isinstance(pain_level, str): try: pain_level = float(pain_level) except ValueError: pain_level = 0 if pain_level >= 7: risk_factors.append("High pain level (≥7/10)") risk_score += 2 elif pain_level >= 5: risk_factors.append("Moderate pain level (5-6/10)") risk_score += 1 # Wound duration duration = str(questionnaire_data.get('wound_duration', '')).lower() if any(term in duration for term in ['month', 'months', 'year', 'years']): risk_factors.append("Chronic wound (>4 weeks)") risk_score += 3 # Moisture level moisture = str(questionnaire_data.get('moisture', '')).lower() if any(term in moisture for term in ['wet', 'saturated']): risk_factors.append("Excessive wound exudate") risk_score += 1 # Medical history analysis medical_history = str(questionnaire_data.get('medical_history', '')).lower() if any(term in medical_history for term in ['vascular', 'circulation', 'heart']): risk_factors.append("Cardiovascular disease") risk_score += 2 if any(term in medical_history for term in ['immune', 'cancer', 'steroid']): risk_factors.append("Immune system compromise") risk_score += 2 if any(term in medical_history for term in ['smoking', 'tobacco']): risk_factors.append("Smoking history") risk_score += 2 # Determine risk level if risk_score >= 8: risk_level = "Very High" elif risk_score >= 6: risk_level = "High" elif risk_score >= 3: risk_level = "Moderate" else: risk_level = "Low" return { 'risk_score': risk_score, 'risk_level': risk_level, 'risk_factors': risk_factors } except Exception as e: logging.error(f"Risk assessment error: {e}") return { 'risk_score': 0, 'risk_level': 'Unknown', 'risk_factors': ['Unable to assess risk due to data processing error'] }