"""Simulate weight training workouts.
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
import json
from pathlib import Path
import random
from loguru import logger # type: ignore
import numpy as np
from src.utils.file_conversions.load_yaml import load_yaml_file # type: ignore
[docs]
class ExerciseRepository:
"""Handles the loading of exercises from a YAML file."""
def __init__(self, training_catalogue: str):
self.training_catalogue = training_catalogue
[docs]
def get_exercises(self, split: str) -> list[dict[str, list[int]]]:
available_exercises = load_yaml_file(self.training_catalogue)
return available_exercises[split]
[docs]
@dataclass
class ExerciseSelector:
"""Selects random exercises from the repository."""
repository: ExerciseRepository
split: str = field(default_factory=lambda: random.choice(["back", "chest", "legs", "shoulders"]))
exercises: list = field(init=False)
def __post_init__(self):
self.exercises = self.select_random_exercises()
[docs]
def select_random_exercises(self) -> list[dict[str, list[int]]]:
available_exercises = self.repository.get_exercises(self.split)
random.shuffle(available_exercises)
return available_exercises[:random.randint(2, 6)]
[docs]
class WorkoutSimulator:
"""Simulates a workout based on selected exercises."""
def __init__(self, exercises: list, progress: int):
if progress < 0:
raise ValueError("Progress must be greater than or equal to zero")
self.exercises = exercises
self.progress = progress
self.exercise_mapping = self.generate_exercise_mapping()
self.workout_data = self.simulate_workout_data()
def _calculate_weight(self, weight_range, actual_reps) -> str:
weight_choice = weight_range[-1] * ((100 - actual_reps * 2.5) / 100) + np.log10(self.progress)
return f"{weight_choice:.2f} kg"
[docs]
def generate_exercise_mapping(self) -> dict:
return {exercise_name: weight_range for exercise in self.exercises for exercise_name, weight_range in exercise.items()}
[docs]
def simulate_workout_data(self) -> dict[str, list[dict[str, str | int]]]:
workout_data: dict[str, list[dict[str, str | int]]] = {}
for exercise_name, weight_range in self.exercise_mapping.items():
no_of_sets = random.randint(1, 6)
workout_data[exercise_name] = []
for actual_set in range(1, no_of_sets + 1):
actual_reps = random.randint(1, 10)
actual_weight = self._calculate_weight(weight_range, actual_reps)
workout_data[exercise_name].append({"set_number": actual_set, "reps": actual_reps, "weight": actual_weight})
return workout_data
# @dataclass
[docs]
def main() -> None:
"""Simulate a workout and write the data to a JSON file.
"""
from pprint import pformat # type: ignore
TRAINING_CATALOGUE = "src/simulations/muscles_and_exercises_weight_ranges.yaml"
selection = ExerciseSelector(ExerciseRepository(TRAINING_CATALOGUE))
simulated_workout = WorkoutSimulator(exercises=selection.exercises, progress=10)
logger.debug(pformat(simulated_workout.workout_data))
# You can now easily change the formatter or add new ones
formatter = JSONWorkoutFormatter(
workout_date="2023-01-01",
output_dir="data/simulated/",
data=simulated_workout.workout_data,
split=selection.split,
)
formatter.write_data()
if __name__ == "__main__":
main()