| """
|
| app.py
|
| ======
|
| Mental Health AI โ Full Pipeline
|
| Files needed:
|
| mental_xlmr_final/ โ XLM-R model folder
|
| mental_model.h5 โ Survey Keras model
|
| scaler.pkl โ Survey scaler
|
| recommendations.py โ same directory
|
|
|
| Install:
|
| pip install streamlit transformers torch tensorflow scikit-learn deep-translator
|
| """
|
| import sys, os
|
| sys.path.append(os.path.dirname(__file__))
|
| import re, pickle, warnings
|
| import numpy as np
|
| import streamlit as st
|
| import torch
|
| from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
| from deep_translator import GoogleTranslator
|
| sys.path.insert(0, os.path.dirname(__file__))
|
| from recommendations import get_recommendations
|
|
|
| warnings.filterwarnings("ignore")
|
|
|
|
|
| st.set_page_config(page_title="Mental Health AI", page_icon="๐ง ", layout="wide")
|
|
|
| st.markdown("""
|
| <style>
|
| .stApp { background: linear-gradient(135deg, #0d1117, #161b22, #0d1117); color: #e6edf3; }
|
| h1 { text-align: center; color: #58a6ff; font-size: 36px; margin-bottom: 4px; }
|
| h2, h3 { color: #c9d1d9; }
|
| .section-card {
|
| background: rgba(22,27,34,0.9);
|
| border: 1px solid #30363d;
|
| border-radius: 14px;
|
| padding: 22px 26px;
|
| margin-bottom: 18px;
|
| }
|
| .result-card {
|
| background: #161b22;
|
| border: 1px solid #30363d;
|
| border-radius: 12px;
|
| padding: 20px;
|
| text-align: center;
|
| margin-bottom: 8px;
|
| }
|
| .result-card.primary { border: 2px solid #58a6ff; }
|
| .result-label { font-size: 15px; color: #8b949e; margin-bottom: 6px; }
|
| .result-value { font-size: 44px; font-weight: 700; }
|
| .severity-badge {
|
| display: inline-block;
|
| padding: 3px 12px;
|
| border-radius: 20px;
|
| font-size: 12px;
|
| font-weight: 600;
|
| margin-top: 6px;
|
| }
|
| .rec-block {
|
| background: #161b22;
|
| border: 1px solid #30363d;
|
| border-radius: 12px;
|
| padding: 18px 22px;
|
| margin-bottom: 14px;
|
| }
|
| .rec-title { font-size: 15px; font-weight: 700; margin-bottom: 10px; }
|
| .rec-item { font-size: 14px; color: #c9d1d9; padding: 4px 0; border-bottom: 1px solid #21262d; }
|
| .rec-item:last-child { border-bottom: none; }
|
| .ar-text { font-size: 13px; color: #8b949e; margin-top: 3px; direction: rtl; }
|
| .referral-box {
|
| background: rgba(248,81,73,0.1);
|
| border: 1px solid rgba(248,81,73,0.4);
|
| border-radius: 10px;
|
| padding: 14px 18px;
|
| margin-top: 12px;
|
| }
|
| .crisis-box {
|
| background: rgba(248,81,73,0.2);
|
| border: 2px solid #f85149;
|
| border-radius: 12px;
|
| padding: 20px 24px;
|
| margin: 16px 0;
|
| }
|
| div.stButton > button {
|
| background: linear-gradient(90deg, #1f6feb, #58a6ff);
|
| color: white; font-size: 17px; font-weight: 700;
|
| border-radius: 10px; height: 52px; width: 100%; border: none;
|
| }
|
| div.stSlider > label { color: #c9d1d9 !important; font-size: 13px; }
|
| .stTextArea textarea {
|
| background: #0d1117 !important;
|
| color: #e6edf3 !important;
|
| border: 1px solid #30363d !important;
|
| border-radius: 8px !important;
|
| }
|
| </style>
|
| """, unsafe_allow_html=True)
|
|
|
|
|
| CLASSES = ["anxiety", "depression", "stress"]
|
| ARABIC_LABELS = {"anxiety": "ุงูููู", "depression": "ุงูุงูุชุฆุงุจ", "stress": "ุงูุถุบุท ุงูููุณู"}
|
| COLORS = {"anxiety": "#ffa657", "depression": "#79c0ff", "stress": "#56d364"}
|
| SEVERITY_AR = {
|
| "normal": "ุทุจูุนู", "mild": "ุฎููู", "moderate": "ู
ุชูุณุท",
|
| "severe": "ุดุฏูุฏ", "extremely_severe": "ุดุฏูุฏ ุฌุฏุงู", "crisis": "ุฃุฒู
ุฉ",
|
| }
|
| SEVERITY_COLORS = {
|
| "normal": "#56d364", "mild": "#e3b341", "moderate": "#ffa657",
|
| "severe": "#f85149", "extremely_severe": "#ff0000", "crisis": "#ff0000",
|
| }
|
|
|
| CAUSE_AR = {
|
| "work": "ุถุบุท ุงูุนู
ู", "relationships": "ุงูุนูุงูุงุช", "financial": "ุงูุถุบุท ุงูู
ุงูู",
|
| "academic": "ุงูุถุบุท ุงูุฃูุงุฏูู
ู", "health": "ุงูู
ุฎุงูู ุงูุตุญูุฉ", "social": "ุงูููู ุงูุงุฌุชู
ุงุนู",
|
| "self_worth": "ุงูุซูุฉ ุจุงูููุณ", "trauma": "ุงูุตุฏู
ุฉ ุงูููุณูุฉ", "general": "ุนุงู
",
|
| }
|
|
|
|
|
| @st.cache_resource
|
| def load_xlmr():
|
| token = st.secrets["HF_TOKEN"]
|
| xlmr_tokenizer = AutoTokenizer.from_pretrained(
|
| "tasneem33355/mental-xlmr", token=token
|
| )
|
| model = AutoModelForSequenceClassification.from_pretrained(
|
| "tasneem33355/mental-xlmr", token=token
|
| )
|
| model.eval()
|
|
|
|
|
| classes = ["anxiety", "depression", "stress"]
|
| return xlmr_tokenizer, model, classes
|
|
|
| @st.cache_resource
|
| def load_survey():
|
| scaler = pickle.load(open(os.path.join(os.path.dirname(__file__), "scaler.pkl"), "rb"))
|
| weights = pickle.load(open(os.path.join(os.path.dirname(__file__), "model_weights.pkl"), "rb"))
|
|
|
| def predict(x):
|
| for w in weights:
|
| if len(w) == 2:
|
| x = np.dot(x, w[0]) + w[1]
|
| x = np.maximum(0, x)
|
| x = np.exp(x) / np.sum(np.exp(x))
|
| return x
|
|
|
| return scaler, predict
|
|
|
| xlmr_tokenizer, xlmr_model, le = load_xlmr()
|
| scaler, survey_predict = load_survey()
|
|
|
|
|
| def clean_text(text):
|
| text = re.sub(r'(.)\1{2,}', r'\1\1', text)
|
| text = re.sub(r'[^\w\s\u0600-\u06FF\[\]]', ' ', text)
|
| return re.sub(r'\s+', ' ', text).strip()
|
|
|
| def translate_to_en(text):
|
| try:
|
| return GoogleTranslator(source="auto", target="en").translate(text)
|
| except Exception:
|
| return ""
|
|
|
|
|
| DEPRESSION_KEYWORDS = [
|
|
|
| "ุงูุชุฆุงุจ", "ู
ูุชุฆุจ", "ู
ูุชุฆุจุฉ", "ุญุฒู", "ุญุฒูู", "ุญุฒููุฉ", "ูุฃุณ", "ูุงุฆุณ", "ูุงุฆุณุฉ",
|
| "ูุฑุงุบ", "ุฅุญุณุงุณ ุจุงููุฑุงุบ", "ุจูุง ู
ุนูู", "ูุง ู
ุนูู", "ู
ุงููุงุด ู
ุนูู", "ุจูุง ูุฏู",
|
| "ูุง ุฃู
ู", "ู
ููุด ุฃู
ู", "ุชุนุจุช ู
ู ุงูุญูุงุฉ", "ุฒููุช ู
ู ุงูุญูุงุฉ",
|
| "ู
ุด ูุงูู ู
ุนูู", "ู
ุด ูุงููุฉ ู
ุนูู", "ุญุงุณุณ ุจุงููุฑุงุบ", "ุญุงุณุฉ ุจุงููุฑุงุบ",
|
| "ู
ููุด ุทุงูุฉ", "ู
ููุด ุฑุบุจุฉ", "ุจูุงุก", "ุนุงูุฒ ุฃุจูู", "ุนุงูุฒุฉ ุฃุจูู",
|
| "ูุญูุฏ", "ูุญูุฏุฉ", "ุนุฒูุฉ", "ู
ูุนุฒู", "ู
ูุนุฒูุฉ",
|
| "ุฅุฑูุงู ููุณู", "ุฅุฑูุงู ุนุงุทูู", "ู
ุด ุญุงุณุณ ุจุญุงุฌุฉ", "ู
ุด ุญุงุณุฉ ุจุญุงุฌุฉ",
|
|
|
| "ุฒููุช", "ุชุนุจุช", "ู
ุด ุทุงูู", "ู
ุด ุทุงููุฉ", "ููุณูุชู ูุญุดุฉ", "ููุณูุชู ูู ุงูุฃุฑุถ",
|
| "ู
ุด ูุงุฏุฑ ุฃูู
ู", "ู
ุด ูุงุฏุฑุฉ ุฃูู
ู", "ู
ุด ุนุงูุด", "ู
ุด ูุงุฏุฑ ุฃุนูุด",
|
| "ู
ุด ุนุงูุฒ ุฃุตุญู", "ู
ุด ุนุงูุฒุฉ ุฃุตุญู", "ุฏู
ูุน", "ุจุฏู
ุน", "ููุจู ุชููู",
|
| "ู
ุด ุญุงุณุณ ุจููุณู", "ู
ุด ุญุงุณุฉ ุจููุณู", "ู
ุง ุจุญุณ ุจุดู", "ู
ุง ูู ูุงูุฏุฉ",
|
| "ู
ุงูู ุงู
ู", "ู
ุง ูู ุงู
ู", "ุญูุงุชู ุฎุฑุจุช", "ุฎุณุฑุช ูู ุญุงุฌุฉ",
|
|
|
| "depressed", "depression", "hopeless", "hopelessness", "empty", "emptiness",
|
| "worthless", "meaningless", "no meaning", "no purpose", "cannot go on",
|
| "cant go on", "no energy", "no motivation", "crying", "feel nothing",
|
| "numb", "isolated", "lonely", "loneliness", "sad", "sadness",
|
| "despair", "grief", "miserable", "broken", "lost all hope",
|
| ]
|
|
|
| ANXIETY_KEYWORDS = [
|
|
|
| "ููู", "ูููุงู", "ูููุงูุฉ", "ุฎูู", "ุฎุงูู", "ุฎุงููุฉ", "ุชูุชุฑ", "ู
ุชูุชุฑ", "ู
ุชูุชุฑุฉ",
|
| "ููุน", "ู
ุด ู
ุฑุชุงุญ", "ู
ุด ู
ุฑุชุงุญุฉ", "ุฐุนุฑ", "ุฑูุงุจ", "ูุณูุงุณ",
|
|
|
| "panic", "anxious", "anxiety", "worried", "worry", "fear",
|
| "scared", "nervous", "restless", "tense", "phobia", "ocd",
|
| ]
|
|
|
| STRESS_KEYWORDS = [
|
|
|
| "ุถุบุท", "ุถุบูุท", "ู
ุถุบูุท", "ู
ุถุบูุทุฉ", "ุฅุฌูุงุฏ", "ู
ุฌูุฏ", "ู
ุฌูุฏุฉ",
|
|
|
| "overwhelmed", "stressed", "stress", "burnout", "exhausted", "overloaded",
|
| ]
|
|
|
| def keyword_boost(text: str, scores: dict) -> dict:
|
| """
|
| ูุนููุถ ุงูู stress bias ูู ุงูู
ูุฏูู ุนู ุทุฑูู override ููู
|
| ูู
ุง ุชููู ููู
ุงุช depression ุฃู anxiety ุฃู stress ูุงุถุญุฉ ูู ุงููุต.
|
| """
|
| text_lower = text.lower()
|
|
|
| dep_hits = sum(1 for kw in DEPRESSION_KEYWORDS if kw.lower() in text_lower)
|
| anx_hits = sum(1 for kw in ANXIETY_KEYWORDS if kw.lower() in text_lower)
|
| str_hits = sum(1 for kw in STRESS_KEYWORDS if kw.lower() in text_lower)
|
|
|
| if dep_hits == 0 and anx_hits == 0 and str_hits == 0:
|
| return scores
|
|
|
| s = dict(scores)
|
|
|
| if dep_hits > 0 and dep_hits >= anx_hits and dep_hits >= str_hits:
|
|
|
| boost = min(0.55 + dep_hits * 0.10, 0.85)
|
| s["depression"] = boost
|
| remaining = 1.0 - boost
|
| total_rest = s["anxiety"] + s["stress"]
|
| if total_rest > 0:
|
| s["anxiety"] = round(remaining * s["anxiety"] / total_rest, 4)
|
| s["stress"] = round(remaining * s["stress"] / total_rest, 4)
|
| s["depression"] = round(boost, 4)
|
|
|
| elif anx_hits > 0 and anx_hits >= dep_hits and anx_hits >= str_hits:
|
|
|
| boost = min(0.55 + anx_hits * 0.10, 0.85)
|
| s["anxiety"] = boost
|
| remaining = 1.0 - boost
|
| total_rest = s["depression"] + s["stress"]
|
| if total_rest > 0:
|
| s["depression"] = round(remaining * s["depression"] / total_rest, 4)
|
| s["stress"] = round(remaining * s["stress"] / total_rest, 4)
|
| s["anxiety"] = round(boost, 4)
|
|
|
| elif str_hits > 0 and str_hits >= dep_hits and str_hits >= anx_hits:
|
|
|
| boost = min(0.55 + str_hits * 0.10, 0.85)
|
| s["stress"] = boost
|
| remaining = 1.0 - boost
|
| total_rest = s["depression"] + s["anxiety"]
|
| if total_rest > 0:
|
| s["depression"] = round(remaining * s["depression"] / total_rest, 4)
|
| s["anxiety"] = round(remaining * s["anxiety"] / total_rest, 4)
|
| s["stress"] = round(boost, 4)
|
|
|
|
|
| total = sum(s.values())
|
| if total > 0:
|
| s = {k: round(v / total, 4) for k, v in s.items()}
|
|
|
| return s
|
|
|
|
|
| def predict_text(text: str) -> dict:
|
| cleaned = clean_text(text)
|
| text_en = translate_to_en(cleaned)
|
| combined = (text_en + " [SEP] " + cleaned) if text_en else cleaned
|
| inputs = xlmr_tokenizer(combined, return_tensors="pt",
|
| truncation=True, max_length=192, padding=True)
|
| with torch.no_grad():
|
| probs = torch.softmax(xlmr_model(**inputs).logits, dim=-1).squeeze().numpy()
|
| raw_scores = {c: round(float(p), 4) for c, p in zip(le, probs)}
|
|
|
| boosted = keyword_boost(text + " " + text_en, raw_scores)
|
| return boosted
|
|
|
| def predict_survey(answers: list) -> dict:
|
| data = scaler.transform(np.array(answers).reshape(1, -1))
|
| pred = survey_predict(data)[0]
|
| return {
|
| "depression": round(float(pred[0]), 4),
|
| "anxiety": round(float(pred[1]), 4),
|
| "stress": round(float(pred[2]), 4),
|
| }
|
|
|
| def fuse_scores(text_s, survey_s, w_text=0.4, w_survey=0.6):
|
| return {c: round(w_text * text_s[c] + w_survey * survey_s[c], 4) for c in CLASSES}
|
|
|
|
|
| SURVEY_Q = [
|
| ("I found it hard to wind down", "ูุฌุฏุช ุตุนูุจุฉ ูู ุงูุงุณุชุฑุฎุงุก"),
|
| ("I was aware of dryness of my mouth", "ูุงุญุธุช ุฌูุงูุงู ูู ูู
ู"),
|
| ("I couldn't seem to experience any positive feeling at all", "ูู
ุฃุณุชุทุน ุงูุดุนูุฑ ุจุฃู ู
ุดุงุนุฑ ุฅูุฌุงุจูุฉ"),
|
| ("I experienced breathing difficulty", "ุฃุญุณุณุช ุจุตุนูุจุฉ ูู ุงูุชููุณ"),
|
| ("I found it difficult to work up the initiative to do things", "ูุฌุฏุช ุตุนูุจุฉ ูู ุงุชุฎุงุฐ ุงูู
ุจุงุฏุฑุฉ ููููุงู
ุจุงูุฃุดูุงุก"),
|
| ("I tended to over-react to situations", "ููุช ุฃุจุงูุบ ูู ุฑุฏูุฏ ุฃูุนุงูู ุชุฌุงู ุงูู
ูุงูู"),
|
| ("I experienced trembling", "ุดุนุฑุช ุจุงูุฑุนุดุฉ"),
|
| ("I felt that I was using a lot of nervous energy", "ุดุนุฑุช ุฃููู ุฃุณุชููู ุงููุซูุฑ ู
ู ุงูุทุงูุฉ ุงูุนุตุจูุฉ"),
|
| ("I was worried about situations in which I might panic", "ููุช ูููุงู ู
ู ู
ูุงูู ูุฏ ุฃุตุงุจ ูููุง ุจุงูุฐุนุฑ"),
|
| ("I felt that I had nothing to look forward to", "ุดุนุฑุช ุฃูู ูุง ููุฌุฏ ุดูุก ุฃุชุทูุน ุฅููู"),
|
| ("I found myself getting agitated", "ูุฌุฏุช ููุณู ุฃุดุนุฑ ุจุงูุงููุนุงู"),
|
| ("I found it difficult to relax", "ูุฌุฏุช ุตุนูุจุฉ ูู ุงูุงุณุชุฑุฎุงุก"),
|
| ("I felt down-hearted and blue", "ุดุนุฑุช ุจุงูุฅุญุจุงุท ูุงููุขุจุฉ"),
|
| ("I was intolerant of anything that kept me from getting on", "ููุช ุบูุฑ ู
ุชุณุงู
ุญ ู
ุน ุฃู ุดูุก ูุนูููู"),
|
| ("I felt I was close to panic", "ุดุนุฑุช ุฃููู ุนูู ูุดู ุงูุฐุนุฑ"),
|
| ("I was unable to become enthusiastic", "ูู
ุฃุณุชุทุน ุฃู ุฃุชุญู
ุณ ูุฃู ุดูุก"),
|
| ("I felt I wasn't worth much as a person", "ุดุนุฑุช ุฃููู ูุณุช ุดุฎุตุงู ุฐุง ููู
ุฉ"),
|
| ("I felt that I was rather touchy", "ุดุนุฑุช ุฃููู ู
ุชููุจ ุงูู
ุฒุงุฌ"),
|
| ("I was aware of the action of my heart", "ููุช ูุงุนูุงู ููุจุถุงุช ููุจู"),
|
| ("I felt scared without any good reason", "ุดุนุฑุช ุจุงูุฎูู ุฏูู ุณุจุจ ูุงุถุญ"),
|
| ("I felt that life was meaningless", "ุดุนุฑุช ุฃู ุงูุญูุงุฉ ุจูุง ู
ุนูู"),
|
| ("I found it hard to calm down", "ูุฌุฏุช ุตุนูุจุฉ ูู ุงูุชูุฏุฆุฉ"),
|
| ("I felt nervous", "ุดุนุฑุช ุจุงูุชูุชุฑ"),
|
| ("I felt sad and depressed", "ุดุนุฑุช ุจุงูุญุฒู ูุงูุงูุชุฆุงุจ"),
|
| ("I found myself getting impatient", "ูุฌุฏุช ููุณู ุฃุดุนุฑ ุจููุงุฏ ุงูุตุจุฑ"),
|
| ("I felt that I was rather emotional", "ุดุนุฑุช ุฃููู ุนุงุทูู ุจุดูู ู
ูุฑุท"),
|
| ("I felt restless", "ุดุนุฑุช ุจุนุฏู
ุงููุฏูุก"),
|
| ("I had difficulty concentrating", "ูุฌุฏุช ุตุนูุจุฉ ูู ุงูุชุฑููุฒ"),
|
| ("I felt lonely", "ุดุนุฑุช ุจุงููุญุฏุฉ"),
|
| ("I found it difficult to relax", "ูุฌุฏุช ุตุนูุจุฉ ูู ุงูุงุณุชุฑุฎุงุก"),
|
| ("I felt hopeless", "ุดุนุฑุช ุจุงููุฃุณ"),
|
| ("I felt worried about many things", "ููุช ูููุงู ุจุดุฃู ุฃุดูุงุก ูุซูุฑุฉ"),
|
| ("I felt that I had no energy", "ุดุนุฑุช ุจุนุฏู
ูุฌูุฏ ุทุงูุฉ"),
|
| ("I felt tense", "ุดุนุฑุช ุจุงูุชูุชุฑ ูุงูุถูู"),
|
| ("I felt tired for no reason", "ุดุนุฑุช ุจุงูุชุนุจ ุฏูู ุณุจุจ"),
|
| ("I felt uneasy", "ุดุนุฑุช ุจุนุฏู
ุงูุงุฑุชูุงุญ"),
|
| ("I felt worthless", "ุดุนุฑุช ุจุฃููู ูุง ููู
ุฉ ูู"),
|
| ("I felt anxious", "ุดุนุฑุช ุจุงูููู"),
|
| ("I felt discouraged", "ุดุนุฑุช ุจุงูุฅุญุจุงุท"),
|
| ("I felt stressed", "ุดุนุฑุช ุจุงูุถุบุท"),
|
| ("I felt overwhelmed", "ุดุนุฑุช ุจุงูุฅุฑูุงู"),
|
| ("I felt emotionally exhausted", "ุดุนุฑุช ุจุงูุฅููุงู ุงูุนุงุทูู"),
|
| ]
|
|
|
|
|
| st.title("๐ง Mental Health AI")
|
| st.markdown(
|
| "<p style='text-align:center;color:#8b949e;font-size:15px;'>"
|
| "Write how you feel and answer the survey for a complete assessment"
|
| "<br><span dir='rtl'>ุงูุชุจ ู
ุง ุชุดุนุฑ ุจู ูุฃุฌุจ ุนูู ุงูุฃุณุฆูุฉ ููุญุตูู ุนูู ุชูููู
ุดุงู
ู</span></p>",
|
| unsafe_allow_html=True,
|
| )
|
| st.markdown("---")
|
|
|
|
|
| st.markdown("<div class='section-card'>", unsafe_allow_html=True)
|
| st.markdown("### ๐ฌ How are you feeling? / ููู ุชุดุนุฑุ")
|
| st.markdown(
|
| "<p style='color:#8b949e;font-size:13px;'>"
|
| "Write in any language โ Arabic (any dialect), English, or both<br>"
|
| "<span dir='rtl'>ุงูุชุจ ุจุฃู ูุบุฉ โ ุนุฑุจู (ุฃู ููุฌุฉ)ุ ุฅูุฌููุฒูุ ุฃู ุงูุงุชููู</span></p>",
|
| unsafe_allow_html=True,
|
| )
|
| user_text = st.text_area(
|
| label="",
|
| placeholder="e.g. I've been feeling very overwhelmed at work and can't sleep...\nู
ุซุงู: ุฃูุง ุชุนุจุงู ุฌุฏุงู ู
ู ุงูุดุบู ูู
ุด ูุงุฏุฑ ุฃูุงู
...",
|
| height=120,
|
| label_visibility="collapsed",
|
| )
|
| st.markdown("</div>", unsafe_allow_html=True)
|
|
|
|
|
| st.markdown("<div class='section-card'>", unsafe_allow_html=True)
|
| st.markdown("### ๐ DASS-42 Survey / ุงุณุชุจูุงู DASS-42")
|
| st.markdown(
|
| "<p style='color:#8b949e;font-size:13px;'>"
|
| "0 = Never | 1 = Sometimes | 2 = Often | "
|
| "3 = Most of the time | 4 = Always<br>"
|
| "<span dir='rtl'>0 = ูู
ูุญุฏุซ ุฃุจุฏุงู | 1 = ุฃุญูุงูุงู | 2 = ูุซูุฑุงู | 3 = ู
ุนุธู
ุงูููุช | 4 = ุฏุงุฆู
ุงู</span></p>",
|
| unsafe_allow_html=True,
|
| )
|
|
|
| survey_answers = []
|
| for i in range(0, len(SURVEY_Q), 2):
|
| cols = st.columns(2)
|
| for j, (en, ar) in enumerate(SURVEY_Q[i:i+2]):
|
| with cols[j]:
|
| val = st.slider(
|
| f"{i+j+1}. {en}\n{ar}",
|
| min_value=0, max_value=3, value=0,
|
| key=f"q_{i+j}",
|
| )
|
| survey_answers.append(val)
|
|
|
| st.markdown("</div>", unsafe_allow_html=True)
|
|
|
|
|
| _, col_btn, _ = st.columns([1, 2, 1])
|
| with col_btn:
|
| predict_btn = st.button("๐ Analyze / ุชุญููู")
|
|
|
|
|
| if predict_btn:
|
| if not user_text.strip():
|
| st.warning("Please write how you feel first. / ู
ู ูุถูู ุงูุชุจ ู
ุง ุชุดุนุฑ ุจู ุฃููุงู.")
|
| st.stop()
|
|
|
| with st.spinner("Analyzing... / ุฌุงุฑู ุงูุชุญููู..."):
|
| text_scores = predict_text(user_text)
|
| survey_scores = predict_survey(survey_answers)
|
| final_scores = fuse_scores(text_scores, survey_scores)
|
| primary = max(final_scores, key=final_scores.get)
|
| rec = get_recommendations(primary, final_scores[primary], user_text)
|
|
|
| st.markdown("---")
|
| st.markdown("## ๐ Results / ุงููุชุงุฆุฌ")
|
|
|
|
|
| cols = st.columns(3)
|
| for col, cls in zip(cols, CLASSES):
|
| pct = int(final_scores[cls] * 100)
|
| is_primary = cls == primary
|
| card_class = "result-card primary" if is_primary else "result-card"
|
| sev = rec["severity"] if is_primary else ""
|
| badge = ""
|
| if is_primary and sev:
|
| sev_color = SEVERITY_COLORS.get(sev, "#8b949e")
|
| badge = (f"<div class='severity-badge' style='background:{sev_color}20;"
|
| f"color:{sev_color};border:1px solid {sev_color};'>"
|
| f"{sev.replace('_',' ').title()} / {SEVERITY_AR.get(sev,'')}</div>")
|
| col.markdown(f"""
|
| <div class='{card_class}'>
|
| <div class='result-label'>{cls.title()} / {ARABIC_LABELS[cls]}</div>
|
| <div class='result-value' style='color:{COLORS[cls]}'>{pct}%</div>
|
| {badge}
|
| </div>""", unsafe_allow_html=True)
|
|
|
|
|
| if not rec["suicidal_flag"]:
|
| cause_label = CAUSE_AR.get(rec["cause"], rec["cause"])
|
| st.markdown(
|
| f"<p style='text-align:center;margin-top:10px;font-size:17px;color:#8b949e;'>"
|
| f"Primary: <strong style='color:{COLORS[primary]}'>{primary.title()} / {ARABIC_LABELS[primary]}</strong>"
|
| f" | Cause detected / ุงูุณุจุจ ุงูู
ูุชุดู: "
|
| f"<strong style='color:#e3b341'>{rec['cause'].replace('_',' ').title()} / {cause_label}</strong></p>",
|
| unsafe_allow_html=True,
|
| )
|
|
|
|
|
| if rec["suicidal_flag"]:
|
| st.markdown("""
|
| <div class='crisis-box'>
|
| <h3 style='color:#f85149;margin-top:0;'>๐จ Crisis Support Needed / ู
ุทููุจ ุฏุนู
ุฃุฒู
ุฉ</h3>
|
| </div>""", unsafe_allow_html=True)
|
|
|
|
|
| with st.expander("Show score breakdown / ุนุฑุถ ุชูุงุตูู ุงููุชุงุฆุฌ"):
|
| c1, c2 = st.columns(2)
|
| c1.markdown("**Text model / ู
ูุฏูู ุงููุต:**")
|
| for cls in CLASSES:
|
| c1.markdown(f"- {cls} / {ARABIC_LABELS[cls]}: **{int(text_scores[cls]*100)}%**")
|
| c2.markdown("**Survey model / ู
ูุฏูู ุงูุณูุฑูุงู:**")
|
| for cls in CLASSES:
|
| c2.markdown(f"- {cls} / {ARABIC_LABELS[cls]}: **{int(survey_scores[cls]*100)}%**")
|
|
|
|
|
| st.markdown("---")
|
| st.markdown("## ๐ก Recommendations / ุงูุชูุตูุงุช")
|
|
|
| col_tips, col_res = st.columns(2)
|
|
|
| with col_tips:
|
| st.markdown("<div class='rec-block'>", unsafe_allow_html=True)
|
| st.markdown("<div class='rec-title'>โ
Practical Tips / ูุตุงุฆุญ ุนู
ููุฉ</div>",
|
| unsafe_allow_html=True)
|
| tips_en = rec.get("tips_en", [])
|
| tips_ar = rec.get("tips_ar", [])
|
| for en, ar in zip(tips_en, tips_ar):
|
| st.markdown(
|
| f"<div class='rec-item'>{en}"
|
| f"<div class='ar-text' dir='rtl'>โข {ar}</div></div>",
|
| unsafe_allow_html=True,
|
| )
|
| st.markdown("</div>", unsafe_allow_html=True)
|
|
|
| with col_res:
|
| st.markdown("<div class='rec-block'>", unsafe_allow_html=True)
|
| st.markdown("<div class='rec-title'>๐ Resources / ู
ูุงุฑุฏ ู
ููุฏุฉ</div>",
|
| unsafe_allow_html=True)
|
| res_en = rec.get("resources_en", [])
|
| res_ar = rec.get("resources_ar", [])
|
| for en, ar in zip(res_en, res_ar):
|
| st.markdown(
|
| f"<div class='rec-item'>{en}"
|
| f"<div class='ar-text' dir='rtl'>โข {ar}</div></div>",
|
| unsafe_allow_html=True,
|
| )
|
| st.markdown("</div>", unsafe_allow_html=True)
|
|
|
|
|
| ref_en = rec.get("referral_en", "")
|
| ref_ar = rec.get("referral_ar", "")
|
| if ref_en:
|
| box_class = "crisis-box" if rec["suicidal_flag"] else "referral-box"
|
| st.markdown(
|
| f"<div class='{box_class}'>"
|
| f"<strong>๐ฅ When to seek help / ู
ุชู ุชุทูุจ ุงูู
ุณุงุนุฏุฉ:</strong><br>"
|
| f"{ref_en}<br>"
|
| f"<span dir='rtl' style='color:#f0a0a0;font-size:13px;'>{ref_ar}</span>"
|
| f"</div>",
|
| unsafe_allow_html=True,
|
| )
|
|
|
| st.markdown(
|
| "<p style='text-align:center;color:#484f58;font-size:12px;margin-top:20px;'>"
|
| "โ ๏ธ This system is for awareness only and is not a substitute for professional medical diagnosis.<br>"
|
| "ูุฐุง ุงููุธุงู
ููุชูุนูุฉ ููุท ูููุณ ุจุฏููุงู ุนู ุงูุชุดุฎูุต ุงูุทุจู ุงูู
ุชุฎุตุต.</p>",
|
| unsafe_allow_html=True,
|
| )
|
|
|