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"
{result}
", 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(""" """, unsafe_allow_html=True)