import logging
import json
from datetime import datetime
from typing import List, Dict, Optional
class PatientHistoryManager:
"""Complete patient history and wound tracking system"""
def __init__(self, database_manager):
self.db = database_manager
def get_patient_complete_history(self, user_id: int, patient_name: str = None) -> List[Dict]:
"""Get complete patient history with all wound analyses and images"""
try:
if patient_name:
query = """
SELECT
qr.id as response_id,
p.name as patient_name,
p.age as patient_age,
p.gender as patient_gender,
qr.response_data,
qr.submitted_at as visit_date,
w.position as wound_location,
w.moisture,
w.infection,
w.notes,
wi.image as image_url,
a.analysis_data,
a.summary,
a.recommendations,
a.risk_score,
a.risk_level
FROM questionnaire_responses qr
JOIN patients p ON qr.patient_id = p.id
LEFT JOIN wounds w ON w.patient_id = CAST(p.id AS CHAR)
LEFT JOIN wound_images wi ON wi.patient_id = CAST(p.id AS CHAR)
LEFT JOIN ai_analyses a ON a.questionnaire_id = qr.id
WHERE qr.practitioner_id = %s AND p.name = %s
ORDER BY qr.submitted_at DESC
"""
params = (user_id, patient_name)
else:
query = """
SELECT
qr.id as response_id,
p.name as patient_name,
p.age as patient_age,
p.gender as patient_gender,
qr.response_data,
qr.submitted_at as visit_date,
w.position as wound_location,
w.moisture,
w.infection,
w.notes,
wi.image as image_url,
a.analysis_data,
a.summary,
a.recommendations,
a.risk_score,
a.risk_level
FROM questionnaire_responses qr
JOIN patients p ON qr.patient_id = p.id
LEFT JOIN wounds w ON w.patient_id = CAST(p.id AS CHAR)
LEFT JOIN wound_images wi ON wi.patient_id = CAST(p.id AS CHAR)
LEFT JOIN ai_analyses a ON a.questionnaire_id = qr.id
WHERE qr.practitioner_id = %s
ORDER BY qr.submitted_at DESC
"""
params = (user_id,)
result = self.db.execute_query(query, params, fetch=True)
return result or []
except Exception as e:
logging.error(f"Error fetching patient history: {e}")
return []
def get_patient_list(self, user_id: int) -> List[Dict]:
"""Get list of all patients for this practitioner"""
try:
query = """
SELECT DISTINCT
p.name as patient_name,
p.age as patient_age,
p.gender as patient_gender,
COUNT(qr.id) as total_visits,
MAX(qr.submitted_at) as last_visit,
MIN(qr.submitted_at) as first_visit
FROM questionnaire_responses qr
JOIN patients p ON qr.patient_id = p.id
WHERE qr.practitioner_id = %s
GROUP BY p.name, p.age, p.gender
ORDER BY last_visit DESC
"""
result = self.db.execute_query(query, (user_id,), fetch=True)
return result or []
except Exception as e:
logging.error(f"Error fetching patient list: {e}")
return []
def get_wound_progression(self, user_id: int, patient_name: str) -> List[Dict]:
"""Get wound progression data for timeline visualization"""
try:
query = """
SELECT
qr.submitted_at as visit_date,
qr.response_data,
w.position as wound_location,
w.moisture,
w.infection,
a.risk_score,
a.risk_level,
a.summary,
wi.image as image_url
FROM questionnaire_responses qr
JOIN patients p ON qr.patient_id = p.id
LEFT JOIN wounds w ON w.patient_id = CAST(p.id AS CHAR)
LEFT JOIN wound_images wi ON wi.patient_id = CAST(p.id AS CHAR)
LEFT JOIN ai_analyses a ON a.questionnaire_id = qr.id
WHERE qr.practitioner_id = %s AND p.name = %s
ORDER BY qr.submitted_at ASC
"""
result = self.db.execute_query(query, (user_id, patient_name), fetch=True)
return result or []
except Exception as e:
logging.error(f"Error fetching wound progression: {e}")
return []
def save_patient_note(self, user_id: int, patient_name: str, note: str) -> bool:
"""Save a clinical note for a patient"""
try:
# Check if notes table exists, if not use questionnaires additional_notes
query = """
UPDATE questionnaires
SET additional_notes = CONCAT(IFNULL(additional_notes, ''), '\n--- Clinical Note (', NOW(), ') ---\n', %s)
WHERE user_id = %s AND patient_name = %s
ORDER BY created_at DESC
LIMIT 1
"""
result = self.db.execute_query(query, (note, user_id, patient_name))
return bool(result)
except Exception as e:
logging.error(f"Error saving patient note: {e}")
return False
class ReportGenerator:
"""Professional HTML report generator for wound analysis"""
def __init__(self):
pass
def generate_analysis_report(self, patient_data: Dict, analysis_data: Dict, image_url: str = None) -> str:
"""Generate comprehensive HTML report for wound analysis"""
report_html = f"""
SmartHeal AI - Wound Analysis Report
🩺 SmartHeal AI Wound Analysis Report
Advanced AI-Powered Clinical Assessment
Patient Information
Patient Name
{patient_data.get('patient_name', 'N/A')}
Age
{patient_data.get('patient_age', 'N/A')} years
Gender
{patient_data.get('patient_gender', 'N/A')}
Assessment Date
{datetime.now().strftime('%B %d, %Y at %I:%M %p')}
"""
return report_html
def _format_recommendations(self, recommendations: str) -> str:
"""Format recommendations as HTML list"""
if not recommendations:
return "
No specific recommendations available.
"
# Split recommendations by common delimiters
items = []
for delimiter in ['\n', '. ', '; ']:
if delimiter in recommendations:
items = [item.strip() for item in recommendations.split(delimiter) if item.strip()]
break
if not items:
items = [recommendations]
html = "
"
for item in items:
if item and len(item) > 3: # Avoid very short fragments
html += f"
{item}
"
html += "
"
return html
def generate_patient_history_report(self, patient_history: List[Dict]) -> str:
"""Generate comprehensive patient history report"""
if not patient_history:
return "
No patient history available.
"
patient_name = patient_history[0].get('patient_name', 'Unknown Patient')
html = f"""
📋 Patient History: {patient_name}
Complete Treatment Timeline
"""
for i, visit in enumerate(patient_history):
visit_date = visit.get('visit_date', '')
if isinstance(visit_date, str):
try:
visit_date = datetime.fromisoformat(visit_date.replace('Z', '+00:00'))
except:
pass
risk_class = f"risk-{visit.get('risk_level', 'unknown').lower()}"
html += f"""
Visit #{len(patient_history) - i}
{visit_date.strftime('%B %d, %Y') if hasattr(visit_date, 'strftime') else str(visit_date)}
Location: {visit.get('wound_location', 'N/A')}
Pain Level: {visit.get('pain_level', 'N/A')}/10
Risk:
{visit.get('risk_level', 'Unknown')}
{f'
Summary: {visit.get("summary", "No summary available")}
' if visit.get("summary") else ""}
{f'
Recommendations: {visit.get("recommendations", "No recommendations available")}