Skip to content

Instantly share code, notes, and snippets.

@mjkpolo
Last active October 1, 2025 20:20
Show Gist options
  • Save mjkpolo/b1b9d1c7606cffa85fc77bd7a1af2499 to your computer and use it in GitHub Desktop.
Save mjkpolo/b1b9d1c7606cffa85fc77bd7a1af2499 to your computer and use it in GitHub Desktop.
Generate workouts based on your one rep max for [5/3/1](https://t-nation.com/t/5-3-1-how-to-build-pure-strength/281694)
#!/usr/bin/env python3
import fire
import pickle
import os
from sys import stderr, stdout
from dataclasses import dataclass
import datetime
from typing import Optional, TextIO
from decimal import Decimal
# https://t-nation.com/t/5-3-1-how-to-build-pure-strength/281694
main_rest_time = datetime.timedelta(minutes=3, seconds=30)
ass_rest_time = datetime.timedelta(minutes=1, seconds=30)
main_lift_time = datetime.timedelta(minutes=1, seconds=0)
ass_lift_time = datetime.timedelta(minutes=1, seconds=0)
main_sets = 3
perc_of_orm = {
0: [.65, .75, .85],
1: [.70, .80, .90],
2: [.75, .85, .95],
3: [.40, .50, .60],
}
# main changes per *cycle*
main_reps = {
0: [5, 5, 5],
1: [3, 3, 3],
2: [5, 3, 1],
3: [5, 5, 5],
}
main_lift = {
0: "Shoulder Press",
1: "Deadlift",
2: "Bench Press",
3: "Squat",
}
# ass changes per *workout*
ass_sets = 5
ass_reps = {
0: [15, 10],
1: [12, 15],
2: [15, 10],
3: [15, 10],
}
ass_lift = {
0: ["Dip", "Chin-Up"],
1: ["Good Morning", "Hanging Leg Raise"],
2: ["Dumbell Chest Press", "Dumbell Row"],
3: ["Leg Press", "Leg Curl"],
}
def fancyprint(msg: str, dim: bool, bold: bool, type: str, color: str, out: TextIO):
styles = ''
if bold:
styles += '\033[1m'
if dim:
styles += '\033[2m'
if type == '':
print(
f'\033[1;{color}m{msg}\033[0m', file=out)
else:
print(
f'\033[1;{color}m{type}\033[0m{styles} {msg}\033[0m', file=out)
def err(msg: str, dim: bool = False, bold: bool = False):
fancyprint(msg, dim, bold, 'ERR: ', "31", stderr)
def warn(msg: str, dim: bool = False, bold: bool = False):
fancyprint(msg, dim, bold, 'WARN:', "33", stderr)
def info(msg: str, dim: bool = False, bold: bool = False):
fancyprint(msg, dim, bold, 'INFO:', "34", stderr)
def emph(type: str, msg: str, dim: bool = False, bold: bool = False):
fancyprint(msg, dim, bold, type, "33", stdout)
def norm(msg: str, dim: bool = False, bold: bool = False):
fancyprint(msg, dim, bold, '', "", stdout)
@dataclass
class Progress:
name: str
start_date: datetime.date
skip_days: int
squat_orm: int
bench_orm: int
deadlift_orm: int
ohp_orm: int
def __str__(self):
return (
f'Name: {self.name}\n'
f'Start Date: {self.start_date}\n'
f'Skip Days: {self.skip_days}'
f'Squat One Rep Max: {self.squat_orm}\n'
f'Bench One Rep Max: {self.bench_orm}\n'
f'Deadlift One Rep Max: {self.deadlift_orm}\n'
f'Overhead Press One Rep Max: {self.ohp_orm}'
)
def weight_to_plate(weight: int, deadlift: bool):
assert weight > 45, "Too small"
assert weight % 5 == 0, "Needs to be multiple of 5"
weight -= 45 # bar
if deadlift:
plates = [110, 90, 70, 50, 20, 10]
else:
plates = [90, 70, 50, 20, 10, 5]
plate_strs = []
for plate in plates:
n_plate = weight // plate
if n_plate > 0:
weight -= n_plate * plate
plate_strs.append(f"{n_plate}x{Decimal(plate)/2}")
return ', '.join(plate_strs)
def get_workout(progress: Progress):
today = datetime.date.today()
days_since_start = (today - progress.start_date).days - progress.skip_days
assert days_since_start >= 0
if days_since_start % 2 == 0:
print(days_since_start)
norm("Have a break!")
return
workout = (((days_since_start % 8) + 1) // 2)-1
assert workout >= 0 and workout <= 3
cycles_since_start = days_since_start // 8
cycle = cycles_since_start % 4
deadlift = False
if main_lift[workout] == "Shoulder Press":
main_orm = progress.ohp_orm
elif main_lift[workout] == "Deadlift":
main_orm = progress.deadlift_orm
deadlift = True
elif main_lift[workout] == "Bench Press":
main_orm = progress.bench_orm
elif main_lift[workout] == "Squat":
main_orm = progress.squat_orm
else:
raise ValueError(f"Unkown main lift: {main_lift[workout]}")
norm(f"Cycle: {cycle+1}")
norm(f"Workout: {workout+1}")
total_time = datetime.timedelta()
for s in range(main_sets):
set_weight = int(5 * round(perc_of_orm[cycle][s]*main_orm*.9 / 5))
weight_to_plate(set_weight, False)
emph(main_lift[workout], f"| {main_reps[cycle][s]}{'+ reps' if s == main_sets-1 and cycle != 3 else ' reps '} | {set_weight:3d} lbs. | {main_rest_time} rest | Plates: {weight_to_plate(set_weight, deadlift)}")
total_time += main_rest_time
total_time += main_lift_time
for ass_idx in range(2):
for s in range(ass_sets):
emph(ass_lift[workout][ass_idx],
f"| {ass_reps[workout][ass_idx]} reps | {ass_rest_time} rest")
total_time += ass_rest_time
total_time += ass_lift_time
norm(f"Est. time: {total_time}")
def main(
progress_pickle: str,
show_progress: bool = False,
name: Optional[str] = None,
start_date: Optional[str] = None,
squat_orm: Optional[int] = None,
bench_orm: Optional[int] = None,
deadlift_orm: Optional[int] = None,
ohp_orm: Optional[int] = None,
skip_days: Optional[int] = None,
):
save_pickle = False
if os.path.exists(progress_pickle):
info("Loading progress pickle...")
with open(progress_pickle, 'rb') as fp:
progress = pickle.load(fp)
assert isinstance(progress, Progress), "Pickle wasn't Progress class"
if show_progress:
print(progress)
return
if squat_orm is not None:
info(
f"Updating Squat one rep max {progress.squat_orm}->{squat_orm}")
progress.squat_orm = squat_orm
save_pickle = True
if bench_orm is not None:
info(
f"Updating Bench one rep max {progress.bench_orm}->{bench_orm}")
progress.bench_orm = bench_orm
save_pickle = True
if deadlift_orm is not None:
info(
f"Updating Deadlift one rep max {progress.deadlift_orm}->{deadlift_orm}")
progress.deadlift_orm = deadlift_orm
save_pickle = True
if ohp_orm is not None:
info(
f"Updating Overhead Press one rep max {progress.ohp_orm}->{ohp_orm}")
progress.ohp_orm = ohp_orm
save_pickle = True
if skip_days is not None:
info(
f"Updating skip days {progress.skip_days}->{skip_days}")
progress.skip_days = skip_days
save_pickle = True
else:
info("Creating progress pickle...")
assert name is not None
assert start_date is not None
assert squat_orm is not None
assert bench_orm is not None
assert deadlift_orm is not None
assert ohp_orm is not None
progress = Progress(
name,
datetime.date.fromisoformat(start_date),
squat_orm,
bench_orm,
deadlift_orm,
ohp_orm,
0 if skip_days is None else skip_days,
)
save_pickle = True
if save_pickle:
info("Saving pickle...")
with open(progress_pickle, 'wb') as fp:
pickle.dump(progress, fp)
get_workout(progress)
if __name__ == "__main__":
fire.Fire(main)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment