|
|
import streamlit as st |
|
|
import pandas as pd |
|
|
import numpy as np |
|
|
import joblib |
|
|
import pickle |
|
|
from PIL import Image |
|
|
import io |
|
|
import cv2 |
|
|
import easyocr |
|
|
import os |
|
|
import plotly.graph_objects as go |
|
|
import plotly.express as px |
|
|
from datetime import datetime |
|
|
import requests |
|
|
import json |
|
|
import base64 |
|
|
import tempfile |
|
|
from groq import Groq |
|
|
|
|
|
|
|
|
st.set_page_config( |
|
|
page_title="AI-Priority OPD System", |
|
|
page_icon="🏥", |
|
|
layout="wide", |
|
|
initial_sidebar_state="expanded" |
|
|
) |
|
|
|
|
|
|
|
|
def local_css(): |
|
|
st.markdown(""" |
|
|
<style> |
|
|
.main-header { |
|
|
font-size: 2.5rem; |
|
|
color: #2E86AB; |
|
|
text-align: center; |
|
|
margin-bottom: 2rem; |
|
|
font-weight: bold; |
|
|
} |
|
|
.urdu-text { |
|
|
font-family: 'Arial', 'Noto Sans Arabic'; |
|
|
font-size: 1.2rem; |
|
|
direction: rtl; |
|
|
text-align: right; |
|
|
} |
|
|
.risk-high { |
|
|
background-color: #ffcccc; |
|
|
padding: 15px; |
|
|
border-radius: 10px; |
|
|
border-left: 5px solid #dc3545; |
|
|
} |
|
|
.risk-medium { |
|
|
background-color: #fff3cd; |
|
|
padding: 15px; |
|
|
border-radius: 10px; |
|
|
border-left: 5px solid #ffc107; |
|
|
} |
|
|
.risk-low { |
|
|
background-color: #d4edda; |
|
|
padding: 15px; |
|
|
border-radius: 10px; |
|
|
border-left: 5px solid #28a745; |
|
|
} |
|
|
.priority-box { |
|
|
border: 2px solid #2E86AB; |
|
|
padding: 20px; |
|
|
border-radius: 10px; |
|
|
margin: 10px 0; |
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); |
|
|
} |
|
|
.stButton button { |
|
|
width: 100%; |
|
|
background: linear-gradient(45deg, #2E86AB, #A23B72); |
|
|
color: white; |
|
|
font-weight: bold; |
|
|
border: none; |
|
|
padding: 12px 24px; |
|
|
border-radius: 8px; |
|
|
} |
|
|
.dataframe table { |
|
|
width: 100%; |
|
|
} |
|
|
.dataframe th { |
|
|
background-color: #2E86AB; |
|
|
color: white; |
|
|
font-weight: bold; |
|
|
} |
|
|
.dataframe tr:nth-child(even) { |
|
|
background-color: #f2f2f2; |
|
|
} |
|
|
</style> |
|
|
""", unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
def init_session_state(): |
|
|
if 'language' not in st.session_state: |
|
|
st.session_state.language = 'English' |
|
|
if 'patient_data' not in st.session_state: |
|
|
st.session_state.patient_data = {} |
|
|
if 'risk_scores' not in st.session_state: |
|
|
st.session_state.risk_scores = {} |
|
|
if 'chat_history' not in st.session_state: |
|
|
st.session_state.chat_history = [] |
|
|
if 'groq_client' not in st.session_state: |
|
|
st.session_state.groq_client = None |
|
|
|
|
|
|
|
|
@st.cache_resource(show_spinner=False) |
|
|
def load_models(): |
|
|
try: |
|
|
|
|
|
|
|
|
models = {} |
|
|
|
|
|
|
|
|
try: |
|
|
heart_model = joblib.load('heart_disease_model.pkl') |
|
|
models['heart'] = heart_model |
|
|
except: |
|
|
st.error("❌ Heart disease model not found. Please ensure 'heart_disease_model.pkl' is in the directory.") |
|
|
return None, None, None |
|
|
|
|
|
|
|
|
try: |
|
|
diabetes_model = joblib.load('diabetes_model.pkl') |
|
|
models['diabetes'] = diabetes_model |
|
|
except: |
|
|
st.error("❌ Diabetes model not found. Please ensure 'diabetes_model.pkl' is in the directory.") |
|
|
return None, None, None |
|
|
|
|
|
|
|
|
try: |
|
|
hypertension_model = joblib.load('hypertension_model.pkl') |
|
|
models['hypertension'] = hypertension_model |
|
|
except: |
|
|
st.error("❌ Hypertension model not found. Please ensure 'hypertension_model.pkl' is in the directory.") |
|
|
return None, None, None |
|
|
|
|
|
return models['heart'], models['diabetes'], models['hypertension'] |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"❌ Error loading models: {str(e)}") |
|
|
return None, None, None |
|
|
|
|
|
|
|
|
def init_groq_client(api_key): |
|
|
try: |
|
|
client = Groq(api_key=api_key) |
|
|
return client |
|
|
except Exception as e: |
|
|
st.error(f"❌ Error initializing Groq client: {str(e)}") |
|
|
return None |
|
|
|
|
|
|
|
|
URDU_TRANSLATIONS = { |
|
|
"AI-Priority OPD System": "AI-ترجیحی OPD سسٹم", |
|
|
"Patient Information": "مریض کی معلومات", |
|
|
"Name": "نام", |
|
|
"Age": "عمر", |
|
|
"Gender": "جنس", |
|
|
"Contact": "رابطہ نمبر", |
|
|
"Medical History": "طبی تاریخ", |
|
|
"Vital Signs": "اہم علامات", |
|
|
"Blood Pressure (systolic)": "بلڈ پریشر (سسٹولک)", |
|
|
"Blood Pressure (diastolic)": "بلڈ پریشر (ڈائیسٹولک)", |
|
|
"Heart Rate": "دل کی دھڑکن", |
|
|
"Cholesterol Level": "کولیسٹرول کی سطح", |
|
|
"Blood Glucose": "خون میں گلوکوز", |
|
|
"BMI": "باڈی ماس انڈیکس", |
|
|
"Symptoms": "علامات", |
|
|
"Chest Pain": "سینے میں درد", |
|
|
"Shortness of Breath": "سانس لینے میں دشواری", |
|
|
"Fatigue": "تھکاوٹ", |
|
|
"Upload Prescription": "نسخہ اپ لوڈ کریں", |
|
|
"Calculate Risk Score": "خطرے کا اسکور معلوم کریں", |
|
|
"High Priority - Emergency Care Required": "اعلی ترجیح - ہنگامی علاج کی ضرورت", |
|
|
"Medium Priority - Same Day Consultation": "درمیانی ترجیح - اسی دن مشورہ", |
|
|
"Low Priority - Routine Appointment": "کم ترجیح - معمول کی ملاقات", |
|
|
"Healthcare Chatbot": "ہیلتھ کیئر چیٹ بوٹ", |
|
|
"Ask health-related questions": "صحت سے متعلق سوالات پوچھیں" |
|
|
} |
|
|
|
|
|
class OCRProcessor: |
|
|
def __init__(self): |
|
|
|
|
|
try: |
|
|
self.reader = easyocr.Reader(['en']) |
|
|
except Exception as e: |
|
|
st.error(f"OCR initialization failed: {str(e)}") |
|
|
self.reader = None |
|
|
|
|
|
def preprocess_image(self, image): |
|
|
"""Enhanced image preprocessing for better OCR accuracy""" |
|
|
try: |
|
|
|
|
|
img_array = np.array(image) |
|
|
|
|
|
|
|
|
if len(img_array.shape) == 3: |
|
|
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY) |
|
|
else: |
|
|
gray = img_array |
|
|
|
|
|
|
|
|
|
|
|
blurred = cv2.GaussianBlur(gray, (3, 3), 0) |
|
|
|
|
|
|
|
|
thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, |
|
|
cv2.THRESH_BINARY, 11, 2) |
|
|
|
|
|
|
|
|
kernel = np.ones((2, 2), np.uint8) |
|
|
processed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) |
|
|
|
|
|
return processed |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Image processing error: {str(e)}") |
|
|
return np.array(image) |
|
|
|
|
|
def extract_text(self, image): |
|
|
"""Extract text from prescription image using EasyOCR""" |
|
|
try: |
|
|
if self.reader is None: |
|
|
return "OCR not available. Please check EasyOCR installation." |
|
|
|
|
|
|
|
|
processed_image = self.preprocess_image(image) |
|
|
|
|
|
|
|
|
results = self.reader.readtext(processed_image, detail=0, paragraph=True) |
|
|
|
|
|
|
|
|
extracted_text = "\n".join(results) |
|
|
|
|
|
return extracted_text.strip() if extracted_text.strip() else "No text detected in the image." |
|
|
|
|
|
except Exception as e: |
|
|
return f"OCR Error: {str(e)}" |
|
|
|
|
|
def calculate_ocr_accuracy(self, extracted_text): |
|
|
"""Estimate OCR accuracy based on text quality""" |
|
|
if not extracted_text or len(extracted_text.strip()) == 0 or "No text detected" in extracted_text: |
|
|
return 0 |
|
|
|
|
|
|
|
|
text_length = len(extracted_text) |
|
|
word_count = len(extracted_text.split()) |
|
|
|
|
|
|
|
|
medical_terms = ['tablet', 'mg', 'ml', 'daily', 'twice', 'capsule', 'injection'] |
|
|
found_terms = sum(1 for term in medical_terms if term in extracted_text.lower()) |
|
|
|
|
|
|
|
|
length_score = min(100, (text_length / 50) * 100) |
|
|
word_score = min(100, (word_count / 10) * 100) |
|
|
medical_score = (found_terms / len(medical_terms)) * 100 |
|
|
|
|
|
|
|
|
accuracy = (length_score * 0.3 + word_score * 0.3 + medical_score * 0.4) |
|
|
|
|
|
return min(95, accuracy) |
|
|
|
|
|
class HealthcareChatbot: |
|
|
def __init__(self, groq_client): |
|
|
self.client = groq_client |
|
|
self.system_prompt = """You are a helpful and professional healthcare assistant designed for Pakistani patients. |
|
|
Provide accurate, culturally appropriate medical advice in both English and Urdu. |
|
|
Focus on preventive care, symptom explanation, and when to seek medical attention. |
|
|
Always emphasize that you are an AI assistant and recommend consulting healthcare professionals for serious conditions.""" |
|
|
|
|
|
def get_response(self, query, language='English'): |
|
|
"""Generate healthcare chatbot response using Groq API""" |
|
|
try: |
|
|
if self.client is None: |
|
|
return "Chatbot service is currently unavailable. Please check the API configuration." |
|
|
|
|
|
|
|
|
enhanced_system_prompt = self.system_prompt |
|
|
if language == 'Urdu': |
|
|
enhanced_system_prompt += " Respond in Urdu with proper medical terminology." |
|
|
else: |
|
|
enhanced_system_prompt += " Respond in English with clear medical advice." |
|
|
|
|
|
|
|
|
messages = [ |
|
|
{"role": "system", "content": enhanced_system_prompt}, |
|
|
{"role": "user", "content": f"Patient query: {query}"} |
|
|
] |
|
|
|
|
|
|
|
|
chat_completion = self.client.chat.completions.create( |
|
|
messages=messages, |
|
|
model="llama3-8b-8192", |
|
|
temperature=0.3, |
|
|
max_tokens=512, |
|
|
top_p=0.9 |
|
|
) |
|
|
|
|
|
response = chat_completion.choices[0].message.content |
|
|
|
|
|
|
|
|
if language == 'Urdu': |
|
|
response += "\n\n⚠️ براہ کرم نوٹ کریں: یہ ایک AI اسسٹنٹ ہے۔ سنگین طبی حالات کے لیے ہمیشہ کوالیفائیڈ ڈاکٹر سے مشورہ کریں۔" |
|
|
else: |
|
|
response += "\n\n⚠️ Please note: This is an AI assistant. Always consult qualified doctors for serious medical conditions." |
|
|
|
|
|
return response |
|
|
|
|
|
except Exception as e: |
|
|
error_msg = f"Error generating response: {str(e)}" |
|
|
if language == 'Urdu': |
|
|
return f"معذرت، میں اس وقت آپ کے سوال کا جواب نہیں دے سکتا۔ براہ کرم بعد میں کوشش کریں۔\n\n{error_msg}" |
|
|
else: |
|
|
return f"Sorry, I'm unable to respond to your question right now. Please try again later.\n\n{error_msg}" |
|
|
|
|
|
def calculate_priority_score(heart_risk, diabetes_risk, hypertension_risk): |
|
|
"""Calculate integrated priority score with clinical weighting""" |
|
|
|
|
|
priority = ( |
|
|
heart_risk * 0.45 + |
|
|
diabetes_risk * 0.25 + |
|
|
hypertension_risk * 0.30 |
|
|
) |
|
|
|
|
|
return min(1.0, priority) |
|
|
|
|
|
def get_priority_recommendation(priority_score, language='English'): |
|
|
"""Get priority-based recommendation with clinical thresholds""" |
|
|
if priority_score >= 0.75: |
|
|
if language == 'Urdu': |
|
|
return "EMERGENCY_CARE", "اعلی ترجیح - فوری ہنگامی علاج کی ضرورت", "risk-high" |
|
|
else: |
|
|
return "EMERGENCY_CARE", "High Priority - Immediate Emergency Care Required", "risk-high" |
|
|
elif priority_score >= 0.55: |
|
|
if language == 'Urdu': |
|
|
return "SAME_DAY_CONSULT", "درمیانی ترجیح - اسی دن مشورہ ضروری", "risk-medium" |
|
|
else: |
|
|
return "SAME_DAY_CONSULT", "Medium Priority - Same Day Consultation Required", "risk-medium" |
|
|
else: |
|
|
if language == 'Urdu': |
|
|
return "ROUTINE_APPOINTMENT", "کم ترجیح - روٹین اپائنٹمنٹ", "risk-low" |
|
|
else: |
|
|
return "ROUTINE_APPOINTMENT", "Low Priority - Routine Appointment", "risk-low" |
|
|
|
|
|
def validate_patient_data(age, bp_systolic, bp_diastolic, heart_rate): |
|
|
"""Validate patient data for realistic clinical values""" |
|
|
errors = [] |
|
|
|
|
|
if age < 1 or age > 120: |
|
|
errors.append("Age must be between 1 and 120 years") |
|
|
if bp_systolic < 70 or bp_systolic > 250: |
|
|
errors.append("Systolic BP must be between 70 and 250 mmHg") |
|
|
if bp_diastolic < 40 or bp_diastolic > 150: |
|
|
errors.append("Diastolic BP must be between 40 and 150 mmHg") |
|
|
if heart_rate < 30 or heart_rate > 200: |
|
|
errors.append("Heart rate must be between 30 and 200 bpm") |
|
|
|
|
|
return errors |
|
|
|
|
|
def prepare_features_for_models(age, bp_systolic, bp_diastolic, heart_rate, cholesterol, glucose, bmi, symptoms): |
|
|
"""Prepare feature arrays for different models based on their training requirements""" |
|
|
|
|
|
|
|
|
heart_features = np.array([[ |
|
|
age, |
|
|
bp_systolic, |
|
|
cholesterol, |
|
|
heart_rate, |
|
|
symptoms['chest_pain'], |
|
|
symptoms['shortness_breath'], |
|
|
symptoms['palpitations'], |
|
|
bmi, |
|
|
glucose |
|
|
]]) |
|
|
|
|
|
|
|
|
diabetes_features = np.array([[ |
|
|
age, |
|
|
glucose, |
|
|
bmi, |
|
|
cholesterol, |
|
|
symptoms['fatigue'], |
|
|
symptoms['blurred_vision'], |
|
|
bp_systolic, |
|
|
heart_rate |
|
|
]]) |
|
|
|
|
|
|
|
|
hypertension_features = np.array([[ |
|
|
age, |
|
|
bp_systolic, |
|
|
bp_diastolic, |
|
|
bmi, |
|
|
symptoms['dizziness'], |
|
|
symptoms['palpitations'], |
|
|
heart_rate, |
|
|
cholesterol |
|
|
]]) |
|
|
|
|
|
return heart_features, diabetes_features, hypertension_features |
|
|
|
|
|
def main(): |
|
|
|
|
|
local_css() |
|
|
init_session_state() |
|
|
|
|
|
|
|
|
with st.spinner("🔄 Loading AI models..."): |
|
|
heart_model, diabetes_model, hypertension_model = load_models() |
|
|
|
|
|
|
|
|
ocr_processor = OCRProcessor() |
|
|
|
|
|
|
|
|
with st.sidebar: |
|
|
st.markdown("<h2 style='text-align: center; color: #2E86AB;'>🏥 AI-Priority OPD</h2>", |
|
|
unsafe_allow_html=True) |
|
|
|
|
|
|
|
|
st.markdown("---") |
|
|
st.subheader("API Configuration") |
|
|
groq_api_key = st.text_input("Enter Groq API Key:", type="password", |
|
|
help="Get your API key from https://console.groq.com") |
|
|
|
|
|
if groq_api_key: |
|
|
if st.session_state.groq_client is None: |
|
|
st.session_state.groq_client = init_groq_client(groq_api_key) |
|
|
if st.session_state.groq_client: |
|
|
st.success("✅ Groq API connected successfully!") |
|
|
chatbot = HealthcareChatbot(st.session_state.groq_client) |
|
|
else: |
|
|
st.warning("⚠️ Please enter Groq API key to enable chatbot") |
|
|
chatbot = HealthcareChatbot(None) |
|
|
|
|
|
language = st.radio( |
|
|
"Select Language / زبان منتخب کریں", |
|
|
["English", "Urdu"], |
|
|
key="language_selector" |
|
|
) |
|
|
|
|
|
st.markdown("---") |
|
|
|
|
|
if language == "English": |
|
|
st.subheader("Quick Actions") |
|
|
if st.button("🆕 New Patient Assessment", use_container_width=True): |
|
|
st.session_state.patient_data = {} |
|
|
st.session_state.risk_scores = {} |
|
|
st.session_state.chat_history = [] |
|
|
st.rerun() |
|
|
else: |
|
|
st.subheader("فوری اقدامات") |
|
|
if st.button("🆕 نیا مریض تشخیص", use_container_width=True): |
|
|
st.session_state.patient_data = {} |
|
|
st.session_state.risk_scores = {} |
|
|
st.session_state.chat_history = [] |
|
|
st.rerun() |
|
|
|
|
|
|
|
|
if language == "English": |
|
|
st.markdown('<h1 class="main-header">🏥 AI-Priority OPD System</h1>', unsafe_allow_html=True) |
|
|
st.markdown("### Smart Patient Triage and Priority Management for Pakistani Healthcare") |
|
|
else: |
|
|
st.markdown('<h1 class="main-header">🏥 AI-ترجیحی OPD سسٹم</h1>', unsafe_allow_html=True) |
|
|
st.markdown("### پاکستانی ہیلتھ کیئر کے لیے ذہین مریض کی درجہ بندی اور ترجیحی انتظام") |
|
|
|
|
|
|
|
|
if language == "English": |
|
|
tab_names = ["Patient Assessment", "Prescription OCR", "Health Chatbot", "Analytics"] |
|
|
else: |
|
|
tab_names = ["مریض تشخیص", "نسخہ OCR", "ہیلتھ چیٹ بوٹ", "تجزیات"] |
|
|
|
|
|
tab1, tab2, tab3, tab4 = st.tabs(tab_names) |
|
|
|
|
|
with tab1: |
|
|
|
|
|
if language == "English": |
|
|
st.header("👨⚕️ Patient Assessment & Risk Scoring") |
|
|
else: |
|
|
st.header("👨⚕️ مریض تشخیص اور خطرے کا اسکورنگ") |
|
|
|
|
|
with st.form("patient_assessment_form"): |
|
|
col1, col2 = st.columns(2) |
|
|
|
|
|
with col1: |
|
|
|
|
|
if language == "English": |
|
|
st.subheader("Personal Information") |
|
|
name = st.text_input("Full Name", placeholder="Enter patient's full name") |
|
|
age = st.number_input("Age", min_value=1, max_value=120, value=45, |
|
|
help="Patient's age in years") |
|
|
gender = st.selectbox("Gender", ["Male", "Female", "Other"]) |
|
|
contact = st.text_input("Contact Number", placeholder="03XX-XXXXXXX") |
|
|
else: |
|
|
st.subheader("ذاتی معلومات") |
|
|
name = st.text_input("مکمل نام", placeholder="مریض کا مکمل نام درج کریں") |
|
|
age = st.number_input("عمر", min_value=1, max_value=120, value=45, |
|
|
help="مریض کی عمر سالوں میں") |
|
|
gender = st.selectbox("جنس", ["مرد", "عورت", "دیگر"]) |
|
|
contact = st.text_input("رابطہ نمبر", placeholder="03XX-XXXXXXX") |
|
|
|
|
|
with col2: |
|
|
|
|
|
if language == "English": |
|
|
st.subheader("Clinical Parameters") |
|
|
bp_systolic = st.number_input("Blood Pressure (systolic)", |
|
|
min_value=70, max_value=250, value=120, |
|
|
help="Systolic blood pressure in mmHg") |
|
|
bp_diastolic = st.number_input("Blood Pressure (diastolic)", |
|
|
min_value=40, max_value=150, value=80, |
|
|
help="Diastolic blood pressure in mmHg") |
|
|
heart_rate = st.number_input("Heart Rate (bpm)", |
|
|
min_value=30, max_value=200, value=72, |
|
|
help="Heart beats per minute") |
|
|
cholesterol = st.number_input("Cholesterol Level (mg/dL)", |
|
|
min_value=100, max_value=400, value=180) |
|
|
glucose = st.number_input("Blood Glucose (mg/dL)", |
|
|
min_value=50, max_value=500, value=95) |
|
|
bmi = st.slider("BMI", min_value=15.0, max_value=40.0, value=23.5, step=0.1) |
|
|
else: |
|
|
st.subheader("کلینیکل پیرامیٹرز") |
|
|
bp_systolic = st.number_input("بلڈ پریشر (سسٹولک)", |
|
|
min_value=70, max_value=250, value=120, |
|
|
help="سسٹولک بلڈ پریشر mmHg میں") |
|
|
bp_diastolic = st.number_input("بلڈ پریشر (ڈائیسٹولک)", |
|
|
min_value=40, max_value=150, value=80, |
|
|
help="ڈائیسٹولک بلڈ پریشر mmHg میں") |
|
|
heart_rate = st.number_input("دل کی دھڑکن (bpm)", |
|
|
min_value=30, max_value=200, value=72, |
|
|
help="دل کی دھڑکن فی منٹ") |
|
|
cholesterol = st.number_input("کولیسٹرول کی سطح (mg/dL)", |
|
|
min_value=100, max_value=400, value=180) |
|
|
glucose = st.number_input("خون میں گلوکوز (mg/dL)", |
|
|
min_value=50, max_value=500, value=95) |
|
|
bmi = st.slider("باڈی ماس انڈیکس", min_value=15.0, max_value=40.0, value=23.5, step=0.1) |
|
|
|
|
|
|
|
|
if language == "English": |
|
|
st.subheader("Reported Symptoms") |
|
|
col3, col4 = st.columns(2) |
|
|
with col3: |
|
|
chest_pain = st.checkbox("Chest Pain or Discomfort") |
|
|
shortness_breath = st.checkbox("Shortness of Breath") |
|
|
palpitations = st.checkbox("Heart Palpitations") |
|
|
with col4: |
|
|
fatigue = st.checkbox("Persistent Fatigue") |
|
|
dizziness = st.checkbox("Dizziness or Lightheadedness") |
|
|
blurred_vision = st.checkbox("Blurred Vision") |
|
|
else: |
|
|
st.subheader("رپورٹ کردہ علامات") |
|
|
col3, col4 = st.columns(2) |
|
|
with col3: |
|
|
chest_pain = st.checkbox("سینے میں درد یا بے چینی") |
|
|
shortness_breath = st.checkbox("سانس لینے میں دشواری") |
|
|
palpitations = st.checkbox("دل کی دھڑکن میں اضافہ") |
|
|
with col4: |
|
|
fatigue = st.checkbox("مسلسل تھکاوٹ") |
|
|
dizziness = st.checkbox("چکر آنا یا سر ہلکا محسوس ہونا") |
|
|
blurred_vision = st.checkbox("دھندلا نظر آنا") |
|
|
|
|
|
|
|
|
if language == "English": |
|
|
assess_button = st.form_submit_button("🚀 Calculate Risk Score & Priority", |
|
|
use_container_width=True) |
|
|
else: |
|
|
assess_button = st.form_submit_button("🚀 خطرے کا اسکور اور ترجیح معلوم کریں", |
|
|
use_container_width=True) |
|
|
|
|
|
if assess_button: |
|
|
|
|
|
validation_errors = validate_patient_data(age, bp_systolic, bp_diastolic, heart_rate) |
|
|
|
|
|
if validation_errors: |
|
|
for error in validation_errors: |
|
|
st.error(f"❌ {error}") |
|
|
elif heart_model is None or diabetes_model is None or hypertension_model is None: |
|
|
st.error("❌ AI models are not loaded properly. Please check model files.") |
|
|
else: |
|
|
try: |
|
|
with st.spinner("🔍 Analyzing patient data and calculating risks..."): |
|
|
|
|
|
symptoms_dict = { |
|
|
'chest_pain': 1 if chest_pain else 0, |
|
|
'shortness_breath': 1 if shortness_breath else 0, |
|
|
'palpitations': 1 if palpitations else 0, |
|
|
'fatigue': 1 if fatigue else 0, |
|
|
'dizziness': 1 if dizziness else 0, |
|
|
'blurred_vision': 1 if blurred_vision else 0 |
|
|
} |
|
|
|
|
|
|
|
|
heart_features, diabetes_features, hypertension_features = prepare_features_for_models( |
|
|
age, bp_systolic, bp_diastolic, heart_rate, cholesterol, glucose, bmi, symptoms_dict |
|
|
) |
|
|
|
|
|
|
|
|
heart_risk_proba = heart_model.predict_proba(heart_features)[0][1] |
|
|
diabetes_risk_proba = diabetes_model.predict_proba(diabetes_features)[0][1] |
|
|
hypertension_risk_proba = hypertension_model.predict_proba(hypertension_features)[0][1] |
|
|
|
|
|
|
|
|
if chest_pain: |
|
|
heart_risk_proba = min(1.0, heart_risk_proba * 1.3) |
|
|
if shortness_breath: |
|
|
heart_risk_proba = min(1.0, heart_risk_proba * 1.2) |
|
|
if fatigue: |
|
|
diabetes_risk_proba = min(1.0, diabetes_risk_proba * 1.2) |
|
|
if dizziness: |
|
|
hypertension_risk_proba = min(1.0, hypertension_risk_proba * 1.3) |
|
|
|
|
|
|
|
|
priority_score = calculate_priority_score( |
|
|
heart_risk_proba, diabetes_risk_proba, hypertension_risk_proba |
|
|
) |
|
|
|
|
|
priority_level, recommendation, risk_class = get_priority_recommendation( |
|
|
priority_score, language |
|
|
) |
|
|
|
|
|
|
|
|
st.session_state.risk_scores = { |
|
|
'heart': heart_risk_proba, |
|
|
'diabetes': diabetes_risk_proba, |
|
|
'hypertension': hypertension_risk_proba, |
|
|
'priority': priority_score, |
|
|
'recommendation': recommendation, |
|
|
'level': priority_level |
|
|
} |
|
|
|
|
|
|
|
|
st.markdown("---") |
|
|
st.success("✅ Risk assessment completed successfully!") |
|
|
|
|
|
|
|
|
if language == "English": |
|
|
st.subheader("📊 Disease Risk Assessment") |
|
|
else: |
|
|
st.subheader("📊 بیماری کے خطرے کا اندازہ") |
|
|
|
|
|
col5, col6, col7, col8 = st.columns(4) |
|
|
|
|
|
risk_metrics = [ |
|
|
(heart_risk_proba, "Heart Disease", "❤️", "#FF6B6B"), |
|
|
(diabetes_risk_proba, "Diabetes", "🩺", "#4ECDC4"), |
|
|
(hypertension_risk_proba, "Hypertension", "💓", "#45B7D1"), |
|
|
(priority_score, "Priority Score", "🎯", "#96CEB4") |
|
|
] |
|
|
|
|
|
for (value, title, emoji, color), col in zip(risk_metrics, [col5, col6, col7, col8]): |
|
|
with col: |
|
|
fig = go.Figure(go.Indicator( |
|
|
mode = "gauge+number+delta", |
|
|
value = value, |
|
|
domain = {'x': [0, 1], 'y': [0, 1]}, |
|
|
title = {'text': f"{emoji} {title}", 'font': {'size': 14}}, |
|
|
gauge = { |
|
|
'axis': {'range': [0, 1], 'tickwidth': 1}, |
|
|
'bar': {'color': color}, |
|
|
'steps': [ |
|
|
{'range': [0, 0.3], 'color': "lightgreen"}, |
|
|
{'range': [0.3, 0.7], 'color': "yellow"}, |
|
|
{'range': [0.7, 1], 'color': "red"} |
|
|
], |
|
|
'threshold': { |
|
|
'line': {'color': "black", 'width': 4}, |
|
|
'thickness': 0.75, |
|
|
'value': 0.7 |
|
|
} |
|
|
} |
|
|
)) |
|
|
fig.update_layout(height=250, margin=dict(l=10, r=10, t=50, b=10)) |
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
|
|
|
st.markdown(f'<div class="priority-box {risk_class}">', unsafe_allow_html=True) |
|
|
if language == "English": |
|
|
st.markdown(f"## 🎯 Clinical Priority Recommendation") |
|
|
st.markdown(f"### {recommendation}") |
|
|
st.markdown(f"**Overall Risk Score:** `{priority_score:.3f}`") |
|
|
st.markdown(f"**Recommended Action:** `{priority_level.replace('_', ' ').title()}`") |
|
|
|
|
|
|
|
|
if priority_level == "EMERGENCY_CARE": |
|
|
st.warning("🚨 **Immediate Action Required:** Patient should be directed to emergency department without delay.") |
|
|
elif priority_level == "SAME_DAY_CONSULT": |
|
|
st.info("ℹ️ **Urgent Consultation:** Schedule appointment within 24 hours.") |
|
|
else: |
|
|
st.success("✅ **Routine Care:** Schedule within regular appointment system.") |
|
|
|
|
|
else: |
|
|
st.markdown(f"## 🎯 کلینیکل ترجیحی سفارش") |
|
|
st.markdown(f"### {recommendation}") |
|
|
st.markdown(f"**کل خطرے کا اسکور:** `{priority_score:.3f}`") |
|
|
st.markdown(f"**سفارش کردہ عمل:** `{priority_level.replace('_', ' ').title()}`") |
|
|
|
|
|
if priority_level == "EMERGENCY_CARE": |
|
|
st.warning("🚨 **فوری کارروائی ضروری:** مریض کو بغیر کسی تاخیر کے ایمرجنسی ڈیپارٹمنٹ بھیجا جائے۔") |
|
|
elif priority_level == "SAME_DAY_CONSULT": |
|
|
st.info("ℹ️ **فوری مشاورت:** 24 گھنٹے کے اندر اپائنٹمنٹ شیڈول کریں۔") |
|
|
else: |
|
|
st.success("✅ **روٹین کیئر:** معمول کی اپائنٹمنٹ سسٹم کے اندر شیڈول کریں۔") |
|
|
|
|
|
st.markdown('</div>', unsafe_allow_html=True) |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"❌ Error in risk assessment: {str(e)}") |
|
|
st.info("💡 Please ensure all model files are properly formatted and compatible.") |
|
|
|
|
|
with tab2: |
|
|
|
|
|
if language == "English": |
|
|
st.header("📄 Prescription OCR Analysis") |
|
|
st.write("Upload a prescription image to extract medication information automatically") |
|
|
else: |
|
|
st.header("📄 نسخہ OCR تجزیہ") |
|
|
st.write("دوائی کی معلومات خود بخود نکالنے کے لیے نسخہ کی تصویر اپ لوڈ کریں") |
|
|
|
|
|
uploaded_file = st.file_uploader( |
|
|
"Choose prescription image..." if language == "English" else "نسخہ تصویر منتخب کریں...", |
|
|
type=['png', 'jpg', 'jpeg'], |
|
|
help="Upload a clear image of the medical prescription" |
|
|
) |
|
|
|
|
|
if uploaded_file is not None: |
|
|
|
|
|
image = Image.open(uploaded_file) |
|
|
st.image(image, caption="📷 Uploaded Prescription" if language == "English" else "📷 اپ لوڈ کردہ نسخہ", |
|
|
use_column_width=True) |
|
|
|
|
|
if st.button("🔍 Extract Text" if language == "English" else "🔍 متن نکالیں", |
|
|
use_container_width=True): |
|
|
with st.spinner("🔄 Processing prescription image..." if language == "English" |
|
|
else "🔄 نسخہ تصویر پروسیس ہو رہی ہے..."): |
|
|
extracted_text = ocr_processor.extract_text(image) |
|
|
accuracy = ocr_processor.calculate_ocr_accuracy(extracted_text) |
|
|
|
|
|
if extracted_text and "No text detected" not in extracted_text and "OCR Error" not in extracted_text: |
|
|
st.success(f"✅ Text extraction completed! (Estimated Accuracy: {accuracy:.1f}%)") |
|
|
|
|
|
if language == "English": |
|
|
st.subheader("Extracted Medication Information:") |
|
|
else: |
|
|
st.subheader("نکالی گئی دوائی کی معلومات:") |
|
|
|
|
|
|
|
|
with st.expander("View Extracted Text" if language == "English" else "نکالا گیا متن دیکھیں", expanded=True): |
|
|
st.text_area("", extracted_text, height=200, key="extracted_text") |
|
|
|
|
|
|
|
|
if language == "English": |
|
|
st.subheader("📋 Medication Analysis") |
|
|
else: |
|
|
st.subheader("📋 دوائی کا تجزیہ") |
|
|
|
|
|
|
|
|
medications_detected = 0 |
|
|
if any(term in extracted_text.lower() for term in ['tablet', 'tab']): |
|
|
medications_detected += 1 |
|
|
if any(term in extracted_text.lower() for term in ['mg', 'ml']): |
|
|
medications_detected += 1 |
|
|
if any(term in extracted_text.lower() for term in ['daily', 'twice', 'thrice']): |
|
|
medications_detected += 1 |
|
|
|
|
|
col_med1, col_med2 = st.columns(2) |
|
|
with col_med1: |
|
|
st.metric("Medications Detected", medications_detected) |
|
|
with col_med2: |
|
|
st.metric("OCR Confidence", f"{accuracy:.1f}%") |
|
|
|
|
|
else: |
|
|
st.error("❌ No text could be extracted from the image. Please try with a clearer image.") |
|
|
|
|
|
if language == "English": |
|
|
st.info("💡 Tips for better OCR results:\n- Use good lighting\n- Ensure clear focus\n- Avoid shadows\n- Straight angle photo") |
|
|
else: |
|
|
st.info("💡 بہتر OCR نتائج کے لیے نکات:\n- اچھی روشنی استعمال کریں\n- واضح فوکس یقینی بنائیں\n- سایوں سے پرہیز کریں\n- سیدھے زاویے کی تصویر") |
|
|
|
|
|
with tab3: |
|
|
|
|
|
if language == "English": |
|
|
st.header("💬 Healthcare Assistant Chatbot") |
|
|
st.write("Ask health-related questions and get personalized advice in English or Urdu") |
|
|
else: |
|
|
st.header("💬 ہیلتھ کیئر اسسٹنٹ چیٹ بوٹ") |
|
|
st.write("صحت سے متعلق سوالات پوچھیں اور انگریزی یا اردو میں ذاتی مشورہ حاصل کریں") |
|
|
|
|
|
if st.session_state.groq_client is None: |
|
|
st.warning("⚠️ Please configure Groq API key in the sidebar to use the chatbot") |
|
|
else: |
|
|
|
|
|
for message in st.session_state.chat_history: |
|
|
with st.chat_message(message["role"]): |
|
|
if message["role"] == "user": |
|
|
st.markdown(message["content"]) |
|
|
else: |
|
|
|
|
|
st.markdown(f"**🤖 Healthcare Assistant:**\n\n{message['content']}") |
|
|
|
|
|
|
|
|
if prompt := st.chat_input( |
|
|
"Type your health question here..." if language == "English" |
|
|
else "اپنا صحت کا سوال یہاں ٹائپ کریں..." |
|
|
): |
|
|
|
|
|
st.session_state.chat_history.append({"role": "user", "content": prompt}) |
|
|
|
|
|
|
|
|
with st.chat_message("assistant"): |
|
|
with st.spinner("💭 Analyzing your question..." if language == "English" else "💭 آپ کا سوال تجزیہ ہو رہا ہے..."): |
|
|
response = chatbot.get_response(prompt, language) |
|
|
st.markdown(f"**🤖 Healthcare Assistant:**\n\n{response}") |
|
|
|
|
|
|
|
|
st.session_state.chat_history.append({"role": "assistant", "content": response}) |
|
|
|
|
|
|
|
|
if len(st.session_state.chat_history) > 10: |
|
|
st.session_state.chat_history = st.session_state.chat_history[-10:] |
|
|
|
|
|
|
|
|
if language == "English": |
|
|
st.subheader("Quick Health Topics") |
|
|
else: |
|
|
st.subheader("فوری صحت کے موضوعات") |
|
|
|
|
|
col_qa1, col_qa2, col_qa3 = st.columns(3) |
|
|
|
|
|
with col_qa1: |
|
|
if st.button("❤️ Heart Health", use_container_width=True): |
|
|
st.session_state.chat_history.append({ |
|
|
"role": "user", |
|
|
"content": "Tell me about heart health and prevention tips" |
|
|
}) |
|
|
st.rerun() |
|
|
|
|
|
with col_qa2: |
|
|
if st.button("🩺 Diabetes", use_container_width=True): |
|
|
st.session_state.chat_history.append({ |
|
|
"role": "user", |
|
|
"content": "What are the symptoms and management of diabetes?" |
|
|
}) |
|
|
st.rerun() |
|
|
|
|
|
with col_qa3: |
|
|
if st.button("💓 Blood Pressure", use_container_width=True): |
|
|
st.session_state.chat_history.append({ |
|
|
"role": "user", |
|
|
"content": "How to control high blood pressure naturally?" |
|
|
}) |
|
|
st.rerun() |
|
|
|
|
|
with tab4: |
|
|
|
|
|
if language == "English": |
|
|
st.header("📈 System Analytics & Performance") |
|
|
else: |
|
|
st.header("📈 سسٹم تجزیات اور کارکردگی") |
|
|
|
|
|
|
|
|
if 'risk_scores' in st.session_state and st.session_state.risk_scores: |
|
|
recent_priority = st.session_state.risk_scores.get('priority', 0) |
|
|
col9, col10, col11, col12 = st.columns(4) |
|
|
|
|
|
with col9: |
|
|
st.metric("Current Patient Priority", f"{recent_priority:.1%}") |
|
|
with col10: |
|
|
st.metric("Risk Assessment", "Completed") |
|
|
with col11: |
|
|
st.metric("Model Confidence", "High") |
|
|
with col12: |
|
|
st.metric("Processing Time", "< 2s") |
|
|
else: |
|
|
if language == "English": |
|
|
st.info("👆 Complete a patient assessment to see analytics") |
|
|
else: |
|
|
st.info("👆 تجزیات دیکھنے کے لیے مریض کی تشخیص مکمل کریں") |
|
|
|
|
|
|
|
|
if 'risk_scores' in st.session_state and st.session_state.risk_scores: |
|
|
col_chart1, col_chart2 = st.columns(2) |
|
|
|
|
|
with col_chart1: |
|
|
if language == "English": |
|
|
st.subheader("Current Patient Risk Distribution") |
|
|
else: |
|
|
st.subheader("موجودہ مریض کے خطرے کی تقسیم") |
|
|
|
|
|
risk_data = pd.DataFrame({ |
|
|
'Condition': ['Heart Disease', 'Diabetes', 'Hypertension'], |
|
|
'Risk Score': [ |
|
|
st.session_state.risk_scores['heart'], |
|
|
st.session_state.risk_scores['diabetes'], |
|
|
st.session_state.risk_scores['hypertension'] |
|
|
] |
|
|
}) |
|
|
|
|
|
fig = px.bar(risk_data, x='Condition', y='Risk Score', |
|
|
color='Risk Score', color_continuous_scale='RdYlGn_r') |
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
with col_chart2: |
|
|
if language == "English": |
|
|
st.subheader("Priority Level") |
|
|
else: |
|
|
st.subheader("ترجیحی سطح") |
|
|
|
|
|
priority_level = st.session_state.risk_scores['level'] |
|
|
priority_colors = { |
|
|
'EMERGENCY_CARE': '#dc3545', |
|
|
'SAME_DAY_CONSULT': '#ffc107', |
|
|
'ROUTINE_APPOINTMENT': '#28a745' |
|
|
} |
|
|
|
|
|
fig = go.Figure(go.Indicator( |
|
|
mode = "gauge+number", |
|
|
value = st.session_state.risk_scores['priority'] * 100, |
|
|
domain = {'x': [0, 1], 'y': [0, 1]}, |
|
|
title = {'text': "Priority Score"}, |
|
|
gauge = { |
|
|
'axis': {'range': [0, 100]}, |
|
|
'bar': {'color': priority_colors.get(priority_level, '#2E86AB')}, |
|
|
'steps': [ |
|
|
{'range': [0, 55], 'color': "lightgray"}, |
|
|
{'range': [55, 75], 'color': "yellow"}, |
|
|
{'range': [75, 100], 'color': "red"} |
|
|
] |
|
|
} |
|
|
)) |
|
|
fig.update_layout(height=300) |
|
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |