Spaces:
Sleeping
Sleeping
| """ | |
| 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") | |
| # โโ PAGE CONFIG โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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) | |
| # โโ CONSTANTS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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": "ุนุงู ", | |
| } | |
| # โโ LOAD MODELS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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() | |
| # Hardcoded โ LabelEncoder ุนูู ['anxiety','depression','stress'] ุฏุงูู ุงู ุจุชุฑุชูุจ alphabetically | |
| # 0=anxiety, 1=depression, 2=stress | |
| classes = ["anxiety", "depression", "stress"] | |
| return xlmr_tokenizer, model, classes | |
| 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) # ReLU | |
| x = np.exp(x) / np.sum(np.exp(x)) # Softmax | |
| return x | |
| return scaler, predict | |
| xlmr_tokenizer, xlmr_model, le = load_xlmr() | |
| scaler, survey_predict = load_survey() | |
| # โโ HELPERS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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 "" | |
| # โโ KEYWORD OVERRIDE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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: | |
| # depression ููู ุงุช ูุงุถุญุฉ โ override ููู | |
| 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: | |
| # anxiety ููู ุงุช ูุงุถุญุฉ โ override ููู | |
| 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: | |
| # stress ููู ุงุช ูุงุถุญุฉ โ override ููู | |
| 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) | |
| # normalize | |
| 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)} | |
| # ุทุจูู ุงูู keyword boost ุนูู ุงููุต ุงูุฃุตูู + ุงูุชุฑุฌู ุฉ | |
| 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 QUESTIONS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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", "ุดุนุฑุช ุจุงูุฅููุงู ุงูุนุงุทูู"), | |
| ] | |
| # โโ UI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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("---") | |
| # โโ PART 1: TEXT โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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) | |
| # โโ PART 2: SURVEY โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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) | |
| # โโ PREDICT BUTTON โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| _, col_btn, _ = st.columns([1, 2, 1]) | |
| with col_btn: | |
| predict_btn = st.button("๐ Analyze / ุชุญููู") | |
| # โโ RESULTS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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 / ุงููุชุงุฆุฌ") | |
| # โโ SCORE CARDS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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) | |
| # โโ PRIMARY LABEL โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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, | |
| ) | |
| # โโ CRISIS BOX โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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) | |
| # โโ SCORE DETAILS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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)}%**") | |
| # โโ RECOMMENDATIONS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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) | |
| # โโ REFERRAL โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| 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, | |
| ) | |