FitPlan_AI / app.py
Rajaluxanaa's picture
Update app.py
512d6c2 verified
import streamlit as st
import pandas as pd
from datetime import datetime
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
import torch
import re
import random
def load_model():
tokenizer = AutoTokenizer.from_pretrained("google/flan-t5-base")
model = AutoModelForSeq2SeqLM.from_pretrained("google/flan-t5-base")
return tokenizer, model
tokenizer, model = load_model()
# Page configuration
st.set_page_config(
page_title="Fitness Profile & BMI Calculator",
page_icon="πŸ’ͺ",
layout="centered"
)
# Custom CSS
st.markdown("""
<style>
.main {
padding: 2rem;
}
.stButton button {
width: 100%;
background-color: #4CAF50;
color: white;
font-weight: bold;
}
.bmi-card {
padding: 20px;
border-radius: 10px;
background-color: #f8f9fa;
border-left: 5px solid #4CAF50;
margin: 10px 0;
}
.category-underweight { color: #3498db; font-weight: bold; }
.category-normal { color: #2ecc71; font-weight: bold; }
.category-overweight { color: #f39c12; font-weight: bold; }
.category-obese { color: #e74c3c; font-weight: bold; }
.day-card {
background-color: #f0f2f6;
padding: 15px;
border-radius: 10px;
margin: 10px 0;
border-left: 5px solid #4CAF50;
}
.day-title {
color: #4CAF50;
font-weight: bold;
font-size: 20px;
margin-bottom: 10px;
}
</style>
""", unsafe_allow_html=True)
# Title
st.title("πŸ’ͺ Fitness Profile & BMI Calculator")
st.markdown("Complete the form below to calculate your BMI and create your fitness profile.")
# Initialize session state for profile data
if 'profile_created' not in st.session_state:
st.session_state.profile_created = False
st.session_state.profile_data = {}
if 'workout_plan' not in st.session_state:
st.session_state.workout_plan = None
# BMI Calculation Function
def calculate_bmi(weight_kg, height_cm):
"""Calculate BMI from weight and height"""
height_m = height_cm / 100
bmi = weight_kg / (height_m ** 2)
return round(bmi, 2), height_m
def get_bmi_category(bmi):
"""Classify BMI into standard health categories"""
if bmi < 18.5:
return "Underweight", "category-underweight"
elif 18.5 <= bmi < 25:
return "Normal", "category-normal"
elif 25 <= bmi < 30:
return "Overweight", "category-overweight"
else:
return "Obese", "category-obese"
def get_bmi_description(category):
"""Get health description based on BMI category"""
descriptions = {
"Underweight": "Consider consulting a healthcare provider about healthy weight gain strategies.",
"Normal": "Great job! Maintain your healthy lifestyle with balanced diet and regular exercise.",
"Overweight": "Focus on gradual weight management through diet and increased physical activity.",
"Obese": "Consult healthcare providers for personalized weight management plans."
}
return descriptions.get(category, "")
# Form validation function
def validate_inputs(name, height, weight, age):
"""Validate all required inputs"""
errors = []
if not name or not name.strip():
errors.append("❌ Name is required")
if height is None or height <= 0:
errors.append("❌ Height must be greater than 0")
if weight is None or weight <= 0:
errors.append("❌ Weight must be greater than 0")
if age is None or age <= 0:
errors.append("❌ Age must be greater than 0")
return errors
def create_workout_prompt(user_info):
"""Create a detailed prompt for workout generation"""
# Determine exercise types based on goal
goal_exercises = {
"Build Muscle": {
"sets": "3-4 sets of 8-12 reps",
"rest": "60-90 seconds",
"focus": "hypertrophy, moderate to heavy weights",
"exercises": ["bench press", "squats", "shoulder press", "rows", "deadlifts"]
},
"Weight Loss": {
"sets": "3 sets of 12-15 reps",
"rest": "30-45 seconds",
"focus": "calorie burn, high intensity, circuit training",
"exercises": ["jumping jacks", "mountain climbers", "burpees", "high knees", "jump squats"]
},
"Strength Gain": {
"sets": "4-5 sets of 4-6 reps",
"rest": "2-3 minutes",
"focus": "heavy compound lifts, progressive overload",
"exercises": ["deadlifts", "squats", "bench press", "overhead press", "barbell rows"]
},
"Abs Building": {
"sets": "3 sets to failure",
"rest": "45-60 seconds",
"focus": "core strength and definition",
"exercises": ["crunches", "leg raises", "planks", "russian twists", "mountain climbers"]
},
"Flexible": {
"sets": "Hold for 30-60 seconds",
"rest": "15-30 seconds",
"focus": "flexibility and mobility",
"exercises": ["downward dog", "cat-cow stretch", "hamstring stretch", "hip flexor stretch", "child's pose"]
},
"Endurance": {
"sets": "2-3 sets of 15-20 reps",
"rest": "20-30 seconds",
"focus": "muscular endurance, light weights high reps",
"exercises": ["bodyweight squats", "pushups", "lunges", "planks", "jumping rope"]
}
}
goal_info = goal_exercises.get(user_info['fitness_goal'], goal_exercises["Build Muscle"])
# Determine difficulty level
if user_info['fitness_level'] == "Beginner":
difficulty = "Start with lighter weights, focus on form, 2-3 exercises per muscle group"
elif user_info['fitness_level'] == "Intermediate":
difficulty = "Moderate weights, focus on progressive overload, 3-4 exercises per muscle group"
else:
difficulty = "Heavy weights, advanced techniques, 4-5 exercises per muscle group"
# Create a very structured prompt
prompt = f"""Generate a personalized 5-day workout plan for {user_info['name']}, a {user_info['age']}-year-old {user_info['gender']} who is {user_info['category'].lower()} (BMI: {user_info['bmi']}).
Their fitness goal is {user_info['fitness_goal']} and they are at {user_info['fitness_level']} level.
Available equipment: {user_info['equipment_list']}.
The workout should follow this structure:
- For {user_info['fitness_goal']}: use {goal_info['sets']} with {goal_info['rest']} rest
- Focus on: {goal_info['focus']}
- Difficulty level: {difficulty}
Create a complete 5-day plan with specific exercises. Each day MUST include:
β€’ A title with the focus area
β€’ 3-4 warmup exercises
β€’ 4-5 main exercises (each with sets x reps and rest time)
β€’ 2-3 cooldown stretches
β€’ Specific notes for this person
Make sure all exercises are appropriate for their BMI category ({user_info['category']}) and fitness level ({user_info['fitness_level']}).
Here's the complete workout plan:
"""
return prompt
def generate_workout_plan(user_info):
"""Generate a truly personalized workout plan using the model"""
# Create the prompt
prompt = create_workout_prompt(user_info)
try:
# First attempt with structured prompt
inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=1024)
outputs = model.generate(
**inputs,
max_new_tokens=1500,
temperature=0.8,
do_sample=True,
num_return_sequences=1,
top_k=50,
top_p=0.95
)
result = tokenizer.decode(outputs[0], skip_special_tokens=True).strip()
# If result is too short or contains template text, use backup
if len(result) < 200 or "Day 1 - [Focus" in result:
return generate_backup_plan(user_info)
return format_workout_plan(result)
except Exception as e:
st.error(f"Error in generation: {e}")
return generate_backup_plan(user_info)
def generate_backup_plan(user_info):
"""Generate a workout plan without using the model"""
# Exercise database
exercises = {
"Upper Body": {
"Beginner": ["Push-ups (knees)", "Dumbbell rows", "Overhead press (light)", "Bicep curls", "Tricep extensions"],
"Intermediate": ["Push-ups", "Pull-ups/rows", "Dumbbell shoulder press", "Hammer curls", "Dips"],
"Advanced": ["Weighted push-ups", "Muscle-ups", "Barbell overhead press", "Chin-ups", "Diamond push-ups"]
},
"Lower Body": {
"Beginner": ["Bodyweight squats", "Lunges", "Glute bridges", "Calf raises", "Step-ups"],
"Intermediate": ["Goblet squats", "Walking lunges", "Romanian deadlifts", "Box jumps", "Bulgarian split squats"],
"Advanced": ["Barbell squats", "Pistol squats", "Deadlifts", "Jump squats", "Weighted lunges"]
},
"Core": {
"Beginner": ["Planks (30 sec)", "Crunches", "Bird dogs", "Dead bugs", "Russian twists (light)"],
"Intermediate": ["Side planks", "Leg raises", "Bicycle crunches", "Mountain climbers", "V-ups"],
"Advanced": ["Dragon flags", "Hanging leg raises", "Ab wheel rollouts", "Turkish get-ups", "L-sits"]
},
"Cardio": {
"Beginner": ["Jumping jacks", "High knees", "Butt kicks", "Skaters", "Mountain climbers"],
"Intermediate": ["Burpees", "Jump rope", "Box jumps", "Kettlebell swings", "Battle ropes"],
"Advanced": ["Plyometric push-ups", "Burpee pull-ups", "Double unders", "Sprint intervals", "Boxing combos"]
},
"Flexibility": {
"Beginner": ["Cat-cow stretch", "Child's pose", "Downward dog", "Standing hamstring stretch", "Quad stretch"],
"Intermediate": ["Pigeon pose", "Sun salutations", "Warrior poses", "Bridge pose", "Seated forward fold"],
"Advanced": ["Splits progression", "Wheel pose", "Crow pose", "King pigeon", "Headstand"]
}
}
# Determine sets/reps based on goal
goal_params = {
"Build Muscle": {"sets": 3, "reps": "8-12", "rest": "60-90 sec"},
"Weight Loss": {"sets": 3, "reps": "12-15", "rest": "30-45 sec"},
"Strength Gain": {"sets": 4, "reps": "4-6", "rest": "2-3 min"},
"Abs Building": {"sets": 3, "reps": "15-20", "rest": "45-60 sec"},
"Flexible": {"sets": 2, "reps": "hold 30-60s", "rest": "15-30 sec"},
"Endurance": {"sets": 3, "reps": "15-20", "rest": "30-45 sec"}
}
params = goal_params.get(user_info['fitness_goal'], goal_params["Build Muscle"])
level = user_info['fitness_level']
# Weekly schedule
schedule = [
("Day 1: Upper Body Focus", ["Upper Body"]),
("Day 2: Lower Body Focus", ["Lower Body"]),
("Day 3: Cardio & Core", ["Cardio", "Core"]),
("Day 4: Full Body", ["Upper Body", "Lower Body"]),
("Day 5: Active Recovery", ["Flexibility", "Core"])
]
# Build the plan
plan = f"🌟 PERSONALIZED WORKOUT PLAN FOR {user_info['name'].upper()}\n"
plan += f"Goal: {user_info['fitness_goal']} | Level: {user_info['fitness_level']} | BMI Category: {user_info['category']}\n"
plan += "=" * 60 + "\n\n"
for day_title, categories in schedule:
plan += f"πŸ‹οΈ {day_title}\n"
plan += "-" * 40 + "\n\n"
# Warmup
plan += "πŸ”Έ WARMUP (5-10 minutes):\n"
warmup_exercises = random.sample(exercises["Flexibility"]["Beginner"] + exercises["Cardio"]["Beginner"], 3)
for ex in warmup_exercises:
plan += f" β€’ {ex} - 30 seconds\n"
plan += "\n"
# Main workout
plan += f"πŸ”Έ MAIN WORKOUT ({params['sets']} sets, {params['reps']} reps, rest {params['rest']}):\n"
main_exercises = []
for cat in categories:
cat_exercises = exercises[cat][level]
main_exercises.extend(random.sample(cat_exercises, min(2, len(cat_exercises))))
# Ensure we have enough exercises
while len(main_exercises) < 4:
main_exercises.append(random.choice(exercises["Core"]["Beginner"]))
for i, ex in enumerate(main_exercises[:4], 1):
plan += f" {i}. {ex} - {params['sets']} sets x {params['reps']} reps\n"
plan += "\n"
# Cooldown
plan += "πŸ”Έ COOLDOWN & STRETCHING:\n"
cooldown = random.sample(exercises["Flexibility"]["Beginner"], 3)
for ex in cooldown:
plan += f" β€’ {ex} - hold 30 seconds\n"
plan += "\n"
# Notes
plan += f"πŸ“ NOTES: {get_workout_note(user_info, day_title)}\n"
plan += "\n" + "β€’" * 40 + "\n\n"
return plan
def get_workout_note(user_info, day_title):
"""Generate personalized workout notes"""
notes = []
if user_info['category'] == "Underweight":
notes.append("Focus on progressive overload with compound exercises to build muscle mass.")
elif user_info['category'] == "Overweight":
notes.append("Maintain good form and focus on consistency. Combine with cardio for best results.")
elif user_info['category'] == "Obese":
notes.append("Start with low-impact variations and gradually increase intensity. Stay hydrated.")
if user_info['fitness_level'] == "Beginner":
notes.append("Focus on form over weight. Stop if you feel pain.")
elif user_info['fitness_level'] == "Intermediate":
notes.append("Try to increase weight or reps each week for progressive overload.")
else:
notes.append("Incorporate advanced techniques like drop sets or supersets.")
if "Upper" in day_title:
notes.append("Keep shoulders back and core engaged throughout upper body exercises.")
elif "Lower" in day_title:
notes.append("Don't let your knees go past your toes during squats and lunges.")
return " ".join(notes[:2]) # Return 2 notes max
def format_workout_plan(plan_text):
"""Format the workout plan for better display"""
# Remove any template markers
plan_text = re.sub(r'\[.*?\]', '', plan_text)
plan_text = plan_text.replace("Create a complete", "")
plan_text = plan_text.replace("Here's the complete workout plan:", "")
# Ensure day headers are properly formatted
plan_text = re.sub(r'(Day \d+)[:\s]*', r'\n\nπŸ‹οΈ \1\n' + "-"*40 + "\n", plan_text)
return plan_text.strip()
# Main Form
with st.form("fitness_form"):
st.subheader("πŸ“‹ Personal Information")
col1, col2 = st.columns(2)
with col1:
name = st.text_input("Full Name *", placeholder="Enter your full name", key="name_input")
gender = st.selectbox("Gender *", ["Male", "Female", "Other", "Prefer not to say"], key="gender_input")
weight = st.number_input("Weight (kg) *", min_value=0.1, max_value=500.0,
value=None, placeholder="Enter weight", step=0.1, key="weight_input")
with col2:
height = st.number_input("Height (cm) *", min_value=1.0, max_value=300.0,
value=None, placeholder="Enter height", step=0.1, key="height_input")
age = st.number_input("Age *", min_value=1, max_value=120,
value=None, placeholder="Enter age", step=1, key="age_input")
st.subheader("πŸ’ͺ Fitness Details")
col3, col4 = st.columns(2)
with col3:
fitness_goal = st.selectbox(
"Fitness Goal *",
["Build Muscle", "Weight Loss", "Strength Gain", "Abs Building", "Flexible", "Endurance"],
key="goal_input"
)
fitness_level = st.radio(
"Fitness Level *",
["Beginner", "Intermediate", "Advanced"],
horizontal=True,
key="level_input"
)
with col4:
equipment_options = ["Dumbbells", "Resistance Band", "Yoga Mat", "Kettlebells",
"Barbell", "Treadmill", "Exercise Bike", "Pull-up Bar",
"Medicine Ball", "No Equipment"]
equipment = st.multiselect(
"Available Equipment *",
equipment_options,
help="Select all equipment you have access to",
key="equipment_input"
)
submitted = st.form_submit_button("πŸš€ Submit Profile", use_container_width=True)
# Process form submission
if submitted:
errors = validate_inputs(name, height, weight, age)
if not equipment:
errors.append("❌ Please select at least one equipment option")
if errors:
for error in errors:
st.error(error)
else:
# Calculate BMI
bmi, height_m = calculate_bmi(weight, height)
category, css_class = get_bmi_category(bmi)
description = get_bmi_description(category)
equipment_list = ", ".join(equipment)
# Store profile data
st.session_state.profile_created = True
st.session_state.profile_data = {
'name': name,
'gender': gender,
'age': age,
'height_cm': height,
'height_m': height_m,
'weight': weight,
'bmi': bmi,
'category': category,
'css_class': css_class,
'description': description,
'fitness_goal': fitness_goal,
'fitness_level': fitness_level,
'equipment': equipment,
'equipment_list': equipment_list,
'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M")
}
st.success("βœ… Profile Submitted Successfully!")
# Generate workout plan
with st.spinner("πŸ€– Creating your personalized 5-day workout plan..."):
workout_plan = generate_workout_plan(st.session_state.profile_data)
if workout_plan:
st.session_state.workout_plan = workout_plan
else:
st.session_state.workout_plan = generate_backup_plan(st.session_state.profile_data)
st.rerun()
# Display Profile Results
if st.session_state.profile_created:
data = st.session_state.profile_data
st.markdown("---")
st.subheader("πŸ“„ Your Fitness Profile")
# BMI Result Card
st.markdown(f"""
<div class="bmi-card">
<h3>πŸ“Š BMI Result</h3>
<h1 style="font-size: 48px; margin: 10px 0;">{data['bmi']}</h1>
<p style="font-size: 20px;">
<span class="{data['css_class']}">{data['category']}</span>
</p>
<p style="color: #666;">{data['description']}</p>
</div>
""", unsafe_allow_html=True)
# Personal Details Card
with st.container():
col5, col6 = st.columns(2)
with col5:
st.markdown("#### πŸ‘€ Personal Details")
st.write(f"**Name:** {data['name']}")
st.write(f"**Gender:** {data.get('gender', 'Not specified')}")
st.write(f"**Age:** {data.get('age', 'Not specified')}")
st.write(f"**Height:** {data['height_cm']} cm ({data['height_m']:.2f} m)")
st.write(f"**Weight:** {data['weight']} kg")
with col6:
st.markdown("#### 🎯 Fitness Goals")
st.write(f"**Primary Goal:** {data['fitness_goal']}")
st.write(f"**Experience Level:** {data['fitness_level']}")
st.write(f"**Equipment:** {', '.join(data['equipment'])}")
# Display Workout Plan
if st.session_state.workout_plan:
st.markdown("---")
st.subheader("πŸ‹οΈ Your Personalized 5-Day Workout Plan")
st.caption("✨ This plan is uniquely created based on your profile")
# Display the workout plan
plan_text = st.session_state.workout_plan
# Fixed regex pattern - properly escaped
days = re.split(r'(Day \d+[:\-]?|πŸ‹οΈ\s*Day\s*\d+)', plan_text)
if len(days) > 1:
for i in range(1, len(days), 2):
if i+1 < len(days):
day_title = days[i].strip()
day_content = days[i+1].strip()
# Clean up the content
day_content = re.sub(r'^[-β€”=_*]+', '', day_content)
with st.container():
st.markdown(f"""
<div class="day-card">
<div class="day-title">{day_title}</div>
<div style="white-space: pre-line;">{day_content}</div>
</div>
""", unsafe_allow_html=True)
else:
# If splitting didn't work, show in a code block
st.code(plan_text, language="text")
# Download button
st.download_button(
label="πŸ“₯ Download Workout Plan",
data=plan_text,
file_name=f"workout_plan_{data['name']}_{datetime.now().strftime('%Y%m%d')}.txt",
mime="text/plain"
)
st.caption("πŸ€– This workout plan was generated by AI based on your unique profile. Consult with a fitness professional before starting any new exercise routine.")
# Option to create new profile
if st.button("πŸ”„ Create New Profile"):
st.session_state.profile_created = False
st.session_state.profile_data = {}
st.session_state.workout_plan = None
st.rerun()
# Footer
st.markdown("---")
st.markdown("""
<div style='text-align: center; color: gray; padding: 20px;'>
<p>πŸ‹οΈβ€β™‚οΈ Your fitness journey starts here! Complete the form to get personalized insights.</p>
<p style='font-size: 12px;'>* Required fields | BMI is a screening tool, not a diagnostic measure</p>
</div>
""", unsafe_allow_html=True)
# Sidebar with additional information
with st.sidebar:
st.image("https://cdn-icons-png.flaticon.com/512/3043/3043650.png", width=100)
st.title("About BMI")
st.markdown("""
**Body Mass Index (BMI)** is a simple measure that uses your height and weight to estimate body fat.
### BMI Categories:
- **Underweight:** < 18.5
- **Normal:** 18.5 - 24.9
- **Overweight:** 25 - 29.9
- **Obese:** β‰₯ 30
### Tips for Accuracy:
- Measure height without shoes
- Weigh yourself in the morning
- Use consistent units
""")
st.markdown("---")
st.markdown("### πŸ“ Quick Example")
st.markdown("""
**Try this example:**
- Name: Sarah Johnson
- Age: 28
- Height: 165 cm
- Weight: 58 kg
- Goal: Weight Loss
- Equipment: Dumbbells, Yoga Mat
- Level: Intermediate
BMI: 21.3 (Normal)
""")