Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,219 +3,250 @@ import joblib
|
|
| 3 |
import pandas as pd
|
| 4 |
import random
|
| 5 |
|
| 6 |
-
# 1. Load
|
| 7 |
model = joblib.load('fitness_model.joblib')
|
| 8 |
|
| 9 |
-
# --- 2.
|
| 10 |
-
# Format:
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
"
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
("Romanian Deadlift", ["Hamstrings"], ["Back"], 0, 30, "Barbell"),
|
| 21 |
-
("Dumbbell RDL", ["Hamstrings"], ["Back"], 0, 35, "Dumbbells"),
|
| 22 |
-
("Lying Leg Curl", ["Hamstrings"], [], 0, 45, "Machine"),
|
| 23 |
-
("Glute Bridge", ["Glutes"], [], 0, 50, "Bodyweight"), # Safe for everyone
|
| 24 |
-
("Walking Lunges", ["Glutes"], ["Knee", "Ankle"], 0, 30, "Bodyweight"),
|
| 25 |
-
("Bulgarian Split Squat", ["Quads"], ["Knee", "Ankle"], 0, 28, "Dumbbells") # Hard!
|
| 26 |
-
],
|
| 27 |
-
"Push": [
|
| 28 |
-
("Barbell Bench Press", ["Chest"], ["Shoulder"], 0, 35, "Rack"),
|
| 29 |
-
("Dumbbell Floor Press", ["Chest"], [], 0, 40, "Dumbbells"), # Shoulder safe
|
| 30 |
-
("Push-ups", ["Chest"], ["Shoulder"], 0, 32, "Bodyweight"),
|
| 31 |
-
("Knee Push-ups", ["Chest"], [], 0, 50, "Bodyweight"),
|
| 32 |
-
("Overhead Press (Barbell)", ["Shoulders"], ["Shoulder", "Back"], 0, 30, "Barbell"),
|
| 33 |
-
("Seated Dumbbell Press", ["Shoulders"], [], 0, 40, "Dumbbells"),
|
| 34 |
-
("Lateral Raises", ["Shoulders"], [], 0, 45, "Dumbbells"),
|
| 35 |
-
("Tricep Dips", ["Triceps"], ["Shoulder"], 0, 28, "Bodyweight"),
|
| 36 |
-
("Tricep Cable Pushdown", ["Triceps"], [], 0, 45, "Machine")
|
| 37 |
-
],
|
| 38 |
-
"Pull": [
|
| 39 |
-
("Pull-ups", ["Back"], ["Shoulder"], 0, 28, "Bodyweight"), # Hard for heavy users
|
| 40 |
-
("Lat Pulldowns", ["Back"], [], 0, 50, "Machine"),
|
| 41 |
-
("Barbell Row", ["Back"], ["Back"], 0, 30, "Barbell"),
|
| 42 |
-
("Chest Supported Row", ["Back"], [], 0, 45, "Machine"), # Back safe
|
| 43 |
-
("Single Arm Dumbbell Row", ["Back"], [], 0, 40, "Dumbbells"),
|
| 44 |
-
("Face Pulls", ["Rear Delts"], [], 0, 50, "Machine"),
|
| 45 |
-
("Bicep Curls", ["Biceps"], [], 0, 50, "Dumbbells")
|
| 46 |
-
],
|
| 47 |
-
"Core": [
|
| 48 |
-
("Plank", ["Abs"], [], 0, 40, "Bodyweight"),
|
| 49 |
-
("Hanging Leg Raises", ["Abs"], ["Shoulder"], 0, 28, "Barbell"),
|
| 50 |
-
("Dead Bug", ["Abs"], [], 0, 50, "Bodyweight"), # Back safe
|
| 51 |
-
("Russian Twists", ["Abs"], ["Back"], 0, 35, "Bodyweight"),
|
| 52 |
-
("Bird-Dog", ["Abs"], [], 0, 60, "Bodyweight")
|
| 53 |
-
],
|
| 54 |
-
"Cardio": [
|
| 55 |
-
("Sprints", ["Cardio"], ["Knee", "Back", "Ankle"], 0, 28, "Bodyweight"),
|
| 56 |
-
("Treadmill Jog", ["Cardio"], ["Knee", "Ankle"], 0, 32, "Machine"),
|
| 57 |
-
("Elliptical", ["Cardio"], [], 0, 50, "Machine"), # Low impact
|
| 58 |
-
("Stationary Bike", ["Cardio"], [], 0, 60, "Machine"), # Very safe
|
| 59 |
-
("Swimming", ["Cardio"], [], 0, 60, "Pool"),
|
| 60 |
-
("Battle Ropes", ["Cardio"], ["Shoulder"], 0, 40, "Gym"),
|
| 61 |
-
("Burpees", ["Cardio"], ["Back", "Knee", "Shoulder"], 0, 28, "Bodyweight")
|
| 62 |
-
]
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
# --- 3. SMART LOGIC FUNCTIONS ---
|
| 66 |
-
|
| 67 |
-
def calculate_bmi(weight, height_cm):
|
| 68 |
-
height_m = height_cm / 100
|
| 69 |
-
return weight / (height_m ** 2)
|
| 70 |
-
|
| 71 |
-
def filter_exercises(category, injury, age, bmi, equipment):
|
| 72 |
-
"""
|
| 73 |
-
Selects valid exercises based on constraints.
|
| 74 |
-
Returns a random selection to ensure variety every time.
|
| 75 |
-
"""
|
| 76 |
-
valid = []
|
| 77 |
|
| 78 |
-
#
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
for name, targets, bad_for, min_age_limit, max_bmi_limit, req_equip in exercise_db[category]:
|
| 86 |
-
# 1. Injury Filter
|
| 87 |
-
if injury in bad_for: continue
|
| 88 |
-
# 2. Age Filter (e.g., Box squats for seniors)
|
| 89 |
-
if age > 60 and min_age_limit > 0 and age < min_age_limit: continue
|
| 90 |
-
# 3. BMI Filter (No burpees for obese users)
|
| 91 |
-
if bmi > max_bmi_limit: continue
|
| 92 |
-
# 4. Equipment Filter
|
| 93 |
-
if req_equip not in equip_map: continue
|
| 94 |
-
|
| 95 |
-
valid.append(name)
|
| 96 |
|
| 97 |
-
#
|
| 98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
-
#
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
"""
|
| 106 |
-
Generates a specialized finisher: Foam Rolling, Stretching, or Burnout.
|
| 107 |
-
"""
|
| 108 |
-
options = []
|
| 109 |
|
| 110 |
-
#
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
-
|
| 121 |
-
if goal in ["Weight Loss", "Athletic Performance"] and injury == "None":
|
| 122 |
-
if equipment == "Bodyweight Only":
|
| 123 |
-
options.append("🔥 BURNOUT: 50 Air Squats for time!")
|
| 124 |
-
else:
|
| 125 |
-
options.append("🔥 BURNOUT: 'Farmer Carry' max distance walking")
|
| 126 |
|
| 127 |
-
|
| 128 |
-
if
|
| 129 |
-
|
| 130 |
-
|
| 131 |
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
| 136 |
-
def
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
bmi_status = "Normal"
|
| 141 |
-
if bmi > 25: bmi_status = "Overweight"
|
| 142 |
-
if bmi > 30: bmi_status = "Obese (Low Impact Priority)"
|
| 143 |
-
|
| 144 |
-
# B. Define Protocol (Sets/Reps) based on Goal & Age
|
| 145 |
-
warmup_time = "5-7 mins"
|
| 146 |
-
if age > 50: warmup_time = "10-12 mins (Joint Focus)"
|
| 147 |
-
|
| 148 |
-
main_scheme = "3 sets x 12 reps"
|
| 149 |
rest = "60s"
|
| 150 |
|
| 151 |
-
if goal == "
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
rest = "90s"
|
| 154 |
-
|
| 155 |
-
main_scheme = "5 sets x 5 reps (Heavy)"
|
| 156 |
-
rest = "3 mins"
|
| 157 |
elif goal == "Weight Loss":
|
| 158 |
-
|
|
|
|
| 159 |
rest = "30s"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
core = filter_exercises("Core", injury, age, bmi, equipment)
|
| 166 |
-
cardio = filter_exercises("Cardio", injury, age, bmi, equipment)
|
| 167 |
-
|
| 168 |
-
finisher = generate_finisher(goal, injury, equipment)
|
| 169 |
-
|
| 170 |
-
# D. Construct the Text
|
| 171 |
-
plan = f"📋 YOUR AI CUSTOM PLAN: {plan_name}\n"
|
| 172 |
-
plan += f"👤 Profile: {age}y | BMI: {bmi:.1f} ({bmi_status}) | Goal: {goal}\n"
|
| 173 |
-
plan += "---------------------------------------------------\n\n"
|
| 174 |
-
|
| 175 |
-
plan += f"🌡️ WARM-UP ({warmup_time}):\n"
|
| 176 |
-
plan += "1. Joint Rotations (Neck, Shoulders, Wrists, Ankles)\n"
|
| 177 |
-
if injury != "Knee": plan += "2. Light Jumping Jacks or Skipping (2 mins)\n"
|
| 178 |
-
else: plan += "2. March in Place (High Knees) (2 mins)\n"
|
| 179 |
-
plan += "3. Dynamic Stretch: Arm Swings & Leg Swings\n\n"
|
| 180 |
-
|
| 181 |
-
plan += f"💪 MAIN WORKOUT (Protocol: {main_scheme} | Rest: {rest}):\n"
|
| 182 |
-
plan += f"1. LEG FOCUS: {legs[0]}\n"
|
| 183 |
-
plan += f"2. PUSH FOCUS: {push[0]}\n"
|
| 184 |
-
plan += f"3. PULL FOCUS: {pull[0]}\n"
|
| 185 |
-
if len(legs) > 1: plan += f"4. SECONDARY LEGS: {legs[1]}\n"
|
| 186 |
-
if len(push) > 1: plan += f"5. SECONDARY UPPER: {push[1]}\n"
|
| 187 |
-
|
| 188 |
-
plan += "\n🛡️ CORE & STABILITY:\n"
|
| 189 |
-
plan += f"1. {core[0]}: 3 sets\n"
|
| 190 |
-
if len(core) > 1: plan += f"2. {core[1]}: 3 sets\n"
|
| 191 |
-
|
| 192 |
-
plan += f"\n🏁 THE FINISHER (One Click Bonus):\n{finisher}\n"
|
| 193 |
-
|
| 194 |
-
return plan
|
| 195 |
-
|
| 196 |
-
# 5. GRADIO WRAPPER
|
| 197 |
-
def predict_wrapper(age, gender, weight, height, goal, equipment, injury, experience):
|
| 198 |
-
# 1. AI Logic (The CSV Model)
|
| 199 |
-
input_df = pd.DataFrame({
|
| 200 |
'Age': [age], 'Gender': [gender], 'Weight_kg': [weight],
|
| 201 |
'Height_cm': [height], 'Goal': [goal], 'Equipment': [equipment],
|
| 202 |
'Injury': [injury], 'Experience': [experience]
|
| 203 |
})
|
| 204 |
-
|
| 205 |
|
| 206 |
-
#
|
| 207 |
-
|
| 208 |
|
| 209 |
-
|
| 210 |
-
note = f"🧠 AI Logic: Model detected '{plan_category}'. We adjusted volume for {goal} and removed dangerous exercises for {injury}."
|
| 211 |
|
| 212 |
-
return
|
| 213 |
|
| 214 |
-
#
|
| 215 |
iface = gr.Interface(
|
| 216 |
-
fn=
|
| 217 |
inputs=[
|
| 218 |
-
gr.Slider(18, 80, value=25, label="Age"),
|
| 219 |
gr.Radio(["Male", "Female"], label="Gender", value="Male"),
|
| 220 |
gr.Number(label="Weight (kg)", value=75),
|
| 221 |
gr.Number(label="Height (cm)", value=175),
|
|
@@ -225,11 +256,11 @@ iface = gr.Interface(
|
|
| 225 |
gr.Radio(['Beginner', 'Intermediate', 'Advanced'], label="Experience", value="Intermediate")
|
| 226 |
],
|
| 227 |
outputs=[
|
| 228 |
-
gr.Textbox(label="
|
| 229 |
-
gr.Textbox(label="
|
| 230 |
],
|
| 231 |
-
title="SmartFit AI
|
| 232 |
-
description="
|
| 233 |
theme="soft"
|
| 234 |
)
|
| 235 |
|
|
|
|
| 3 |
import pandas as pd
|
| 4 |
import random
|
| 5 |
|
| 6 |
+
# 1. Load the AI Model
|
| 7 |
model = joblib.load('fitness_model.joblib')
|
| 8 |
|
| 9 |
+
# --- 2. THE EXTENDED EXERCISE DATABASE ---
|
| 10 |
+
# Format: Name, Equipment, Type, Main Muscle, Bad For (Injury), Min Level (0=All, 1=Int, 2=Adv)
|
| 11 |
+
|
| 12 |
+
EXERCISE_DB = [
|
| 13 |
+
# LEGS - QUAD DOMINANT
|
| 14 |
+
{"name": "Barbell Back Squat", "equip": ["Barbell", "Gym"], "type": "Compound", "muscle": "Quads", "bad_for": ["Back", "Knee"], "level": 2},
|
| 15 |
+
{"name": "Goblet Squat", "equip": ["Dumbbells", "Gym"], "type": "Compound", "muscle": "Quads", "bad_for": ["Knee"], "level": 0},
|
| 16 |
+
{"name": "Leg Press Machine", "equip": ["Gym"], "type": "Compound", "muscle": "Quads", "bad_for": [], "level": 0},
|
| 17 |
+
{"name": "Bulgarian Split Squat", "equip": ["Dumbbells", "Bodyweight"], "type": "Compound", "muscle": "Quads", "bad_for": ["Knee"], "level": 2},
|
| 18 |
+
{"name": "Step-Ups", "equip": ["Bodyweight", "Dumbbells"], "type": "Isolation", "muscle": "Quads", "bad_for": ["Knee"], "level": 0},
|
| 19 |
+
{"name": "Leg Extensions", "equip": ["Gym"], "type": "Isolation", "muscle": "Quads", "bad_for": ["Knee"], "level": 0},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
+
# LEGS - GLUTE/HAMSTRING DOMINANT
|
| 22 |
+
{"name": "Romanian Deadlift", "equip": ["Barbell", "Dumbbells"], "type": "Compound", "muscle": "Hamstrings", "bad_for": ["Back"], "level": 1},
|
| 23 |
+
{"name": "Glute Bridges", "equip": ["Bodyweight"], "type": "Isolation", "muscle": "Glutes", "bad_for": [], "level": 0},
|
| 24 |
+
{"name": "Hip Thrusts", "equip": ["Barbell", "Dumbbells", "Gym"], "type": "Compound", "muscle": "Glutes", "bad_for": ["Back"], "level": 1},
|
| 25 |
+
{"name": "Lying Leg Curl", "equip": ["Gym"], "type": "Isolation", "muscle": "Hamstrings", "bad_for": [], "level": 0},
|
| 26 |
+
{"name": "Walking Lunges", "equip": ["Bodyweight", "Dumbbells"], "type": "Compound", "muscle": "Glutes", "bad_for": ["Knee"], "level": 1},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
+
# PUSH - CHEST
|
| 29 |
+
{"name": "Barbell Bench Press", "equip": ["Barbell", "Gym"], "type": "Compound", "muscle": "Chest", "bad_for": ["Shoulder"], "level": 1},
|
| 30 |
+
{"name": "Dumbbell Chest Press", "equip": ["Dumbbells", "Gym"], "type": "Compound", "muscle": "Chest", "bad_for": [], "level": 0},
|
| 31 |
+
{"name": "Push-ups", "equip": ["Bodyweight"], "type": "Compound", "muscle": "Chest", "bad_for": ["Shoulder", "Wrist"], "level": 0},
|
| 32 |
+
{"name": "Incline Dumbbell Press", "equip": ["Dumbbells", "Gym"], "type": "Compound", "muscle": "Chest", "bad_for": ["Shoulder"], "level": 1},
|
| 33 |
+
{"name": "Cable Flys", "equip": ["Gym"], "type": "Isolation", "muscle": "Chest", "bad_for": [], "level": 1},
|
| 34 |
+
{"name": "Machine Chest Press", "equip": ["Gym"], "type": "Compound", "muscle": "Chest", "bad_for": [], "level": 0},
|
| 35 |
|
| 36 |
+
# PUSH - SHOULDERS
|
| 37 |
+
{"name": "Overhead Barbell Press", "equip": ["Barbell", "Gym"], "type": "Compound", "muscle": "Shoulders", "bad_for": ["Shoulder", "Back"], "level": 2},
|
| 38 |
+
{"name": "Seated Dumbbell Press", "equip": ["Dumbbells", "Gym"], "type": "Compound", "muscle": "Shoulders", "bad_for": ["Shoulder"], "level": 0},
|
| 39 |
+
{"name": "Lateral Raises", "equip": ["Dumbbells", "Gym"], "type": "Isolation", "muscle": "Shoulders", "bad_for": [], "level": 0},
|
| 40 |
+
{"name": "Front Raises", "equip": ["Dumbbells", "Gym"], "type": "Isolation", "muscle": "Shoulders", "bad_for": [], "level": 0},
|
| 41 |
+
{"name": "Face Pulls", "equip": ["Gym"], "type": "Isolation", "muscle": "Rear Delts", "bad_for": [], "level": 0},
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
+
# PUSH - TRICEPS
|
| 44 |
+
{"name": "Tricep Dips", "equip": ["Bodyweight", "Gym"], "type": "Compound", "muscle": "Triceps", "bad_for": ["Shoulder"], "level": 1},
|
| 45 |
+
{"name": "Skullcrushers", "equip": ["Barbell", "Dumbbells"], "type": "Isolation", "muscle": "Triceps", "bad_for": ["Elbow"], "level": 1},
|
| 46 |
+
{"name": "Cable Tricep Pushdown", "equip": ["Gym"], "type": "Isolation", "muscle": "Triceps", "bad_for": [], "level": 0},
|
| 47 |
+
{"name": "Diamond Push-ups", "equip": ["Bodyweight"], "type": "Compound", "muscle": "Triceps", "bad_for": ["Wrist"], "level": 2},
|
| 48 |
+
|
| 49 |
+
# PULL - BACK
|
| 50 |
+
{"name": "Pull-ups", "equip": ["Bodyweight", "Gym"], "type": "Compound", "muscle": "Back", "bad_for": ["Shoulder"], "level": 2},
|
| 51 |
+
{"name": "Lat Pulldown Machine", "equip": ["Gym"], "type": "Compound", "muscle": "Back", "bad_for": [], "level": 0},
|
| 52 |
+
{"name": "Barbell Bent Over Row", "equip": ["Barbell", "Gym"], "type": "Compound", "muscle": "Back", "bad_for": ["Back"], "level": 2},
|
| 53 |
+
{"name": "One Arm Dumbbell Row", "equip": ["Dumbbells", "Gym"], "type": "Compound", "muscle": "Back", "bad_for": [], "level": 0},
|
| 54 |
+
{"name": "Seated Cable Row", "equip": ["Gym"], "type": "Compound", "muscle": "Back", "bad_for": ["Back"], "level": 1},
|
| 55 |
+
{"name": "Inverted Row", "equip": ["Bodyweight"], "type": "Compound", "muscle": "Back", "bad_for": [], "level": 0},
|
| 56 |
+
|
| 57 |
+
# PULL - BICEPS
|
| 58 |
+
{"name": "Barbell Curl", "equip": ["Barbell", "Gym"], "type": "Isolation", "muscle": "Biceps", "bad_for": ["Wrist"], "level": 0},
|
| 59 |
+
{"name": "Hammer Curls", "equip": ["Dumbbells", "Gym"], "type": "Isolation", "muscle": "Biceps", "bad_for": [], "level": 0},
|
| 60 |
+
{"name": "Incline Dumbbell Curl", "equip": ["Dumbbells", "Gym"], "type": "Isolation", "muscle": "Biceps", "bad_for": [], "level": 0},
|
| 61 |
+
{"name": "Chin-ups", "equip": ["Bodyweight", "Gym"], "type": "Compound", "muscle": "Biceps", "bad_for": ["Shoulder"], "level": 2},
|
| 62 |
+
|
| 63 |
+
# CORE
|
| 64 |
+
{"name": "Plank", "equip": ["Bodyweight"], "type": "Isolation", "muscle": "Core", "bad_for": [], "level": 0},
|
| 65 |
+
{"name": "Hanging Leg Raises", "equip": ["Gym", "Barbell"], "type": "Isolation", "muscle": "Core", "bad_for": ["Shoulder"], "level": 2},
|
| 66 |
+
{"name": "Russian Twists", "equip": ["Bodyweight", "Dumbbells"], "type": "Isolation", "muscle": "Core", "bad_for": ["Back"], "level": 0},
|
| 67 |
+
{"name": "Ab Wheel Rollout", "equip": ["Gym", "Dumbbells"], "type": "Isolation", "muscle": "Core", "bad_for": ["Back"], "level": 2},
|
| 68 |
+
{"name": "Dead Bug", "equip": ["Bodyweight"], "type": "Isolation", "muscle": "Core", "bad_for": [], "level": 0},
|
| 69 |
+
|
| 70 |
+
# CARDIO
|
| 71 |
+
{"name": "Treadmill Run", "equip": ["Gym"], "type": "Cardio", "muscle": "Cardio", "bad_for": ["Knee"], "level": 0},
|
| 72 |
+
{"name": "Burpees", "equip": ["Bodyweight"], "type": "Cardio", "muscle": "Cardio", "bad_for": ["Knee", "Back"], "level": 2},
|
| 73 |
+
{"name": "Stationary Bike", "equip": ["Gym"], "type": "Cardio", "muscle": "Cardio", "bad_for": [], "level": 0},
|
| 74 |
+
{"name": "Jumping Jacks", "equip": ["Bodyweight"], "type": "Cardio", "muscle": "Cardio", "bad_for": ["Knee", "Ankle"], "level": 0},
|
| 75 |
+
{"name": "Mountain Climbers", "equip": ["Bodyweight"], "type": "Cardio", "muscle": "Cardio", "bad_for": ["Shoulder"], "level": 1}
|
| 76 |
+
]
|
| 77 |
|
| 78 |
+
# --- 3. LOGIC FUNCTIONS ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
+
def get_level_score(experience):
|
| 81 |
+
if experience == "Beginner": return 0
|
| 82 |
+
if experience == "Intermediate": return 1
|
| 83 |
+
return 2
|
| 84 |
|
| 85 |
+
def get_available_equipment(user_equip):
|
| 86 |
+
if user_equip == "Gym Membership": return ["Gym", "Barbell", "Dumbbells", "Bodyweight"]
|
| 87 |
+
if user_equip == "Full Home Gym (Rack+Barbell)": return ["Barbell", "Dumbbells", "Bodyweight"]
|
| 88 |
+
if user_equip == "Home Dumbbells": return ["Dumbbells", "Bodyweight"]
|
| 89 |
+
return ["Bodyweight"]
|
| 90 |
|
| 91 |
+
def filter_exercises(target_muscle, equipment_list, injury, user_level_score, exercise_type=None):
|
| 92 |
+
valid = []
|
| 93 |
+
for ex in EXERCISE_DB:
|
| 94 |
+
# Muscle Match
|
| 95 |
+
if ex["muscle"] != target_muscle: continue
|
| 96 |
+
|
| 97 |
+
# Equipment Match
|
| 98 |
+
has_equip = any(eq in equipment_list for eq in ex["equip"])
|
| 99 |
+
if not has_equip: continue
|
| 100 |
+
|
| 101 |
+
# Injury Match
|
| 102 |
+
if injury in ex["bad_for"]: continue
|
| 103 |
+
|
| 104 |
+
# Level Match (Don't give advanced moves to beginners)
|
| 105 |
+
if ex["level"] > user_level_score: continue
|
| 106 |
+
|
| 107 |
+
# Type Match (Compound vs Isolation) - Optional
|
| 108 |
+
if exercise_type and ex["type"] != exercise_type: continue
|
| 109 |
+
|
| 110 |
+
valid.append(ex["name"])
|
| 111 |
+
|
| 112 |
+
return valid
|
| 113 |
|
| 114 |
+
def generate_parameters(goal, experience, age):
|
| 115 |
+
# Calculates Sets/Reps/Rest based on exact inputs
|
| 116 |
+
sets = 3
|
| 117 |
+
reps = "10-12"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
rest = "60s"
|
| 119 |
|
| 120 |
+
if goal == "Strength":
|
| 121 |
+
sets = 5
|
| 122 |
+
reps = "3-5"
|
| 123 |
+
rest = "3 min"
|
| 124 |
+
if experience == "Beginner": sets = 3; reps = "5-8" # Safe start
|
| 125 |
+
|
| 126 |
+
elif goal == "Muscle Gain":
|
| 127 |
+
sets = 4
|
| 128 |
+
reps = "8-12"
|
| 129 |
rest = "90s"
|
| 130 |
+
|
|
|
|
|
|
|
| 131 |
elif goal == "Weight Loss":
|
| 132 |
+
sets = 3
|
| 133 |
+
reps = "15-20"
|
| 134 |
rest = "30s"
|
| 135 |
+
|
| 136 |
+
elif goal == "Endurance":
|
| 137 |
+
sets = 2
|
| 138 |
+
reps = "20+"
|
| 139 |
+
rest = "15s"
|
| 140 |
+
|
| 141 |
+
# Age Adjustment
|
| 142 |
+
if age >= 50:
|
| 143 |
+
rest = f"{rest} (+30s)"
|
| 144 |
+
if goal == "Strength": sets -= 1 # Reduce volume for seniors
|
| 145 |
+
|
| 146 |
+
return f"{sets} Sets | {reps} Reps | {rest} Rest"
|
| 147 |
+
|
| 148 |
+
def select_exercise(muscle, equip, injury, level, type_pref, fallback_name):
|
| 149 |
+
# Tries to find specific type (Compound), falls back to any valid
|
| 150 |
+
options = filter_exercises(muscle, equip, injury, level, type_pref)
|
| 151 |
+
if not options:
|
| 152 |
+
options = filter_exercises(muscle, equip, injury, level) # Try any type
|
| 153 |
+
|
| 154 |
+
if not options:
|
| 155 |
+
return f"Standard {muscle} Exercise (Safe Mode)"
|
| 156 |
+
|
| 157 |
+
return random.choice(options)
|
| 158 |
+
|
| 159 |
+
# --- 4. MAIN WORKOUT BUILDER ---
|
| 160 |
+
|
| 161 |
+
def generate_workout(plan_name, age, weight, goal, equipment, injury, experience):
|
| 162 |
+
# A. Setup
|
| 163 |
+
level_score = get_level_score(experience)
|
| 164 |
+
equip_list = get_available_equipment(equipment)
|
| 165 |
+
params = generate_parameters(goal, experience, age)
|
| 166 |
+
|
| 167 |
+
# B. Define Routine Skeleton based on Body Part Split
|
| 168 |
+
# Logic: AI Predicted "Upper Body" -> We give Chest+Back+Shoulders
|
| 169 |
+
|
| 170 |
+
skeleton = [] # List of (Muscle, Type) tuples
|
| 171 |
+
|
| 172 |
+
if "Upper" in plan_name or "Push" in plan_name:
|
| 173 |
+
skeleton = [
|
| 174 |
+
("Chest", "Compound"), ("Back", "Compound"),
|
| 175 |
+
("Shoulders", "Compound"), ("Chest", "Isolation"),
|
| 176 |
+
("Back", "Isolation"), ("Triceps", "Isolation"), ("Biceps", "Isolation")
|
| 177 |
+
]
|
| 178 |
+
title = "UPPER BODY POWER"
|
| 179 |
+
|
| 180 |
+
elif "Lower" in plan_name or "Legs" in plan_name:
|
| 181 |
+
skeleton = [
|
| 182 |
+
("Quads", "Compound"), ("Hamstrings", "Compound"),
|
| 183 |
+
("Glutes", "Compound"), ("Quads", "Isolation"),
|
| 184 |
+
("Hamstrings", "Isolation"), ("Core", "Isolation")
|
| 185 |
+
]
|
| 186 |
+
title = "LOWER BODY & CORE"
|
| 187 |
+
|
| 188 |
+
else: # Full Body / Generic
|
| 189 |
+
skeleton = [
|
| 190 |
+
("Quads", "Compound"), ("Chest", "Compound"),
|
| 191 |
+
("Back", "Compound"), ("Hamstrings", "Isolation"),
|
| 192 |
+
("Shoulders", "Isolation"), ("Core", "Isolation")
|
| 193 |
+
]
|
| 194 |
+
title = "FULL BODY CONDITIONING"
|
| 195 |
+
|
| 196 |
+
# C. Build Text
|
| 197 |
+
text = f"WORKOUT PLAN: {title}\n"
|
| 198 |
+
text += f"Profile: {int(age)} years old | Level: {experience} | Goal: {goal}\n"
|
| 199 |
+
text += f"Config: {equipment} | Injury Avoidance: {injury}\n"
|
| 200 |
+
text += "-" * 50 + "\n\n"
|
| 201 |
+
|
| 202 |
+
text += f"PROTOCOL: {params}\n\n"
|
| 203 |
+
|
| 204 |
+
text += "WARM-UP (5-7 mins):\n"
|
| 205 |
+
if injury == "Knee":
|
| 206 |
+
text += "1. Arm Circles & Torso Rotation\n2. Glute Bridges\n3. Cat-Cow Stretch\n\n"
|
| 207 |
+
else:
|
| 208 |
+
text += "1. Jumping Jacks (2 mins)\n2. Bodyweight Squats\n3. Arm Swings\n\n"
|
| 209 |
+
|
| 210 |
+
text += "MAIN EXERCISES:\n"
|
| 211 |
+
|
| 212 |
+
for i, (muscle, ex_type) in enumerate(skeleton):
|
| 213 |
+
ex_name = select_exercise(muscle, equip_list, injury, level_score, ex_type, "Generic Move")
|
| 214 |
+
text += f"{i+1}. {ex_name} ({muscle} - {ex_type})\n"
|
| 215 |
+
|
| 216 |
+
text += "\nFINISHER (Burnout):\n"
|
| 217 |
+
if goal in ["Weight Loss", "Cardio"]:
|
| 218 |
+
cardio_opts = filter_exercises("Cardio", equip_list, injury, level_score)
|
| 219 |
+
finisher = random.choice(cardio_opts) if cardio_opts else "March in Place"
|
| 220 |
+
text += f"--> {finisher}: 3 Rounds x 45 Seconds (Max Effort)"
|
| 221 |
+
else:
|
| 222 |
+
core_opts = filter_exercises("Core", equip_list, injury, level_score)
|
| 223 |
+
finisher = random.choice(core_opts) if core_opts else "Plank"
|
| 224 |
+
text += f"--> {finisher}: 3 Sets x Failure"
|
| 225 |
+
|
| 226 |
+
return text
|
| 227 |
|
| 228 |
+
# 5. GRADIO INTERFACE
|
| 229 |
+
def predict(age, gender, weight, height, goal, equipment, injury, experience):
|
| 230 |
+
# AI Prediction
|
| 231 |
+
input_data = pd.DataFrame({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
'Age': [age], 'Gender': [gender], 'Weight_kg': [weight],
|
| 233 |
'Height_cm': [height], 'Goal': [goal], 'Equipment': [equipment],
|
| 234 |
'Injury': [injury], 'Experience': [experience]
|
| 235 |
})
|
| 236 |
+
plan_name = model.predict(input_data)[0]
|
| 237 |
|
| 238 |
+
# Python Generation
|
| 239 |
+
final_routine = generate_workout(plan_name, age, weight, goal, equipment, injury, experience)
|
| 240 |
|
| 241 |
+
logic_note = f"System detected '{plan_name}'. Parameters adjusted for {experience} level."
|
|
|
|
| 242 |
|
| 243 |
+
return final_routine, logic_note
|
| 244 |
|
| 245 |
+
# Launch
|
| 246 |
iface = gr.Interface(
|
| 247 |
+
fn=predict,
|
| 248 |
inputs=[
|
| 249 |
+
gr.Slider(18, 80, step=1, value=25, label="Age"), # Step=1 forces integers
|
| 250 |
gr.Radio(["Male", "Female"], label="Gender", value="Male"),
|
| 251 |
gr.Number(label="Weight (kg)", value=75),
|
| 252 |
gr.Number(label="Height (cm)", value=175),
|
|
|
|
| 256 |
gr.Radio(['Beginner', 'Intermediate', 'Advanced'], label="Experience", value="Intermediate")
|
| 257 |
],
|
| 258 |
outputs=[
|
| 259 |
+
gr.Textbox(label="Generated Workout", lines=18),
|
| 260 |
+
gr.Textbox(label="Logic Trace")
|
| 261 |
],
|
| 262 |
+
title="SmartFit AI - Pro Edition",
|
| 263 |
+
description="Generates strict, equipment-specific workouts based on AI analysis.",
|
| 264 |
theme="soft"
|
| 265 |
)
|
| 266 |
|