Calisthenics exercises recommendation software
Tags: python, computerscience, beginners
Published: 2023-02-17 04:20:25

This is de Codecademy CS102 final project.
This program is a recommendations program for calisthenics enthusiasts looking for progressions to train for some of the most popular calisthenics skills.
As a fitness enthusiast myself, I decided to make a recommendation program (although basic) of something I would actually find useful, probably as I learn more skills I'll turn this into a full blown app.
Anyways...

This project has a few files:

## data.py
This has all the data used in the project, a dictionary with key-value pairs of the names of the skills and a list containing the details, these lists contain two elements: the description of the movement and another list containing all the progressions for each skill, and finally this list contains tuples with three parameters, the level of difficulty, name of the progression and details on how to perform it.

Here's a snippet of that data:

```
planche_progressions = [
    (1, 'Tuck Planche', 'Start in a push-up position with your hands shoulder-width apart and your fingers pointing forward. Lower your hips and raise your feet, keeping your back straight. Try to hold this position for as long as possible.'),
    (2, 'Advanced Tuck Planche', 'From a tucked position, straighten your legs and lift your hips to create a horizontal line with your body.'),
    (3, 'Straddle Planche', 'Open your legs into a straddle position, keeping your hips high and your back straight.'),
    (4, 'Half-Lay Planche', 'Lower one leg to create a half lay position while keeping the other leg straight and high.'),
    (5, 'Full Planche', 'Straighten both legs and lower your body until it is parallel to the ground.'),
    (6, 'Planche Push-Ups', 'From a full planche position, lower your body until your chest touches the ground, then push back up to the starting position.')
]

front_lever_progressions = [
    (1, 'Tuck Front Lever', 'Start by hanging from a bar with your knees tucked to your chest. Keep your arms straight and engage your lats to hold your body in a horizontal position.'),
    (2, 'Advanced Tuck Front Lever', 'Straighten your legs out in front of you while maintaining a horizontal position.'),
    (3, 'Straddle Front Lever', 'Open your legs into a straddle position while keeping your body horizontal.'),
    (4, 'Half-Lay Front Lever', 'Lower one leg to create a half lay position while keeping the other leg straight and high.'),
    (5, 'Full Front Lever', 'Straighten both legs and hold your body in a horizontal position.'),
    (6, 'Front Lever Pull-Ups', 'From a full front lever position, pull your body up to the bar while maintaining a horizontal position.')
]

back_lever_progressions = [ ...

calisthenics_skills = {
    "Planche": ["A gymnastics move where the body is held parallel to the ground in a horizontal position, supported only by the hands.", planche_progressions],
    "Front Lever": ["A gymnastics move where the body is held parallel to the ground in a horizontal position, supported only by the arms., ", front_lever_progressions],
    "Back Lever": ["A gymnastics move where the body is held parallel to the ground in a horizontal position, supported only by the arms.", back_lever_progressions], ...
}
```
This data was generated with ChatGPT so some of the descriptions are a little bit off.
## LinkedList.py
The data of the dictionary was introduced into a linked list in the actual program (More details on this later), so this file has the Node and LinkedList classes:
THE NODE CLASS

```
class Node:
    def __init__(self, value, next_node=None):
        self.value = value
        self.next_node = next_node
    
    def get_value(self):
        return self.value
  
    def get_next_node(self):
        return self.next_node
  
    def set_next_node(self, next_node):
        self.next_node = next_node
```
This class only has the constructor and three methods that serve to get the value of a node, get the next node and set the next node
THE LINKED LIST CLASS:

```
class LinkedList:
    def __init__(self, value=None):
        self.head_node = Node(value)
  
    def get_head_node(self):
        return self.head_node

    def insert_beginning(self, new_value):
        new_node = Node(new_value)
        new_node.set_next_node(self.head_node)
        self.head_node = new_node

    def __iter__(self):
        current_node = self.head_node
        while(current_node):
            yield current_node.get_value()
            current_node = current_node.get_next_node()

    def stringify_list(self):
        string_list = ""
        current_node = self.get_head_node()
        while current_node:
            if current_node.get_value() != None:
                string_list += str(current_node.get_value()) + "\n"
            current_node = current_node.get_next_node()
        return string_list

    def remove_node(self, value_to_remove):
        current_node = self.get_head_node()
        if current_node.get_value() == value_to_remove:
            self.head_node = self.head_node.get_next_node()
        else:
            while current_node:
                current_next_node = current_node.get_next_node()
                if current_next_node.get_value() == value_to_remove:
                    current_node.set_next_node(current_next_node.get_next_node())
                    current_node = None
                else:
                    current_node = current_next_node

```
This class has several more methods:

1. Get the head node of the linked list.
2. Insert a node at the beginning of the linked list with a specified value.
3. Iterator, in order to iterate through the linked list using a for loop.
4. Stringify, although not used here, this can be used to display the contents of the linked list.
5. Remove a node by it's value, again, not used here.

## UI
This only has a function to display the welcome message

## The main script file

