Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -6,196 +6,252 @@ import random
|
|
| 6 |
# 1. Load Model
|
| 7 |
model = joblib.load('fitness_model.joblib')
|
| 8 |
|
| 9 |
-
#
|
| 10 |
-
#
|
| 11 |
-
#
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
if base_ratio == 0: return "Bodyweight"
|
| 62 |
|
| 63 |
load = user_weight * base_ratio
|
| 64 |
|
| 65 |
-
#
|
| 66 |
if gender == "Female": load *= 0.6
|
|
|
|
|
|
|
| 67 |
|
| 68 |
-
#
|
| 69 |
-
|
| 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(
|
| 78 |
-
|
| 79 |
-
# Define Equipment Lists
|
| 80 |
my_gear = []
|
| 81 |
-
if
|
| 82 |
-
elif
|
| 83 |
-
elif
|
| 84 |
else: my_gear = ["Bodyweight"]
|
| 85 |
|
| 86 |
-
|
|
|
|
|
|
|
| 87 |
|
| 88 |
-
for ex in
|
| 89 |
-
#
|
| 90 |
-
if ex["
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
|
|
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 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 |
-
#
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
|
|
|
| 122 |
|
| 123 |
-
#
|
| 124 |
-
#
|
| 125 |
slots = []
|
| 126 |
|
| 127 |
if "Upper" in plan_name or "Push" in plan_name:
|
| 128 |
-
slots = [
|
| 129 |
-
|
| 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 |
-
|
| 136 |
-
("Legs", "Isolation"), ("Legs", "Isolation"),
|
| 137 |
-
("Core", "Isolation")
|
| 138 |
-
]
|
| 139 |
else: # Full Body
|
| 140 |
-
slots = [
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
text
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
text += "
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 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 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 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[
|
| 186 |
|
| 187 |
-
#
|
| 188 |
-
|
| 189 |
|
| 190 |
-
text += f"{i+1}. {selected['name']}\n"
|
| 191 |
-
text += f"
|
| 192 |
-
text += f" Type: {muscle} {selected.get('type', '')}\n\n"
|
| 193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
return text
|
| 195 |
|
| 196 |
-
#
|
| 197 |
def predict_wrapper(age, gender, weight, height, goal, equipment, injury, experience):
|
| 198 |
-
# AI
|
| 199 |
input_df = pd.DataFrame({
|
| 200 |
'Age': [age], 'Gender': [gender], 'Weight_kg': [weight],
|
| 201 |
'Height_cm': [height], 'Goal': [goal], 'Equipment': [equipment],
|
|
@@ -203,12 +259,15 @@ def predict_wrapper(age, gender, weight, height, goal, equipment, injury, experi
|
|
| 203 |
})
|
| 204 |
plan_name = model.predict(input_df)[0]
|
| 205 |
|
| 206 |
-
#
|
| 207 |
routine = generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience)
|
| 208 |
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
-
#
|
| 212 |
iface = gr.Interface(
|
| 213 |
fn=predict_wrapper,
|
| 214 |
inputs=[
|
|
@@ -216,18 +275,18 @@ iface = gr.Interface(
|
|
| 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(
|
| 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.
|
| 223 |
],
|
| 224 |
outputs=[
|
| 225 |
-
gr.Textbox(label="Result", lines=
|
| 226 |
-
gr.Textbox(label="Note")
|
| 227 |
],
|
| 228 |
-
title="SmartFit AI -
|
| 229 |
-
description="
|
| 230 |
theme="soft"
|
| 231 |
)
|
| 232 |
|
| 233 |
-
iface.launch()
|
|
|
|
| 6 |
# 1. Load Model
|
| 7 |
model = joblib.load('fitness_model.joblib')
|
| 8 |
|
| 9 |
+
# ==========================================
|
| 10 |
+
# CONFIG DICTIONARIES (THE BRAIN)
|
| 11 |
+
# ==========================================
|
| 12 |
+
|
| 13 |
+
# 1. GOAL CONFIGURATION
|
| 14 |
+
# Defines the structure (slots), sets, reps, and specific instructions per goal.
|
| 15 |
+
GOAL_CONFIG = {
|
| 16 |
+
"Strength": {
|
| 17 |
+
"sets": 5, "reps": "3-5", "rest": "3-5 min",
|
| 18 |
+
"intensity": "85-90% 1RM",
|
| 19 |
+
"structure_modifier": "Focus on Compound Lifts",
|
| 20 |
+
"finisher": "Core Static Hold"
|
| 21 |
+
},
|
| 22 |
+
"Muscle Gain": {
|
| 23 |
+
"sets": 3, "reps": "8-12", "rest": "60-90 sec",
|
| 24 |
+
"intensity": "70-80% 1RM",
|
| 25 |
+
"structure_modifier": "Mix of Compound and Isolation",
|
| 26 |
+
"finisher": "Pump Work (High Reps)"
|
| 27 |
+
},
|
| 28 |
+
"Weight Loss": {
|
| 29 |
+
"sets": 3, "reps": "15-20", "rest": "30-45 sec",
|
| 30 |
+
"intensity": "60% 1RM",
|
| 31 |
+
"structure_modifier": "Circuit Style (Minimal Rest)",
|
| 32 |
+
"finisher": "High Intensity Interval (HIIT)"
|
| 33 |
+
},
|
| 34 |
+
"Endurance": {
|
| 35 |
+
"sets": 2, "reps": "20+", "rest": "30 sec",
|
| 36 |
+
"intensity": "50% 1RM",
|
| 37 |
+
"structure_modifier": "High Volume",
|
| 38 |
+
"finisher": "Steady State Cardio"
|
| 39 |
+
},
|
| 40 |
+
"General Health": {
|
| 41 |
+
"sets": 3, "reps": "12-15", "rest": "60 sec",
|
| 42 |
+
"intensity": "Moderate",
|
| 43 |
+
"structure_modifier": "Balanced Full Body",
|
| 44 |
+
"finisher": "Mobility Flow"
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
# 2. AGE CONFIGURATION
|
| 49 |
+
# Adjusts warmup and safety parameters based on age groups.
|
| 50 |
+
AGE_CONFIG = {
|
| 51 |
+
"Young (18-35)": {"warmup": "5 min", "rest_mod": 0, "type": "Standard"},
|
| 52 |
+
"Middle (36-55)": {"warmup": "8 min", "rest_mod": 15, "type": "Joint Prep Focus"},
|
| 53 |
+
"Senior (56+)": {"warmup": "12 min", "rest_mod": 30, "type": "Low Impact & Stability"}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
# 3. LEVEL CONFIGURATION
|
| 57 |
+
# Defines which difficulty tier (0-2) a user can access.
|
| 58 |
+
LEVEL_CONFIG = {
|
| 59 |
+
"Beginner": {"tier": 0, "complexity": "Low (Machines/Stable)"},
|
| 60 |
+
"Intermediate": {"tier": 1, "complexity": "Medium (Free Weights)"},
|
| 61 |
+
"Advanced": {"tier": 2, "complexity": "High (Complex/Explosive)"}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
# 4. EXERCISE DATABASE (Categorized)
|
| 65 |
+
# Level 0 = Easy/Safe, Level 1 = Medium, Level 2 = Hard
|
| 66 |
+
EXERCISE_DB = {
|
| 67 |
+
"Chest": [
|
| 68 |
+
{"name": "Machine Chest Press", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.6},
|
| 69 |
+
{"name": "Push-ups (Knees)", "equip": ["Bodyweight"], "bad": ["Wrist"], "level": 0, "ratio": 0},
|
| 70 |
+
{"name": "Push-ups (Regular)", "equip": ["Bodyweight"], "bad": ["Wrist"], "level": 1, "ratio": 0},
|
| 71 |
+
{"name": "Dumbbell Bench Press", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.35},
|
| 72 |
+
{"name": "Barbell Bench Press", "equip": ["Barbell", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0.9},
|
| 73 |
+
{"name": "Incline Dumbbell Press", "equip": ["Dumbbells", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0.3},
|
| 74 |
+
{"name": "Weighted Dips", "equip": ["Gym"], "bad": ["Shoulder"], "level": 2, "ratio": 0},
|
| 75 |
+
{"name": "Cable Flys", "equip": ["Gym"], "bad": [], "level": 1, "ratio": 0.2},
|
| 76 |
+
],
|
| 77 |
+
"Back": [
|
| 78 |
+
{"name": "Lat Pulldown", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.6},
|
| 79 |
+
{"name": "Seated Cable Row", "equip": ["Gym"], "bad": ["Back"], "level": 0, "ratio": 0.6},
|
| 80 |
+
{"name": "Chest Supported Row", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.5},
|
| 81 |
+
{"name": "Single Arm Dumbbell Row", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.3},
|
| 82 |
+
{"name": "Pull-ups", "equip": ["Bodyweight", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0},
|
| 83 |
+
{"name": "Deadlift", "equip": ["Barbell", "Gym"], "bad": ["Back"], "level": 2, "ratio": 1.4},
|
| 84 |
+
{"name": "Barbell Row", "equip": ["Barbell", "Gym"], "bad": ["Back"], "level": 1, "ratio": 0.8},
|
| 85 |
+
{"name": "Inverted Row", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 86 |
+
],
|
| 87 |
+
"Legs": [
|
| 88 |
+
{"name": "Bodyweight Squats", "equip": ["Bodyweight"], "bad": ["Knee"], "level": 0, "ratio": 0},
|
| 89 |
+
{"name": "Goblet Squat", "equip": ["Dumbbells", "Gym"], "bad": ["Knee"], "level": 0, "ratio": 0.4},
|
| 90 |
+
{"name": "Leg Press", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 1.8},
|
| 91 |
+
{"name": "Lunge Walk", "equip": ["Bodyweight", "Dumbbells"], "bad": ["Knee"], "level": 1, "ratio": 0},
|
| 92 |
+
{"name": "Bulgarian Split Squat", "equip": ["Dumbbells", "Bodyweight"], "bad": ["Knee"], "level": 2, "ratio": 0.2},
|
| 93 |
+
{"name": "Barbell Back Squat", "equip": ["Barbell", "Gym"], "bad": ["Back", "Knee"], "level": 2, "ratio": 1.2},
|
| 94 |
+
{"name": "Romanian Deadlift", "equip": ["Barbell", "Dumbbells"], "bad": ["Back"], "level": 1, "ratio": 0.9},
|
| 95 |
+
{"name": "Leg Curls", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.4},
|
| 96 |
+
{"name": "Glute Bridges", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 97 |
+
],
|
| 98 |
+
"Shoulders": [
|
| 99 |
+
{"name": "Seated Dumbbell Press", "equip": ["Dumbbells", "Gym"], "bad": ["Shoulder"], "level": 0, "ratio": 0.25},
|
| 100 |
+
{"name": "Lateral Raises", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.1},
|
| 101 |
+
{"name": "Face Pulls", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.2},
|
| 102 |
+
{"name": "Overhead Barbell Press", "equip": ["Barbell", "Gym"], "bad": ["Shoulder", "Back"], "level": 2, "ratio": 0.5},
|
| 103 |
+
{"name": "Pike Pushups", "equip": ["Bodyweight"], "bad": ["Shoulder"], "level": 1, "ratio": 0},
|
| 104 |
+
],
|
| 105 |
+
"Arms": [
|
| 106 |
+
{"name": "Dumbbell Curls", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.15},
|
| 107 |
+
{"name": "Tricep Pushdowns", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.3},
|
| 108 |
+
{"name": "Barbell Curls", "equip": ["Barbell", "Gym"], "bad": ["Wrist"], "level": 0, "ratio": 0.25},
|
| 109 |
+
{"name": "Skullcrushers", "equip": ["Barbell", "Dumbbells"], "bad": ["Elbow"], "level": 1, "ratio": 0.2},
|
| 110 |
+
{"name": "Bench Dips", "equip": ["Bodyweight"], "bad": ["Shoulder"], "level": 0, "ratio": 0},
|
| 111 |
+
],
|
| 112 |
+
"Core": [
|
| 113 |
+
{"name": "Plank", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 114 |
+
{"name": "Dead Bug", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 115 |
+
{"name": "Hanging Leg Raises", "equip": ["Gym"], "bad": ["Shoulder"], "level": 2, "ratio": 0},
|
| 116 |
+
{"name": "Cable Woodchoppers", "equip": ["Gym"], "bad": ["Back"], "level": 1, "ratio": 0.3},
|
| 117 |
+
],
|
| 118 |
+
"Cardio": [
|
| 119 |
+
{"name": "Walking", "equip": ["Bodyweight", "Gym"], "bad": [], "level": 0, "ratio": 0},
|
| 120 |
+
{"name": "Elliptical", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0},
|
| 121 |
+
{"name": "Burpees", "equip": ["Bodyweight"], "bad": ["Back", "Knee"], "level": 2, "ratio": 0},
|
| 122 |
+
{"name": "Sprints", "equip": ["Bodyweight"], "bad": ["Knee"], "level": 1, "ratio": 0},
|
| 123 |
+
]
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
# ==========================================
|
| 127 |
+
# LOGIC ENGINE
|
| 128 |
+
# ==========================================
|
| 129 |
+
|
| 130 |
+
def get_age_group(age):
|
| 131 |
+
if age <= 35: return "Young (18-35)"
|
| 132 |
+
if age <= 55: return "Middle (36-55)"
|
| 133 |
+
return "Senior (56+)"
|
| 134 |
+
|
| 135 |
+
def calculate_weight(base_ratio, user_weight, gender, level_tier):
|
| 136 |
if base_ratio == 0: return "Bodyweight"
|
| 137 |
|
| 138 |
load = user_weight * base_ratio
|
| 139 |
|
| 140 |
+
# Adjustments
|
| 141 |
if gender == "Female": load *= 0.6
|
| 142 |
+
if level_tier == 0: load *= 0.6 # Beginner
|
| 143 |
+
if level_tier == 2: load *= 1.3 # Advanced
|
| 144 |
|
| 145 |
+
# Rounding
|
| 146 |
+
load = 2.5 * round(load / 2.5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
return f"{int(load)} kg"
|
| 148 |
|
| 149 |
+
def get_exercises(muscle, equipment, injury, level_tier, count=1):
|
| 150 |
+
# 1. Define Equipment List
|
|
|
|
| 151 |
my_gear = []
|
| 152 |
+
if equipment == "Gym Membership": my_gear = ["Gym", "Barbell", "Dumbbells", "Bodyweight"]
|
| 153 |
+
elif equipment == "Full Home Gym (Rack+Barbell)": my_gear = ["Barbell", "Dumbbells", "Bodyweight"]
|
| 154 |
+
elif equipment == "Home Dumbbells": my_gear = ["Dumbbells", "Bodyweight"]
|
| 155 |
else: my_gear = ["Bodyweight"]
|
| 156 |
|
| 157 |
+
# 2. Filter Pool
|
| 158 |
+
pool = EXERCISE_DB.get(muscle, [])
|
| 159 |
+
valid = []
|
| 160 |
|
| 161 |
+
for ex in pool:
|
| 162 |
+
# Equip check
|
| 163 |
+
if not any(g in my_gear for g in ex["equip"]): continue
|
| 164 |
+
# Injury check
|
| 165 |
+
if injury in ex["bad"]: continue
|
| 166 |
+
# Level check (User Tier must be >= Exercise Level)
|
| 167 |
+
if level_tier < ex["level"]: continue
|
| 168 |
|
| 169 |
+
valid.append(ex)
|
| 170 |
+
|
| 171 |
+
# 3. Shuffle for Variety
|
| 172 |
+
random.shuffle(valid)
|
| 173 |
+
|
| 174 |
+
# 4. Return
|
| 175 |
+
if not valid:
|
| 176 |
+
return [{"name": f"Standard {muscle} Move", "ratio": 0}]
|
| 177 |
+
return valid[:count]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
def generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience):
|
| 180 |
|
| 181 |
+
# --- STEP 1: RESOLVE CONFIGS ---
|
| 182 |
+
goal_settings = GOAL_CONFIG[goal]
|
| 183 |
+
level_settings = LEVEL_CONFIG[experience]
|
| 184 |
+
age_group = get_age_group(age)
|
| 185 |
+
age_settings = AGE_CONFIG[age_group]
|
| 186 |
|
| 187 |
+
# --- STEP 2: BUILD STRUCTURE BASED ON PLAN ---
|
| 188 |
+
# This determines WHICH muscles we hit
|
| 189 |
slots = []
|
| 190 |
|
| 191 |
if "Upper" in plan_name or "Push" in plan_name:
|
| 192 |
+
slots = ["Chest", "Back", "Shoulders", "Chest", "Back", "Arms"]
|
| 193 |
+
routine_name = "Upper Body Focus"
|
|
|
|
|
|
|
|
|
|
| 194 |
elif "Lower" in plan_name or "Legs" in plan_name:
|
| 195 |
+
slots = ["Legs", "Legs", "Legs", "Core", "Core", "Cardio"]
|
| 196 |
+
routine_name = "Lower Body Focus"
|
|
|
|
|
|
|
|
|
|
| 197 |
else: # Full Body
|
| 198 |
+
slots = ["Legs", "Chest", "Back", "Shoulders", "Legs", "Core"]
|
| 199 |
+
routine_name = "Full Body Mix"
|
| 200 |
+
|
| 201 |
+
# --- STEP 3: GENERATE TEXT ---
|
| 202 |
+
text = f"WORKOUT PLAN: {routine_name}\n"
|
| 203 |
+
text += f"User: {age}y | {gender} | {weight}kg | {experience}\n"
|
| 204 |
+
text += f"Config: {goal} ({goal_settings['intensity']}) | Equip: {equipment}\n"
|
| 205 |
+
text += "="*45 + "\n\n"
|
| 206 |
+
|
| 207 |
+
# Warmup
|
| 208 |
+
text += f"WARM-UP ({age_settings['warmup']}):\n"
|
| 209 |
+
text += f"Type: {age_settings['type']}\n"
|
| 210 |
+
if injury == "Knee": text += "- Avoid high impact jumping.\n"
|
| 211 |
+
text += "- 3 mins Light Cardio\n- Dynamic Stretching\n\n"
|
| 212 |
+
|
| 213 |
+
# Main Block
|
| 214 |
+
text += f"MAIN WORKOUT:\n"
|
| 215 |
+
text += f"Sets: {goal_settings['sets']} | Reps: {goal_settings['reps']}\n"
|
| 216 |
+
text += f"Rest: {goal_settings['rest']}\n\n"
|
| 217 |
+
|
| 218 |
+
used_names = []
|
| 219 |
+
|
| 220 |
+
for i, muscle in enumerate(slots):
|
| 221 |
+
# Fetch 1 exercise, exclude used ones if possible
|
| 222 |
+
candidates = get_exercises(muscle, equipment, injury, level_settings['tier'], count=5)
|
|
|
|
|
|
|
|
|
|
| 223 |
|
| 224 |
+
selected = candidates[0]
|
| 225 |
+
# Try to find one not used yet
|
| 226 |
+
for cand in candidates:
|
| 227 |
+
if cand['name'] not in used_names:
|
| 228 |
+
selected = cand
|
| 229 |
+
break
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
|
| 231 |
+
used_names.append(selected['name'])
|
| 232 |
|
| 233 |
+
# Calc Weight
|
| 234 |
+
load = calculate_weight(selected['ratio'], weight, gender, level_settings['tier'])
|
| 235 |
|
| 236 |
+
text += f"{i+1}. {selected['name']} ({muscle})\n"
|
| 237 |
+
text += f" Load: {load}\n\n"
|
|
|
|
| 238 |
|
| 239 |
+
# Finisher
|
| 240 |
+
text += f"FINISHER:\n"
|
| 241 |
+
text += f"Style: {goal_settings['finisher']}\n"
|
| 242 |
+
|
| 243 |
+
if goal in ["Weight Loss", "Endurance"]:
|
| 244 |
+
cardio = get_exercises("Cardio", equipment, injury, level_settings['tier'])[0]
|
| 245 |
+
text += f"Activity: {cardio['name']} - 5 mins burnout"
|
| 246 |
+
else:
|
| 247 |
+
core = get_exercises("Core", equipment, injury, level_settings['tier'])[0]
|
| 248 |
+
text += f"Activity: {core['name']} - 3 sets to failure"
|
| 249 |
+
|
| 250 |
return text
|
| 251 |
|
| 252 |
+
# 6. WRAPPER
|
| 253 |
def predict_wrapper(age, gender, weight, height, goal, equipment, injury, experience):
|
| 254 |
+
# AI Prediction
|
| 255 |
input_df = pd.DataFrame({
|
| 256 |
'Age': [age], 'Gender': [gender], 'Weight_kg': [weight],
|
| 257 |
'Height_cm': [height], 'Goal': [goal], 'Equipment': [equipment],
|
|
|
|
| 259 |
})
|
| 260 |
plan_name = model.predict(input_df)[0]
|
| 261 |
|
| 262 |
+
# Logic Generation
|
| 263 |
routine = generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience)
|
| 264 |
|
| 265 |
+
info = f"AI Strategy: {plan_name}\n"
|
| 266 |
+
info += f"Adjusted for {experience} level and {goal} goal."
|
| 267 |
+
|
| 268 |
+
return routine, info
|
| 269 |
|
| 270 |
+
# 7. LAUNCH
|
| 271 |
iface = gr.Interface(
|
| 272 |
fn=predict_wrapper,
|
| 273 |
inputs=[
|
|
|
|
| 275 |
gr.Radio(["Male", "Female"], label="Gender", value="Male"),
|
| 276 |
gr.Number(label="Weight (kg)", value=75),
|
| 277 |
gr.Number(label="Height (cm)", value=175),
|
| 278 |
+
gr.Dropdown(list(GOAL_CONFIG.keys()), label="Goal", value="Muscle Gain"),
|
| 279 |
gr.Dropdown(['Gym Membership', 'Home Dumbbells', 'Bodyweight Only', 'Full Home Gym (Rack+Barbell)'], label="Equipment", value="Gym Membership"),
|
| 280 |
gr.Dropdown(['None', 'Knee', 'Back', 'Shoulder', 'Ankle'], label="Injury", value="None"),
|
| 281 |
+
gr.Dropdown(list(LEVEL_CONFIG.keys()), label="Experience", value="Intermediate")
|
| 282 |
],
|
| 283 |
outputs=[
|
| 284 |
+
gr.Textbox(label="Result", lines=22),
|
| 285 |
+
gr.Textbox(label="System Note")
|
| 286 |
],
|
| 287 |
+
title="SmartFit AI - Matrix Edition",
|
| 288 |
+
description="Fully modular workout engine controlled by strict Logic Dictionaries.",
|
| 289 |
theme="soft"
|
| 290 |
)
|
| 291 |
|
| 292 |
+
iface.launch()
|