# app.py
import gradio as gr
from components.auth import register, login, logout
from components.predict import predict
from components.report_generator import create_report
from components.diabetes_factors import render_factors_info
from components.food_suggestions import render_food_guide
from components.doctor_info import render_doctor_list
from components.how_to_use import render_how_to_use_guide
# --- Style and Theme with FINAL Mobile-Responsive CSS ---
css = """
/* --- CRITICAL FIX FOR SCROLLING ON MOBILE --- */
/* This forces the entire page to allow vertical scrolling and grow with its content */
html, body {
overflow-y: auto !important; /* Allow vertical scrolling! */
height: auto !important; /* Let the body grow beyond the screen height */
}
/* General container style for all screens */
.gradio-container { max-width: 1000px !important; margin: auto !important; }
.centered-text { text-align: center; }
/* Fix for invisible button text in dark mode */
.gradio-button.primary, .gradio-button.secondary {
color: white !important;
}
/* Responsive rules for stacking content WITHIN the app */
@media (max-width: 768px) {
.gradio-row {
flex-direction: column !important;
gap: 1rem !important;
}
.gradio-row > * {
width: 100% !important;
min-width: unset !important;
margin: 0 !important;
}
}
"""
with gr.Blocks(title="DiaSpark: Your Health Management Platform", css=css) as demo:
user_state = gr.State({"email": None, "id": None, "logged_in": False, "gender": None, "username": None})
# --- Static Header (Always Visible) ---
gr.Markdown("# 🩺 Welcome to DiaSpark: Your Health Management Platform")
# --- Personalized Header (Visible only when logged in) ---
with gr.Row(visible=False) as logged_in_header:
welcome_message = gr.Markdown()
# --- Main Tabs ---
with gr.Tabs() as main_tabs:
# --- Tab 1: Homepage ---
with gr.TabItem("Home 🏠", id="home_tab"):
gr.Markdown(
"
A Smart First Step in Understanding Your Health
",
elem_classes="centered-text"
)
gr.Markdown(
"""
DiaSpark is a confidential tool that uses machine learning to provide an educational analysis of your diabetes risk factors.
Our goal is to promote health awareness, not to provide a medical diagnosis.
"""
)
gr.Markdown("---")
with gr.Accordion("🎯 Our Mission: The 'Why'", open=True):
gr.Markdown(
"""
We believe that awareness is the first and most powerful step towards a healthier life.
This tool was built to help you:
* **Gain Insight:** Understand how key health metrics can contribute to your overall risk profile.
* **Encourage Proactive Care:** Use this information as a starting point for meaningful conversations with your doctor.
* **Do It All Privately:** Your health data is sensitive. Our secure login ensures your prediction history is for your eyes only.
"""
)
with gr.Accordion("🚀 Getting Started: The 'How'", open=False):
gr.Markdown(
"""
1. **Create Your Secure Account:** Navigate to the **'Login / Signup'** tab. The process is quick and requires a username, email, password, and gender.
2. **Enter Your Health Metrics:** Once logged in, go to the **'Prediction'** tab. Fill in the fields with your most recent health information.
3. **Receive Your Instant Analysis:** Click the 'Predict My Risk' button. Our AI model will provide an immediate, educational assessment based on your data.
"""
)
with gr.Accordion("⚠️ Important Disclaimer", open=False):
gr.Markdown(
"""
**This tool is NOT a substitute for professional medical advice, diagnosis, or treatment.**
The results from DiaSpark are for informational purposes only. Always seek the advice of your physician or another qualified health provider with any questions you may have regarding a medical condition. Never disregard professional medical advice or delay in seeking it because of something you have read or seen on this application.
"""
)
# --- Tab 2: Authentication ---
with gr.TabItem("Login / Signup 🔑", id="auth_tab") as auth_tab_item:
auth_message = gr.Markdown()
with gr.Column(visible=True) as login_col:
gr.Markdown("### Login to Your Account")
email_login = gr.Textbox(label="Email", placeholder="Enter your email")
pwd_login = gr.Textbox(label="Password", type="password", placeholder="Enter your password")
login_btn = gr.Button("Login", variant="primary")
show_signup_btn = gr.Button("New user? Sign up here.", variant="secondary")
with gr.Column(visible=False) as signup_col:
gr.Markdown("### Create a New Account")
username_signup = gr.Textbox(label="Username", placeholder="Choose a unique username")
email_signup = gr.Textbox(label="Email", placeholder="Enter your email")
pwd_signup = gr.Textbox(
label="Password",
type="password",
placeholder="Create a password",
info="Must be at least 6 characters long."
)
gender_signup = gr.Radio(["Female", "Male"], label="Gender", info="This determines if the 'Pregnancies' field is shown.")
signup_btn = gr.Button("Sign Up", variant="primary")
show_login_btn = gr.Button("Already have an account? Login.", variant="secondary")
# --- NEW "How to Use" Tab ---
with gr.TabItem("How to Use 💡"):
render_how_to_use_guide()
# --- The rest of the informational and tool tabs ---
with gr.TabItem("Learn About Diabetes 📚"):
render_factors_info()
with gr.TabItem("Healthy Eating Guide 🥗"):
render_food_guide()
with gr.TabItem("Find a Specialist 🧑⚕️"):
render_doctor_list()
# --- Prediction Tab ---
with gr.TabItem("Prediction Tool 🔬", id="prediction_tab"):
with gr.Column(visible=False) as prediction_view:
gr.Markdown("### Enter Your Health Metrics for Prediction")
gr.Markdown("---")
with gr.Row(visible=False) as pregnancies_row:
preg = gr.Number(label="Pregnancies", info="Number of times pregnant")
with gr.Row():
with gr.Column(scale=2):
glucose = gr.Number(label="Glucose", info="Plasma glucose concentration (mg/dL)")
bp = gr.Number(label="Blood Pressure", info="Diastolic (bottom number, mm Hg)")
insulin = gr.Number(label="Insulin", info="2-Hour serum insulin (mu U/ml)")
age = gr.Number(label="Age", info="Age in years")
with gr.Column(scale=1):
bmi = gr.Number(label="BMI", info="Your Body Mass Index. We can calculate this for you.")
bmi_choice = gr.Radio(
["I know my BMI", "Calculate from Weight/Height"],
label="Provide BMI",
value="I know my BMI"
)
with gr.Column(visible=False) as bmi_calc_view:
weight = gr.Number(label="Your Weight (kg)")
height = gr.Number(label="Your Height (cm)")
with gr.Row():
predict_btn = gr.Button("Predict My Risk", variant="primary")
result_output = gr.Textbox(label="Prediction Result", interactive=False)
generate_report_btn = gr.Button("Download Report as PDF", variant="secondary", visible=False)
report_file_output = gr.File(label="Your Report", visible=False)
logout_btn = gr.Button("Logout", variant="stop")
with gr.Column(visible=True) as logged_out_view:
gr.Markdown(
"""
🔒 Please log in to access the prediction tool.
You can create an account or log in via the 'Login / Signup' tab.
"""
)
# --- Component Logic (Event Handlers) ---
def switch_to_signup(): return gr.update(visible=False), gr.update(visible=True)
def switch_to_login(): return gr.update(visible=True), gr.update(visible=False)
show_signup_btn.click(fn=switch_to_signup, outputs=[login_col, signup_col])
show_login_btn.click(fn=switch_to_login, outputs=[login_col, signup_col])
def handle_user_state_change(user_data):
is_logged_in = user_data.get("logged_in", False)
is_female = user_data.get("gender") == "Female"
username = user_data.get("username", "User")
welcome_text = f"### 👋 Welcome, {username}!"
return (gr.update(visible=is_logged_in), gr.update(visible=not is_logged_in), gr.update(visible=is_female), gr.update(value=welcome_text), gr.update(visible=is_logged_in), gr.update(visible=not is_logged_in))
user_state.change(fn=handle_user_state_change, inputs=user_state, outputs=[prediction_view, logged_out_view, pregnancies_row, welcome_message, logged_in_header, auth_tab_item])
signup_btn.click(fn=register, inputs=[email_signup, pwd_signup, gender_signup, username_signup], outputs=[auth_message, email_signup, pwd_signup, gender_signup, username_signup])
login_btn.click(fn=login, inputs=[email_login, pwd_login, user_state], outputs=[auth_message, user_state, main_tabs])
# The logout handler must now also clear the weight and height fields.
logout_btn.click(fn=logout, inputs=[user_state], outputs=[auth_message, user_state, main_tabs, email_login, pwd_login, preg, glucose, bp, insulin, bmi, age, result_output, pregnancies_row, weight, height, bmi_choice])
# --- CORRECTED BMI LOGIC ---
def toggle_bmi_inputs(choice):
if choice == "I know my BMI":
# Return 4 values: one for each output component
return gr.update(interactive=True, value=None), None, None, gr.update(visible=False)
else: # Calculate from Weight/Height
# Also return 4 values here to match the outputs list
return gr.update(interactive=False, value=None), gr.update(), gr.update(), gr.update(visible=True)
bmi_choice.change(fn=toggle_bmi_inputs, inputs=bmi_choice, outputs=[bmi, weight, height, bmi_calc_view])
def calculate_bmi(w, h):
if w and h and w > 0 and h > 0:
bmi_val = round(w / ((h / 100) ** 2), 2)
return bmi_val
return None
weight.change(fn=calculate_bmi, inputs=[weight, height], outputs=bmi)
height.change(fn=calculate_bmi, inputs=[weight, height], outputs=bmi)
predict_btn.click(fn=predict, inputs=[preg, glucose, bp, insulin, bmi, age, user_state], outputs=[result_output])
def handle_create_report(user, p, g, b, i, bmi_val, age_val, result):
if not result:
return gr.update(visible=False)
# Recalculate is no longer needed, just use the value from the bmi box
final_bmi_report = bmi_val if bmi_val is not None else 0
input_data = {"Pregnancies": p if p is not None else 0, "Glucose": g, "Blood Pressure": b, "Insulin": i, "BMI": final_bmi_report, "Age": age_val}
file_path = create_report(user, input_data, result)
# CORRECTED TYPO: It should be 'file_path', not 'file_file'
return gr.update(value=file_path, visible=True)
generate_report_btn.click(fn=handle_create_report, inputs=[user_state, preg, glucose, bp, insulin, bmi, age, result_output], outputs=[report_file_output])
result_output.change(fn=lambda x: gr.update(visible=bool(x)), inputs=result_output, outputs=generate_report_btn)
# --- Launch the App ---
if __name__ == "__main__":
demo.launch()