|
import io |
|
import os |
|
import re |
|
from datetime import datetime, timedelta |
|
import numpy as np |
|
import pandas as pd |
|
import requests |
|
import streamlit as st |
|
from dotenv import load_dotenv |
|
from fuzzywuzzy import fuzz |
|
from groq import Groq |
|
from reportlab.lib.pagesizes import letter |
|
from reportlab.pdfgen import canvas |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
from database import get_user_history, register_user, user_exists, check_user_credentials, save_user_history |
|
|
|
except ImportError: |
|
st.error("Could not import database functions. Please ensure database.py exists and contains required functions.") |
|
|
|
def get_user_history(user_id): |
|
st.warning("Database function 'get_user_history' not implemented. History will not be persistent.") |
|
return [] |
|
def register_user(user_id, full_name, dob, email, password): |
|
st.warning("Database function 'register_user' not implemented. User registration will not be persistent.") |
|
return True |
|
def user_exists(user_id): |
|
st.warning("Database function 'user_exists' not implemented. User existence check will not work.") |
|
return False |
|
def check_user_credentials(user_id, password): |
|
st.warning("Database function 'check_user_credentials' not implemented. Login will not work.") |
|
return False |
|
def save_user_history(user_id, symptoms, predicted_diseases): |
|
st.warning("Database function 'save_user_history' not implemented. History saving will not be persistent.") |
|
pass |
|
|
|
|
|
|
|
|
|
from user_management import render_user_management_sidebar, save_history_to_db_if_logged_in |
|
|
|
|
|
try: |
|
from utils import extract_keyword |
|
except ImportError: |
|
st.error("Could not import utility functions. Please ensure utils.py exists and contains required functions.") |
|
def extract_keyword(text, keywords): |
|
|
|
for kw in keywords: |
|
if kw.lower() in text.lower(): |
|
return kw |
|
return "Unknown" |
|
|
|
|
|
|
|
|
|
load_dotenv(dotenv_path=".env.local") |
|
|
|
|
|
st.set_page_config(page_title="MediBot - Health Assistant", page_icon="🏥", layout="wide") |
|
|
|
|
|
groq_client = None |
|
GROQ_AVAILABLE = False |
|
try: |
|
|
|
GROQ_API_KEY = st.secrets.get("GROQ_API_KEY") |
|
if not GROQ_API_KEY: |
|
|
|
GROQ_API_KEY = os.getenv("GROQ_API_KEY") |
|
if GROQ_API_KEY: |
|
groq_client = Groq(api_key=GROQ_API_KEY) |
|
GROQ_AVAILABLE = True |
|
else: |
|
st.error("Groq API Key not found. Groq chatbot will not be available.") |
|
except Exception as e: |
|
st.error(f"Error initializing Groq client: {e}. Groq chatbot will not be available.") |
|
|
|
|
|
HF_MODEL_AVAILABLE = False |
|
HF_API_TOKEN = None |
|
try: |
|
|
|
HF_API_TOKEN = st.secrets.get("med_model") |
|
if not HF_API_TOKEN: |
|
|
|
HF_API_TOKEN = os.getenv("MED_MODEL") |
|
if HF_API_TOKEN: |
|
HF_MODEL_AVAILABLE = True |
|
else: |
|
st.warning("Hugging Face 'med_model' API Key not found. Clinical diagnosis assessment will not be available.") |
|
except Exception as e: |
|
st.warning(f"Error retrieving Hugging Face API key: {e}. Clinical diagnosis assessment will not be available.") |
|
|
|
|
|
if "chat_history" not in st.session_state: |
|
st.session_state.chat_history = [] |
|
if "feedback" not in st.session_state: |
|
st.session_state.feedback = [] |
|
if "show_welcome" not in st.session_state: |
|
st.session_state.show_welcome = True |
|
if "chat_input_value" not in st.session_state: |
|
st.session_state.chat_input_value = "" |
|
if "last_chat_response" not in st.session_state: |
|
st.session_state.last_chat_response = "" |
|
if "feedback_input" not in st.session_state: |
|
st.session_state.feedback_input = "" |
|
|
|
if "user_id" not in st.session_state: |
|
st.session_state.user_id = None |
|
if "logged_in_user" not in st.session_state: |
|
st.session_state.logged_in_user = None |
|
|
|
|
|
@st.cache_data |
|
def load_csv_data(): |
|
try: |
|
|
|
dataset_df = pd.read_csv('dataset.csv').fillna('') |
|
description_df = pd.read_csv('symptom_Description.csv').fillna('') |
|
precaution_df = pd.read_csv('symptom_precaution.csv').fillna('') |
|
severity_df = pd.read_csv('Symptom-severity.csv').fillna('') |
|
|
|
|
|
|
|
disease_symptoms_map = {} |
|
for index, row in dataset_df.iterrows(): |
|
disease = row['Disease'] |
|
|
|
symptoms = [s.strip().replace('_', ' ') for s in row.values[1:] if s.strip()] |
|
disease_symptoms_map[disease] = symptoms |
|
|
|
|
|
disease_description_map = {row['Disease']: row['Description'] for index, row in description_df.iterrows()} |
|
|
|
|
|
disease_precaution_map = {row['Disease']: [p.strip() for p in row.values[1:] if p.strip()] for index, row in precaution_df.iterrows()} |
|
|
|
|
|
|
|
symptom_severity_map = {row['Symptom'].strip().replace('_', ' ').lower(): row['weight'] for index, row in severity_df.iterrows()} |
|
|
|
|
|
all_unique_symptoms = sorted(list(set(symptom for symptoms_list in disease_symptoms_map.values() for symptom in symptoms_list))) |
|
return disease_symptoms_map, disease_description_map, disease_precaution_map, all_unique_symptoms, symptom_severity_map |
|
except FileNotFoundError as e: |
|
st.error(f"Error: Required CSV file not found. Make sure 'dataset.csv', 'symptom_Description.csv', 'symptom_precaution.csv', and 'Symptom-severity.csv' are in the correct directory. Details: {e}") |
|
st.stop() |
|
except Exception as e: |
|
st.error(f"Error loading CSV data: {e}") |
|
st.stop() |
|
|
|
disease_symptoms_map, disease_description_map, disease_precaution_map, hardcoded_symptoms, symptom_severity_map = load_csv_data() |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
/* Basic Page Layout & Background */ |
|
.stApp { |
|
background-color: #f8f9fa; /* Very light grey */ |
|
font-family: 'Arial', sans-serif; /* Modern sans-serif font */ |
|
color: #343a40; /* Darker grey for primary text */ |
|
} |
|
/* Main Container Styling (for sections like Home, History) */ |
|
/* Streamlit's main content block often has a data-testid="stVerticalBlock" */ |
|
.stApp > header, .stApp > div:first-child > div:first-child > div:first-child { |
|
/* This targets the container that usually holds the main content in Streamlit */ |
|
padding: 2.5rem; /* Increased padding */ |
|
border-radius: 12px; /* More rounded corners */ |
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); /* Stronger, softer shadow */ |
|
margin-top: 2rem; /* Space from top elements */ |
|
margin-bottom: 2rem; |
|
background-color: #ffffff; /* White background */ |
|
} |
|
/* Headers */ |
|
h1, h2, h3, h4, h5, h6 { |
|
color: #004d99; /* A professional darker blue */ |
|
font-weight: 700; /* Bold headers */ |
|
margin-top: 1.5em; |
|
margin-bottom: 0.8em; |
|
} |
|
h1 { font-size: 2.8em; text-align: center; color: #003366; } /* Larger, darker blue for main title */ |
|
h2 { font-size: 2.2em; border-bottom: 2px solid #e0e7ee; padding-bottom: 0.5em; margin-bottom: 1em; } /* Subtle line under h2 */ |
|
h3 { font-size: 1.6em; } |
|
/* Main Tabs Styling (Home, History, Feedback, About, Insights) */ |
|
.stTabs [data-baseweb="tab-list"] { |
|
gap: 20px; /* More space between tabs */ |
|
margin-bottom: 30px; /* More space below tabs */ |
|
justify-content: center; /* Center the main tabs */ |
|
flex-wrap: wrap; /* Allow tabs to wrap on smaller screens */ |
|
} |
|
.stTabs [data-baseweb="tab"] { |
|
height: 60px; /* Taller tabs */ |
|
padding: 15px 30px; /* More padding */ |
|
background-color: #e9f0f6; /* Light blue-grey background for tabs */ |
|
border-radius: 12px 12px 0 0; /* Rounded top corners */ |
|
font-size: 1.25em; /* Larger font size */ |
|
font-weight: 600; /* Semi-bold */ |
|
color: #4a6a8c; /* Muted blue text color */ |
|
border: none; /* Remove default border */ |
|
border-bottom: 4px solid transparent; /* Thicker highlight bottom border */ |
|
transition: all 0.3s ease-in-out; /* Smooth transitions */ |
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05); /* Subtle shadow */ |
|
outline: none !important; /* Explicitly remove outline */ |
|
text-align: center; /* Center text within tab */ |
|
min-width: 120px; /* Ensure a minimum width for tabs on mobile */ |
|
} |
|
.stTabs [data-baseweb="tab"]:hover { |
|
background-color: #dbe4ee; /* Slightly darker blue-grey on hover */ |
|
color: #004d99; /* Darker blue text on hover */ |
|
transform: translateY(-2px); /* Slight lift effect */ |
|
outline: none !important; /* Ensure no outline on hover either */ |
|
} |
|
/* Updated styling for selected main tabs */ |
|
.stTabs [data-baseweb="tab"][aria-selected="true"] { |
|
background-color: #004d99; /* Sober darker blue for selected tab */ |
|
color: white; /* White text for selected tab */ |
|
border-bottom: 4px solid #004d99; /* Keep the consistent bottom border */ |
|
box-shadow: 0 -4px 15px rgba(0, 0, 0, 0.15); /* More pronounced shadow for selected */ |
|
outline: none !important; /* Ensure no outline when selected */ |
|
} |
|
/* Ensure no outline on focus for tabs */ |
|
.stTabs [data-baseweb="tab"]:focus { |
|
outline: none !important; |
|
box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25) !important; /* Subtle focus glow if needed */ |
|
} |
|
/* Nested Tabs Styling (Symptom Checker, Chat with MediBot) */ |
|
/* This targets tabs that are children of other tabs, making them smaller */ |
|
.stTabs [data-baseweb="tab-list"]:has(.stTabs [data-baseweb="tab-list"]) [data-baseweb="tab"], |
|
.stTabs [data-baseweb="tab-list"] [role="tablist"] [data-baseweb="tab"] { /* More robust selector for nested tabs */ |
|
height: 45px; /* Slightly smaller height for sub-tabs */ |
|
padding: 10px 20px; /* Slightly smaller padding */ |
|
font-size: 1.1em; |
|
background-color: #f0f5f9; /* Lighter blue-grey for sub-tabs */ |
|
border-radius: 10px; /* Fully rounded corners for sub-tabs */ |
|
border: 1px solid #d8e2ed; |
|
margin: 0 8px; /* Small margin between sub-tabs */ |
|
box-shadow: none; /* No individual shadow for sub-tabs */ |
|
color: #5f7a96; |
|
outline: none !important; /* Remove outline on focus */ |
|
min-width: unset; /* Remove min-width for sub-tabs */ |
|
} |
|
.stTabs [data-baseweb="tab-list"]:has(.stTabs [data-baseweb="tab-list"]) [data-baseweb="tab"]:hover, |
|
.stTabs [data-baseweb="tab-list"] [role="tablist"] [data-baseweb="tab"]:hover { |
|
background-color: #e3ecf5; |
|
color: #004d99; |
|
transform: none; /* No lift for sub-tabs */ |
|
outline: none !important; /* Ensure no outline on hover either */ |
|
} |
|
/* Updated styling for selected nested tabs */ |
|
.stTabs [data-baseweb="tab-list"]:has(.stTabs [data-baseweb="tab-list"]) [aria-selected="true"], |
|
.stTabs [data-baseweb="tab-list"] [role="tablist"] [aria-selected="true"] { |
|
background-color: #0056b3; /* Slightly darker blue for selected sub-tab */ |
|
color: white; |
|
border: 1px solid #0056b3; |
|
box-shadow: 0 2px 8px rgba(0,0,0,0.15); /* Subtle shadow for selected sub-tab */ |
|
outline: none !important; /* Ensure no outline when selected */ |
|
} |
|
/* General Button Styling */ |
|
.stButton>button { |
|
background-color: #007bff; /* Primary blue button */ |
|
color: white; |
|
border-radius: 10px; /* More rounded */ |
|
padding: 12px 28px; /* More padding */ |
|
font-weight: 600; /* Semi-bold */ |
|
border: none; |
|
cursor: pointer; |
|
box-shadow: 0 5px 15px rgba(0,0,0,0.1); /* Softer, more diffuse shadow */ |
|
transition: all 0.3s ease-in-out; /* Smooth transitions */ |
|
font-size: 1.05em; /* Slightly larger text */ |
|
outline: none; /* Remove outline on focus */ |
|
width: auto; /* Allow buttons to size naturally, but consider max-width on mobile */ |
|
min-width: 100px; /* Ensure minimum tappable area */ |
|
} |
|
.stButton>button:hover { |
|
background-color: #0056b3; /* Darker blue on hover */ |
|
box-shadow: 0 8px 20px rgba(0,0,0,0.15); /* Larger shadow on hover */ |
|
transform: translateY(-3px); /* More pronounced lift */ |
|
outline: none; /* Ensure no outline on hover either */ |
|
} |
|
/* Full width buttons in sidebar for better mobile experience */ |
|
.sidebar .stButton>button { |
|
width: 100%; /* Full width buttons in sidebar */ |
|
margin-bottom: 10px; |
|
} |
|
/* Input Fields, Selects, etc. */ |
|
.stTextInput>div>div>input, |
|
.stTextArea>div>div>textarea, /* Target textarea as well */ |
|
.stDateInput>div>div>input, |
|
.stMultiSelect>div>div { |
|
border-radius: 10px; /* More rounded */ |
|
border: 1px solid #ced4da; /* Light grey border */ |
|
padding: 10px 15px; /* More padding */ |
|
box-shadow: inset 0 1px 4px rgba(0,0,0,0.05); /* Inner shadow */ |
|
transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; |
|
background-color: #fefefe; /* Slightly off-white background */ |
|
outline: none; /* Remove default outline */ |
|
color: #343a40; /* Explicitly set text color to dark grey */ |
|
} |
|
.stTextInput>div>div>input:focus, |
|
.stTextArea>div>div>textarea:focus, |
|
.stDateInput>div>div>input:focus, |
|
.stMultiSelect>div>div:focus-within { |
|
border-color: #007bff; /* Primary blue border on focus */ |
|
box-shadow: 0 0 0 0.25rem rgba(0,123,255,.25); /* Focus glow */ |
|
outline: none; /* Remove default outline */ |
|
} |
|
/* Specific styling for the multiselect selected items */ |
|
.stMultiSelect div[data-baseweb="tag"] { |
|
background-color: #e0f2f7 !important; /* Light blue tag background */ |
|
color: #004d99 !important; /* Dark blue tag text */ |
|
border-radius: 5px !important; |
|
padding: 5px 10px !important; |
|
margin: 2px !important; |
|
} |
|
/* Expander (for disease info) */ |
|
.stExpander { |
|
border: 1px solid #e0e7ee; /* Lighter grey border */ |
|
border-radius: 12px; /* More rounded */ |
|
margin-bottom: 15px; /* More space below */ |
|
background-color: #ffffff; /* White background */ |
|
box-shadow: 0 4px 12px rgba(0,0,0,0.05); /* Softer shadow */ |
|
overflow: hidden; /* Ensure rounded corners clip content */ |
|
} |
|
.stExpander > div > div > .streamlit-expanderHeader { |
|
background-color: #f0f5f9; /* Light blue header */ |
|
padding: 15px 20px; |
|
font-weight: 600; |
|
font-size: 1.1em; |
|
color: #004d99; |
|
border-bottom: 1px solid #e0e7ee; |
|
outline: none !important; /* Remove outline on focus */ |
|
} |
|
.stExpander > div > div > .streamlit-expanderHeader:focus { |
|
outline: none !important; /* Ensure no outline on focus */ |
|
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25) !important; /* Subtle focus ring */ |
|
} |
|
.stExpander > div > div > .streamlit-expanderContent { |
|
padding: 20px; /* Increased padding inside */ |
|
} |
|
/* Sidebar Styling */ |
|
/* Streamlit's sidebar elements are usually inside a div with role="complementary" */ |
|
.st-emotion-cache-vk33as { /* This targets the Streamlit sidebar div with padding */ |
|
background-color: #ffffff; /* White sidebar background */ |
|
padding: 25px; |
|
border-radius: 12px; |
|
box-shadow: 0 8px 24px rgba(0,0,0,0.08); /* Consistent shadow */ |
|
margin-top: 2rem; |
|
} |
|
.sidebar .stButton>button { |
|
width: 100%; /* Full width buttons in sidebar */ |
|
margin-bottom: 10px; |
|
} |
|
.sidebar h2, .sidebar h3 { |
|
color: #004d99; |
|
text-align: center; |
|
margin-bottom: 1.5em; |
|
} |
|
.sidebar .stTextInput, .sidebar .stDateInput, .sidebar .stSelectbox { |
|
margin-bottom: 1em; /* Space between sidebar inputs */ |
|
} |
|
/* Alerts */ |
|
.stAlert { |
|
border-radius: 10px; /* Rounded corners for alerts */ |
|
padding: 15px; |
|
font-size: 1.05em; |
|
} |
|
.stAlert.st-success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; } |
|
.stAlert.st-info { background-color: #d1ecf1; color: #0c5460; border-color: #bee5eb; } |
|
.stAlert.st-warning { background-color: #fff3cd; color: #856404; border-color: #ffeeba; } |
|
.stAlert.st-error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; } |
|
/* Spinners */ |
|
.stSpinner { color: #007bff; } /* Spinner color matching primary button */ |
|
/* Disclaimer Box - using a more reliable target */ |
|
div[data-testid="stVerticalBlock"] > div:first-child > div:has(p > strong:contains("Disclaimer")) { |
|
background-color: #fff8e1; /* Light yellow for warnings/disclaimers */ |
|
border: 1px solid #ffeeba; |
|
padding: 1.5rem; |
|
border-radius: 10px; |
|
margin-bottom: 2rem; |
|
color: #856404; |
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05); |
|
} |
|
/* This targets the specific disclaimer block by looking for its content */ |
|
.stAlert[data-testid="stAlert"] p strong:contains("Important Disclaimer") { |
|
color: #664d03; |
|
} |
|
/* General text improvements */ |
|
p { |
|
line-height: 1.7; |
|
margin-bottom: 1em; |
|
} |
|
ul, ol { |
|
margin-bottom: 1em; |
|
padding-left: 25px; |
|
} |
|
li { |
|
margin-bottom: 0.5em; |
|
} |
|
/* Increased font size for history expander titles */ |
|
.stExpander > div > div > .streamlit-expanderHeader { |
|
font-size: 1.2em; /* Increased font size */ |
|
} |
|
/* Media queries for specific mobile adjustments if needed (example) */ |
|
@media (max-width: 768px) { |
|
h1 { |
|
font-size: 2.2em; /* Slightly smaller H1 on mobile */ |
|
} |
|
h2 { |
|
font-size: 1.8em; /* Slightly smaller H2 on mobile */ |
|
} |
|
.stTabs [data-baseweb="tab"] { |
|
font-size: 1em; /* Smaller tab font size on mobile */ |
|
padding: 10px 15px; /* Reduced padding for tabs on mobile */ |
|
height: auto; /* Allow height to adjust */ |
|
min-width: unset; /* Remove min-width to allow more flexibility */ |
|
flex: 1 1 auto; /* Allow tabs to grow and shrink more flexibly */ |
|
} |
|
.stTabs [data-baseweb="tab-list"] { |
|
gap: 10px; /* Smaller gap between tabs on mobile */ |
|
} |
|
/* Make multiselect items slightly smaller on mobile for better fit */ |
|
.stMultiSelect div[data-baseweb="tag"] { |
|
font-size: 0.9em !important; |
|
padding: 3px 8px !important; |
|
} |
|
} |
|
</style>""", unsafe_allow_html=True) |
|
|
|
|
|
def get_groq_response(user_query, severity_label="Undetermined Severity", model="llama-3.3-70b-versatile"): |
|
""" |
|
Function to get a response from Groq API for health questions. |
|
Augments the system prompt with severity information if available. |
|
""" |
|
if not GROQ_AVAILABLE or groq_client is None: |
|
return "AI assistant is not available." |
|
|
|
|
|
|
|
system_prompt = ( |
|
"You are an experienced physician specialized in diagnosis and clinical decision-making. " |
|
"Your primary role is to analyze presented symptoms or health queries and provide a differential diagnosis along with evidence-based recommendations for next steps. " |
|
f"Based on initial analysis, the perceived severity of the user's condition is: {severity_label}. " |
|
"Adjust your tone and recommendations accordingly, emphasizing urgency if severity is 'High Severity'.\n\n" |
|
"When a user describes symptoms or asks a health question, you should:\n\n" |
|
"1. **Prioritized Differential Diagnosis**: Provide a prioritized list of possible diagnoses based on the information given, indicating their relative likelihood or confidence (e.g., 'most likely', 'possible', 'less likely').\n" |
|
"2. **Reasoning**: Briefly explain the clinical reasoning for each diagnosis, referencing common clinical features, pathophysiology, or typical presentations.\n" |
|
"3. **Recommended Investigations**: Suggest appropriate next diagnostic tests or investigations to confirm or rule out these conditions.\n\n" |
|
"4. **Initial Management/Treatment**: Propose evidence-based initial management or general treatment options (e.g., symptomatic relief, lifestyle changes, over-the-counter suggestions).\n" |
|
"5. **Red Flags/Urgency**: Clearly highlight any red flags or warning signs that would require immediate emergency care or urgent specialist referral.\n\n" |
|
"Always maintain a professional, empathetic, and medically accurate tone. Present information clearly, using bullet points or numbered lists where appropriate. " |
|
"Crucially, always conclude your response by strongly advising the user to consult a qualified healthcare professional for a definitive diagnosis and personalized treatment plan, " |
|
"as this AI is for informational purposes only and not a substitute for professional medical advice." |
|
) |
|
try: |
|
chat_completion = groq_client.chat.completions.create( |
|
messages=[ |
|
{"role": "system", "content": system_prompt}, |
|
{"role": "user", "content": user_query}, |
|
], |
|
model=model, |
|
temperature=0.7, |
|
max_tokens=800, |
|
top_p=1, |
|
stop=None, |
|
stream=False, |
|
) |
|
return chat_completion.choices[0].message.content |
|
except Exception as e: |
|
st.error(f"Groq API call failed: {e}") |
|
return "Sorry, I am unable to process that request at the moment due to an AI service error." |
|
|
|
|
|
def get_diagnosis_and_severity_from_model(symptoms_text): |
|
""" |
|
Function to get diagnosis predictions and infer severity from DATEXIS/CORe-clinical-diagnosis-prediction |
|
using Hugging Face Inference API. |
|
""" |
|
if not HF_MODEL_AVAILABLE or not HF_API_TOKEN: |
|
st.warning("Hugging Face API key not available. Cannot assess severity.") |
|
return "Severity Assessment Unavailable (API Key Missing)", [] |
|
API_URL = "https://api-inference.huggingface.co/models/DATEXIS/CORe-clinical-diagnosis-prediction" |
|
headers = {"Authorization": f"Bearer {HF_API_TOKEN}"} |
|
payload = {"inputs": symptoms_text} |
|
try: |
|
response = requests.post(API_URL, headers=headers, json=payload) |
|
response.raise_for_status() |
|
result = response.json() |
|
raw_predicted_diagnoses = [] |
|
threshold = 0.5 |
|
if result and isinstance(result, list) and len(result) > 0 and isinstance(result[0], list): |
|
for prediction_set in result: |
|
for item in prediction_set: |
|
if item['score'] >= threshold: |
|
raw_predicted_diagnoses.append(item['label']) |
|
|
|
filtered_diagnoses = [] |
|
|
|
generic_filter_keywords = [ |
|
'unspecified', 'acute', 'chronic', 'use', 'status', 'other', 'not elsewhere classified', |
|
'no diagnosis', 'history of', 'finding', 'problem', 'syndrome', 'disease', |
|
'disorder', 'condition', 'code', 'category', 'episode', 'complication', |
|
'sequelae', 'factor', 'manifestation', 'procedure', 'examination', 'observation', |
|
'symptoms', 'sign', 'unconfirmed', 'type', 'group', 'normal', 'unknown', 'level', |
|
'positive', 'negative', 'patient', 'value', 'test', 'result', 'diagnosis', |
|
'kidney', 'stage', 'without', 'essential', 'with', 'due to', 'related to', 'of', |
|
'organ', 'function', 'system', 'body', 'region', |
|
'clinical', 'consideration', 'presence', 'absence', 'mild', 'moderate', 'severe', |
|
'manifesting', 'affecting', 'affect', 'area', 'part', 'general', 'specific', |
|
'diagnosis of', 'history of', 'finding of', 'problem of', 'type of', 'group of', |
|
'unlikely', 'possible', 'likely', 'primary', 'secondary', 'and', 'or', 'by', 'for', 'in', 'on', |
|
'symptom', 'sign', 'pain', 'fever', 'cough', 'vomiting', 'nausea', 'rash', 'headache', 'fatigue', 'diarrhea', 'sore throat', 'hemorrhage', |
|
|
|
'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix', 'x', |
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', |
|
'longterm', 'shortterm', 'controlled', 'uncontrolled', 'recurrent', 'intermittent', 'persistent', |
|
'follow-up', 'observation', 'screening', 'encounter', 'admission', 'discharge', |
|
'acute on chronic', 'other specified', 'not otherwise specified' |
|
] |
|
|
|
for diagnosis_label in raw_predicted_diagnoses: |
|
lower_diag = diagnosis_label.lower().strip() |
|
is_generic = False |
|
|
|
|
|
for generic_kw in generic_filter_keywords: |
|
|
|
if re.fullmatch(r'\b' + re.escape(generic_kw) + r'\b', lower_diag): |
|
is_generic = True |
|
break |
|
if is_generic: |
|
continue |
|
|
|
|
|
if len(lower_diag) <= 2 and (lower_diag.replace('.', '').isdigit() or lower_diag.isalnum()): |
|
is_generic = True |
|
if is_generic: |
|
continue |
|
|
|
|
|
|
|
if re.fullmatch(r'[\d\.]+', lower_diag) and len(lower_diag) < 7: |
|
is_generic = True |
|
if is_generic: |
|
continue |
|
|
|
|
|
|
|
if len(lower_diag.split()) == 1 and lower_diag in ['pain', 'fever', 'cough', 'vomiting', 'nausea', 'rash', 'headache', 'fatigue', 'diarrhea', 'sore throat', 'hemorrhage']: |
|
is_generic = True |
|
if is_generic: |
|
continue |
|
|
|
|
|
filtered_diagnoses.append(diagnosis_label) |
|
|
|
filtered_diagnoses = list(dict.fromkeys(filtered_diagnoses)) |
|
if len(filtered_diagnoses) > 5: |
|
filtered_diagnoses = filtered_diagnoses[:5] |
|
|
|
if not filtered_diagnoses: |
|
return "Overall Symptom Severity (AI Assessment): Undetermined Severity", [] |
|
|
|
severity_map_keywords = { |
|
"High Severity": ['heart attack', 'stroke', 'failure', 'hemorrhage', 'cancer', 'acute respiratory', 'sepsis', 'cardiac arrest', 'severe', 'malignant', 'emergency', 'rupture', 'infarction', 'coma', 'shock', 'decompensation', 'crisis', 'perforation', 'ischemia', 'embolism', 'aneurysm', 'critical'], |
|
"Moderate Severity": ['hypertension', 'diabetes', 'pneumonia', 'infection', 'chronic', 'inflammation', 'moderate', 'insufficiency', 'fracture', 'ulcer', 'hepatitis', 'renal', 'vascular', 'disease', 'disorder', 'syndrome', 'acute', 'bronchitis', 'appendicitis', 'gallstones', 'pancreatitis', 'lupus', 'arthritis'], |
|
"Low Severity": ['headache', 'pain', 'mild', 'allergy', 'fever', 'cough', 'common cold', 'dermatitis', 'arthritis', 'influenza', 'viral', 'sprain', 'strain', 'gastritis', 'sore throat', 'conjunctivitis', 'sinusitis', 'bruise', 'rash', 'minor'] |
|
} |
|
|
|
overall_severity = "Low Severity" |
|
for diagnosis_label in filtered_diagnoses: |
|
diag_lower = diagnosis_label.lower() |
|
if any(keyword in diag_lower for keyword in severity_map_keywords["High Severity"]): |
|
return "Overall Symptom Severity (AI Assessment): High Severity", filtered_diagnoses |
|
if any(keyword in diag_lower for keyword in severity_map_keywords["Moderate Severity"]): |
|
if overall_severity == "Low Severity": |
|
overall_severity = "Moderate Severity" |
|
|
|
|
|
return f"Overall Symptom Severity (AI Assessment): {overall_severity}", filtered_diagnoses |
|
except requests.exceptions.RequestException as e: |
|
st.error(f"Network or API Error during clinical diagnosis model call: {e}. Check API key and internet connection.") |
|
return "Severity Assessment Unavailable (Network Error)", [] |
|
except Exception as e: |
|
st.error(f"Error processing clinical diagnosis model response: {e}. Unexpected data format or mapping issue.") |
|
return "Severity Assessment Unavailable (Processing Error)", [] |
|
|
|
|
|
def generate_pdf_report(history_data): |
|
buffer = io.BytesIO() |
|
c = canvas.Canvas(buffer, pagesize=letter) |
|
width, height = letter |
|
c.setFont("Helvetica-Bold", 16) |
|
c.drawString(50, height - 50, "MediBot Health Report") |
|
c.setFont("Helvetica", 10) |
|
c.drawString(50, height - 70, f"Report Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") |
|
y = height - 100 |
|
for entry in history_data: |
|
|
|
timestamp, symptoms_text, predicted_diseases = entry[3], entry[1], entry[2] |
|
if y < 100: |
|
c.showPage() |
|
c.setFont("Helvetica-Bold", 16) |
|
c.drawString(50, height - 50, "MediBot Health Report (Continued)") |
|
c.setFont("Helvetica", 10) |
|
y = height - 100 |
|
|
|
c.setFont("Helvetica-Bold", 10) |
|
c.drawString(50, y, f"Timestamp: {timestamp.strftime('%Y-%m-%d %H:%M:%S')}") |
|
y -= 15 |
|
c.setFont("Helvetica", 10) |
|
symptoms_line = f"Symptoms/Question: {symptoms_text}" |
|
insight_line = f"Insight/Response: {predicted_diseases}" |
|
|
|
textobject = c.beginText(50, y) |
|
textobject.setFont("Helvetica", 10) |
|
|
|
|
|
def draw_wrapped_text(text_obj, text, max_width): |
|
nonlocal y |
|
words = text.split(' ') |
|
current_line_words = [] |
|
for word_idx, word in enumerate(words): |
|
temp_line = ' '.join(current_line_words + [word]) |
|
if c.stringWidth(temp_line, "Helvetica", 10) < max_width: |
|
current_line_words.append(word) |
|
else: |
|
text_obj.textLine(' '.join(current_line_words)) |
|
y -= 12 |
|
current_line_words = [word] |
|
|
|
if c.stringWidth(word, "Helvetica", 10) >= max_width: |
|
|
|
chunk_size = 50 |
|
for i in range(0, len(word), chunk_size): |
|
text_obj.textLine(word[i:i + chunk_size]) |
|
y -= 12 |
|
current_line_words = [] |
|
continue |
|
if current_line_words: |
|
text_obj.textLine(' '.join(current_line_words)) |
|
y -= 12 |
|
|
|
draw_wrapped_text(textobject, symptoms_line, width - 100) |
|
draw_wrapped_text(textobject, insight_line, width - 100) |
|
c.drawText(textobject) |
|
y -= 20 |
|
c.save() |
|
buffer.seek(0) |
|
return buffer |
|
|
|
|
|
def get_disease_info_from_csv(selected_symptoms: list, disease_symptoms_map, disease_description_map, disease_precaution_map, symptom_severity_map) -> list[tuple[str, str, list[str]]]: |
|
""" |
|
Finds potential diseases, descriptions, and precautions based on selected symptoms |
|
using the hardcoded CSV data, incorporating symptom severity. |
|
Returns a list of tuples: (disease_name, description, [precautions]) |
|
""" |
|
matching_diseases_with_scores = {} |
|
|
|
normalized_selected_symptoms = [s.strip().lower() for s in selected_symptoms] |
|
|
|
for disease, symptoms_list in disease_symptoms_map.items(): |
|
|
|
normalized_disease_symptoms = [s.lower() for s in symptoms_list] |
|
weighted_score = 0 |
|
for selected_symptom in normalized_selected_symptoms: |
|
if selected_symptom in normalized_disease_symptoms: |
|
|
|
|
|
weight = symptom_severity_map.get(selected_symptom, 1) |
|
weighted_score += weight |
|
if weighted_score > 0: |
|
matching_diseases_with_scores[disease] = weighted_score |
|
|
|
|
|
sorted_matching_diseases = sorted(matching_diseases_with_scores.items(), key=lambda item: item[1], reverse=True) |
|
|
|
results = [] |
|
|
|
for disease_name, _ in sorted_matching_diseases[:5]: |
|
description = disease_description_map.get(disease_name, "No description available.") |
|
precautions = disease_precaution_map.get(disease_name, ["No precautions available."]) |
|
results.append((disease_name, description, precautions)) |
|
return results |
|
|
|
|
|
def parse_insight_text_for_conditions(insight_text: str) -> list[str]: |
|
""" |
|
Parses the combined insight text from history to extract condition names. |
|
Expected format: "Severity. Dataset Conditions: Cond1, Cond2; AI Suggestions: AICond1, AIC2" |
|
or just "Severity. No specific insights found." |
|
""" |
|
conditions = [] |
|
|
|
dataset_match = re.search(r"Dataset Conditions:\s*([^;]+)", insight_text) |
|
ai_match = re.search(r"AI Suggestions:\s*(.+)", insight_text) |
|
|
|
if dataset_match: |
|
dataset_str = dataset_match.group(1).strip() |
|
if dataset_str and dataset_str.lower() != "no specific insights found": |
|
conditions.extend([c.strip() for c in dataset_str.split(',') if c.strip()]) |
|
|
|
if ai_match: |
|
ai_str = ai_match.group(1).strip() |
|
if ai_str and ai_str.lower() != "no specific insights found" and ai_str.lower() != "ai assistant is not available.": |
|
conditions.extend([c.strip() for c in ai_str.split(',') if c.strip()]) |
|
|
|
|
|
return list(set(conditions)) |
|
|
|
|
|
|
|
if "feedback_input" not in st.session_state: |
|
st.session_state.feedback_input = "" |
|
|
|
if st.session_state.show_welcome: |
|
|
|
st.markdown("<h1 style='color: #0056b3; font-size: 3.5em; margin-top: 50px; text-shadow: 2px 2px 4px rgba(0,0,0,0.1);'>MediBot - Your Personal Health Assistant 🏥</h1>", unsafe_allow_html=True) |
|
st.markdown(""" |
|
<div style='background-color: #e6f7ff; padding: 30px; border-radius: 15px; text-align: center; margin-top: 30px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);'> |
|
<p style='font-size: 1.2em; line-height: 1.8; color: #336699;'> |
|
Explore possible causes and precautions for your symptoms and get answers to health-related questions using advanced AI. |
|
Your health journey, simplified and supported. |
|
</p> |
|
<ul style='list-style-type: none; padding: 0; margin-top: 25px; display: inline-block; text-align: left;'> |
|
<li style='font-size: 1.1em; margin-bottom: 10px; color: #2a527a;'>✅ <b>Symptom Checker:</b> Get insights from a comprehensive symptom list.</li> |
|
<li style='font-size: 1.1em; margin-bottom: 10px; color: #2a527a;'>💬 <b>AI Chatbot:</b> Ask general health questions.</li> |
|
<li style='font-size: 1.1em; margin-bottom: 10px; color: #2a527a;'>👤 <b>User Management:</b> Save your health history.</li> |
|
<li style='font-size: 1.1em; margin-bottom: 10px; color: #2a527a;'>📜 <b>Persistent History:</b> View and download past records.</li> |
|
<li style='font-size: 1.1em; margin-bottom: 10px; color: #2a527a;'>📈 <b>Usage Insights:</b> See trends in conditions.</li> |
|
</ul> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
st.markdown("<div style='text-align: center; margin-top: 40px;'>", unsafe_allow_html=True) |
|
if st.button("Get Started", key="welcome_button"): |
|
st.session_state.show_welcome = False |
|
st.rerun() |
|
st.markdown("</div>", unsafe_allow_html=True) |
|
st.markdown(""" |
|
<div style='background-color: #fff8e1; padding: 20px; border-radius: 10px; margin-top: 50px; text-align: center; color: #8a6d3b; box-shadow: 0 4px 10px rgba(0,0,0,0.05);'> |
|
<p style='font-size: 0.9em; margin-bottom: 0;'> |
|
<b>Important Disclaimer:</b> This app provides preliminary health information based on symptoms and AI analysis. |
|
<b>It is not a substitute for professional medical advice, diagnosis, or treatment.</b> Always consult a qualified healthcare provider for a definitive diagnosis and personalized treatment plan, as this AI is for informational purposes only and not a substitute for professional medical advice. |
|
</p> |
|
</div> |
|
""", unsafe_allow_html=True) |
|
else: |
|
|
|
st.markdown("<div class='stContainer' style='background-color: #fff8e1; border: 1px solid #ffeeba;'>" |
|
"<p style='margin-bottom: 0; font-size: 0.95em; color: #856404;'>" |
|
"<strong>Disclaimer</strong>: This app provides preliminary health information based on symptoms and AI analysis. " |
|
"It is not a substitute for professional medical advice, diagnosis, or treatment. Always consult a qualified healthcare provider." |
|
"</p>" |
|
"</div>", unsafe_allow_html=True) |
|
|
|
|
|
with st.sidebar: |
|
st.header("User Management") |
|
|
|
render_user_management_sidebar(st.session_state) |
|
|
|
st.markdown("---") |
|
st.header("Navigation") |
|
|
|
if st.button("Clear Chat History", help="Clears all messages from the current session."): |
|
st.session_state.chat_history = [] |
|
st.session_state.last_chat_response = "" |
|
st.session_state.chat_input_value = "" |
|
st.success("Chat history cleared!") |
|
|
|
|
|
if st.session_state.get("user_id"): |
|
user_history = get_user_history(st.session_state["user_id"]) |
|
if user_history: |
|
pdf_buffer = generate_pdf_report(user_history) |
|
st.download_button( |
|
label="Download Health Report (PDF)", |
|
data=pdf_buffer, |
|
file_name="medibot_health_report.pdf", |
|
mime="application/pdf", |
|
help="Download your chat and symptom checker history as a PDF report." |
|
) |
|
else: |
|
st.info("No history to download yet. Interact with MediBot to generate a report.") |
|
else: |
|
st.info("Log in to download your health report.") |
|
|
|
st.markdown("<h1 style='text-align: center; color: #0056b3;'>MediBot - Your Health Assistant 🩺</h1>", unsafe_allow_html=True) |
|
|
|
|
|
main_tab_home, main_tab_history, main_tab_insights, main_tab_feedback, main_tab_about = st.tabs(["Home", "History", "Insights", "Feedback", "About"]) |
|
|
|
with main_tab_home: |
|
st.markdown("<h2>How can I help you today?</h2>", unsafe_allow_html=True) |
|
|
|
tab_symptom_checker, tab_chatbot = st.tabs(["Symptom Checker", "Chat with MediBot"]) |
|
|
|
with tab_symptom_checker: |
|
st.markdown("<h3>Select from common symptoms to get insights:</h3>", unsafe_allow_html=True) |
|
selected_symptoms = st.multiselect( |
|
"Select your symptoms:", |
|
options=hardcoded_symptoms, |
|
default=[], |
|
key="symptom_select", |
|
help="Choose one or more symptoms you are experiencing." |
|
) |
|
|
|
if st.button("Get Health Insight", key="diagnose_button"): |
|
if not selected_symptoms: |
|
st.error("Please select at least one symptom.") |
|
else: |
|
with st.spinner("Analyzing symptoms..."): |
|
|
|
combined_symptoms_text = ", ".join(selected_symptoms) |
|
severity_assessment_text, predicted_diagnoses_from_model = get_diagnosis_and_severity_from_model(combined_symptoms_text) |
|
|
|
|
|
diseases_from_csv = get_disease_info_from_csv(selected_symptoms, disease_symptoms_map, disease_description_map, disease_precaution_map, symptom_severity_map) |
|
|
|
st.markdown(f"**{severity_assessment_text}**", unsafe_allow_html=True) |
|
st.markdown("<h2>Possible Conditions and Insights:</h2>", unsafe_allow_html=True) |
|
|
|
|
|
if diseases_from_csv: |
|
st.markdown("<h4>From our Medical Knowledge Base:</h4>", unsafe_allow_html=True) |
|
for disease_name, description, precautions_list in diseases_from_csv: |
|
with st.expander(disease_name): |
|
st.write(f"**Description**: {description}") |
|
st.write("**Precautions**:") |
|
if precautions_list: |
|
for precaution in precautions_list: |
|
if precaution: |
|
st.write(f"- {precaution}") |
|
else: |
|
st.write("- No specific precautions listed.") |
|
st.markdown("---") |
|
else: |
|
st.info("No common conditions found for these symptoms in our dataset. Please try adding more specific symptoms or switch to 'Chat with MediBot' for a general query.") |
|
|
|
|
|
additional_ai_diagnoses = [] |
|
|
|
csv_disease_names_normalized = {d[0].lower() for d in diseases_from_csv} |
|
|
|
for model_diag in predicted_diagnoses_from_model: |
|
model_diag_normalized = model_diag.lower() |
|
is_covered_by_csv = False |
|
|
|
for csv_diag_name in csv_disease_names_normalized: |
|
|
|
if fuzz.token_set_ratio(model_diag_normalized, csv_diag_name) > 85: |
|
is_covered_by_csv = True |
|
break |
|
if not is_covered_by_csv: |
|
additional_ai_diagnoses.append(model_diag) |
|
|
|
if additional_ai_diagnoses: |
|
st.markdown("<h4>Additional AI-Suggested Clinical Considerations:</h4>", unsafe_allow_html=True) |
|
st.write("The AI model suggests the following clinical terms based on your symptoms:") |
|
for diag in additional_ai_diagnoses: |
|
st.write(f"- **{diag}**: This is a medical term that the AI found relevant. Please consult a healthcare professional for more details.") |
|
st.info("These are general medical terms and require professional interpretation. They may not have specific descriptions or precautions available in our dataset.") |
|
elif not diseases_from_csv: |
|
st.info("The AI model could not confidently predict specific or unique diagnoses from the provided symptoms. Try different symptoms or consult a doctor.") |
|
|
|
st.write("---") |
|
|
|
|
|
history_content_parts = [] |
|
if diseases_from_csv: |
|
history_content_parts.append("Dataset Conditions: " + ", ".join([d[0] for d in diseases_from_csv])) |
|
if additional_ai_diagnoses: |
|
history_content_parts.append("AI Suggestions: " + ", ".join(additional_ai_diagnoses)) |
|
|
|
|
|
predicted_diseases_str_for_history = f"{severity_assessment_text.replace('Overall Symptom Severity (AI Assessment): ', '')}. " + "; ".join(history_content_parts) |
|
if not history_content_parts: |
|
predicted_diseases_str_for_history = f"{severity_assessment_text.replace('Overall Symptom Severity (AI Assessment): ', '')}. No specific insights found." |
|
|
|
if st.session_state.get("user_id"): |
|
|
|
save_history_to_db_if_logged_in(st.session_state.user_id, ", ".join(selected_symptoms), predicted_diseases_str_for_history) |
|
st.success("Analysis saved to your history.") |
|
else: |
|
st.info("Log in to save this analysis to your history.") |
|
|
|
with tab_chatbot: |
|
st.markdown("<h3>Describe your symptoms or ask a general health question:</h3>", unsafe_allow_html=True) |
|
|
|
with st.form("chat_form"): |
|
user_question = st.text_area( |
|
"Input your issue:", |
|
value=st.session_state.chat_input_value, |
|
key="chat_input_widget", |
|
placeholder="e.g., I am having a severe fever and body aches. Or, what are the causes of high blood pressure?", |
|
height=120 |
|
) |
|
chat_submit_button = st.form_submit_button("Ask MediBot") |
|
|
|
if chat_submit_button: |
|
if user_question: |
|
|
|
|
|
|
|
severity_for_groq_prompt = "Undetermined Severity" |
|
with st.spinner("MediBot is thinking..."): |
|
groq_answer = get_groq_response(user_question, severity_label=severity_for_groq_prompt) |
|
st.markdown("**MediBot's Answer:**") |
|
st.write(groq_answer) |
|
|
|
st.session_state.chat_history.append({"user": user_question, "bot": groq_answer}) |
|
|
|
|
|
if st.session_state.get("user_id"): |
|
|
|
save_history_to_db_if_logged_in(st.session_state.user_id, f"Question: {user_question}", groq_answer) |
|
st.success("Your question and answer have been saved to history.") |
|
else: |
|
st.info("Log in to save this Q&A to your history.") |
|
else: |
|
st.warning("Please type your question to get an answer.") |
|
|
|
with main_tab_history: |
|
st.header("Your Health History") |
|
user_id = st.session_state.get("user_id") |
|
if user_id: |
|
st.info("This section shows your saved symptom analyses and health questions.") |
|
history_data = get_user_history(user_id) |
|
if history_data: |
|
st.subheader("Past Interactions:") |
|
|
|
for entry in reversed(history_data): |
|
timestamp, symptoms_text, insight_text = entry[3], entry[1], entry[2] |
|
|
|
summary_title_prefix = "" |
|
expander_icon = "" |
|
if symptoms_text.startswith("Question: "): |
|
keyword = extract_keyword(symptoms_text, hardcoded_symptoms) |
|
summary_title_prefix = f"Question: {keyword}" if keyword != "Unknown" else "General Question" |
|
expander_icon = "💬" |
|
else: |
|
summary_conditions = parse_insight_text_for_conditions(insight_text) |
|
if summary_conditions: |
|
summary_title_prefix = ", ".join(summary_conditions[:3]) |
|
if len(summary_conditions) > 3: |
|
summary_title_prefix += "..." |
|
else: |
|
|
|
input_symptoms_match = re.search(r"Symptoms/Question: (.*)", symptoms_text) |
|
if input_symptoms_match: |
|
extracted_input_symptoms = input_symptoms_match.group(1).strip() |
|
|
|
clean_symptoms = [s.strip() for s in extracted_input_symptoms.split(',') if s.strip()] |
|
if clean_symptoms: |
|
summary_title_prefix = ", ".join(clean_symptoms[:3]) |
|
if len(clean_symptoms) > 3: |
|
summary_title_prefix += "..." |
|
else: |
|
summary_title_prefix = "No conditions identified" |
|
else: |
|
summary_title_prefix = "No conditions identified" |
|
expander_icon = "🩺" |
|
|
|
|
|
expander_label = f"### **{timestamp.strftime('%Y-%m-%d %H:%M')}** - {expander_icon} *{summary_title_prefix}*" |
|
with st.expander(expander_label): |
|
st.write(f"**Your Input:** {symptoms_text}") |
|
st.write(f"**MediBot's Insight:** {insight_text}") |
|
|
|
|
|
pdf_buffer = generate_pdf_report(history_data) |
|
st.download_button( |
|
label="Download History as PDF", |
|
data=pdf_buffer, |
|
file_name="MediBot_Health_Report.pdf", |
|
mime="application/pdf" |
|
) |
|
else: |
|
st.info("No history found for your account. Start interacting with MediBot!") |
|
else: |
|
st.warning("Please log in to view your health history.") |
|
|
|
with main_tab_insights: |
|
st.header("Your Health Insights") |
|
user_id = st.session_state.get("user_id") |
|
if user_id: |
|
st.info("This section provides insights into the conditions identified in your past interactions.") |
|
history_data = get_user_history(user_id) |
|
if history_data: |
|
|
|
thirty_days_ago = datetime.now() - timedelta(days=30) |
|
recent_history = [ |
|
entry for entry in history_data |
|
if entry[3] and entry[3] > thirty_days_ago |
|
] |
|
if recent_history: |
|
all_conditions = [] |
|
for entry in recent_history: |
|
|
|
conditions_from_entry = parse_insight_text_for_conditions(entry[2]) |
|
all_conditions.extend(conditions_from_entry) |
|
|
|
if all_conditions: |
|
|
|
condition_counts = pd.Series(all_conditions).value_counts().reset_index() |
|
condition_counts.columns = ['Condition', 'Count'] |
|
st.subheader("Most Frequent Conditions in Last 30 Days:") |
|
|
|
st.bar_chart(condition_counts, x='Condition', y='Count', use_container_width=True) |
|
st.write("This chart shows how many times each condition (from both dataset and AI suggestions) appeared in your analyses over the past 30 days.") |
|
else: |
|
st.info("No specific conditions were identified in your recent history (last 30 days).") |
|
else: |
|
st.info("You have no health interactions in the last 30 days to generate insights.") |
|
else: |
|
st.info("No history found for your account. Start interacting with MediBot to see insights!") |
|
else: |
|
st.warning("Please log in to view your health insights.") |
|
|
|
with main_tab_feedback: |
|
st.header("Share Your Feedback") |
|
st.write("We appreciate your feedback to improve MediBot.") |
|
with st.form("feedback_form", clear_on_submit=True): |
|
feedback_text = st.text_area("Your feedback:", height=150, key="feedback_text_area") |
|
rating = st.slider("Rate your experience (1-5 stars):", 1, 5, 3, key="feedback_rating") |
|
feedback_submit_button = st.form_submit_button("Submit Feedback") |
|
|
|
if feedback_submit_button: |
|
if feedback_text: |
|
|
|
st.session_state.feedback.append({"text": feedback_text, "rating": rating, "timestamp": datetime.now()}) |
|
st.success("Thank you for your feedback! It has been submitted.") |
|
st.session_state.feedback_input = "" |
|
else: |
|
st.warning("Please enter your feedback before submitting.") |
|
|
|
with main_tab_about: |
|
st.header("About MediBot") |
|
st.write(""" |
|
MediBot is a health assistant designed to provide general information based on your symptoms and answer health-related questions. |
|
It utilizes various AI models and medical datasets to offer insights. |
|
|
|
**Technology Stack:** |
|
- **Streamlit**: For the interactive web application interface. |
|
- **Groq API**: Powers the conversational AI for health questions using high-performance language models (e.g., Llama-3), enabling rapid and relevant responses. |
|
- **Hugging Face Inference API**: Integrates with specialized medical text classification models, specifically `DATEXIS/CORe-clinical-diagnosis-prediction`, to predict possible clinical diagnoses (often mapping to ICD-9 codes) from symptom inputs. |
|
- **Pandas**: For efficient data handling and processing of local CSV medical datasets (symptoms, descriptions, precautions). |
|
- **ReportLab**: For generating downloadable PDF reports of your personalized health history. |
|
- **Database**: PostgreSQL is used for efficient handling of user's data, interaction history and diagnosis record keeping. |
|
|
|
|
|
**Datasets Used:** |
|
- Symptom-to-Disease mapping, Descriptions, and Precautions sourced from: |
|
"Disease Symptom Prediction" dataset available on Kaggle. |
|
[https://www.kaggle.com/datasets/itachi9604/disease-symptom-description-dataset](https://www.kaggle.com/datasets/itachi9604/disease-symptom-description-dataset) |
|
*Credit to Itachi9604 for compiling this valuable dataset.* |
|
|
|
**Important Disclaimer**: This application is for informational purposes only and should not be used as a substitute for professional medical advice, diagnosis, or treatment. Always consult with a qualified healthcare provider for any health concerns or before making any decisions related to your health. |
|
""") |
|
st.markdown("[Learn more about Streamlit](https://streamlit.io/)") |
|
st.markdown("[Learn more about Groq](https://groq.com/)") |
|
st.markdown("[Learn more about Hugging Face](https://huggingface.co/)") |
|
|
|
st.markdown("---") |
|
st.markdown("For professional medical advice, always consult a qualified healthcare provider.") |
|
|