Tomertg commited on
Commit
74ed72f
·
verified ·
1 Parent(s): a78d19b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -203
app.py CHANGED
@@ -3,264 +3,230 @@ import joblib
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),
253
- gr.Dropdown(['Weight Loss', 'Muscle Gain', 'Endurance', 'General Health', 'Strength'], label="Goal", value="General Health"),
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="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
 
 
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