Spaces:
Sleeping
Sleeping
| import torch.utils.data as _tud | |
| from pytorch_tabular.tabular_datamodule import TabularDatamodule | |
| from streamlit_option_menu import option_menu | |
| import google.generativeai as genai | |
| import os | |
| from deep_translator import GoogleTranslator | |
| translator = GoogleTranslator() | |
| genai.configure(api_key=os.getenv("GEMINI_API_KEY")) | |
| _OriginalDataLoader = _tud.DataLoader | |
| class SafeDataLoader(_OriginalDataLoader): | |
| def __init__(self, *args, **kwargs): | |
| kwargs.pop("batch_size", None) | |
| kwargs.pop("num_workers", None) | |
| super().__init__(*args, **kwargs) | |
| _tud.DataLoader = SafeDataLoader | |
| _old_prepare = TabularDatamodule.prepare_inference_dataloader | |
| def _patched_prepare(self, df): | |
| if not hasattr(self.config, "dataloader_kwargs"): | |
| self.config.dataloader_kwargs = {} | |
| return _old_prepare(self, df) | |
| TabularDatamodule.prepare_inference_dataloader = _patched_prepare | |
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import torch | |
| import json | |
| import pickle | |
| from pytorch_tabular import TabularModel | |
| from sklearn.preprocessing import LabelEncoder | |
| from omegaconf import OmegaConf, DictConfig | |
| from types import SimpleNamespace | |
| # Load model | |
| model = TabularModel.load_model("FTTransformerModel") | |
| dm = model.datamodule | |
| if hasattr(dm, "label_encoder") and isinstance(dm.label_encoder, LabelEncoder): | |
| dm.label_encoder = [dm.label_encoder] | |
| dm._inferred_config = SimpleNamespace(output_cardinality=[2]) | |
| # Load threshold | |
| with open("FTTransformerModel/threshold.json", "r") as f: | |
| threshold = json.load(f)["threshold"] | |
| # Sidebar: language selection | |
| with st.sidebar: | |
| lang = st.selectbox("🌐 Language / 語言 / Bahasa", ["English", "中文", "Malay"]) | |
| # Label dictionary for multilingual UI | |
| LABELS = { | |
| "user_type": {"English": "I am a...", "中文": "我的身份是...", "Malay": "Saya seorang..."}, | |
| "user_type_options": {"English": ["Patient", "Medical Professional"], "中文": ["病患", "醫護人員"], "Malay": ["Pesakit", "Profesional Perubatan"]}, | |
| "input_header": {"English": "Enter Health Info", "中文": "輸入健康資料", "Malay": "Masukkan Maklumat Kesihatan"}, | |
| "age": {"English": "Patient's Age", "中文": "年齡", "Malay": "Umur"}, | |
| "height": {"English": "Patient's Height (cm)", "中文": "身高 (cm)", "Malay": "Tinggi (cm)"}, | |
| "weight": {"English": "Patient's Weight (kg)", "中文": "體重 (kg)", "Malay": "Berat (kg)"}, | |
| "systolic": {"English": "Patient's Systolic BP", "中文": "收縮壓", "Malay": "Tekanan Sistolik"}, | |
| "diastolic": {"English": "Patient's Diastolic BP", "中文": "舒張壓", "Malay": "Tekanan Diastolik"}, | |
| "cholesterol": {"English": "Patient's Cholesterol", "中文": "膽固醇", "Malay": "Kolesterol"}, | |
| "gluc": {"English": "Patient's Glucose", "中文": "血糖", "Malay": "Glukosa"}, | |
| "gender": {"English": "Patient's Gender", "中文": "性別", "Malay": "Jantina"}, | |
| "smoke": {"English": "Do patient smoke?", "中文": "是否吸菸?", "Malay": "Adakah merokok?"}, | |
| "alco": {"English": "Do patient drink alcohol?", "中文": "是否喝酒?", "Malay": "Adakah minum alkohol?"}, | |
| "active": {"English": "Are patient physically active?", "中文": "是否有運動習慣?", "Malay": "Adakah aktif secara fizikal?"}, | |
| "predict": {"English": "Predict CVD Risk", "中文": "預測心血管風險", "Malay": "Ramalkan Risiko CVD"} | |
| } | |
| MENU = { | |
| "English": ["Prediction Tool", "Let's know more about CVD!"], | |
| "中文": ["預測工具", "了解更多心血管資訊"], | |
| "Malay": ["Alat Ramalan", "Ketahui lebih lanjut tentang CVD"] | |
| } | |
| chol_options = { | |
| "English": {0: "Normal", 1: "High", 2: "Very High"}, | |
| "中文": {0: "正常", 1: "偏高", 2: "非常高"}, | |
| "Malay": {0: "Normal", 1: "Tinggi", 2: "Sangat Tinggi"} | |
| } | |
| gluc_options = chol_options.copy() | |
| gender_options = { | |
| "English": {0: "Female", 1: "Male"}, | |
| "中文": {0: "女性", 1: "男性"}, | |
| "Malay": {0: "Perempuan", 1: "Lelaki"} | |
| } | |
| yn_options = { | |
| "English": {0: "No", 1: "Yes"}, | |
| "中文": {0: "否", 1: "是"}, | |
| "Malay": {0: "Tidak", 1: "Ya"} | |
| } | |
| # User input | |
| with st.sidebar: | |
| selected = option_menu( | |
| menu_title="Menu", | |
| options=MENU[lang], | |
| icons=["activity", "book"], | |
| menu_icon="cast", | |
| default_index=0, | |
| ) | |
| if selected == MENU[lang][0]: | |
| with st.sidebar: | |
| st.header(LABELS["input_header"][lang]) | |
| user_type = st.selectbox(LABELS["user_type"][lang], LABELS["user_type_options"][lang]) | |
| age = st.number_input(LABELS["age"][lang], min_value=18, max_value=100, value=50) | |
| height = st.number_input(LABELS["height"][lang], min_value=100, max_value=250, value=170) | |
| weight = st.number_input(LABELS["weight"][lang], min_value=30, max_value=200, value=70) | |
| systolic = st.number_input(LABELS["systolic"][lang], min_value=80, max_value=250, value=120) | |
| diastolic = st.number_input(LABELS["diastolic"][lang], min_value=40, max_value=150, value=80) | |
| cholesterol = st.selectbox(LABELS["cholesterol"][lang], [0, 1, 2], format_func=lambda x: chol_options[lang][x]) | |
| gluc = st.selectbox(LABELS["gluc"][lang], [0, 1, 2], format_func=lambda x: gluc_options[lang][x]) | |
| gender = st.selectbox(LABELS["gender"][lang], [0, 1], format_func=lambda x: gender_options[lang][x]) | |
| smoke = st.selectbox(LABELS["smoke"][lang], [0, 1], format_func=lambda x: yn_options[lang][x]) | |
| alco = st.selectbox(LABELS["alco"][lang], [0, 1], format_func=lambda x: yn_options[lang][x]) | |
| active = st.selectbox(LABELS["active"][lang], [0, 1], format_func=lambda x: yn_options[lang][x]) | |
| input_data = pd.DataFrame([{ "age": age * 365, "height": height, "weight": weight, "ap_hi": systolic, "ap_lo": diastolic, "cholesterol": cholesterol, "gluc": gluc, "gender": gender, "smoke": smoke, "alco": alco, "active": active }]) | |
| predict_clicked = st.button(LABELS["predict"][lang]) | |
| if predict_clicked: | |
| input_data["bmi"] = input_data["weight"] / ((input_data["height"]/100)**2) | |
| input_data["pulse_pressure"] = input_data["ap_hi"] - input_data["ap_lo"] | |
| input_data["hypertension"] = ((input_data["ap_hi"] > 140) | (input_data["ap_lo"] > 90)).astype(int) | |
| preds = model.predict(input_data) | |
| proba = preds["cardio_1_probability"].iloc[0] | |
| result = "❌ At Risk of CVD" if proba >= threshold else " ✅ Low Risk" | |
| with st.container(): | |
| st.markdown("### 🧬Prediction Result🧬") | |
| st.markdown(f"<div style='text-align:center; font-size:26px'>{result}</div>", unsafe_allow_html=True) | |
| # st.write(f"(Probability: {proba:.2%}, Threshold: {threshold:.2f})") | |
| is_patient = user_type in ["Patient", "病患", "Pesakit"] | |
| if is_patient: | |
| prompt = ( | |
| f"As a healthcare professional, explain to the patient what this result '{result}' means " | |
| f"Then, give five actionable lifestyle suggestions " | |
| f"based on this result. Lastly, remind them clearly that this prediction is just a reference and " | |
| f"they should consult a certified medical professional for an official diagnosis." | |
| f"The title of response must be Health Advice From Your Lovely AI Friend." | |
| f"Always care about the patient and be empathetic." | |
| f"Add interesting emoji." | |
| f"Do not use Markdown symbols such as # or **" | |
| f"The punctuation format needs to follow the language standard。 For example, Chinese using Chinese punctuation format, Malay follows punctuation rules in Malay language" | |
| f"Please keep the entire response within 150 words." | |
| ) | |
| else: | |
| prompt = ( | |
| f"You are a cardiovascular disease specialist reviewing a prediction result of '{result}' " | |
| f"Please interpret this result for clinical use, and suggest the next diagnostic steps such as ECG, " | |
| f"angiogram, or lab work. Make it clear that this model is a support tool and not a substitute for " | |
| f"clinical judgment." | |
| f"The title of response must be Diagnosis Support From Your Lovely AI Friend." | |
| f"Keep the response in a clear and clean format." | |
| f"Do not use Markdown symbols such as # or **" | |
| f"The punctuation format needs to follow the language standard。 For example, Chinese using Chinese punctuation format, Malay follows punctuation rules in Malay language" | |
| f"Please keep the entire response within 150 words." | |
| ) | |
| model_gemini = genai.GenerativeModel( | |
| model_name="models/gemini-2.0-flash", | |
| generation_config=genai.types.GenerationConfig(temperature=0.2) | |
| ) | |
| response = model_gemini.generate_content(prompt) | |
| original_text = response.text | |
| if lang == "中文": | |
| translated = GoogleTranslator(source="auto", target="zh-TW").translate(original_text) | |
| st.markdown("### 🔹 AI 健康建議") | |
| st.write(translated) | |
| elif lang == "Malay": | |
| translated = GoogleTranslator(source="auto", target="ms").translate(original_text) | |
| st.markdown("### 🔹 Nasihat Kesihatan AI") | |
| st.write(translated) | |
| else: | |
| st.markdown("### 🔹 AI Health Advice") | |
| st.write(original_text) | |
| elif selected == MENU[lang][1]: | |
| st.title({ | |
| "English": "Understanding Cardiovascular Disease", | |
| "中文": "了解心血管疾病", | |
| "Malay": "Memahami Penyakit Kardiovaskular" | |
| }[lang]) | |
| st.markdown({ | |
| "English": """ | |
| **What is Cardiovascular Disease (CVD)?** | |
| CVDs are a group of heart and blood vessel conditions, such as coronary heart disease, stroke, and heart failure. They are the leading cause of death globally. | |
| **Key Facts (by WHO):** | |
| - Over 4 out of 5 CVD deaths are due to heart attacks and strokes. | |
| - One-third of these deaths happen in people under age 70. | |
| - Many CVDs can be prevented by addressing risk factors such as tobacco use, poor diet, obesity, inactivity, and harmful alcohol use. | |
| **Prevention:** | |
| - Stop smoking and avoid secondhand smoke | |
| - Choose healthy foods with less salt and saturated fat | |
| - Stay active at least 30 minutes a day | |
| - Get regular health checkups | |
| """, | |
| "中文": """ | |
| **什麼是心血管疾病(CVD)?** | |
| CVD 是一類包括冠心病、中風、心力衰竭在內的心臟與血管疾病。它是全球主要死因之一。 | |
| **世界衛生組織重點資訊:** | |
| - 超過八成的心血管死亡來自心臟病和中風 | |
| - 三分之一發生在70歲以下的人 | |
| - 多數可透過控制菸酒、不良飲食、運動不足、肥胖等風險因素來預防 | |
| **預防建議:** | |
| - 戒菸,遠離二手菸 | |
| - 飲食清淡,減少油脂和鹽分 | |
| - 每天至少運動 30 分鐘 | |
| - 定期健康檢查 | |
| """, | |
| "Malay": """ | |
| **Apakah Penyakit Kardiovaskular (CVD)?** | |
| CVD ialah kumpulan penyakit jantung dan saluran darah seperti penyakit jantung koronari, strok dan kegagalan jantung. Ia adalah penyebab utama kematian di dunia. | |
| **Fakta Penting (WHO):** | |
| - Lebih 80% kematian CVD berpunca daripada serangan jantung dan strok | |
| - Sepertiga berlaku pada mereka di bawah umur 70 tahun | |
| - Sebahagian besar boleh dicegah melalui gaya hidup sihat | |
| **Langkah Pencegahan:** | |
| - Berhenti merokok dan elakkan asap rokok | |
| - Pilih makanan sihat kurang garam dan lemak | |
| - Kekal aktif sekurang-kurangnya 30 minit sehari | |
| - Lakukan pemeriksaan kesihatan secara berkala | |
| """ | |
| }[lang]) | |
| st.markdown("## " + { | |
| "English": "🥗 Healthy Diet Guidelines", | |
| "中文": "🥗 健康飲食指引", | |
| "Malay": "🥗 Garis Panduan Pemakanan Sihat" | |
| }[lang]) | |
| st.markdown({ | |
| "English": """ | |
| - Eat at least 400g of fruits and vegetables per day. | |
| - Reduce total fat intake, especially saturated and trans fats. | |
| - Limit salt to less than 5g per day. | |
| - Avoid sugar-sweetened beverages and processed snacks. | |
| - Breastfeed exclusively for 6 months where applicable. | |
| """, | |
| "中文": """ | |
| - 每天攝取至少400克蔬果 | |
| - 減少脂肪攝取,特別是飽和脂肪與反式脂肪 | |
| - 每日鹽分少於5克 | |
| - 避免含糖飲料與加工零食 | |
| - 鼓勵6個月內純母乳哺育(如適用) | |
| """, | |
| "Malay": """ | |
| - Makan sekurang-kurangnya 400g buah dan sayur setiap hari | |
| - Kurangkan pengambilan lemak tepu dan lemak trans | |
| - Hadkan garam kurang daripada 5g sehari | |
| - Elakkan minuman bergula dan makanan ringan diproses | |
| - Susukan bayi secara eksklusif selama 6 bulan jika boleh | |
| """ | |
| }[lang]) | |
| st.markdown("## " + { | |
| "English": "🧘️♂️ Physical Activity Tips", | |
| "中文": "🧘️♂️ 身體活動建議", | |
| "Malay": "🧘️♂️ Petua Aktiviti Fizikal" | |
| }[lang]) | |
| st.markdown({ | |
| "English": """ | |
| - Adults: At least 150–300 minutes of moderate-intensity aerobic activity weekly | |
| - Children/teens: 60 minutes per day of physical activity | |
| - Reduce sedentary behavior (screen time, sitting) | |
| - Include muscle-strengthening exercises twice a week | |
| """, | |
| "中文": """ | |
| - 成人:每週應進行150至300分鐘中等強度的有氧運動 | |
| - 兒童與青少年:每天至少60分鐘身體活動 | |
| - 減少久坐與看螢幕時間 | |
| - 每週應有兩次肌力訓練 | |
| """, | |
| "Malay": """ | |
| - Dewasa: Sekurang-kurangnya 150–300 minit senaman aerobik sederhana setiap minggu | |
| - Kanak-kanak/remaja: 60 minit sehari aktiviti fizikal | |
| - Kurangkan masa duduk atau menghadap skrin | |
| - Lakukan latihan kekuatan otot dua kali seminggu | |
| """ | |
| }[lang]) | |
| MAP_TITLE = { | |
| "English": "🏥 Search Nearby Hospitals", | |
| "中文": "🏥 搜尋附近的醫院", | |
| "Malay": "🏥 Cari Hospital Berdekatan" | |
| } | |
| st.markdown(f"### {MAP_TITLE[lang]}") | |
| st.markdown(""" | |
| <iframe src="https://www.google.com/maps?q=hospital+near+me&output=embed" | |
| width="100%" height="400" style="border:0;" allowfullscreen="" loading="lazy"> | |
| </iframe> | |
| """, unsafe_allow_html=True) | |