Yeoram's Project Guide

Project: Workout Weight Calculator Category: Web App (Flask) Last updated: April 21

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

Good progress on your project!

  • weights.py holds all the logic: EXERCISES, VALID_UNITS/GOALS/EXERCISES, ROUTINE_MAP, calculate_weight, and get_workout_data.
  • main.py walks the user through 4 steps (unit → exercise → goal → 1RM), validates each one with prompt_for_choice / prompt_for_number, and prints a full routine with a top set, back-off sets, and accessories.
  • flask is already in your pyproject.toml, so uv add flask is done.

Project Structure

Your project splits into two kinds of code:

  • Business logic — you already handwrote this. Everything in weights.py.
  • View / web code — agent-assisted is fine. app.py (reads the form, calls get_workout_data, renders a template) and the HTML files in templates/.

Example target layout:

workout-weight-calculator/
├── main.py                 ← keep the CLI working (optional — nice for demo)
├── weights.py              ← already done — don't touch
├── app.py                  ← NEW: Flask routes — read form + call weights.get_workout_data
├── templates/
│   ├── base.html           ← NEW: Tailwind CDN + mobile viewport + shared layout
│   └── index.html          ← NEW: the form + the result card
├── pyproject.toml
└── README.md

Phase 1: Scaffold the Flask App

Agent-assisted is fine.

Objective

Get a Flask app running locally that shows a styled "Workout Weight Calculator" heading on http://127.0.0.1:5000.

Instructions

Hints

app.py:

from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")

templates/base.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Workout Weight Calculator</title>
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body class="bg-slate-50 text-slate-900">
    <main class="max-w-md mx-auto px-4 py-6">
      {% block content %}{% endblock %}
    </main>
  </body>
</html>

templates/index.html:

{% extends "base.html" %} {% block content %}
<h1 class="text-2xl font-bold mb-4">Workout Weight Calculator</h1>
<p class="text-slate-600">Plan your next session.</p>
{% endblock %}

Optional — get help from your agent:

I just scaffolded a Flask app with a base.html using the Tailwind
Play CDN. Walk me through how {% extends %} and {% block content %}
connect base.html and index.html. Don't change my code — just explain.

Phase 2: Build the Form

Mixed phase. The decisions about which fields exist (unit, exercise, goal, 1RM) are yours — they mirror the 4 steps in your CLI. The Tailwind styling is agent-assisted.

Objective

Put your 4 CLI questions on one mobile-friendly page as a single <form>. Submitting it reloads / with the answers in the URL.

Instructions

Hints

Reuse the numeric string keys ("1", "2", ...) that weights.py already uses — don't change weights.py, just send the same strings from the form.

<fieldset class="mb-5">
  <legend class="font-semibold mb-2">Goal</legend>
  <div class="grid grid-cols-3 gap-2">
    {% for value, label in [("1", "Strength"), ("2", "Hypertrophy"), ("3",
    "Endurance")] %}
    <label class="block">
      <input
        type="radio"
        name="goal"
        value="{{ value }}"
        class="peer sr-only"
        required
      />
      <span
        class="block text-center py-3 rounded-xl border border-slate-300
                   peer-checked:bg-indigo-600 peer-checked:text-white peer-checked:border-indigo-600"
      >
        {{ label }}
      </span>
    </label>
    {% endfor %}
  </div>
</fieldset>

1RM input + submit button:

<label class="block mb-2 font-semibold" for="one_rm">Your 1RM</label>
<input
  id="one_rm"
  name="one_rm"
  type="number"
  step="any"
  min="0"
  required
  class="block w-full px-4 py-3 rounded-xl border border-slate-300 text-lg mb-5"
  placeholder="e.g. 225"
/>

<button
  type="submit"
  class="w-full py-4 rounded-xl bg-indigo-600 text-white font-semibold text-lg
               hover:bg-indigo-700 active:bg-indigo-800"
>
  Calculate
</button>

Optional — get help from your agent:

Here is my index.html form. Keep the name attributes and values
exactly as they are. Help me polish the mobile styling — better
spacing, bigger tap targets, a soft card background on the form.
Don't change any form field names.

Phase 3: Show the Workout Card

Handwrite the route yourself.

Objective

When the form submits, read the 4 values from the URL, call weights.get_workout_data(...), and render the same routine your CLI prints — as styled cards on the page.

Instructions

Hints

app.py:

from flask import Flask, render_template, request
import weights

app = Flask(__name__)

@app.route("/")
def index():
    unit = request.args.get("unit")
    exercise = request.args.get("exercise")
    goal = request.args.get("goal")
    one_rm_raw = request.args.get("one_rm")

    data = None
    error = None
    if unit and exercise and goal and one_rm_raw:
        try:
            one_rm = float(one_rm_raw)
            if one_rm <= 0:
                raise ValueError
            data = weights.get_workout_data(unit, goal, exercise, one_rm)
        except ValueError:
            error = "Please enter a positive number for your 1RM."

    return render_template("index.html", data=data, error=error)

index.html:

{% if error %}
<p class="mt-5 p-3 rounded-xl bg-red-100 text-red-800">{{ error }}</p>
{% endif %} {% if data %}
<section class="mt-6 p-5 rounded-2xl bg-white shadow">
  <h2 class="text-xl font-bold">{{ data.exercise }}</h2>
  <p class="text-slate-500 mb-4">Goal: {{ data.goal }}</p>

  <h3 class="font-semibold mb-2">Main lift</h3>
  <ul class="mb-4 space-y-1">
    {% for lift in data.main_lifts %}
    <li class="flex justify-between">
      <span>{{ lift.type }}</span>
      <span class="font-mono">
        {{ lift.weight }}{{ data.unit }} × {{ lift.reps }} × {{ lift.sets }}
        sets
      </span>
    </li>
    {% endfor %}
  </ul>

  <h3 class="font-semibold mb-2">Accessories</h3>
  <ul class="space-y-1">
    {% for acc in data.accessories %}
    <li class="flex justify-between">
      <span>{{ acc.name }}</span>
      <span class="font-mono">
        {{ acc.weight }}{{ data.unit }} × {{ acc.reps }} × {{ acc.sets }} sets
      </span>
    </li>
    {% endfor %}
  </ul>
</section>
{% endif %}

Optional — get help from your agent:

My /dashboard-style result card is rendering. Keep my route logic
and the {% for %} loops exactly as they are. Help me polish the
Tailwind styling on the result card — nicer typography hierarchy,
a subtle divider between main lift and accessories, colored pill
for the goal name. Don't change app.py.

Checkpoint 2 Readiness

By Thursday April 23 at 3pm, you should have:

Helpful Resources