Update src/streamlit_app.py
Browse files- src/streamlit_app.py +56 -23
src/streamlit_app.py
CHANGED
|
@@ -1,19 +1,20 @@
|
|
| 1 |
import streamlit as st
|
|
|
|
| 2 |
|
| 3 |
# --- Page Config ---
|
| 4 |
-
st.set_page_config(page_title="FitPlan AI", page_icon="π", layout="wide")
|
| 5 |
|
| 6 |
# --- Custom Styling ---
|
| 7 |
st.markdown("""
|
| 8 |
<style>
|
| 9 |
.stApp { background-color: #0E1117; color: white; }
|
| 10 |
[data-testid="stSidebar"] { background-color: #161B22 !important; }
|
| 11 |
-
.stButton > button { width: 100%; border-radius: 8px; font-weight: bold; }
|
|
|
|
| 12 |
</style>
|
| 13 |
""", unsafe_allow_html=True)
|
| 14 |
|
| 15 |
# --- Initialize Session State ---
|
| 16 |
-
# This ensures data persists across navigation
|
| 17 |
if 'user_data' not in st.session_state:
|
| 18 |
st.session_state.user_data = {
|
| 19 |
'name': '', 'age': 25, 'height': 0.0, 'weight': 0.0,
|
|
@@ -22,7 +23,7 @@ if 'user_data' not in st.session_state:
|
|
| 22 |
if 'profile_complete' not in st.session_state:
|
| 23 |
st.session_state.profile_complete = False
|
| 24 |
if 'edit_mode' not in st.session_state:
|
| 25 |
-
st.session_state.edit_mode = True
|
| 26 |
|
| 27 |
# --- Helper Functions ---
|
| 28 |
def calculate_bmi(w, h_cm):
|
|
@@ -38,7 +39,7 @@ def get_bmi_cat(bmi):
|
|
| 38 |
|
| 39 |
# --- Sidebar Navigation ---
|
| 40 |
with st.sidebar:
|
| 41 |
-
st.markdown("<h1 style='color: #00F260;'>π FitPlan AI</h1>", unsafe_allow_html=True)
|
| 42 |
st.markdown("---")
|
| 43 |
menu = st.radio("MENU", ["π€ Profile", "π Dashboard", "π Health Metrics"])
|
| 44 |
st.markdown("---")
|
|
@@ -52,24 +53,23 @@ with st.sidebar:
|
|
| 52 |
if menu == "π€ Profile":
|
| 53 |
st.title("π€ Profile")
|
| 54 |
|
| 55 |
-
# CASE 1: Profile is complete and NOT in edit mode (Locked View)
|
| 56 |
if st.session_state.profile_complete and not st.session_state.edit_mode:
|
| 57 |
-
st.info("Your profile is synced!
|
| 58 |
|
| 59 |
-
# Display Static Data
|
| 60 |
c1, c2, c3 = st.columns(3)
|
| 61 |
-
c1.
|
| 62 |
-
c2.
|
| 63 |
-
c3.
|
| 64 |
|
|
|
|
| 65 |
if st.button("βοΈ Edit Profile"):
|
| 66 |
st.session_state.edit_mode = True
|
| 67 |
st.rerun()
|
| 68 |
|
| 69 |
-
# CASE 2: Profile setup or Editing (Form View)
|
| 70 |
else:
|
| 71 |
with st.form("profile_form"):
|
| 72 |
-
st.markdown("###
|
| 73 |
u_name = st.text_input("Name", value=st.session_state.user_data['name'])
|
| 74 |
|
| 75 |
col1, col2, col3 = st.columns(3)
|
|
@@ -86,9 +86,8 @@ if menu == "π€ Profile":
|
|
| 86 |
u_level = st.select_slider("Experience", options=["Beginner", "Intermediate", "Advanced"],
|
| 87 |
value=st.session_state.user_data['level'])
|
| 88 |
|
| 89 |
-
if st.form_submit_button("
|
| 90 |
if u_name and u_height > 0 and u_weight > 0:
|
| 91 |
-
# Save to Session State
|
| 92 |
st.session_state.user_data = {
|
| 93 |
'name': u_name, 'age': u_age, 'height': u_height, 'weight': u_weight,
|
| 94 |
'goal': u_goal, 'level': u_level, 'equip': u_equip
|
|
@@ -102,7 +101,7 @@ if menu == "π€ Profile":
|
|
| 102 |
|
| 103 |
elif menu == "π Dashboard":
|
| 104 |
if not st.session_state.profile_complete:
|
| 105 |
-
st.warning("Please complete your **
|
| 106 |
else:
|
| 107 |
ud = st.session_state.user_data
|
| 108 |
st.title(f"Dashboard: {ud['name']} π₯")
|
|
@@ -113,12 +112,37 @@ elif menu == "π Dashboard":
|
|
| 113 |
m1, m2, m3, m4 = st.columns(4)
|
| 114 |
m1.metric("BMI", f"{bmi}")
|
| 115 |
m2.metric("Category", f"{icon} {cat}")
|
| 116 |
-
m3.metric("
|
| 117 |
m4.metric("Age", ud['age'])
|
| 118 |
|
| 119 |
st.markdown("---")
|
| 120 |
-
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
elif menu == "π Health Metrics":
|
| 124 |
if not st.session_state.profile_complete:
|
|
@@ -129,11 +153,20 @@ elif menu == "π Health Metrics":
|
|
| 129 |
cat, icon = get_bmi_cat(bmi)
|
| 130 |
|
| 131 |
st.title("π Health Analysis")
|
| 132 |
-
st.markdown(f"### Your BMI is **{bmi}**")
|
| 133 |
-
st.info(f"Classification: **{cat}**")
|
| 134 |
|
| 135 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
h_m = ud['height'] / 100
|
| 137 |
ideal_low = round(18.5 * (h_m**2), 1)
|
| 138 |
ideal_high = round(24.9 * (h_m**2), 1)
|
| 139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
|
| 4 |
# --- Page Config ---
|
| 5 |
+
st.set_page_config(page_title="FitPlan AI", page_icon="ποΈββοΈ", layout="wide")
|
| 6 |
|
| 7 |
# --- Custom Styling ---
|
| 8 |
st.markdown("""
|
| 9 |
<style>
|
| 10 |
.stApp { background-color: #0E1117; color: white; }
|
| 11 |
[data-testid="stSidebar"] { background-color: #161B22 !important; }
|
| 12 |
+
.stButton > button { width: 100%; border-radius: 8px; font-weight: bold; background: linear-gradient(45deg, #00F260, #0575E6); color: white; border: none; }
|
| 13 |
+
.metric-card { background-color: #1c2128; padding: 15px; border-radius: 10px; border: 1px solid #30363d; }
|
| 14 |
</style>
|
| 15 |
""", unsafe_allow_html=True)
|
| 16 |
|
| 17 |
# --- Initialize Session State ---
|
|
|
|
| 18 |
if 'user_data' not in st.session_state:
|
| 19 |
st.session_state.user_data = {
|
| 20 |
'name': '', 'age': 25, 'height': 0.0, 'weight': 0.0,
|
|
|
|
| 23 |
if 'profile_complete' not in st.session_state:
|
| 24 |
st.session_state.profile_complete = False
|
| 25 |
if 'edit_mode' not in st.session_state:
|
| 26 |
+
st.session_state.edit_mode = True
|
| 27 |
|
| 28 |
# --- Helper Functions ---
|
| 29 |
def calculate_bmi(w, h_cm):
|
|
|
|
| 39 |
|
| 40 |
# --- Sidebar Navigation ---
|
| 41 |
with st.sidebar:
|
| 42 |
+
st.markdown("<h1 style='color: #00F260;'>ποΈββοΈ FitPlan AI</h1>", unsafe_allow_html=True)
|
| 43 |
st.markdown("---")
|
| 44 |
menu = st.radio("MENU", ["π€ Profile", "π Dashboard", "π Health Metrics"])
|
| 45 |
st.markdown("---")
|
|
|
|
| 53 |
if menu == "π€ Profile":
|
| 54 |
st.title("π€ Profile")
|
| 55 |
|
|
|
|
| 56 |
if st.session_state.profile_complete and not st.session_state.edit_mode:
|
| 57 |
+
st.info("Your profile is synced! View your progress in the Dashboard.")
|
| 58 |
|
| 59 |
+
# Display Static Data in Cards
|
| 60 |
c1, c2, c3 = st.columns(3)
|
| 61 |
+
with c1: st.markdown(f'<div class="metric-card"><b>Name:</b><br>{st.session_state.user_data["name"]}</div>', unsafe_allow_html=True)
|
| 62 |
+
with c2: st.markdown(f'<div class="metric-card"><b>Goal:</b><br>{st.session_state.user_data["goal"]}</div>', unsafe_allow_html=True)
|
| 63 |
+
with c3: st.markdown(f'<div class="metric-card"><b>Level:</b><br>{st.session_state.user_data["level"]}</div>', unsafe_allow_html=True)
|
| 64 |
|
| 65 |
+
st.markdown("<br>", unsafe_allow_html=True)
|
| 66 |
if st.button("βοΈ Edit Profile"):
|
| 67 |
st.session_state.edit_mode = True
|
| 68 |
st.rerun()
|
| 69 |
|
|
|
|
| 70 |
else:
|
| 71 |
with st.form("profile_form"):
|
| 72 |
+
st.markdown("### Athlete Stats")
|
| 73 |
u_name = st.text_input("Name", value=st.session_state.user_data['name'])
|
| 74 |
|
| 75 |
col1, col2, col3 = st.columns(3)
|
|
|
|
| 86 |
u_level = st.select_slider("Experience", options=["Beginner", "Intermediate", "Advanced"],
|
| 87 |
value=st.session_state.user_data['level'])
|
| 88 |
|
| 89 |
+
if st.form_submit_button("π₯ Sync & Save Profile"):
|
| 90 |
if u_name and u_height > 0 and u_weight > 0:
|
|
|
|
| 91 |
st.session_state.user_data = {
|
| 92 |
'name': u_name, 'age': u_age, 'height': u_height, 'weight': u_weight,
|
| 93 |
'goal': u_goal, 'level': u_level, 'equip': u_equip
|
|
|
|
| 101 |
|
| 102 |
elif menu == "π Dashboard":
|
| 103 |
if not st.session_state.profile_complete:
|
| 104 |
+
st.warning("Please complete your **Profile** first.")
|
| 105 |
else:
|
| 106 |
ud = st.session_state.user_data
|
| 107 |
st.title(f"Dashboard: {ud['name']} π₯")
|
|
|
|
| 112 |
m1, m2, m3, m4 = st.columns(4)
|
| 113 |
m1.metric("BMI", f"{bmi}")
|
| 114 |
m2.metric("Category", f"{icon} {cat}")
|
| 115 |
+
m3.metric("Goal", ud['goal'])
|
| 116 |
m4.metric("Age", ud['age'])
|
| 117 |
|
| 118 |
st.markdown("---")
|
| 119 |
+
|
| 120 |
+
# --- Innovative Visual Modules ---
|
| 121 |
+
left_col, right_col = st.columns([2, 1])
|
| 122 |
+
|
| 123 |
+
with left_col:
|
| 124 |
+
st.subheader("π Goal Consistency Forecast")
|
| 125 |
+
# Creating dummy data for the graph
|
| 126 |
+
chart_data = pd.DataFrame({
|
| 127 |
+
'Days': ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
| 128 |
+
'Performance %': [40, 55, 45, 70, 85, 90, 100]
|
| 129 |
+
})
|
| 130 |
+
st.area_chart(chart_data.set_index('Days'))
|
| 131 |
+
|
| 132 |
+
with right_col:
|
| 133 |
+
st.subheader("π Level Progress")
|
| 134 |
+
progress_map = {"Beginner": 33, "Intermediate": 66, "Advanced": 100}
|
| 135 |
+
current_progress = progress_map.get(ud['level'], 0)
|
| 136 |
+
st.write(f"Level: **{ud['level']}**")
|
| 137 |
+
st.progress(current_progress / 100)
|
| 138 |
+
|
| 139 |
+
st.markdown("<br>", unsafe_allow_html=True)
|
| 140 |
+
st.markdown(f"""
|
| 141 |
+
<div class="metric-card">
|
| 142 |
+
<b>Equipment Checklist:</b><br>
|
| 143 |
+
<small>{", ".join(ud['equip'])}</small>
|
| 144 |
+
</div>
|
| 145 |
+
""", unsafe_allow_html=True)
|
| 146 |
|
| 147 |
elif menu == "π Health Metrics":
|
| 148 |
if not st.session_state.profile_complete:
|
|
|
|
| 153 |
cat, icon = get_bmi_cat(bmi)
|
| 154 |
|
| 155 |
st.title("π Health Analysis")
|
|
|
|
|
|
|
| 156 |
|
| 157 |
+
# BMI Gauge-like progress bar
|
| 158 |
+
st.write(f"BMI Score: **{bmi}**")
|
| 159 |
+
st.progress(min(bmi/40, 1.0))
|
| 160 |
+
|
| 161 |
+
st.info(f"Classification: **{cat}** {icon}")
|
| 162 |
+
|
| 163 |
+
# Calculation for ideal weight
|
| 164 |
h_m = ud['height'] / 100
|
| 165 |
ideal_low = round(18.5 * (h_m**2), 1)
|
| 166 |
ideal_high = round(24.9 * (h_m**2), 1)
|
| 167 |
+
|
| 168 |
+
st.markdown(f"""
|
| 169 |
+
### π‘ AI Insights
|
| 170 |
+
* To stay in the **Normal** range, your weight should be between **{ideal_low}kg and {ideal_high}kg**.
|
| 171 |
+
* Based on your goal of **{ud['goal']}**, your metabolic age is approximately **{ud['age'] - 2 if bmi < 25 else ud['age'] + 2}**.
|
| 172 |
+
""")
|