Yeoram's Project Guide

Project: Workout Weight Calculator Category: Data Science / CLI Last updated: April 18

Note: This guide reflects the latest state of your project repo. It may not match the most up-to-date version if you've worked since.

Where You Are

Clean starting slice: your main.py asks for a 1RM and returns a hypertrophy weight range. Code is short and readable. pyproject.toml is set up.

Your Checkpoint 1 journal asked a great question: "should the input be in main or in a function?"In a function. That way the math is reusable and testable. main() just orchestrates: ask → call → print. Your existing calculate_hypertrophy_weight() is already in its own function — good.

This week: move your calculators into a weights.py business-logic module, add strength + endurance, and harden the input handling.

Project Structure

Your project splits into two kinds of code:

  • Business logic — you handwrite this. The weight-range formulas, rep-range rules, valid-goal definitions. This is the "exercise science" of your tool.
  • View / CLI — agent-assisted is fine. Pretty menu printing, number formatting. Nothing about workouts happens in view code.

Target layout by Thursday:

workout-weight-calculator/
├── main.py                 ← CLI driver (menu, input) — mostly yours, some agent-assisted
├── weights.py              ← business logic — handwrite (yours to own)
└── pyproject.toml

Why the split? On demo day you'll be asked "why is the strength range 85–100%?" The answer lives in weights.py as a named constant or a function. The print formatting doesn't make it into the answer.

weights.py should not call input() or print(). It's pure math + data. main.py handles all user interaction.

Phase 1: Move the Calculator Into weights.py

Handwrite this yourself. These formulas are your project. Even the constants (65%, 85%, 100%) are real exercise-science decisions you're making.

Objective

Create weights.py, move your existing hypertrophy function into it, and add strength + endurance.

Instructions

Reference Ranges

GoalWeight (% of 1RM)Reps
Strength85–100%3–5
Hypertrophy65–85%6–12
Endurance50–65%12–20

Hints

Pattern for one calculator:

def calculate_strength_weight(one_rm):
    low = one_rm * 0.85
    high = one_rm * 1.00
    return low, high

Optional but nice — define the rep ranges as constants in weights.py:

GOAL_REPS = {
    "strength": "3-5",
    "hypertrophy": "6-12",
    "endurance": "12-20",
}

Now the rep ranges have a single home — if you change one, you change it once.

Write the endurance function yourself. It mirrors strength exactly.

Optional — get help from your agent:

Skip — these are three small mirror functions you can write yourself.

Phase 2: Goal-Selection Menu

Mixed phase. The decision about what counts as a valid choice (1/2/3) is yours. The print formatting is just library code.

Objective

Let the user pick a goal, then call the matching calculator.

Instructions

Sample Output

Workout Weight Calculator

What is your goal?
  1. Strength (max effort, low reps)
  2. Hypertrophy (muscle building)
  3. Endurance (high reps)
Enter 1, 2, or 3: 2

Enter your 1RM (max weight): 200
Recommended weight for Hypertrophy: 130.0 - 170.0 lbs
Recommended reps: 6-12

Hints

Read the menu choice:

print("What is your goal?")
print("  1. Strength (max effort, low reps)")
print("  2. Hypertrophy (muscle building)")
print("  3. Endurance (high reps)")
choice = input("Enter 1, 2, or 3: ").strip()

Dispatch with if/elif (business logic — yours):

from weights import (
    calculate_strength_weight,
    calculate_hypertrophy_weight,
    calculate_endurance_weight,
    GOAL_REPS,
)

if choice == "1":
    low, high = calculate_strength_weight(one_rm)
    goal_name = "Strength"
    reps = GOAL_REPS["strength"]
elif choice == "2":
    low, high = calculate_hypertrophy_weight(one_rm)
    goal_name = "Hypertrophy"
    reps = GOAL_REPS["hypertrophy"]
elif choice == "3":
    low, high = calculate_endurance_weight(one_rm)
    goal_name = "Endurance"
    reps = GOAL_REPS["endurance"]

Optional — get help from your agent:

My dispatch block has three if/elif branches that look similar.
Walk me through whether I should simplify with a dict lookup instead.
Show me both versions. Don't change my code yet.

Phase 4: Handle Bad Input Without Crashing

Handwrite this yourself. "Keep asking until the user gives valid input" is a common loop pattern. You'll use it again in future projects.

Objective

If the user types "abc" for 1RM, or "5" for the goal choice, the program currently crashes. Add loops that re-ask until the answer is valid.

Instructions

Sample Output

Enter 1, 2, or 3: 9
Please enter 1, 2, or 3.
Enter 1, 2, or 3: 2

Enter your 1RM (max weight): abc
That's not a number. Try again.
Enter your 1RM (max weight): -10
Please enter a positive number.
Enter your 1RM (max weight): 200

Hints

"Ask until valid number" pattern:

def prompt_for_number(message):
    while True:
        raw = input(message).strip()
        try:
            n = float(raw)
        except ValueError:
            print("That's not a number. Try again.")
            continue
        if n <= 0:
            print("Please enter a positive number.")
            continue
        return n

"Ask until in a set" pattern:

def prompt_for_choice(message, valid):
    while True:
        choice = input(message).strip()
        if choice in valid:
            return choice
        print(f"Please enter {' or '.join(sorted(valid))}.")

These belong in main.py (they're about input), not weights.py (which is about math).

Optional — get help from your agent:

Walk me through why I need try/except ValueError to convert input
to a number. What happens if I just do float(input(...)) with "abc"?
Don't change my code — I want to check my understanding.

Checkpoint 2 Readiness

By Thursday April 23 at 3pm:

Helpful Resources