```
from heapq import heappop, heappush
from ui import *
from data import *
from LinkedList import LinkedList



skill_names = LinkedList()

class recommendation_system:
    def __init__(self):
        self.skills = LinkedList()
    
    def load_data(self):
        for name, details in calisthenics_skills.items():
            self.skills.insert_beginning([name, details])
            print(f"{name} added")

    def get_skill(self):
        while True:
            print("What calisthenics skill are you interested in?", 
            "Type the beggining of of the name and press enter to see if it's here:",
            "(Or type 0 to exit)", sep = "\n")
            answer = input()
            if answer == "0":
                break
            answer = answer.lower()
            coincidences = []
            for skill in self.skills:
                if skill:
                    skill_name = skill[0].lower()
                    if answer == skill_name[:len(answer)]:
                        coincidences.append(skill)
            if len(coincidences) == 0:
                print("No matches were found for " + answer)
                print("Try again: ")
                self.get_skill()
            elif len(coincidences) == 1:
                print("The only match for your search is {0}".format(coincidences[0][0]))
                continue_or_not = input("Do you want to continue with this skill?\n[Y or N]: ")
                if continue_or_not.lower() == "y":
                    self.get_choice_between_description_and_progressions(coincidences[0])
                else:
                    continue
            else:
                print("Here are the matches to your search: ")
                for match in coincidences:
                    print(match[0])
                print("Try again: ")
                self.get_skill()

    def get_choice_between_description_and_progressions(self, skill):
        try:
            current_skill = skill.copy()
            choice = int(input(f"What do you want to learn about the {current_skill[0]}?\nDescription [1], progressions [2], exit[3]: "))
            if choice == 1:
                print(current_skill[1][0])
                self.get_choice_between_description_and_progressions(current_skill)
            elif choice == 2:
                while (True):
                    current_progression = heappop(current_skill[1][1])
                    print("Progression #{0}: {1}.\nDescription: {2}".format(current_progression[0], current_progression[1], current_progression[2]))
                    choice = int(input("Do you want to see the next progression [1] or exit to main menu [2]? "))
                    if choice == 1:
                        continue
                    elif choice == 2:
                        break
            elif choice == 3:
                exit
            else:
                print("Please select a valid option.")
                self.get_choice_between_description_and_progressions()
        except:
            print("There are no more progressions to display, returning to main menu")

    def main(self):
        welcome()
        self.get_skill()
        

cal_skills = recommendation_system()
cal_skills.load_data()
cal_skills.main()
    
```
This program has a class named recommendation system which has the following methods:
- The constructor: It creates an empty linked list
```
def __init__(self):
        self.skills = LinkedList()
```
- LOAD_DATA
This loads the data from data.py into the linked list created in the constructor, it adds every key-value pair of the dictionary into lists in a Node at the beginning of the linked list.
```
def load_data(self):
        for name, details in calisthenics_skills.items():
            self.skills.insert_beginning([name, details])
```
- GET_SKILL
This method basically takes the user input in order to identify the calisthenics skill the user wants to learn about, it does it by comparing the user input to the beginning letters of the name of the skills, if there's a match the skill is added to a temporary list. The user is prompted to input the name of the desired skill until there's only one option, after the user is asked whether or not they would like to continue with that skill, if so the next method is called, otherwise the user returns to the main menu.
```
def get_skill(self):
        while True:
            print("What calisthenics skill are you interested in?", 
            "Type the beggining of of the name and press enter to see if it's here:",
            "(Or type 0 to exit)", sep = "\n")
            answer = input()
            if answer == "0":
                break
            answer = answer.lower()
            coincidences = []
            for skill in self.skills:
                if skill:
                    skill_name = skill[0].lower()
                    if answer == skill_name[:len(answer)]:
                        coincidences.append(skill)
            if len(coincidences) == 0:
                print("No matches were found for " + answer)
                print("Try again: ")
                self.get_skill()
            elif len(coincidences) == 1:
                print("The only match for your search is {0}".format(coincidences[0][0]))
                continue_or_not = input("Do you want to continue with this skill?\n[Y or N]: ")
                if continue_or_not.lower() == "y":
                    self.get_choice_between_description_and_progressions(coincidences[0])
                else:
                    continue
            else:
                print("Here are the matches to your search: ")
                for match in coincidences:
                    print(match[0])
                print("Try again: ")
                self.get_skill()
```
- GET_CHOICE_BETWEEN_DESCRIPTION_AND_PROGRESSIONS
In this code the user is propted to choose between displaying the details of the skill or the progressions.
If the user chooses to display the description they are prompted to choose again, if they choose to see the progressions they are displayed one by one from easiest to hardest using the heappop function.

```
def get_choice_between_description_and_progressions(self, skill):
        try:
            current_skill = skill.copy()
            choice = int(input(f"What do you want to learn about the {current_skill[0]}?\nDescription [1], progressions [2], exit[3]: "))
            if choice == 1:
                print(current_skill[1][0])
                self.get_choice_between_description_and_progressions(current_skill)
            elif choice == 2:
                while (True):
                    current_progression = heappop(current_skill[1][1])
                    print("Progression #{0}: {1}.\nDescription: {2}".format(current_progression[0], current_progression[1], current_progression[2]))
                    choice = int(input("Do you want to see the next progression [1] or exit to main menu [2]? "))
                    if choice == 1:
                        continue
                    elif choice == 2:
                        break
            elif choice == 3:
                exit
            else:
                print("Please select a valid option.")
                self.get_choice_between_description_and_progressions()
        except:
            print("There are no more progressions to display, returning to main menu")
```
Here's the full code:
https://github.com/ftapiaa27/Recommendation-Software.git