test-app / src /enhanced_ui_components.py
SmartHeal's picture
Update src/enhanced_ui_components.py
223dce1 verified
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)
# Start dashboard integration
self.dashboard_integration.start_integration()
# UI styling
self.theme = gr.themes.Soft()
self.custom_css = self._load_custom_css()
# Session tracking
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:
# Header
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 display
integration_status = gr.HTML(self._get_integration_status_html())
# Session info
session_info = gr.HTML(self._get_session_info_html())
with gr.Tabs():
# Authentication Tab
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>")
# Enhanced Analysis Tab
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 Information
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"
)
# Wound Details
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)
# Clinical Assessment
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"
)
# Medical History
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>
""")
# Image Upload
wound_image = gr.Image(
label="Wound Image",
type="pil",
height=400
)
# Analysis Controls
analyze_btn = gr.Button("πŸ” Analyze Wound", variant="primary", size="lg")
# Processing indicator
processing_status = gr.HTML(visible=False)
# Analysis Metrics
analysis_metrics = gr.HTML(visible=False)
# Results Section
with gr.Row():
with gr.Column():
gr.HTML("""
<div class="section-header">
<h2>Analysis Results</h2>
<h3>Comprehensive AI-powered wound assessment</h3>
</div>
""")
# Visual Analysis Results
with gr.Row():
detection_image = gr.Image(label="Wound Detection", visible=False)
segmentation_image = gr.Image(label="Wound Segmentation", visible=False)
# Analysis Report
analysis_report = gr.Markdown(visible=False)
# Download Options
with gr.Row():
download_report = gr.File(label="Download Report", visible=False)
download_images = gr.File(label="Download Analysis Images", visible=False)
# Dashboard Integration Tab
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
analytics_summary = gr.HTML()
# Recent Activity
recent_activity = gr.HTML()
# Event Handlers
# Authentication
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]
)
# Analysis
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
]
)
# Dashboard Integration
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]
)
# Auto-refresh integration status on load
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()
)
# Authenticate user
user_info = self.auth_manager.authenticate_user(username, password)
if user_info:
# Start session tracking
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:
# Log session end
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), # Convert to minutes
'analyses_count': self.current_session.get('analyses_count', 0)
}
# Clear session
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:
# Check authentication
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()
)
# Validate inputs
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()
)
# Prepare patient information
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
}
# Save questionnaire response to dashboard database
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")
# Save wound image
image_id = None
if questionnaire_id:
image_id = self.database_manager.save_wound_image(questionnaire_id, wound_image, "wound_analysis.jpg")
# Perform comprehensive AI analysis
analysis_results = self.ai_processor.perform_comprehensive_analysis(wound_image, patient_info)
processing_time = analysis_results.get('processing_time', 0)
# Save AI analysis results to dashboard database
analysis_data = {
'questionnaire_id': questionnaire_id,
'image_id': image_id,
'analysis_data': analysis_results,
'summary': analysis_results.get('report', '')[:1000], # First 1000 chars as summary
'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)
# Log analysis session
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)
# Log bot interaction
interaction_data = {
'patient_id': None, # Would need to get from patients table
'practitioner_id': user_id,
'input_text': f"Wound analysis for {patient_name}",
'output_text': analysis_results.get('report', '')[:500], # First 500 chars
'wound_image_url': f"uploads/wound_analysis_{int(time.time())}.jpg",
'interaction_type': 'wound_analysis'
}
self.dashboard_integration.log_bot_interaction(interaction_data)
# Update session count
self.current_session['analyses_count'] = self.current_session.get('analyses_count', 0) + 1
# Prepare results for display
visual_results = analysis_results.get('visual_results', {})
report = analysis_results.get('report', 'Analysis completed but no report generated.')
# Get analysis images
detection_image = visual_results.get('detection_image_pil')
segmentation_image = visual_results.get('segmentation_image_pil')
# Create downloadable report
report_file = self._create_report_file(analysis_results, patient_info)
# Create metrics display
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, # Images download placeholder
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)
# Ensure uploads directory exists
os.makedirs("uploads", exist_ok=True)
# Create comprehensive report
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)
# Create detailed analytics HTML
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>
"""
# Create recent activity HTML
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>"
)