Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,22 +2,38 @@ import gradio as gr
|
|
| 2 |
import joblib
|
| 3 |
import pandas as pd
|
| 4 |
import random
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
#
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
GOAL_CONFIG = {
|
| 15 |
"Strength": {"sets": 5, "reps": "3-5", "rest": "3 min", "intensity": "Heavy"},
|
| 16 |
"Muscle Gain": {"sets": 3, "reps": "8-12", "rest": "90 sec", "intensity": "Moderate"},
|
| 17 |
"Weight Loss": {"sets": 4, "reps": "15-20", "rest": "45 sec", "intensity": "High Tempo"},
|
| 18 |
"Endurance": {"sets": 2, "reps": "20-25", "rest": "30 sec", "intensity": "Light"},
|
| 19 |
"General Health": {"sets": 3, "reps": "12-15", "rest": "60 sec", "intensity": "Moderate"},
|
| 20 |
-
"Athletic Performance": {"sets": 4, "reps": "6-10", "rest": "2 min", "intensity": "Explosive"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
}
|
| 22 |
|
| 23 |
LEVEL_CONFIG = {
|
|
@@ -26,92 +42,65 @@ LEVEL_CONFIG = {
|
|
| 26 |
"Advanced": {"tier": 2}
|
| 27 |
}
|
| 28 |
|
|
|
|
| 29 |
EXERCISE_DB = {
|
| 30 |
"Chest": [
|
| 31 |
{"name": "Barbell Bench Press", "equip": ["Barbell", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0.9},
|
| 32 |
{"name": "Dumbbell Chest Press", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.35},
|
| 33 |
-
{"name": "Incline Dumbbell Press", "equip": ["Dumbbells", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0.3},
|
| 34 |
-
{"name": "Machine Chest Press", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.6},
|
| 35 |
{"name": "Push-ups", "equip": ["Bodyweight"], "bad": ["Wrist"], "level": 0, "ratio": 0},
|
| 36 |
-
{"name": "
|
| 37 |
-
{"name": "Dumbbell Flys", "equip": ["Dumbbells"], "bad": ["Shoulder"], "level": 0, "ratio": 0.12},
|
| 38 |
-
{"name": "Weighted Dips", "equip": ["Gym"], "bad": ["Shoulder"], "level": 2, "ratio": 0},
|
| 39 |
],
|
| 40 |
"Back": [
|
| 41 |
{"name": "Deadlift", "equip": ["Barbell", "Gym"], "bad": ["Back"], "level": 2, "ratio": 1.4},
|
| 42 |
{"name": "Lat Pulldown", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.6},
|
| 43 |
-
{"name": "
|
| 44 |
-
{"name": "Barbell Bent Over Row", "equip": ["Barbell", "Gym"], "bad": ["Back"], "level": 1, "ratio": 0.7},
|
| 45 |
-
{"name": "Single Arm Dumbbell Row", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.3},
|
| 46 |
{"name": "Pull-ups", "equip": ["Bodyweight", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0},
|
| 47 |
-
{"name": "Chest Supported Row", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.5},
|
| 48 |
-
{"name": "Inverted Row", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 49 |
],
|
| 50 |
"Legs": [
|
| 51 |
{"name": "Barbell Squat", "equip": ["Barbell", "Gym"], "bad": ["Back", "Knee"], "level": 2, "ratio": 1.1},
|
| 52 |
{"name": "Leg Press", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 1.8},
|
| 53 |
{"name": "Goblet Squat", "equip": ["Dumbbells", "Gym"], "bad": ["Knee"], "level": 0, "ratio": 0.4},
|
| 54 |
-
{"name": "
|
| 55 |
-
{"name": "
|
| 56 |
-
{"name": "Lying Leg Curl", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.35},
|
| 57 |
-
{"name": "Leg Extensions", "equip": ["Gym"], "bad": ["Knee"], "level": 0, "ratio": 0.35},
|
| 58 |
-
{"name": "Bulgarian Split Squat", "equip": ["Dumbbells"], "bad": ["Knee"], "level": 2, "ratio": 0.2},
|
| 59 |
-
{"name": "Glute Bridges", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 60 |
],
|
| 61 |
"Shoulders": [
|
| 62 |
-
{"name": "Overhead
|
| 63 |
-
{"name": "Seated Dumbbell Press", "equip": ["Dumbbells", "Gym"], "bad": ["Shoulder"], "level": 0, "ratio": 0.25},
|
| 64 |
{"name": "Lateral Raises", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.08},
|
| 65 |
-
{"name": "
|
| 66 |
-
{"name": "Front Raises", "equip": ["Dumbbells"], "bad": [], "level": 0, "ratio": 0.08},
|
| 67 |
-
],
|
| 68 |
-
"Arms": [
|
| 69 |
-
{"name": "Barbell Curls", "equip": ["Barbell", "Gym"], "bad": ["Wrist"], "level": 0, "ratio": 0.25},
|
| 70 |
-
{"name": "Dumbbell Hammer Curls", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.12},
|
| 71 |
-
{"name": "Tricep Rope Pushdown", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.25},
|
| 72 |
-
{"name": "Skullcrushers", "equip": ["Barbell", "Dumbbells"], "bad": ["Elbow"], "level": 1, "ratio": 0.2},
|
| 73 |
-
{"name": "Dips", "equip": ["Bodyweight"], "bad": ["Shoulder"], "level": 1, "ratio": 0},
|
| 74 |
],
|
| 75 |
"Core": [
|
| 76 |
{"name": "Plank", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 77 |
-
{"name": "Cable Woodchoppers", "equip": ["Gym"], "bad": ["Back"], "level": 1, "ratio": 0.3},
|
| 78 |
-
{"name": "Russian Twists", "equip": ["Bodyweight"], "bad": ["Back"], "level": 0, "ratio": 0},
|
| 79 |
{"name": "Hanging Leg Raises", "equip": ["Gym"], "bad": ["Shoulder"], "level": 2, "ratio": 0},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
],
|
| 81 |
"Cardio": [
|
| 82 |
-
{"name": "
|
| 83 |
-
{"name": "
|
| 84 |
-
{"name": "
|
| 85 |
-
{"name": "Jump Rope", "equip": ["Bodyweight"], "bad": ["Knee", "Ankle"], "level": 1, "ratio": 0},
|
| 86 |
]
|
| 87 |
}
|
| 88 |
|
| 89 |
-
# ==========================================
|
| 90 |
# LOGIC ENGINE
|
| 91 |
-
# ==========================================
|
| 92 |
-
|
| 93 |
def calculate_weight(base_ratio, user_weight, gender, level_tier, muscle_group):
|
| 94 |
-
if base_ratio == 0: return "Bodyweight"
|
| 95 |
-
|
| 96 |
load = user_weight * base_ratio
|
| 97 |
-
|
| 98 |
# Gender Logic
|
| 99 |
if gender == "Female":
|
| 100 |
-
if muscle_group in ["Legs", "Core"]:
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
load *= 0.55
|
| 104 |
-
|
| 105 |
# Level Logic
|
| 106 |
if level_tier == 0: load *= 0.6
|
| 107 |
if level_tier == 2: load *= 1.3
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
if load < 10:
|
| 111 |
-
final_load = round(load)
|
| 112 |
-
else:
|
| 113 |
-
final_load = 2.5 * round(load / 2.5)
|
| 114 |
-
|
| 115 |
return f"{int(final_load)} kg"
|
| 116 |
|
| 117 |
def get_exercises(muscle, equipment, injury, level_tier, count=1):
|
|
@@ -119,95 +108,105 @@ def get_exercises(muscle, equipment, injury, level_tier, count=1):
|
|
| 119 |
if equipment == "Gym Membership": my_gear = ["Gym", "Barbell", "Dumbbells", "Bodyweight"]
|
| 120 |
elif equipment == "Full Home Gym (Rack+Barbell)": my_gear = ["Barbell", "Dumbbells", "Bodyweight"]
|
| 121 |
elif equipment == "Home Dumbbells": my_gear = ["Dumbbells", "Bodyweight"]
|
| 122 |
-
else: my_gear = ["Bodyweight"]
|
| 123 |
|
| 124 |
pool = EXERCISE_DB.get(muscle, [])
|
| 125 |
valid = []
|
| 126 |
-
|
| 127 |
for ex in pool:
|
|
|
|
| 128 |
if not any(g in my_gear for g in ex["equip"]): continue
|
|
|
|
| 129 |
if injury in ex["bad"]: continue
|
|
|
|
| 130 |
if level_tier < ex["level"]: continue
|
| 131 |
valid.append(ex)
|
| 132 |
-
|
| 133 |
random.shuffle(valid)
|
| 134 |
-
|
| 135 |
-
if not valid: return [{"name": f"Standard {muscle} Move", "ratio": 0}]
|
| 136 |
return valid[:count]
|
| 137 |
|
| 138 |
def generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience):
|
| 139 |
-
|
| 140 |
goal_settings = GOAL_CONFIG.get(goal, GOAL_CONFIG["General Health"])
|
| 141 |
level_tier = LEVEL_CONFIG.get(experience, {"tier": 1})["tier"]
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
elif "Lower" in plan_name or "Legs" in plan_name:
|
| 148 |
-
slots = ["Legs", "Legs", "
|
| 149 |
-
title = "Lower Body
|
| 150 |
else:
|
|
|
|
| 151 |
slots = ["Legs", "Chest", "Back", "Shoulders", "Core"]
|
| 152 |
title = "Full Body Mix"
|
| 153 |
|
| 154 |
-
|
|
|
|
|
|
|
| 155 |
text += f"User: {age}y | {gender} | {weight}kg | {experience}\n"
|
| 156 |
-
text += f"Goal: {goal} | Equip: {equipment}\n"
|
| 157 |
text += "="*40 + "\n\n"
|
| 158 |
-
|
| 159 |
-
text += f"
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
text += f"
|
| 164 |
-
text += f"Sets: {goal_settings['sets']} | Reps: {goal_settings['reps']} | Rest: {goal_settings['rest']}\n\n"
|
| 165 |
-
|
| 166 |
used_names = []
|
| 167 |
-
|
| 168 |
for i, muscle in enumerate(slots):
|
| 169 |
candidates = get_exercises(muscle, equipment, injury, level_tier, count=3)
|
| 170 |
selected = candidates[0]
|
|
|
|
| 171 |
for cand in candidates:
|
| 172 |
if cand['name'] not in used_names:
|
| 173 |
selected = cand
|
| 174 |
break
|
| 175 |
used_names.append(selected['name'])
|
| 176 |
-
|
| 177 |
load = calculate_weight(selected['ratio'], weight, gender, level_tier, muscle)
|
| 178 |
|
| 179 |
text += f"{i+1}. {selected['name']} ({muscle})\n"
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
if goal in ["Weight Loss", "Endurance"]:
|
| 184 |
-
text += "HIIT: 30s Work / 30s Rest (5 Rounds)"
|
| 185 |
-
else:
|
| 186 |
-
text += "Core Stability: 3 Sets to Failure"
|
| 187 |
|
| 188 |
return text
|
| 189 |
|
| 190 |
-
# ==========================================
|
| 191 |
# MAIN PREDICTOR
|
| 192 |
-
# ==========================================
|
| 193 |
|
| 194 |
def predict_wrapper(age, gender, weight, height, goal, equipment, injury, experience):
|
| 195 |
input_df = pd.DataFrame({
|
| 196 |
-
'Age': [age], 'Gender': [gender], 'Weight_kg': [weight],
|
| 197 |
-
'Height_cm': [height], 'Goal': [goal], 'Equipment': [equipment],
|
| 198 |
'Injury': [injury], 'Experience': [experience]
|
| 199 |
})
|
| 200 |
-
|
| 201 |
-
#
|
| 202 |
plan_name = model.predict(input_df)[0]
|
| 203 |
|
|
|
|
| 204 |
routine = generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience)
|
| 205 |
|
| 206 |
-
return routine
|
| 207 |
|
| 208 |
-
# ==========================================
|
| 209 |
# UI LAUNCH (GRADIO)
|
| 210 |
-
|
|
|
|
|
|
|
| 211 |
|
| 212 |
iface = gr.Interface(
|
| 213 |
fn=predict_wrapper,
|
|
@@ -216,27 +215,16 @@ 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(
|
| 221 |
gr.Dropdown(['None', 'Knee', 'Back', 'Shoulder', 'Ankle'], label="Injury", value="None"),
|
| 222 |
gr.Dropdown(list(LEVEL_CONFIG.keys()), label="Experience", value="Intermediate")
|
| 223 |
],
|
| 224 |
outputs=[
|
| 225 |
-
gr.Textbox(label="
|
| 226 |
-
gr.Textbox(label="AI Note")
|
| 227 |
-
],
|
| 228 |
-
title="SmartFit AI - Biological Engine",
|
| 229 |
-
description="Advanced generator with gender-specific strength standards.",
|
| 230 |
-
examples=[
|
| 231 |
-
# Example 1: The Bodybuilder (Young Male, Gym, No Injury)
|
| 232 |
-
[24, "Male", 80, 180, "Muscle Gain", "Gym Membership", "None", "Advanced"],
|
| 233 |
-
|
| 234 |
-
# Example 2: The Rehab/Home User (Female, Knee Injury, Home)
|
| 235 |
-
[35, "Female", 65, 165, "Weight Loss", "Home Dumbbells", "Knee", "Beginner"],
|
| 236 |
-
|
| 237 |
-
# Example 3: The Senior Health (Older Male, Bodyweight, Back safe)
|
| 238 |
-
[60, "Male", 85, 175, "General Health", "Bodyweight Only", "Back", "Beginner"]
|
| 239 |
],
|
|
|
|
|
|
|
| 240 |
theme="soft"
|
| 241 |
)
|
| 242 |
|
|
|
|
| 2 |
import joblib
|
| 3 |
import pandas as pd
|
| 4 |
import random
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
# 1. Load Model with the CORRECT filename
|
| 8 |
+
# We check if the file exists just to be safe
|
| 9 |
+
model_filename = 'fitness_model (2).joblib'
|
| 10 |
+
|
| 11 |
+
if os.path.exists(model_filename):
|
| 12 |
+
model = joblib.load(model_filename)
|
| 13 |
+
print(f"Successfully loaded: {model_filename}")
|
| 14 |
+
else:
|
| 15 |
+
# Fallback in case the name changed during upload
|
| 16 |
+
try:
|
| 17 |
+
model = joblib.load('fitness_model.joblib')
|
| 18 |
+
print("Loaded standard 'fitness_model.joblib' instead.")
|
| 19 |
+
except:
|
| 20 |
+
raise FileNotFoundError(f"Could not find {model_filename} or fitness_model.joblib")
|
| 21 |
+
|
| 22 |
+
# CONFIG DICTIONARIES (UPDATED)
|
| 23 |
+
# Added new goals from the dataset
|
| 24 |
GOAL_CONFIG = {
|
| 25 |
"Strength": {"sets": 5, "reps": "3-5", "rest": "3 min", "intensity": "Heavy"},
|
| 26 |
"Muscle Gain": {"sets": 3, "reps": "8-12", "rest": "90 sec", "intensity": "Moderate"},
|
| 27 |
"Weight Loss": {"sets": 4, "reps": "15-20", "rest": "45 sec", "intensity": "High Tempo"},
|
| 28 |
"Endurance": {"sets": 2, "reps": "20-25", "rest": "30 sec", "intensity": "Light"},
|
| 29 |
"General Health": {"sets": 3, "reps": "12-15", "rest": "60 sec", "intensity": "Moderate"},
|
| 30 |
+
"Athletic Performance": {"sets": 4, "reps": "6-10", "rest": "2 min", "intensity": "Explosive"},
|
| 31 |
+
# NEW GOALS
|
| 32 |
+
"Flexibility": {"sets": 2, "reps": "Hold 30s", "rest": "None", "intensity": "Low Impact"},
|
| 33 |
+
"Tone Up": {"sets": 3, "reps": "15-20", "rest": "45 sec", "intensity": "Burnout"},
|
| 34 |
+
"Powerlifting": {"sets": 5, "reps": "1-3", "rest": "3-5 min", "intensity": "Max Effort"},
|
| 35 |
+
"CrossFit": {"sets": "AMRAP", "reps": "High", "rest": "Minimal", "intensity": "Very High"},
|
| 36 |
+
"Marathon Training": {"sets": "Distance", "reps": "Km", "rest": "Pacing", "intensity": "Steady State"}
|
| 37 |
}
|
| 38 |
|
| 39 |
LEVEL_CONFIG = {
|
|
|
|
| 42 |
"Advanced": {"tier": 2}
|
| 43 |
}
|
| 44 |
|
| 45 |
+
# Expanded Exercise Database
|
| 46 |
EXERCISE_DB = {
|
| 47 |
"Chest": [
|
| 48 |
{"name": "Barbell Bench Press", "equip": ["Barbell", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0.9},
|
| 49 |
{"name": "Dumbbell Chest Press", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.35},
|
|
|
|
|
|
|
| 50 |
{"name": "Push-ups", "equip": ["Bodyweight"], "bad": ["Wrist"], "level": 0, "ratio": 0},
|
| 51 |
+
{"name": "Burpees", "equip": ["Bodyweight"], "bad": ["Knee"], "level": 1, "ratio": 0},
|
|
|
|
|
|
|
| 52 |
],
|
| 53 |
"Back": [
|
| 54 |
{"name": "Deadlift", "equip": ["Barbell", "Gym"], "bad": ["Back"], "level": 2, "ratio": 1.4},
|
| 55 |
{"name": "Lat Pulldown", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 0.6},
|
| 56 |
+
{"name": "Dumbbell Row", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.3},
|
|
|
|
|
|
|
| 57 |
{"name": "Pull-ups", "equip": ["Bodyweight", "Gym"], "bad": ["Shoulder"], "level": 1, "ratio": 0},
|
|
|
|
|
|
|
| 58 |
],
|
| 59 |
"Legs": [
|
| 60 |
{"name": "Barbell Squat", "equip": ["Barbell", "Gym"], "bad": ["Back", "Knee"], "level": 2, "ratio": 1.1},
|
| 61 |
{"name": "Leg Press", "equip": ["Gym"], "bad": [], "level": 0, "ratio": 1.8},
|
| 62 |
{"name": "Goblet Squat", "equip": ["Dumbbells", "Gym"], "bad": ["Knee"], "level": 0, "ratio": 0.4},
|
| 63 |
+
{"name": "Box Jumps", "equip": ["Bodyweight", "Gym"], "bad": ["Knee", "Ankle"], "level": 1, "ratio": 0},
|
| 64 |
+
{"name": "Lunges", "equip": ["Dumbbells", "Bodyweight"], "bad": ["Knee"], "level": 0, "ratio": 0.2},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
],
|
| 66 |
"Shoulders": [
|
| 67 |
+
{"name": "Overhead Press", "equip": ["Barbell", "Gym"], "bad": ["Shoulder", "Back"], "level": 2, "ratio": 0.5},
|
|
|
|
| 68 |
{"name": "Lateral Raises", "equip": ["Dumbbells", "Gym"], "bad": [], "level": 0, "ratio": 0.08},
|
| 69 |
+
{"name": "Handstand Pushups", "equip": ["Bodyweight"], "bad": ["Shoulder", "Wrist"], "level": 2, "ratio": 0},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
],
|
| 71 |
"Core": [
|
| 72 |
{"name": "Plank", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
|
|
|
|
|
|
| 73 |
{"name": "Hanging Leg Raises", "equip": ["Gym"], "bad": ["Shoulder"], "level": 2, "ratio": 0},
|
| 74 |
+
{"name": "Crunches", "equip": ["Bodyweight"], "bad": ["Back"], "level": 0, "ratio": 0},
|
| 75 |
+
],
|
| 76 |
+
"Mobility": [
|
| 77 |
+
{"name": "Downward Dog", "equip": ["Bodyweight", "Yoga Mat"], "bad": ["Wrist"], "level": 0, "ratio": 0},
|
| 78 |
+
{"name": "Cat-Cow Stretch", "equip": ["Bodyweight"], "bad": [], "level": 0, "ratio": 0},
|
| 79 |
+
{"name": "Pigeon Pose", "equip": ["Bodyweight"], "bad": ["Knee"], "level": 1, "ratio": 0},
|
| 80 |
],
|
| 81 |
"Cardio": [
|
| 82 |
+
{"name": "5km Run", "equip": ["Bodyweight", "Gym"], "bad": ["Knee"], "level": 1, "ratio": 0},
|
| 83 |
+
{"name": "Rowing Machine", "equip": ["Gym"], "bad": ["Back"], "level": 0, "ratio": 0},
|
| 84 |
+
{"name": "Cycling", "equip": ["Gym", "Stationary"], "bad": [], "level": 0, "ratio": 0},
|
|
|
|
| 85 |
]
|
| 86 |
}
|
| 87 |
|
|
|
|
| 88 |
# LOGIC ENGINE
|
|
|
|
|
|
|
| 89 |
def calculate_weight(base_ratio, user_weight, gender, level_tier, muscle_group):
|
| 90 |
+
if base_ratio == 0: return "Bodyweight / NA"
|
| 91 |
+
|
| 92 |
load = user_weight * base_ratio
|
| 93 |
+
|
| 94 |
# Gender Logic
|
| 95 |
if gender == "Female":
|
| 96 |
+
if muscle_group in ["Legs", "Core"]: load *= 0.75
|
| 97 |
+
else: load *= 0.55
|
| 98 |
+
|
|
|
|
|
|
|
| 99 |
# Level Logic
|
| 100 |
if level_tier == 0: load *= 0.6
|
| 101 |
if level_tier == 2: load *= 1.3
|
| 102 |
+
|
| 103 |
+
final_load = round(load / 2.5) * 2.5 if load > 10 else round(load)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
return f"{int(final_load)} kg"
|
| 105 |
|
| 106 |
def get_exercises(muscle, equipment, injury, level_tier, count=1):
|
|
|
|
| 108 |
if equipment == "Gym Membership": my_gear = ["Gym", "Barbell", "Dumbbells", "Bodyweight"]
|
| 109 |
elif equipment == "Full Home Gym (Rack+Barbell)": my_gear = ["Barbell", "Dumbbells", "Bodyweight"]
|
| 110 |
elif equipment == "Home Dumbbells": my_gear = ["Dumbbells", "Bodyweight"]
|
| 111 |
+
else: my_gear = ["Bodyweight", "Yoga Mat"]
|
| 112 |
|
| 113 |
pool = EXERCISE_DB.get(muscle, [])
|
| 114 |
valid = []
|
| 115 |
+
|
| 116 |
for ex in pool:
|
| 117 |
+
# Check gear
|
| 118 |
if not any(g in my_gear for g in ex["equip"]): continue
|
| 119 |
+
# Check injury
|
| 120 |
if injury in ex["bad"]: continue
|
| 121 |
+
# Check level
|
| 122 |
if level_tier < ex["level"]: continue
|
| 123 |
valid.append(ex)
|
| 124 |
+
|
| 125 |
random.shuffle(valid)
|
| 126 |
+
if not valid: return [{"name": f"Generic {muscle} Move", "ratio": 0}]
|
|
|
|
| 127 |
return valid[:count]
|
| 128 |
|
| 129 |
def generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience):
|
| 130 |
+
|
| 131 |
goal_settings = GOAL_CONFIG.get(goal, GOAL_CONFIG["General Health"])
|
| 132 |
level_tier = LEVEL_CONFIG.get(experience, {"tier": 1})["tier"]
|
| 133 |
+
|
| 134 |
+
# PLAN LOGIC SELECTOR
|
| 135 |
+
# We map the AI's predicted plan name to a structure of muscles to work
|
| 136 |
+
|
| 137 |
+
if "Yoga" in plan_name:
|
| 138 |
+
slots = ["Mobility", "Mobility", "Core", "Mobility", "Core"]
|
| 139 |
+
title = "Yoga & Mobility Flow"
|
| 140 |
+
elif "Running" in plan_name or "Marathon" in plan_name:
|
| 141 |
+
slots = ["Cardio", "Legs", "Core", "Mobility"]
|
| 142 |
+
title = "Endurance Run & Core"
|
| 143 |
+
elif "CrossFit" in plan_name or "WOD" in plan_name:
|
| 144 |
+
slots = ["Legs", "Chest", "Cardio", "Shoulders", "Core"]
|
| 145 |
+
title = "Metabolic Conditioning (WOD)"
|
| 146 |
+
elif "Upper" in plan_name or "Push" in plan_name:
|
| 147 |
+
slots = ["Chest", "Back", "Shoulders", "Core"]
|
| 148 |
+
title = "Upper Body Strength"
|
| 149 |
elif "Lower" in plan_name or "Legs" in plan_name:
|
| 150 |
+
slots = ["Legs", "Legs", "Core", "Cardio"]
|
| 151 |
+
title = "Lower Body Power"
|
| 152 |
else:
|
| 153 |
+
# Default Full Body
|
| 154 |
slots = ["Legs", "Chest", "Back", "Shoulders", "Core"]
|
| 155 |
title = "Full Body Mix"
|
| 156 |
|
| 157 |
+
# --- TEXT GENERATION ---
|
| 158 |
+
text = f"AI PLAN: {title}\n"
|
| 159 |
+
text += f"Strategy: {plan_name}\n"
|
| 160 |
text += f"User: {age}y | {gender} | {weight}kg | {experience}\n"
|
|
|
|
| 161 |
text += "="*40 + "\n\n"
|
| 162 |
+
|
| 163 |
+
text += f"PROTOCOL:\n"
|
| 164 |
+
text += f"Sets: {goal_settings['sets']} | Reps: {goal_settings['reps']}\n"
|
| 165 |
+
text += f"Intensity: {goal_settings['intensity']} | Rest: {goal_settings['rest']}\n\n"
|
| 166 |
+
|
| 167 |
+
text += f"WORKOUT:\n"
|
|
|
|
|
|
|
| 168 |
used_names = []
|
| 169 |
+
|
| 170 |
for i, muscle in enumerate(slots):
|
| 171 |
candidates = get_exercises(muscle, equipment, injury, level_tier, count=3)
|
| 172 |
selected = candidates[0]
|
| 173 |
+
# Avoid duplicates
|
| 174 |
for cand in candidates:
|
| 175 |
if cand['name'] not in used_names:
|
| 176 |
selected = cand
|
| 177 |
break
|
| 178 |
used_names.append(selected['name'])
|
| 179 |
+
|
| 180 |
load = calculate_weight(selected['ratio'], weight, gender, level_tier, muscle)
|
| 181 |
|
| 182 |
text += f"{i+1}. {selected['name']} ({muscle})\n"
|
| 183 |
+
if load != "Bodyweight / NA":
|
| 184 |
+
text += f" Target Load: {load}\n"
|
| 185 |
+
text += "\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
return text
|
| 188 |
|
|
|
|
| 189 |
# MAIN PREDICTOR
|
|
|
|
| 190 |
|
| 191 |
def predict_wrapper(age, gender, weight, height, goal, equipment, injury, experience):
|
| 192 |
input_df = pd.DataFrame({
|
| 193 |
+
'Age': [age], 'Gender': [gender], 'Weight_kg': [weight],
|
| 194 |
+
'Height_cm': [height], 'Goal': [goal], 'Equipment': [equipment],
|
| 195 |
'Injury': [injury], 'Experience': [experience]
|
| 196 |
})
|
| 197 |
+
|
| 198 |
+
# Predict using the NEW model (trained on 10k rows)
|
| 199 |
plan_name = model.predict(input_df)[0]
|
| 200 |
|
| 201 |
+
# Generate the readable text routine
|
| 202 |
routine = generate_routine(plan_name, age, gender, weight, goal, equipment, injury, experience)
|
| 203 |
|
| 204 |
+
return routine
|
| 205 |
|
|
|
|
| 206 |
# UI LAUNCH (GRADIO)
|
| 207 |
+
|
| 208 |
+
goal_options = list(GOAL_CONFIG.keys()) # Auto-update from config
|
| 209 |
+
equip_options = ['Gym Membership', 'Home Dumbbells', 'Bodyweight Only', 'Full Home Gym (Rack+Barbell)', 'Yoga Mat & Bands']
|
| 210 |
|
| 211 |
iface = gr.Interface(
|
| 212 |
fn=predict_wrapper,
|
|
|
|
| 215 |
gr.Radio(["Male", "Female"], label="Gender", value="Male"),
|
| 216 |
gr.Number(label="Weight (kg)", value=75),
|
| 217 |
gr.Number(label="Height (cm)", value=175),
|
| 218 |
+
gr.Dropdown(goal_options, label="Goal", value="Muscle Gain"),
|
| 219 |
+
gr.Dropdown(equip_options, label="Equipment", value="Gym Membership"),
|
| 220 |
gr.Dropdown(['None', 'Knee', 'Back', 'Shoulder', 'Ankle'], label="Injury", value="None"),
|
| 221 |
gr.Dropdown(list(LEVEL_CONFIG.keys()), label="Experience", value="Intermediate")
|
| 222 |
],
|
| 223 |
outputs=[
|
| 224 |
+
gr.Textbox(label="AI Personalized Workout", lines=18)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
],
|
| 226 |
+
title="SmartFit AI 2.0 - Hyper-Personalized Engine",
|
| 227 |
+
description="Now supporting CrossFit, Yoga, Marathon Training, and Rehab protocols.",
|
| 228 |
theme="soft"
|
| 229 |
)
|
| 230 |
|