|
from sklearn.preprocessing import OneHotEncoder |
|
import random |
|
import pandas as pd |
|
import os |
|
import pickle |
|
|
|
SERVER_FILE_DIR = os.path.dirname(os.path.abspath(__file__)) |
|
FITNESS_MODEL_PATH = os.path.join( |
|
SERVER_FILE_DIR, *"../resources/models/fitness_model.pkl".split("/") |
|
) |
|
|
|
|
|
class FitnessModel: |
|
def __init__(self, excercise_path, kmeans_path, plan_classifier_path): |
|
self.data = pd.read_csv(excercise_path) |
|
self.kmeans = None |
|
self.plan_classifier = None |
|
self.encoder = None |
|
self.cluster_data = {} |
|
self.X_train_cols = [ |
|
"level_Advanced", |
|
"level_Beginner", |
|
"level_Intermediate", |
|
"goal_ Get Fitter", |
|
"goal_ Lose Weight", |
|
"goal_Gain Muscle", |
|
"goal_Get Fitter", |
|
"goal_Increase Endurance", |
|
"goal_Increase Strength", |
|
"goal_Sports Performance", |
|
"gender_Female", |
|
"gender_Male", |
|
"gender_Male & Female", |
|
] |
|
|
|
|
|
with open(kmeans_path, "rb") as f: |
|
self.kmeans = pickle.load(f) |
|
|
|
|
|
with open(plan_classifier_path, "rb") as f: |
|
self.plan_classifier = pickle.load(f) |
|
|
|
|
|
for cluster_label in range(90): |
|
|
|
cluster_subset = self.data[self.data["cluster"] == cluster_label] |
|
|
|
|
|
self.cluster_data[cluster_label] = cluster_subset |
|
|
|
features = self.data[["Level", "goal", "bodyPart"]] |
|
|
|
|
|
self.encoder = OneHotEncoder(sparse=False) |
|
encoded_features = self.encoder.fit_transform(features) |
|
|
|
def choose_plan(self, level, goal, gender): |
|
global plan_classifier |
|
|
|
input_data = pd.DataFrame( |
|
{"level": [level], "goal": [goal], "gender": [gender]} |
|
) |
|
|
|
|
|
input_encoded = pd.get_dummies(input_data, columns=["level", "goal", "gender"]) |
|
|
|
|
|
|
|
missing_cols = set(self.X_train_cols) - set(input_encoded.columns) |
|
for col in missing_cols: |
|
input_encoded[col] = 0 |
|
|
|
|
|
input_encoded = input_encoded[self.X_train_cols] |
|
|
|
|
|
prediction = self.plan_classifier.predict(input_encoded) |
|
|
|
|
|
daily_activities_lists = [day.split(", ") for day in prediction[0]] |
|
|
|
return daily_activities_lists |
|
|
|
def get_daily_recommendation(self, home_or_gym, level, goal, bodyParts, equipments): |
|
if goal in ["Lose Weight", "Get Fitter"]: |
|
goal = "Get Fitter & Lose Weight" |
|
daily_recommendations = [] |
|
|
|
bodyParts = [bp for bp in bodyParts if "-" not in bp] |
|
|
|
while len(bodyParts) < 6: |
|
bodyParts += bodyParts |
|
|
|
|
|
bodyParts = bodyParts[:6] |
|
|
|
for bodyPart in bodyParts: |
|
|
|
input_data = [[level, goal, bodyPart]] |
|
predicted_cluster = self.kmeans.predict(self.encoder.transform(input_data))[ |
|
0 |
|
] |
|
print(predicted_cluster) |
|
|
|
cluster_subset = self.cluster_data[predicted_cluster] |
|
|
|
|
|
if home_or_gym == 0: |
|
cluster_subset = cluster_subset[ |
|
~cluster_subset["equipment"].isin(equipments) |
|
] |
|
|
|
|
|
if not cluster_subset.empty: |
|
selected_exercise = random.choice( |
|
cluster_subset.to_dict(orient="records") |
|
) |
|
daily_recommendations.append(selected_exercise) |
|
|
|
|
|
unique_recommendations = [] |
|
seen_names = set() |
|
for exercise in daily_recommendations: |
|
if exercise["name"] not in seen_names: |
|
unique_recommendations.append(exercise) |
|
seen_names.add(exercise["name"]) |
|
|
|
return unique_recommendations |
|
|
|
def get_gender_adjustment(self, gender): |
|
return 1.0 if gender == "Male" else 0.7 |
|
|
|
def get_age_adjustment(self, age): |
|
if age < 30: |
|
return 1.0 |
|
elif 30 <= age < 50: |
|
return 0.5 |
|
else: |
|
return 0.1 |
|
|
|
def get_level_adjustment(self, level): |
|
if level == "Beginner": |
|
return 0.8 |
|
elif level == "Intermediate": |
|
return 1.0 |
|
elif level == "Advanced": |
|
return 1.2 |
|
|
|
def get_body_part_adjustment(self, body_part): |
|
body_parts = { |
|
"chest": 1, |
|
"shoulders": 0.8, |
|
"waist": 0.6, |
|
"upper legs": 0.7, |
|
"back": 0.9, |
|
"lower legs": 0.5, |
|
"upper arms": 0.8, |
|
"cardio": 0.7, |
|
"lower arms": 0.6, |
|
"neck": 0.5, |
|
} |
|
return body_parts.get(body_part, 0) |
|
|
|
def adjust_workout(self, gender, age, feedback, body_part, level, old_weight): |
|
gender_adjustment = self.get_gender_adjustment(gender) |
|
age_adjustment = self.get_age_adjustment(age) |
|
level_adjustment = self.get_level_adjustment(level) |
|
body_part_adjustment = self.get_body_part_adjustment(body_part) |
|
|
|
increasing_factor_of_weight = ( |
|
age_adjustment |
|
* body_part_adjustment |
|
* gender_adjustment |
|
* level_adjustment |
|
* 0.3 |
|
) |
|
|
|
if not feedback: |
|
increasing_factor_of_weight = (1 - increasing_factor_of_weight) * -0.1 |
|
|
|
new_weight = old_weight + increasing_factor_of_weight * old_weight |
|
|
|
return new_weight |
|
|
|
def calculate_new_repetition(self, level, goal): |
|
if goal in ["Lose Weight", "Get Fitter"]: |
|
if level == "Beginner": |
|
return 15 |
|
elif level == "Intermediate": |
|
return 12 |
|
elif level == "Expert": |
|
return 10 |
|
elif goal == "Gain Muscle": |
|
if level == "Beginner": |
|
return 10 |
|
elif level == "Intermediate": |
|
return 8 |
|
elif level == "Advanced": |
|
return 6 |
|
|
|
def calculate_new_duration(self, level): |
|
|
|
if level == "Beginner": |
|
return 20 |
|
elif level == "Intermediate": |
|
return 50 |
|
elif level == "Advanced": |
|
return 80 |
|
|
|
def predict( |
|
self, home_or_gym, level, goal, gender, age, feedback, old_weight, equipments |
|
): |
|
|
|
plan = self.choose_plan(level, goal, gender) |
|
print(plan) |
|
|
|
while len(plan) < 30: |
|
plan.extend(plan) |
|
plan = plan[:30] |
|
|
|
all_recommendations = [] |
|
for day_body_parts in plan: |
|
daily_exercises = self.get_daily_recommendation( |
|
home_or_gym, level, goal, day_body_parts, equipments |
|
) |
|
daily_recommendations = [] |
|
|
|
for exercise in daily_exercises: |
|
weights = self.adjust_workout( |
|
gender, age, feedback, exercise["bodyPart"], level, old_weight |
|
) |
|
repetitions = self.calculate_new_repetition(level, goal) |
|
duration = self.calculate_new_duration(level) |
|
weights_or_duration = ( |
|
weights if exercise["type"] == "weight" else duration |
|
) |
|
exercise_recommendations = { |
|
"name": exercise["name"], |
|
"type": exercise["type"], |
|
"equipment": exercise["equipment"], |
|
"bodyPart": exercise["bodyPart"], |
|
"target": exercise["target"], |
|
"weights_or_duration": weights_or_duration, |
|
"sets": exercise["sets"], |
|
"repetitions": repetitions, |
|
} |
|
daily_recommendations.append(exercise_recommendations) |
|
all_recommendations.append(daily_recommendations) |
|
|
|
return all_recommendations |
|
|
|
@classmethod |
|
def load(cls): |
|
with open(FITNESS_MODEL_PATH, "rb") as f: |
|
print(f) |
|
fitness_model = pickle.load(f) |
|
|
|
return fitness_model |
|
|