Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,264 +3,230 @@ import joblib
|
|
| 3 |
import pandas as pd
|
| 4 |
import random
|
| 5 |
|
| 6 |
-
# 1. Load
|
| 7 |
model = joblib.load('fitness_model.joblib')
|
| 8 |
|
| 9 |
-
# --- 2. THE
|
| 10 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 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 |
-
|
| 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 |
-
#
|
| 50 |
-
|
| 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 |
-
#
|
| 58 |
-
|
| 59 |
-
|
| 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 |
-
#
|
| 64 |
-
|
| 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 |
-
|
| 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
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
| 90 |
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
for ex in
|
| 94 |
-
# Muscle Match
|
| 95 |
if ex["muscle"] != target_muscle: continue
|
| 96 |
|
| 97 |
-
#
|
| 98 |
-
|
| 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 |
-
#
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
-
|
|
|
|
| 111 |
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 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 |
-
|
| 142 |
-
if age >= 50:
|
| 143 |
-
rest = f"{rest} (+30s)"
|
| 144 |
-
if goal == "Strength": sets -= 1 # Reduce volume for seniors
|
| 145 |
|
| 146 |
-
return
|
| 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.
|
| 160 |
|
| 161 |
-
def
|
| 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 |
-
#
|
| 168 |
-
|
|
|
|
|
|
|
| 169 |
|
| 170 |
-
|
|
|
|
|
|
|
| 171 |
|
| 172 |
if "Upper" in plan_name or "Push" in plan_name:
|
| 173 |
-
|
| 174 |
("Chest", "Compound"), ("Back", "Compound"),
|
| 175 |
("Shoulders", "Compound"), ("Chest", "Isolation"),
|
| 176 |
-
("Back", "Isolation"), ("
|
| 177 |
]
|
| 178 |
-
title = "UPPER BODY POWER"
|
| 179 |
-
|
| 180 |
elif "Lower" in plan_name or "Legs" in plan_name:
|
| 181 |
-
|
| 182 |
-
("
|
| 183 |
-
("
|
| 184 |
-
("
|
| 185 |
]
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
("
|
| 191 |
-
("Back", "Compound"), ("Hamstrings", "Isolation"),
|
| 192 |
-
("Shoulders", "Isolation"), ("Core", "Isolation")
|
| 193 |
]
|
| 194 |
-
title = "FULL BODY CONDITIONING"
|
| 195 |
|
| 196 |
-
# C. Build
|
| 197 |
-
text = f"WORKOUT PLAN
|
| 198 |
-
text += f"
|
| 199 |
-
text += f"
|
| 200 |
-
text += "
|
| 201 |
|
| 202 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
|
| 204 |
-
text += "
|
| 205 |
-
|
| 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 |
-
|
| 211 |
|
| 212 |
-
for i, (muscle, ex_type) in enumerate(
|
| 213 |
-
|
| 214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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.
|
| 229 |
-
def
|
| 230 |
-
# AI
|
| 231 |
-
|
| 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(
|
| 237 |
-
|
| 238 |
-
# Python Generation
|
| 239 |
-
final_routine = generate_workout(plan_name, age, weight, goal, equipment, injury, experience)
|
| 240 |
|
| 241 |
-
|
|
|
|
| 242 |
|
| 243 |
-
return
|
| 244 |
|
| 245 |
-
#
|
| 246 |
iface = gr.Interface(
|
| 247 |
-
fn=
|
| 248 |
inputs=[
|
| 249 |
-
gr.Slider(18, 80, step=1, value=25, label="Age"),
|
| 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),
|
| 253 |
-
gr.Dropdown(['Weight Loss', 'Muscle Gain', 'Endurance', 'General Health', 'Strength'], label="Goal", value="
|
| 254 |
gr.Dropdown(['Gym Membership', 'Home Dumbbells', 'Bodyweight Only', 'Full Home Gym (Rack+Barbell)'], label="Equipment", value="Gym Membership"),
|
| 255 |
gr.Dropdown(['None', 'Knee', 'Back', 'Shoulder', 'Ankle'], label="Injury", value="None"),
|
| 256 |
gr.Radio(['Beginner', 'Intermediate', 'Advanced'], label="Experience", value="Intermediate")
|
| 257 |
],
|
| 258 |
outputs=[
|
| 259 |
-
gr.Textbox(label="
|
| 260 |
-
gr.Textbox(label="
|
| 261 |
],
|
| 262 |
-
title="SmartFit AI -
|
| 263 |
-
description="Generates strict
|
| 264 |
theme="soft"
|
| 265 |
)
|
| 266 |
|
|
|
|
| 3 |
import pandas as pd
|
| 4 |
import random
|
| 5 |
|
| 6 |
+
# 1. Load Model
|
| 7 |
model = joblib.load('fitness_model.joblib')
|
| 8 |
|
| 9 |
+
# --- 2. THE INTELLIGENT EXERCISE DATABASE ---
|
| 10 |
+
# base_ratio: How much of bodyweight a standard male can lift in this exercise.
|
| 11 |
+
# min_level: 0=Beginner, 1=Intermediate, 2=Advanced
|
| 12 |
+
|
| 13 |
+
DB = [
|
| 14 |
+
# --- CHEST ---
|
| 15 |
+
{"name": "Barbell Bench Press", "muscle": "Chest", "type": "Compound", "equip": ["Barbell", "Gym"], "bad_for": ["Shoulder"], "min_level": 1, "base_ratio": 0.8},
|
| 16 |
+
{"name": "Dumbbell Chest Press", "muscle": "Chest", "type": "Compound", "equip": ["Dumbbells", "Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.35}, # Per hand
|
| 17 |
+
{"name": "Machine Chest Press", "muscle": "Chest", "type": "Isolation", "equip": ["Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.6},
|
| 18 |
+
{"name": "Incline Dumbbell Press", "muscle": "Chest", "type": "Compound", "equip": ["Dumbbells", "Gym"], "bad_for": ["Shoulder"], "min_level": 1, "base_ratio": 0.3},
|
| 19 |
+
{"name": "Cable Crossovers", "muscle": "Chest", "type": "Isolation", "equip": ["Gym"], "bad_for": [], "min_level": 1, "base_ratio": 0.15},
|
| 20 |
+
{"name": "Push-ups", "muscle": "Chest", "type": "Compound", "equip": ["Bodyweight"], "bad_for": ["Wrist"], "min_level": 0, "base_ratio": 0},
|
| 21 |
+
{"name": "Weighted Dips", "muscle": "Chest", "type": "Compound", "equip": ["Gym", "Bodyweight"], "bad_for": ["Shoulder"], "min_level": 2, "base_ratio": 0},
|
| 22 |
+
|
| 23 |
+
# --- BACK ---
|
| 24 |
+
{"name": "Deadlift", "muscle": "Back", "type": "Compound", "equip": ["Barbell", "Gym"], "bad_for": ["Back"], "min_level": 2, "base_ratio": 1.2},
|
| 25 |
+
{"name": "Lat Pulldown", "muscle": "Back", "type": "Compound", "equip": ["Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.6},
|
| 26 |
+
{"name": "Seated Cable Row", "muscle": "Back", "type": "Isolation", "equip": ["Gym"], "bad_for": ["Back"], "min_level": 0, "base_ratio": 0.6},
|
| 27 |
+
{"name": "Barbell Bent Over Row", "muscle": "Back", "type": "Compound", "equip": ["Barbell", "Gym"], "bad_for": ["Back"], "min_level": 2, "base_ratio": 0.7},
|
| 28 |
+
{"name": "Single Arm Dumbbell Row", "muscle": "Back", "type": "Isolation", "equip": ["Dumbbells", "Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.3},
|
| 29 |
+
{"name": "Pull-ups", "muscle": "Back", "type": "Compound", "equip": ["Bodyweight", "Gym"], "bad_for": ["Shoulder"], "min_level": 1, "base_ratio": 0},
|
| 30 |
+
|
| 31 |
+
# --- LEGS ---
|
| 32 |
+
{"name": "Barbell Squat", "muscle": "Legs", "type": "Compound", "equip": ["Barbell", "Gym"], "bad_for": ["Knee", "Back"], "min_level": 2, "base_ratio": 1.0},
|
| 33 |
+
{"name": "Leg Press", "muscle": "Legs", "type": "Compound", "equip": ["Gym"], "bad_for": [], "min_level": 0, "base_ratio": 1.5},
|
| 34 |
+
{"name": "Goblet Squat", "muscle": "Legs", "type": "Compound", "equip": ["Dumbbells", "Gym"], "bad_for": ["Knee"], "min_level": 0, "base_ratio": 0.3},
|
| 35 |
+
{"name": "Walking Lunges", "muscle": "Legs", "type": "Compound", "equip": ["Dumbbells", "Bodyweight"], "bad_for": ["Knee"], "min_level": 1, "base_ratio": 0.15},
|
| 36 |
+
{"name": "Leg Extensions", "muscle": "Legs", "type": "Isolation", "equip": ["Gym"], "bad_for": ["Knee"], "min_level": 0, "base_ratio": 0.4},
|
| 37 |
+
{"name": "Romanian Deadlift", "muscle": "Legs", "type": "Compound", "equip": ["Barbell", "Dumbbells"], "bad_for": ["Back"], "min_level": 1, "base_ratio": 0.8},
|
| 38 |
+
{"name": "Lying Leg Curl", "muscle": "Legs", "type": "Isolation", "equip": ["Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.3},
|
| 39 |
+
|
| 40 |
+
# --- SHOULDERS ---
|
| 41 |
+
{"name": "Overhead Barbell Press", "muscle": "Shoulders", "type": "Compound", "equip": ["Barbell", "Gym"], "bad_for": ["Shoulder", "Back"], "min_level": 2, "base_ratio": 0.5},
|
| 42 |
+
{"name": "Seated Dumbbell Press", "muscle": "Shoulders", "type": "Compound", "equip": ["Dumbbells", "Gym"], "bad_for": ["Shoulder"], "min_level": 0, "base_ratio": 0.2},
|
| 43 |
+
{"name": "Lateral Raises", "muscle": "Shoulders", "type": "Isolation", "equip": ["Dumbbells", "Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.05}, # Light!
|
| 44 |
+
{"name": "Face Pulls", "muscle": "Shoulders", "type": "Isolation", "equip": ["Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.2},
|
| 45 |
+
|
| 46 |
+
# --- ARMS ---
|
| 47 |
+
{"name": "Barbell Curl", "muscle": "Arms", "type": "Isolation", "equip": ["Barbell", "Gym"], "bad_for": ["Wrist"], "min_level": 0, "base_ratio": 0.3},
|
| 48 |
+
{"name": "Dumbbell Hammer Curl", "muscle": "Arms", "type": "Isolation", "equip": ["Dumbbells", "Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.15},
|
| 49 |
+
{"name": "Tricep Rope Pushdown", "muscle": "Arms", "type": "Isolation", "equip": ["Gym"], "bad_for": [], "min_level": 0, "base_ratio": 0.3},
|
| 50 |
+
{"name": "Skullcrushers", "muscle": "Arms", "type": "Isolation", "equip": ["Barbell", "Gym"], "bad_for": ["Elbow"], "min_level": 1, "base_ratio": 0.25},
|
| 51 |
+
|
| 52 |
+
# --- CORE ---
|
| 53 |
+
{"name": "Plank", "muscle": "Core", "type": "Static", "equip": ["Bodyweight"], "bad_for": [], "min_level": 0, "base_ratio": 0},
|
| 54 |
+
{"name": "Cable Woodchoppers", "muscle": "Core", "type": "Isolation", "equip": ["Gym"], "bad_for": ["Back"], "min_level": 1, "base_ratio": 0.3},
|
| 55 |
+
{"name": "Hanging Leg Raises", "muscle": "Core", "type": "Isolation", "equip": ["Gym"], "bad_for": ["Shoulder"], "min_level": 2, "base_ratio": 0}
|
| 56 |
+
]
|
| 57 |
|
| 58 |
+
# --- 3. THE CALCULATOR ENGINE ---
|
| 59 |
+
|
| 60 |
+
def calculate_weight(base_ratio, user_weight, gender, experience):
|
| 61 |
+
if base_ratio == 0: return "Bodyweight"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
+
load = user_weight * base_ratio
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
|
| 65 |
+
# Adjust for Gender
|
| 66 |
+
if gender == "Female": load *= 0.6
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
+
# Adjust for Experience
|
| 69 |
+
if experience == "Beginner": load *= 0.7
|
| 70 |
+
elif experience == "Advanced": load *= 1.3
|
|
|
|
|
|
|
| 71 |
|
| 72 |
+
# Round to nearest 2.5kg (standard plate size)
|
| 73 |
+
load = round(load / 2.5) * 2.5
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
return f"{int(load)} kg"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
+
def get_exercises(target_muscle, target_type, user_equip, user_injury, user_level_score):
|
| 78 |
+
|
| 79 |
+
# Define Equipment Lists
|
| 80 |
+
my_gear = []
|
| 81 |
+
if user_equip == "Gym Membership": my_gear = ["Gym", "Barbell", "Dumbbells", "Bodyweight"]
|
| 82 |
+
elif user_equip == "Full Home Gym (Rack+Barbell)": my_gear = ["Barbell", "Dumbbells", "Bodyweight"]
|
| 83 |
+
elif user_equip == "Home Dumbbells": my_gear = ["Dumbbells", "Bodyweight"]
|
| 84 |
+
else: my_gear = ["Bodyweight"]
|
| 85 |
|
| 86 |
+
candidates = []
|
| 87 |
+
|
| 88 |
+
for ex in DB:
|
| 89 |
+
# 1. Muscle Match
|
| 90 |
if ex["muscle"] != target_muscle: continue
|
| 91 |
|
| 92 |
+
# 2. Type Match (Compound vs Isolation) - Optional but good for structure
|
| 93 |
+
if target_type and ex["type"] != target_type: continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
+
# 3. Equipment Match
|
| 96 |
+
has_gear = False
|
| 97 |
+
for gear in ex["equip"]:
|
| 98 |
+
if gear in my_gear:
|
| 99 |
+
has_gear = True
|
| 100 |
+
break
|
| 101 |
+
if not has_gear: continue
|
| 102 |
|
| 103 |
+
# 4. Injury Check
|
| 104 |
+
if user_injury in ex["bad_for"]: continue
|
| 105 |
|
| 106 |
+
# 5. Level Check (Strict)
|
| 107 |
+
# Beginner (0) cannot do Advanced (2)
|
| 108 |
+
if user_level_score < ex["min_level"]: continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
+
candidates.append(ex)
|
|
|
|
|
|
|
|
|
|
| 111 |
|
| 112 |
+
return candidates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
+
# --- 4. ROUTINE GENERATOR ---
|
| 115 |
|
| 116 |
+
def generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
+
# A. Parse Level
|
| 119 |
+
level_score = 0
|
| 120 |
+
if experience == "Intermediate": level_score = 1
|
| 121 |
+
if experience == "Advanced": level_score = 2
|
| 122 |
|
| 123 |
+
# B. Define Structure based on AI Plan
|
| 124 |
+
# We create slots: (Muscle, Type)
|
| 125 |
+
slots = []
|
| 126 |
|
| 127 |
if "Upper" in plan_name or "Push" in plan_name:
|
| 128 |
+
slots = [
|
| 129 |
("Chest", "Compound"), ("Back", "Compound"),
|
| 130 |
("Shoulders", "Compound"), ("Chest", "Isolation"),
|
| 131 |
+
("Back", "Isolation"), ("Arms", "Isolation")
|
| 132 |
]
|
|
|
|
|
|
|
| 133 |
elif "Lower" in plan_name or "Legs" in plan_name:
|
| 134 |
+
slots = [
|
| 135 |
+
("Legs", "Compound"), ("Legs", "Compound"), # Two big lifts
|
| 136 |
+
("Legs", "Isolation"), ("Legs", "Isolation"),
|
| 137 |
+
("Core", "Isolation")
|
| 138 |
]
|
| 139 |
+
else: # Full Body
|
| 140 |
+
slots = [
|
| 141 |
+
("Legs", "Compound"), ("Chest", "Compound"),
|
| 142 |
+
("Back", "Compound"), ("Shoulders", "Compound"),
|
| 143 |
+
("Arms", "Isolation"), ("Core", "Static")
|
|
|
|
|
|
|
| 144 |
]
|
|
|
|
| 145 |
|
| 146 |
+
# C. Build Output
|
| 147 |
+
text = f"CUSTOM WORKOUT PLAN\n"
|
| 148 |
+
text += f"Config: {gender}, {weight}kg, {experience}, {equipment}\n"
|
| 149 |
+
text += f"Focus: {goal} | Avoid: {injury}\n"
|
| 150 |
+
text += "="*40 + "\n\n"
|
| 151 |
|
| 152 |
+
# Sets/Reps Logic
|
| 153 |
+
sets = "3"
|
| 154 |
+
reps = "10"
|
| 155 |
+
rest = "60s"
|
| 156 |
+
if goal == "Strength": sets="5"; reps="5"; rest="3 min"
|
| 157 |
+
if goal == "Muscle Gain": sets="4"; reps="8-12"; rest="90s"
|
| 158 |
+
if goal == "Weight Loss": sets="3"; reps="15+"; rest="30s"
|
| 159 |
|
| 160 |
+
text += f"GLOBAL PROTOCOL: {sets} Sets | {reps} Reps | {rest} Rest\n\n"
|
| 161 |
+
text += "EXERCISES:\n"
|
|
|
|
|
|
|
|
|
|
| 162 |
|
| 163 |
+
used_names = [] # To prevent duplicates
|
| 164 |
|
| 165 |
+
for i, (muscle, ex_type) in enumerate(slots):
|
| 166 |
+
# 1. Get Options
|
| 167 |
+
options = get_exercises(muscle, ex_type, equipment, injury, level_score)
|
| 168 |
+
|
| 169 |
+
# 2. Fallback if empty (e.g., no compound chest for bodyweight beginner)
|
| 170 |
+
if not options:
|
| 171 |
+
options = get_exercises(muscle, None, equipment, injury, level_score)
|
| 172 |
+
|
| 173 |
+
# 3. Random Selection (Variety!)
|
| 174 |
+
if not options:
|
| 175 |
+
selected = {"name": f"Generic {muscle} Move", "base_ratio": 0}
|
| 176 |
+
else:
|
| 177 |
+
# Shuffle and pick one that hasn't been used
|
| 178 |
+
random.shuffle(options)
|
| 179 |
+
selected = options[0]
|
| 180 |
+
for opt in options:
|
| 181 |
+
if opt["name"] not in used_names:
|
| 182 |
+
selected = opt
|
| 183 |
+
break
|
| 184 |
+
|
| 185 |
+
used_names.append(selected["name"])
|
| 186 |
+
|
| 187 |
+
# 4. Calculate Weight
|
| 188 |
+
rec_weight = calculate_weight(selected.get("base_ratio", 0), weight, gender, experience)
|
| 189 |
+
|
| 190 |
+
text += f"{i+1}. {selected['name']}\n"
|
| 191 |
+
text += f" Target Load: {rec_weight}\n"
|
| 192 |
+
text += f" Type: {muscle} {selected.get('type', '')}\n\n"
|
| 193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
return text
|
| 195 |
|
| 196 |
+
# 5. WRAPPER
|
| 197 |
+
def predict_wrapper(age, gender, weight, height, goal, equipment, injury, experience):
|
| 198 |
+
# AI Logic
|
| 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 |
+
plan_name = model.predict(input_df)[0]
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
+
# Generator Logic
|
| 207 |
+
routine = generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience)
|
| 208 |
|
| 209 |
+
return routine, f"AI Strategy: Selected '{plan_name}' based on injury profile."
|
| 210 |
|
| 211 |
+
# 6. LAUNCH
|
| 212 |
iface = gr.Interface(
|
| 213 |
+
fn=predict_wrapper,
|
| 214 |
inputs=[
|
| 215 |
+
gr.Slider(18, 80, step=1, value=25, label="Age"),
|
| 216 |
gr.Radio(["Male", "Female"], label="Gender", value="Male"),
|
| 217 |
gr.Number(label="Weight (kg)", value=75),
|
| 218 |
gr.Number(label="Height (cm)", value=175),
|
| 219 |
+
gr.Dropdown(['Weight Loss', 'Muscle Gain', 'Endurance', 'General Health', 'Strength'], label="Goal", value="Muscle Gain"),
|
| 220 |
gr.Dropdown(['Gym Membership', 'Home Dumbbells', 'Bodyweight Only', 'Full Home Gym (Rack+Barbell)'], label="Equipment", value="Gym Membership"),
|
| 221 |
gr.Dropdown(['None', 'Knee', 'Back', 'Shoulder', 'Ankle'], label="Injury", value="None"),
|
| 222 |
gr.Radio(['Beginner', 'Intermediate', 'Advanced'], label="Experience", value="Intermediate")
|
| 223 |
],
|
| 224 |
outputs=[
|
| 225 |
+
gr.Textbox(label="Result", lines=20),
|
| 226 |
+
gr.Textbox(label="Note")
|
| 227 |
],
|
| 228 |
+
title="SmartFit AI - Calculator Edition",
|
| 229 |
+
description="Generates strict workouts with specific weight recommendations.",
|
| 230 |
theme="soft"
|
| 231 |
)
|
| 232 |
|