Add weekly RPE list, OpenGym push, and accessories

- Plan takes rpe_targets, one RPE per week; its length sets the block length.
- src/language/opengym.py converts a plan into OpenGym routines queued as a
  rotation and pushes them through the API, with a state backup before each write.
- scratch.py gains --opengym, --opengym-dry-run, --opengym-replace,
  --opengym-migrate and --opengym-accessories, plus the current block's numbers
  and accessory layout.
- Fix the kg-to-lb factor in scratch.py (2.20462).
This commit is contained in:
connor committed 2026-10-13 18:53:55 -04:00
1 parent 180b468c01
commit f1de9ff3fb
4 files changed
+663 -231

No files matched your search

+244 -217
View File
@@ -1,218 +1,245 @@
Week,Day,Lift,Goal,Set,Reps,RPE,Weight,Set Volume
1,1,Squat,Volume,1,4,4.0,125.0,500.0
1,1,Squat,Volume,2,4,5.5,132.5,530.0
1,1,Squat,Volume,3,4,7.0,137.5,550.0
1,1,Squat,Volume,4,4,7.0,137.5,550.0
1,1,Pause Squat,Volume,1,5,7,122.5,612.5
1,1,Pause Squat,Volume,2,5,7,122.5,612.5
1,1,Deadlift,Strength,1,5,4.0,120.0,600.0
1,1,Deadlift,Strength,2,3,5.5,135.0,405.0
1,1,Deadlift,Strength,3,1,7.0,152.5,152.5
1,1,Deadlift,Strength,4,3,7.0,142.5,427.5
1,1,Deadlift,Strength,5,3,7.0,142.5,427.5
1,2,Squat,Strength,1,5,4.0,120.0,600.0
1,2,Squat,Strength,2,3,5.5,135.0,405.0
1,2,Squat,Strength,3,1,7.0,152.5,152.5
1,2,Squat,Strength,4,3,7.0,142.5,427.5
1,2,Squat,Strength,5,3,7.0,142.5,427.5
1,2,Bench,Volume,1,4,4.0,102.5,410.0
1,2,Bench,Volume,2,4,5.5,107.5,430.0
1,2,Bench,Volume,3,4,7.0,112.5,450.0
1,2,Bench,Volume,4,4,7.0,112.5,450.0
1,2,Close Grip Bench,Volume,1,5,7,87.5,437.5
1,2,Close Grip Bench,Volume,2,5,7,87.5,437.5
1,3,Bench,Strength,1,5,4.0,100.0,500.0
1,3,Bench,Strength,2,3,5.5,112.5,337.5
1,3,Bench,Strength,3,1,7.0,125.0,125.0
1,3,Bench,Strength,4,3,7.0,117.5,352.5
1,3,Bench,Strength,5,3,7.0,117.5,352.5
1,3,Deadlift,Volume,1,4,4.0,125.0,500.0
1,3,Deadlift,Volume,2,4,5.5,132.5,530.0
1,3,Deadlift,Volume,3,4,7.0,137.5,550.0
1,3,Deadlift,Volume,4,4,7.0,137.5,550.0
1,3,Pause Deadlift,Volume,1,5,7,122.5,612.5
1,3,Pause Deadlift,Volume,2,5,7,122.5,612.5
1,4,Squat,Strength,1,5,4.5,122.5,612.5
1,4,Squat,Strength,2,3,6.0,137.5,412.5
1,4,Squat,Strength,3,1,7.5,155.0,155.0
1,4,Squat,Strength,4,3,7.5,145.0,435.0
1,4,Bench,Strength,1,5,4.5,100.0,500.0
1,4,Bench,Strength,2,3,6.0,112.5,337.5
1,4,Bench,Strength,3,1,7.5,127.5,127.5
1,4,Bench,Strength,4,3,7.5,120.0,360.0
1,4,Deadlift,Strength,1,5,4.5,122.5,612.5
1,4,Deadlift,Strength,2,3,6.0,137.5,412.5
1,4,Deadlift,Strength,3,1,7.5,155.0,155.0
1,4,Deadlift,Strength,4,3,7.5,145.0,435.0
2,1,Squat,Volume,1,4,4.5,127.5,510.0
2,1,Squat,Volume,2,4,6.0,132.5,530.0
2,1,Squat,Volume,3,4,7.5,140.0,560.0
2,1,Squat,Volume,4,4,7.5,140.0,560.0
2,1,Pause Squat,Volume,1,5,7,122.5,612.5
2,1,Pause Squat,Volume,2,5,7,122.5,612.5
2,1,Deadlift,Strength,1,5,4.5,122.5,612.5
2,1,Deadlift,Strength,2,3,6.0,137.5,412.5
2,1,Deadlift,Strength,3,1,7.5,155.0,155.0
2,1,Deadlift,Strength,4,3,7.5,145.0,435.0
2,1,Deadlift,Strength,5,3,7.5,145.0,435.0
2,2,Squat,Strength,1,5,4.5,122.5,612.5
2,2,Squat,Strength,2,3,6.0,137.5,412.5
2,2,Squat,Strength,3,1,7.5,155.0,155.0
2,2,Squat,Strength,4,3,7.5,145.0,435.0
2,2,Squat,Strength,5,3,7.5,145.0,435.0
2,2,Bench,Volume,1,4,4.5,105.0,420.0
2,2,Bench,Volume,2,4,6.0,110.0,440.0
2,2,Bench,Volume,3,4,7.5,115.0,460.0
2,2,Bench,Volume,4,4,7.5,115.0,460.0
2,2,Close Grip Bench,Volume,1,5,7,87.5,437.5
2,2,Close Grip Bench,Volume,2,5,7,87.5,437.5
2,3,Bench,Strength,1,5,4.5,100.0,500.0
2,3,Bench,Strength,2,3,6.0,112.5,337.5
2,3,Bench,Strength,3,1,7.5,127.5,127.5
2,3,Bench,Strength,4,3,7.5,120.0,360.0
2,3,Bench,Strength,5,3,7.5,120.0,360.0
2,3,Deadlift,Volume,1,4,4.5,127.5,510.0
2,3,Deadlift,Volume,2,4,6.0,132.5,530.0
2,3,Deadlift,Volume,3,4,7.5,140.0,560.0
2,3,Deadlift,Volume,4,4,7.5,140.0,560.0
2,3,Pause Deadlift,Volume,1,5,7,122.5,612.5
2,3,Pause Deadlift,Volume,2,5,7,122.5,612.5
2,4,Squat,Strength,1,5,5.0,125.0,625.0
2,4,Squat,Strength,2,3,6.5,140.0,420.0
2,4,Squat,Strength,3,1,8.0,157.5,157.5
2,4,Squat,Strength,4,3,8.0,147.5,442.5
2,4,Bench,Strength,1,5,5.0,102.5,512.5
2,4,Bench,Strength,2,3,6.5,115.0,345.0
2,4,Bench,Strength,3,1,8.0,130.0,130.0
2,4,Bench,Strength,4,3,8.0,120.0,360.0
2,4,Deadlift,Strength,1,5,5.0,125.0,625.0
2,4,Deadlift,Strength,2,3,6.5,140.0,420.0
2,4,Deadlift,Strength,3,1,8.0,157.5,157.5
2,4,Deadlift,Strength,4,3,8.0,147.5,442.5
3,1,Squat,Volume,1,4,5.0,130.0,520.0
3,1,Squat,Volume,2,4,6.5,135.0,540.0
3,1,Squat,Volume,3,4,8.0,142.5,570.0
3,1,Squat,Volume,4,4,8.0,142.5,570.0
3,1,Pause Squat,Volume,1,5,7,122.5,612.5
3,1,Pause Squat,Volume,2,5,7,122.5,612.5
3,1,Deadlift,Strength,1,5,5.0,125.0,625.0
3,1,Deadlift,Strength,2,3,6.5,140.0,420.0
3,1,Deadlift,Strength,3,1,8.0,157.5,157.5
3,1,Deadlift,Strength,4,3,8.0,147.5,442.5
3,1,Deadlift,Strength,5,3,8.0,147.5,442.5
3,2,Squat,Strength,1,5,5.0,125.0,625.0
3,2,Squat,Strength,2,3,6.5,140.0,420.0
3,2,Squat,Strength,3,1,8.0,157.5,157.5
3,2,Squat,Strength,4,3,8.0,147.5,442.5
3,2,Squat,Strength,5,3,8.0,147.5,442.5
3,2,Bench,Volume,1,4,5.0,107.5,430.0
3,2,Bench,Volume,2,4,6.5,112.5,450.0
3,2,Bench,Volume,3,4,8.0,117.5,470.0
3,2,Bench,Volume,4,4,8.0,117.5,470.0
3,2,Close Grip Bench,Volume,1,5,7,87.5,437.5
3,2,Close Grip Bench,Volume,2,5,7,87.5,437.5
3,3,Bench,Strength,1,5,5.0,102.5,512.5
3,3,Bench,Strength,2,3,6.5,115.0,345.0
3,3,Bench,Strength,3,1,8.0,130.0,130.0
3,3,Bench,Strength,4,3,8.0,120.0,360.0
3,3,Bench,Strength,5,3,8.0,120.0,360.0
3,3,Deadlift,Volume,1,4,5.0,130.0,520.0
3,3,Deadlift,Volume,2,4,6.5,135.0,540.0
3,3,Deadlift,Volume,3,4,8.0,142.5,570.0
3,3,Deadlift,Volume,4,4,8.0,142.5,570.0
3,3,Pause Deadlift,Volume,1,5,7,122.5,612.5
3,3,Pause Deadlift,Volume,2,5,7,122.5,612.5
3,4,Squat,Strength,1,5,5.5,127.5,637.5
3,4,Squat,Strength,2,3,7.0,142.5,427.5
3,4,Squat,Strength,3,1,8.5,160.0,160.0
3,4,Squat,Strength,4,3,8.5,150.0,450.0
3,4,Bench,Strength,1,5,5.5,105.0,525.0
3,4,Bench,Strength,2,3,7.0,117.5,352.5
3,4,Bench,Strength,3,1,8.5,132.5,132.5
3,4,Bench,Strength,4,3,8.5,122.5,367.5
3,4,Deadlift,Strength,1,5,5.5,127.5,637.5
3,4,Deadlift,Strength,2,3,7.0,142.5,427.5
3,4,Deadlift,Strength,3,1,8.5,160.0,160.0
3,4,Deadlift,Strength,4,3,8.5,150.0,450.0
4,1,Squat,Volume,1,4,5.5,132.5,530.0
4,1,Squat,Volume,2,4,7.0,137.5,550.0
4,1,Squat,Volume,3,4,8.5,145.0,580.0
4,1,Squat,Volume,4,4,8.5,145.0,580.0
4,1,Pause Squat,Volume,1,5,7,122.5,612.5
4,1,Pause Squat,Volume,2,5,7,122.5,612.5
4,1,Deadlift,Strength,1,5,5.5,127.5,637.5
4,1,Deadlift,Strength,2,3,7.0,142.5,427.5
4,1,Deadlift,Strength,3,1,8.5,160.0,160.0
4,1,Deadlift,Strength,4,3,8.5,150.0,450.0
4,1,Deadlift,Strength,5,3,8.5,150.0,450.0
4,2,Squat,Strength,1,5,5.5,127.5,637.5
4,2,Squat,Strength,2,3,7.0,142.5,427.5
4,2,Squat,Strength,3,1,8.5,160.0,160.0
4,2,Squat,Strength,4,3,8.5,150.0,450.0
4,2,Squat,Strength,5,3,8.5,150.0,450.0
4,2,Bench,Volume,1,5,5.5,105.0,525.0
4,2,Bench,Volume,2,5,7.0,110.0,550.0
4,2,Bench,Volume,3,5,8.5,115.0,575.0
4,2,Close Grip Bench,Volume,1,5,7,87.5,437.5
4,2,Close Grip Bench,Volume,2,5,7,87.5,437.5
4,3,Bench,Strength,1,5,5.5,105.0,525.0
4,3,Bench,Strength,2,3,7.0,117.5,352.5
4,3,Bench,Strength,3,1,8.5,132.5,132.5
4,3,Bench,Strength,4,3,8.5,122.5,367.5
4,3,Bench,Strength,5,3,8.5,122.5,367.5
4,3,Deadlift,Volume,1,4,5.5,132.5,530.0
4,3,Deadlift,Volume,2,4,7.0,137.5,550.0
4,3,Deadlift,Volume,3,4,8.5,145.0,580.0
4,3,Deadlift,Volume,4,4,8.5,145.0,580.0
4,3,Pause Deadlift,Volume,1,5,7,122.5,612.5
4,3,Pause Deadlift,Volume,2,5,7,122.5,612.5
4,4,Squat,Strength,1,5,6.0,130.0,650.0
4,4,Squat,Strength,2,3,7.5,145.0,435.0
4,4,Squat,Strength,3,1,9.0,162.5,162.5
4,4,Squat,Strength,4,3,9.0,152.5,457.5
4,4,Bench,Strength,1,5,6.0,107.5,537.5
4,4,Bench,Strength,2,3,7.5,120.0,360.0
4,4,Bench,Strength,3,1,9.0,132.5,132.5
4,4,Bench,Strength,4,3,9.0,125.0,375.0
4,4,Deadlift,Strength,1,5,6.0,130.0,650.0
4,4,Deadlift,Strength,2,3,7.5,145.0,435.0
4,4,Deadlift,Strength,3,1,9.0,162.5,162.5
4,4,Deadlift,Strength,4,3,9.0,152.5,457.5
5,1,Squat,Volume,1,4,3.0,120.0,480.0
5,1,Squat,Volume,2,4,4.5,127.5,510.0
5,1,Squat,Volume,3,4,6.0,132.5,530.0
5,1,Squat,Volume,4,4,6.0,132.5,530.0
5,1,Deadlift,Strength,1,5,3.0,115.0,575.0
5,1,Deadlift,Strength,2,3,4.5,132.5,397.5
5,1,Deadlift,Strength,3,1,6.0,147.5,147.5
5,1,Deadlift,Strength,4,2,6.0,142.5,285.0
5,2,Squat,Strength,1,5,3.0,115.0,575.0
5,2,Squat,Strength,2,3,4.5,132.5,397.5
5,2,Squat,Strength,3,1,6.0,147.5,147.5
5,2,Squat,Strength,4,2,6.0,142.5,285.0
5,2,Bench,Volume,1,5,3.0,95.0,475.0
5,2,Bench,Volume,2,5,4.5,100.0,500.0
5,2,Bench,Volume,3,5,6.0,107.5,537.5
5,3,Bench,Strength,1,5,3.0,95.0,475.0
5,3,Bench,Strength,2,3,4.5,107.5,322.5
5,3,Bench,Strength,3,1,6.0,120.0,120.0
5,3,Bench,Strength,4,2,6.0,117.5,235.0
5,3,Deadlift,Volume,1,4,3.0,120.0,480.0
5,3,Deadlift,Volume,2,4,4.5,127.5,510.0
5,3,Deadlift,Volume,3,4,6.0,132.5,530.0
5,3,Deadlift,Volume,4,4,6.0,132.5,530.0
5,4,Squat,Strength,1,5,6.0,130.0,650.0
5,4,Squat,Strength,2,3,7.0,142.5,427.5
5,4,Squat,Strength,3,1,7.0,152.5,152.5
5,4,Squat,Strength,4,1,8.5,160.0,160.0
5,4,Squat,Strength,5,1,10.0,170.0,170.0
5,4,Bench,Strength,1,5,6.0,107.5,537.5
5,4,Bench,Strength,2,3,7.0,117.5,352.5
5,4,Bench,Strength,3,1,7.0,125.0,125.0
5,4,Bench,Strength,4,1,8.5,132.5,132.5
5,4,Bench,Strength,5,1,10.0,140.0,140.0
5,4,Deadlift,Strength,1,5,6.0,130.0,650.0
5,4,Deadlift,Strength,2,3,7.0,142.5,427.5
5,4,Deadlift,Strength,3,1,7.0,152.5,152.5
5,4,Deadlift,Strength,4,1,8.5,160.0,160.0
5,4,Deadlift,Strength,5,1,10.0,170.0,170.0
1,1,Squat,Volume,1,5,4.0,100.0,500.0
1,1,Squat,Volume,2,5,5.5,107.5,537.5
1,1,Squat,Volume,3,5,7.0,112.5,562.5
1,1,Squat,Volume,4,5,7.0,112.5,562.5
1,1,Pause Squat,Volume,1,6,7,100.0,600.0
1,1,Pause Squat,Volume,2,6,7,100.0,600.0
1,1,Bench,Volume,1,5,4.0,82.5,412.5
1,1,Bench,Volume,2,5,5.5,87.5,437.5
1,1,Bench,Volume,3,5,7.0,90.0,450.0
1,1,Bench,Volume,4,5,7.0,90.0,450.0
1,1,Close Grip Bench,Volume,1,4,7,75.0,300.0
1,1,Close Grip Bench,Volume,2,4,7,75.0,300.0
1,1,Close Grip Bench,Volume,3,4,7,75.0,300.0
1,2,Squat,Volume,1,5,4.0,100.0,500.0
1,2,Squat,Volume,2,5,5.5,107.5,537.5
1,2,Squat,Volume,3,5,7.0,112.5,562.5
1,2,Squat,Volume,4,5,7.0,112.5,562.5
1,2,Pause Squat,Volume,1,6,7,100.0,600.0
1,2,Pause Squat,Volume,2,6,7,100.0,600.0
1,2,Deadlift,Volume,1,5,4.0,110.0,550.0
1,2,Deadlift,Volume,2,5,5.5,117.5,587.5
1,2,Deadlift,Volume,3,5,7.0,122.5,612.5
1,2,Deadlift,Volume,4,5,7.0,122.5,612.5
1,2,Pause Deadlift,Volume,1,5,7,112.5,562.5
1,2,Pause Deadlift,Volume,2,5,7,112.5,562.5
1,3,Bench,Volume,1,5,4.0,82.5,412.5
1,3,Bench,Volume,2,5,5.5,87.5,437.5
1,3,Bench,Volume,3,5,7.0,90.0,450.0
1,3,Bench,Volume,4,5,7.0,90.0,450.0
1,3,Close Grip Bench,Volume,1,4,7,75.0,300.0
1,3,Close Grip Bench,Volume,2,4,7,75.0,300.0
1,3,Close Grip Bench,Volume,3,4,7,75.0,300.0
1,3,Deadlift,Volume,1,5,4.0,110.0,550.0
1,3,Deadlift,Volume,2,5,5.5,117.5,587.5
1,3,Deadlift,Volume,3,5,7.0,122.5,612.5
1,3,Deadlift,Volume,4,5,7.0,122.5,612.5
1,3,Pause Deadlift,Volume,1,5,7,112.5,562.5
1,3,Pause Deadlift,Volume,2,5,7,112.5,562.5
1,4,Squat,Strength,1,5,4.5,102.5,512.5
1,4,Squat,Strength,2,3,6.0,115.0,345.0
1,4,Squat,Strength,3,1,7.5,130.0,130.0
1,4,Squat,Strength,4,3,7.5,120.0,360.0
1,4,Squat,Strength,5,3,7.5,120.0,360.0
1,4,Bench,Strength,1,5,4.5,82.5,412.5
1,4,Bench,Strength,2,3,6.0,92.5,277.5
1,4,Bench,Strength,3,1,7.5,105.0,105.0
1,4,Bench,Strength,4,3,7.5,97.5,292.5
1,4,Bench,Strength,5,3,7.5,97.5,292.5
1,4,Deadlift,Strength,1,5,4.5,112.5,562.5
1,4,Deadlift,Strength,2,3,6.0,125.0,375.0
1,4,Deadlift,Strength,3,1,7.5,140.0,140.0
1,4,Deadlift,Strength,4,2,7.5,135.0,270.0
1,4,Deadlift,Strength,5,2,7.5,135.0,270.0
2,1,Squat,Volume,1,5,4.5,102.5,512.5
2,1,Squat,Volume,2,5,6.0,107.5,537.5
2,1,Squat,Volume,3,5,7.5,112.5,562.5
2,1,Squat,Volume,4,5,7.5,112.5,562.5
2,1,Pause Squat,Volume,1,6,7,100.0,600.0
2,1,Pause Squat,Volume,2,6,7,100.0,600.0
2,1,Bench,Volume,1,5,4.5,82.5,412.5
2,1,Bench,Volume,2,5,6.0,87.5,437.5
2,1,Bench,Volume,3,5,7.5,92.5,462.5
2,1,Bench,Volume,4,5,7.5,92.5,462.5
2,1,Close Grip Bench,Volume,1,4,7,75.0,300.0
2,1,Close Grip Bench,Volume,2,4,7,75.0,300.0
2,1,Close Grip Bench,Volume,3,4,7,75.0,300.0
2,2,Squat,Volume,1,5,4.5,102.5,512.5
2,2,Squat,Volume,2,5,6.0,107.5,537.5
2,2,Squat,Volume,3,5,7.5,112.5,562.5
2,2,Squat,Volume,4,5,7.5,112.5,562.5
2,2,Pause Squat,Volume,1,6,7,100.0,600.0
2,2,Pause Squat,Volume,2,6,7,100.0,600.0
2,2,Deadlift,Volume,1,6,4.5,107.5,645.0
2,2,Deadlift,Volume,2,6,6.0,115.0,690.0
2,2,Deadlift,Volume,3,6,7.5,120.0,720.0
2,2,Pause Deadlift,Volume,1,5,7,112.5,562.5
2,2,Pause Deadlift,Volume,2,5,7,112.5,562.5
2,3,Bench,Volume,1,5,4.5,82.5,412.5
2,3,Bench,Volume,2,5,6.0,87.5,437.5
2,3,Bench,Volume,3,5,7.5,92.5,462.5
2,3,Bench,Volume,4,5,7.5,92.5,462.5
2,3,Close Grip Bench,Volume,1,4,7,75.0,300.0
2,3,Close Grip Bench,Volume,2,4,7,75.0,300.0
2,3,Close Grip Bench,Volume,3,4,7,75.0,300.0
2,3,Deadlift,Volume,1,6,4.5,107.5,645.0
2,3,Deadlift,Volume,2,6,6.0,115.0,690.0
2,3,Deadlift,Volume,3,6,7.5,120.0,720.0
2,3,Pause Deadlift,Volume,1,5,7,112.5,562.5
2,3,Pause Deadlift,Volume,2,5,7,112.5,562.5
2,4,Squat,Strength,1,5,5.0,105.0,525.0
2,4,Squat,Strength,2,3,6.5,117.5,352.5
2,4,Squat,Strength,3,1,8.0,130.0,130.0
2,4,Squat,Strength,4,3,8.0,122.5,367.5
2,4,Squat,Strength,5,3,8.0,122.5,367.5
2,4,Bench,Strength,1,5,5.0,85.0,425.0
2,4,Bench,Strength,2,3,6.5,95.0,285.0
2,4,Bench,Strength,3,1,8.0,105.0,105.0
2,4,Bench,Strength,4,3,8.0,100.0,300.0
2,4,Bench,Strength,5,3,8.0,100.0,300.0
2,4,Deadlift,Strength,1,5,5.0,115.0,575.0
2,4,Deadlift,Strength,2,3,6.5,127.5,382.5
2,4,Deadlift,Strength,3,1,8.0,142.5,142.5
2,4,Deadlift,Strength,4,4,8.0,130.0,520.0
3,1,Squat,Volume,1,5,5.0,105.0,525.0
3,1,Squat,Volume,2,5,6.5,110.0,550.0
3,1,Squat,Volume,3,5,8.0,115.0,575.0
3,1,Squat,Volume,4,5,8.0,115.0,575.0
3,1,Pause Squat,Volume,1,6,7,100.0,600.0
3,1,Pause Squat,Volume,2,6,7,100.0,600.0
3,1,Bench,Volume,1,5,5.0,85.0,425.0
3,1,Bench,Volume,2,5,6.5,90.0,450.0
3,1,Bench,Volume,3,5,8.0,92.5,462.5
3,1,Bench,Volume,4,5,8.0,92.5,462.5
3,1,Close Grip Bench,Volume,1,4,7,75.0,300.0
3,1,Close Grip Bench,Volume,2,4,7,75.0,300.0
3,1,Close Grip Bench,Volume,3,4,7,75.0,300.0
3,2,Squat,Volume,1,5,5.0,105.0,525.0
3,2,Squat,Volume,2,5,6.5,110.0,550.0
3,2,Squat,Volume,3,5,8.0,115.0,575.0
3,2,Squat,Volume,4,5,8.0,115.0,575.0
3,2,Pause Squat,Volume,1,6,7,100.0,600.0
3,2,Pause Squat,Volume,2,6,7,100.0,600.0
3,2,Deadlift,Volume,1,6,5.0,110.0,660.0
3,2,Deadlift,Volume,2,6,6.5,117.5,705.0
3,2,Deadlift,Volume,3,6,8.0,122.5,735.0
3,2,Pause Deadlift,Volume,1,5,7,112.5,562.5
3,2,Pause Deadlift,Volume,2,5,7,112.5,562.5
3,3,Bench,Volume,1,5,5.0,85.0,425.0
3,3,Bench,Volume,2,5,6.5,90.0,450.0
3,3,Bench,Volume,3,5,8.0,92.5,462.5
3,3,Bench,Volume,4,5,8.0,92.5,462.5
3,3,Close Grip Bench,Volume,1,4,7,75.0,300.0
3,3,Close Grip Bench,Volume,2,4,7,75.0,300.0
3,3,Close Grip Bench,Volume,3,4,7,75.0,300.0
3,3,Deadlift,Volume,1,6,5.0,110.0,660.0
3,3,Deadlift,Volume,2,6,6.5,117.5,705.0
3,3,Deadlift,Volume,3,6,8.0,122.5,735.0
3,3,Pause Deadlift,Volume,1,5,7,112.5,562.5
3,3,Pause Deadlift,Volume,2,5,7,112.5,562.5
3,4,Squat,Strength,1,5,5.5,107.5,537.5
3,4,Squat,Strength,2,3,7.0,120.0,360.0
3,4,Squat,Strength,3,1,8.5,132.5,132.5
3,4,Squat,Strength,4,3,8.5,125.0,375.0
3,4,Squat,Strength,5,3,8.5,125.0,375.0
3,4,Bench,Strength,1,5,5.5,87.5,437.5
3,4,Bench,Strength,2,3,7.0,97.5,292.5
3,4,Bench,Strength,3,1,8.5,107.5,107.5
3,4,Bench,Strength,4,3,8.5,100.0,300.0
3,4,Bench,Strength,5,3,8.5,100.0,300.0
3,4,Deadlift,Strength,1,5,5.5,117.5,587.5
3,4,Deadlift,Strength,2,3,7.0,130.0,390.0
3,4,Deadlift,Strength,3,1,8.5,145.0,145.0
3,4,Deadlift,Strength,4,4,8.5,132.5,530.0
4,1,Squat,Volume,1,5,5.5,107.5,537.5
4,1,Squat,Volume,2,5,7.0,112.5,562.5
4,1,Squat,Volume,3,5,8.5,117.5,587.5
4,1,Squat,Volume,4,5,8.5,117.5,587.5
4,1,Pause Squat,Volume,1,6,7,100.0,600.0
4,1,Pause Squat,Volume,2,6,7,100.0,600.0
4,1,Bench,Volume,1,5,5.5,87.5,437.5
4,1,Bench,Volume,2,5,7.0,90.0,450.0
4,1,Bench,Volume,3,5,8.5,95.0,475.0
4,1,Bench,Volume,4,5,8.5,95.0,475.0
4,1,Close Grip Bench,Volume,1,4,7,75.0,300.0
4,1,Close Grip Bench,Volume,2,4,7,75.0,300.0
4,1,Close Grip Bench,Volume,3,4,7,75.0,300.0
4,2,Squat,Volume,1,5,5.5,107.5,537.5
4,2,Squat,Volume,2,5,7.0,112.5,562.5
4,2,Squat,Volume,3,5,8.5,117.5,587.5
4,2,Squat,Volume,4,5,8.5,117.5,587.5
4,2,Pause Squat,Volume,1,6,7,100.0,600.0
4,2,Pause Squat,Volume,2,6,7,100.0,600.0
4,2,Deadlift,Volume,1,4,5.5,120.0,480.0
4,2,Deadlift,Volume,2,4,7.0,125.0,500.0
4,2,Deadlift,Volume,3,4,8.5,132.5,530.0
4,2,Deadlift,Volume,4,4,8.5,132.5,530.0
4,2,Pause Deadlift,Volume,1,5,7,112.5,562.5
4,2,Pause Deadlift,Volume,2,5,7,112.5,562.5
4,3,Bench,Volume,1,5,5.5,87.5,437.5
4,3,Bench,Volume,2,5,7.0,90.0,450.0
4,3,Bench,Volume,3,5,8.5,95.0,475.0
4,3,Bench,Volume,4,5,8.5,95.0,475.0
4,3,Close Grip Bench,Volume,1,4,7,75.0,300.0
4,3,Close Grip Bench,Volume,2,4,7,75.0,300.0
4,3,Close Grip Bench,Volume,3,4,7,75.0,300.0
4,3,Deadlift,Volume,1,4,5.5,120.0,480.0
4,3,Deadlift,Volume,2,4,7.0,125.0,500.0
4,3,Deadlift,Volume,3,4,8.5,132.5,530.0
4,3,Deadlift,Volume,4,4,8.5,132.5,530.0
4,3,Pause Deadlift,Volume,1,5,7,112.5,562.5
4,3,Pause Deadlift,Volume,2,5,7,112.5,562.5
4,4,Squat,Strength,1,5,6.0,107.5,537.5
4,4,Squat,Strength,2,3,7.5,120.0,360.0
4,4,Squat,Strength,3,1,9.0,135.0,135.0
4,4,Squat,Strength,4,3,9.0,127.5,382.5
4,4,Squat,Strength,5,3,9.0,127.5,382.5
4,4,Bench,Strength,1,5,6.0,87.5,437.5
4,4,Bench,Strength,2,3,7.5,97.5,292.5
4,4,Bench,Strength,3,1,9.0,110.0,110.0
4,4,Bench,Strength,4,3,9.0,102.5,307.5
4,4,Bench,Strength,5,3,9.0,102.5,307.5
4,4,Deadlift,Strength,1,5,6.0,117.5,587.5
4,4,Deadlift,Strength,2,3,7.5,132.5,397.5
4,4,Deadlift,Strength,3,1,9.0,147.5,147.5
4,4,Deadlift,Strength,4,4,9.0,135.0,540.0
5,1,Squat,Volume,1,5,3.0,97.5,487.5
5,1,Squat,Volume,2,5,4.5,102.5,512.5
5,1,Squat,Volume,3,5,6.0,107.5,537.5
5,1,Squat,Volume,4,5,6.0,107.5,537.5
5,1,Bench,Volume,1,5,3.0,77.5,387.5
5,1,Bench,Volume,2,5,4.5,82.5,412.5
5,1,Bench,Volume,3,5,6.0,87.5,437.5
5,1,Bench,Volume,4,5,6.0,87.5,437.5
5,2,Squat,Volume,1,5,3.0,97.5,487.5
5,2,Squat,Volume,2,5,4.5,102.5,512.5
5,2,Squat,Volume,3,5,6.0,107.5,537.5
5,2,Squat,Volume,4,5,6.0,107.5,537.5
5,2,Deadlift,Volume,1,4,3.0,110.0,440.0
5,2,Deadlift,Volume,2,4,4.5,117.5,470.0
5,2,Deadlift,Volume,3,4,6.0,122.5,490.0
5,2,Deadlift,Volume,4,4,6.0,122.5,490.0
5,3,Bench,Volume,1,5,3.0,77.5,387.5
5,3,Bench,Volume,2,5,4.5,82.5,412.5
5,3,Bench,Volume,3,5,6.0,87.5,437.5
5,3,Bench,Volume,4,5,6.0,87.5,437.5
5,3,Deadlift,Volume,1,4,3.0,110.0,440.0
5,3,Deadlift,Volume,2,4,4.5,117.5,470.0
5,3,Deadlift,Volume,3,4,6.0,122.5,490.0
5,3,Deadlift,Volume,4,4,6.0,122.5,490.0
5,4,Squat,Strength,1,5,6.0,107.5,537.5
5,4,Squat,Strength,2,3,7.0,120.0,360.0
5,4,Squat,Strength,3,1,7.0,127.5,127.5
5,4,Squat,Strength,4,1,8.5,132.5,132.5
5,4,Squat,Strength,5,1,10.0,142.5,142.5
5,4,Bench,Strength,1,5,6.0,87.5,437.5
5,4,Bench,Strength,2,3,7.0,97.5,292.5
5,4,Bench,Strength,3,1,7.0,102.5,102.5
5,4,Bench,Strength,4,1,8.5,107.5,107.5
5,4,Bench,Strength,5,1,10.0,115.0,115.0
5,4,Deadlift,Strength,1,5,6.0,117.5,587.5
5,4,Deadlift,Strength,2,3,7.0,130.0,390.0
5,4,Deadlift,Strength,3,1,7.0,137.5,137.5
5,4,Deadlift,Strength,4,1,8.5,145.0,145.0
5,4,Deadlift,Strength,5,1,10.0,155.0,155.0
1 Week Day Lift Goal Set Reps RPE Weight Set Volume
2 1 1 Squat Volume 1 4 5 4.0 125.0 100.0 500.0
3 1 1 Squat Volume 2 4 5 5.5 132.5 107.5 530.0 537.5
4 1 1 Squat Volume 3 4 5 7.0 137.5 112.5 550.0 562.5
5 1 1 Squat Volume 4 4 5 7.0 137.5 112.5 550.0 562.5
6 1 1 Pause Squat Volume 1 5 6 7 122.5 100.0 612.5 600.0
7 1 1 Pause Squat Volume 2 5 6 7 122.5 100.0 612.5 600.0
8 1 1 Deadlift Bench Strength Volume 1 5 4.0 120.0 82.5 600.0 412.5
9 1 1 Deadlift Bench Strength Volume 2 3 5 5.5 135.0 87.5 405.0 437.5
10 1 1 Deadlift Bench Strength Volume 3 1 5 7.0 152.5 90.0 152.5 450.0
11 1 1 Deadlift Bench Strength Volume 4 3 5 7.0 142.5 90.0 427.5 450.0
12 1 1 Deadlift Close Grip Bench Strength Volume 5 1 3 4 7.0 7 142.5 75.0 427.5 300.0
13 1 2 1 Squat Close Grip Bench Strength Volume 1 2 5 4 4.0 7 120.0 75.0 600.0 300.0
14 1 2 1 Squat Close Grip Bench Strength Volume 2 3 3 4 5.5 7 135.0 75.0 405.0 300.0
15 1 2 Squat Strength Volume 3 1 1 5 7.0 4.0 152.5 100.0 152.5 500.0
16 1 2 Squat Strength Volume 4 2 3 5 7.0 5.5 142.5 107.5 427.5 537.5
17 1 2 Squat Strength Volume 5 3 3 5 7.0 142.5 112.5 427.5 562.5
18 1 2 Bench Squat Volume 1 4 4 5 4.0 7.0 102.5 112.5 410.0 562.5
19 1 2 Bench Pause Squat Volume 2 1 4 6 5.5 7 107.5 100.0 430.0 600.0
20 1 2 Bench Pause Squat Volume 3 2 4 6 7.0 7 112.5 100.0 450.0 600.0
21 1 2 Bench Deadlift Volume 4 1 4 5 7.0 4.0 112.5 110.0 450.0 550.0
22 1 2 Close Grip Bench Deadlift Volume 1 2 5 7 5.5 87.5 117.5 437.5 587.5
23 1 2 Close Grip Bench Deadlift Volume 2 3 5 7 7.0 87.5 122.5 437.5 612.5
24 1 3 2 Bench Deadlift Strength Volume 1 4 5 4.0 7.0 100.0 122.5 500.0 612.5
25 1 3 2 Bench Pause Deadlift Strength Volume 2 1 3 5 5.5 7 112.5 337.5 562.5
26 1 3 2 Bench Pause Deadlift Strength Volume 3 2 1 5 7.0 7 125.0 112.5 125.0 562.5
27 1 3 Bench Strength Volume 4 1 3 5 7.0 4.0 117.5 82.5 352.5 412.5
28 1 3 Bench Strength Volume 5 2 3 5 7.0 5.5 117.5 87.5 352.5 437.5
29 1 3 Deadlift Bench Volume 1 3 4 5 4.0 7.0 125.0 90.0 500.0 450.0
30 1 3 Deadlift Bench Volume 2 4 4 5 5.5 7.0 132.5 90.0 530.0 450.0
31 1 3 Deadlift Close Grip Bench Volume 3 1 4 7.0 7 137.5 75.0 550.0 300.0
32 1 3 Deadlift Close Grip Bench Volume 4 2 4 7.0 7 137.5 75.0 550.0 300.0
33 1 3 Pause Deadlift Close Grip Bench Volume 1 3 5 4 7 122.5 75.0 612.5 300.0
34 1 3 Pause Deadlift Deadlift Volume 2 1 5 7 4.0 122.5 110.0 612.5 550.0
35 1 4 3 Squat Deadlift Strength Volume 1 2 5 4.5 5.5 122.5 117.5 612.5 587.5
36 1 4 3 Squat Deadlift Strength Volume 2 3 3 5 6.0 7.0 137.5 122.5 412.5 612.5
37 1 4 3 Squat Deadlift Strength Volume 3 4 1 5 7.5 7.0 155.0 122.5 155.0 612.5
38 1 4 3 Squat Pause Deadlift Strength Volume 4 1 3 5 7.5 7 145.0 112.5 435.0 562.5
39 1 4 3 Bench Pause Deadlift Strength Volume 1 2 5 4.5 7 100.0 112.5 500.0 562.5
40 1 4 Bench Squat Strength 2 1 3 5 6.0 4.5 112.5 102.5 337.5 512.5
41 1 4 Bench Squat Strength 3 2 1 3 7.5 6.0 127.5 115.0 127.5 345.0
42 1 4 Bench Squat Strength 4 3 3 1 7.5 120.0 130.0 360.0 130.0
43 1 4 Deadlift Squat Strength 1 4 5 3 4.5 7.5 122.5 120.0 612.5 360.0
44 1 4 Deadlift Squat Strength 2 5 3 6.0 7.5 137.5 120.0 412.5 360.0
45 1 4 Deadlift Bench Strength 3 1 1 5 7.5 4.5 155.0 82.5 155.0 412.5
46 1 4 Deadlift Bench Strength 4 2 3 7.5 6.0 145.0 92.5 435.0 277.5
47 2 1 1 4 Squat Bench Volume Strength 1 3 4 1 4.5 7.5 127.5 105.0 510.0 105.0
48 2 1 1 4 Squat Bench Volume Strength 2 4 4 3 6.0 7.5 132.5 97.5 530.0 292.5
49 2 1 1 4 Squat Bench Volume Strength 3 5 4 3 7.5 140.0 97.5 560.0 292.5
50 2 1 1 4 Squat Deadlift Volume Strength 4 1 4 5 7.5 4.5 140.0 112.5 560.0 562.5
51 2 1 1 4 Pause Squat Deadlift Volume Strength 1 2 5 3 7 6.0 122.5 125.0 612.5 375.0
52 2 1 1 4 Pause Squat Deadlift Volume Strength 2 3 5 1 7 7.5 122.5 140.0 612.5 140.0
53 2 1 1 4 Deadlift Strength 1 4 5 2 4.5 7.5 122.5 135.0 612.5 270.0
54 2 1 1 4 Deadlift Strength 2 5 3 2 6.0 7.5 137.5 135.0 412.5 270.0
55 2 1 Deadlift Squat Strength Volume 3 1 1 5 7.5 4.5 155.0 102.5 155.0 512.5
56 2 1 Deadlift Squat Strength Volume 4 2 3 5 7.5 6.0 145.0 107.5 435.0 537.5
57 2 1 Deadlift Squat Strength Volume 5 3 3 5 7.5 145.0 112.5 435.0 562.5
58 2 2 1 Squat Strength Volume 1 4 5 4.5 7.5 122.5 112.5 612.5 562.5
59 2 2 1 Squat Pause Squat Strength Volume 2 1 3 6 6.0 7 137.5 100.0 412.5 600.0
60 2 2 1 Squat Pause Squat Strength Volume 3 2 1 6 7.5 7 155.0 100.0 155.0 600.0
61 2 2 1 Squat Bench Strength Volume 4 1 3 5 7.5 4.5 145.0 82.5 435.0 412.5
62 2 2 1 Squat Bench Strength Volume 5 2 3 5 7.5 6.0 145.0 87.5 435.0 437.5
63 2 2 1 Bench Volume 1 3 4 5 4.5 7.5 105.0 92.5 420.0 462.5
64 2 2 1 Bench Volume 2 4 4 5 6.0 7.5 110.0 92.5 440.0 462.5
65 2 2 1 Bench Close Grip Bench Volume 3 1 4 7.5 7 115.0 75.0 460.0 300.0
66 2 2 1 Bench Close Grip Bench Volume 4 2 4 7.5 7 115.0 75.0 460.0 300.0
67 2 2 1 Close Grip Bench Volume 1 3 5 4 7 87.5 75.0 437.5 300.0
68 2 2 Close Grip Bench Squat Volume 2 1 5 7 4.5 87.5 102.5 437.5 512.5
69 2 3 2 Bench Squat Strength Volume 1 2 5 4.5 6.0 100.0 107.5 500.0 537.5
70 2 3 2 Bench Squat Strength Volume 2 3 3 5 6.0 7.5 112.5 337.5 562.5
71 2 3 2 Bench Squat Strength Volume 3 4 1 5 7.5 127.5 112.5 127.5 562.5
72 2 3 2 Bench Pause Squat Strength Volume 4 1 3 6 7.5 7 120.0 100.0 360.0 600.0
73 2 3 2 Bench Pause Squat Strength Volume 5 2 3 6 7.5 7 120.0 100.0 360.0 600.0
74 2 3 2 Deadlift Volume 1 4 6 4.5 127.5 107.5 510.0 645.0
75 2 3 2 Deadlift Volume 2 4 6 6.0 132.5 115.0 530.0 690.0
76 2 3 2 Deadlift Volume 3 4 6 7.5 140.0 120.0 560.0 720.0
77 2 3 2 Deadlift Pause Deadlift Volume 4 1 4 5 7.5 7 140.0 112.5 560.0 562.5
78 2 3 2 Pause Deadlift Volume 1 2 5 7 122.5 112.5 612.5 562.5
79 2 3 Pause Deadlift Bench Volume 2 1 5 7 4.5 122.5 82.5 612.5 412.5
80 2 4 3 Squat Bench Strength Volume 1 2 5 5.0 6.0 125.0 87.5 625.0 437.5
81 2 4 3 Squat Bench Strength Volume 2 3 3 5 6.5 7.5 140.0 92.5 420.0 462.5
82 2 4 3 Squat Bench Strength Volume 3 4 1 5 8.0 7.5 157.5 92.5 157.5 462.5
83 2 4 3 Squat Close Grip Bench Strength Volume 4 1 3 4 8.0 7 147.5 75.0 442.5 300.0
84 2 4 3 Bench Close Grip Bench Strength Volume 1 2 5 4 5.0 7 102.5 75.0 512.5 300.0
85 2 4 3 Bench Close Grip Bench Strength Volume 2 3 3 4 6.5 7 115.0 75.0 345.0 300.0
86 2 4 3 Bench Deadlift Strength Volume 3 1 1 6 8.0 4.5 130.0 107.5 130.0 645.0
87 2 4 3 Bench Deadlift Strength Volume 4 2 3 6 8.0 6.0 120.0 115.0 360.0 690.0
88 2 4 3 Deadlift Strength Volume 1 3 5 6 5.0 7.5 125.0 120.0 625.0 720.0
89 2 4 3 Deadlift Pause Deadlift Strength Volume 2 1 3 5 6.5 7 140.0 112.5 420.0 562.5
90 2 4 3 Deadlift Pause Deadlift Strength Volume 3 2 1 5 8.0 7 157.5 112.5 157.5 562.5
91 2 4 Deadlift Squat Strength 4 1 3 5 8.0 5.0 147.5 105.0 442.5 525.0
92 3 2 1 4 Squat Volume Strength 1 2 4 3 5.0 6.5 130.0 117.5 520.0 352.5
93 3 2 1 4 Squat Volume Strength 2 3 4 1 6.5 8.0 135.0 130.0 540.0 130.0
94 3 2 1 4 Squat Volume Strength 3 4 4 3 8.0 142.5 122.5 570.0 367.5
95 3 2 1 4 Squat Volume Strength 4 5 4 3 8.0 142.5 122.5 570.0 367.5
96 3 2 1 4 Pause Squat Bench Volume Strength 1 5 7 5.0 122.5 85.0 612.5 425.0
97 3 2 1 4 Pause Squat Bench Volume Strength 2 5 3 7 6.5 122.5 95.0 612.5 285.0
98 3 2 1 4 Deadlift Bench Strength 1 3 5 1 5.0 8.0 125.0 105.0 625.0 105.0
99 3 2 1 4 Deadlift Bench Strength 2 4 3 6.5 8.0 140.0 100.0 420.0 300.0
100 3 2 1 4 Deadlift Bench Strength 3 5 1 3 8.0 157.5 100.0 157.5 300.0
101 3 2 1 4 Deadlift Strength 4 1 3 5 8.0 5.0 147.5 115.0 442.5 575.0
102 3 2 1 4 Deadlift Strength 5 2 3 8.0 6.5 147.5 127.5 442.5 382.5
103 3 2 2 4 Squat Deadlift Strength 1 3 5 1 5.0 8.0 125.0 142.5 625.0 142.5
104 3 2 2 4 Squat Deadlift Strength 2 4 3 4 6.5 8.0 140.0 130.0 420.0 520.0
105 3 2 1 Squat Strength Volume 3 1 1 5 8.0 5.0 157.5 105.0 157.5 525.0
106 3 2 1 Squat Strength Volume 4 2 3 5 8.0 6.5 147.5 110.0 442.5 550.0
107 3 2 1 Squat Strength Volume 5 3 3 5 8.0 147.5 115.0 442.5 575.0
108 3 2 1 Bench Squat Volume 1 4 4 5 5.0 8.0 107.5 115.0 430.0 575.0
109 3 2 1 Bench Pause Squat Volume 2 1 4 6 6.5 7 112.5 100.0 450.0 600.0
110 3 2 1 Bench Pause Squat Volume 3 2 4 6 8.0 7 117.5 100.0 470.0 600.0
111 3 2 1 Bench Volume 4 1 4 5 8.0 5.0 117.5 85.0 470.0 425.0
112 3 2 1 Close Grip Bench Bench Volume 1 2 5 7 6.5 87.5 90.0 437.5 450.0
113 3 2 1 Close Grip Bench Bench Volume 2 3 5 7 8.0 87.5 92.5 437.5 462.5
114 3 3 1 Bench Strength Volume 1 4 5 5.0 8.0 102.5 92.5 512.5 462.5
115 3 3 1 Bench Close Grip Bench Strength Volume 2 1 3 4 6.5 7 115.0 75.0 345.0 300.0
116 3 3 1 Bench Close Grip Bench Strength Volume 3 2 1 4 8.0 7 130.0 75.0 130.0 300.0
117 3 3 1 Bench Close Grip Bench Strength Volume 4 3 3 4 8.0 7 120.0 75.0 360.0 300.0
118 3 3 2 Bench Squat Strength Volume 5 1 3 5 8.0 5.0 120.0 105.0 360.0 525.0
119 3 3 2 Deadlift Squat Volume 1 2 4 5 5.0 6.5 130.0 110.0 520.0 550.0
120 3 3 2 Deadlift Squat Volume 2 3 4 5 6.5 8.0 135.0 115.0 540.0 575.0
121 3 3 2 Deadlift Squat Volume 3 4 4 5 8.0 142.5 115.0 570.0 575.0
122 3 3 2 Deadlift Pause Squat Volume 4 1 4 6 8.0 7 142.5 100.0 570.0 600.0
123 3 3 2 Pause Deadlift Pause Squat Volume 1 2 5 6 7 122.5 100.0 612.5 600.0
124 3 3 2 Pause Deadlift Deadlift Volume 2 1 5 6 7 5.0 122.5 110.0 612.5 660.0
125 3 4 2 Squat Deadlift Strength Volume 1 2 5 6 5.5 6.5 127.5 117.5 637.5 705.0
126 3 4 2 Squat Deadlift Strength Volume 2 3 3 6 7.0 8.0 142.5 122.5 427.5 735.0
127 3 4 2 Squat Pause Deadlift Strength Volume 3 1 1 5 8.5 7 160.0 112.5 160.0 562.5
128 3 4 2 Squat Pause Deadlift Strength Volume 4 2 3 5 8.5 7 150.0 112.5 450.0 562.5
129 3 4 3 Bench Strength Volume 1 5 5.5 5.0 105.0 85.0 525.0 425.0
130 3 4 3 Bench Strength Volume 2 3 5 7.0 6.5 117.5 90.0 352.5 450.0
131 3 4 3 Bench Strength Volume 3 1 5 8.5 8.0 132.5 92.5 132.5 462.5
132 3 4 3 Bench Strength Volume 4 3 5 8.5 8.0 122.5 92.5 367.5 462.5
133 3 4 3 Deadlift Close Grip Bench Strength Volume 1 5 4 5.5 7 127.5 75.0 637.5 300.0
134 3 4 3 Deadlift Close Grip Bench Strength Volume 2 3 4 7.0 7 142.5 75.0 427.5 300.0
135 3 4 3 Deadlift Close Grip Bench Strength Volume 3 1 4 8.5 7 160.0 75.0 160.0 300.0
136 3 4 3 Deadlift Strength Volume 4 1 3 6 8.5 5.0 150.0 110.0 450.0 660.0
137 4 3 1 3 Squat Deadlift Volume 1 2 4 6 5.5 6.5 132.5 117.5 530.0 705.0
138 4 3 1 3 Squat Deadlift Volume 2 3 4 6 7.0 8.0 137.5 122.5 550.0 735.0
139 4 3 1 3 Squat Pause Deadlift Volume 3 1 4 5 8.5 7 145.0 112.5 580.0 562.5
140 4 3 1 3 Squat Pause Deadlift Volume 4 2 4 5 8.5 7 145.0 112.5 580.0 562.5
141 4 3 1 4 Pause Squat Squat Volume Strength 1 5 7 5.5 122.5 107.5 612.5 537.5
142 4 3 1 4 Pause Squat Squat Volume Strength 2 5 3 7 7.0 122.5 120.0 612.5 360.0
143 4 3 1 4 Deadlift Squat Strength 1 3 5 1 5.5 8.5 127.5 132.5 637.5 132.5
144 4 3 1 4 Deadlift Squat Strength 2 4 3 7.0 8.5 142.5 125.0 427.5 375.0
145 4 3 1 4 Deadlift Squat Strength 3 5 1 3 8.5 160.0 125.0 160.0 375.0
146 4 3 1 4 Deadlift Bench Strength 4 1 3 5 8.5 5.5 150.0 87.5 450.0 437.5
147 4 3 1 4 Deadlift Bench Strength 5 2 3 8.5 7.0 150.0 97.5 450.0 292.5
148 4 3 2 4 Squat Bench Strength 1 3 5 1 5.5 8.5 127.5 107.5 637.5 107.5
149 4 3 2 4 Squat Bench Strength 2 4 3 7.0 8.5 142.5 100.0 427.5 300.0
150 4 3 2 4 Squat Bench Strength 3 5 1 3 8.5 160.0 100.0 160.0 300.0
151 4 3 2 4 Squat Deadlift Strength 4 1 3 5 8.5 5.5 150.0 117.5 450.0 587.5
152 4 3 2 4 Squat Deadlift Strength 5 2 3 8.5 7.0 150.0 130.0 450.0 390.0
153 4 3 2 4 Bench Deadlift Volume Strength 1 3 5 1 5.5 8.5 105.0 145.0 525.0 145.0
154 4 3 2 4 Bench Deadlift Volume Strength 2 4 5 4 7.0 8.5 110.0 132.5 550.0 530.0
155 4 2 1 Bench Squat Volume 3 1 5 8.5 5.5 115.0 107.5 575.0 537.5
156 4 2 1 Close Grip Bench Squat Volume 1 2 5 7 7.0 87.5 112.5 437.5 562.5
157 4 2 1 Close Grip Bench Squat Volume 2 3 5 7 8.5 87.5 117.5 437.5 587.5
158 4 3 1 Bench Squat Strength Volume 1 4 5 5.5 8.5 105.0 117.5 525.0 587.5
159 4 3 1 Bench Pause Squat Strength Volume 2 1 3 6 7.0 7 117.5 100.0 352.5 600.0
160 4 3 1 Bench Pause Squat Strength Volume 3 2 1 6 8.5 7 132.5 100.0 132.5 600.0
161 4 3 1 Bench Strength Volume 4 1 3 5 8.5 5.5 122.5 87.5 367.5 437.5
162 4 3 1 Bench Strength Volume 5 2 3 5 8.5 7.0 122.5 90.0 367.5 450.0
163 4 3 1 Deadlift Bench Volume 1 3 4 5 5.5 8.5 132.5 95.0 530.0 475.0
164 4 3 1 Deadlift Bench Volume 2 4 4 5 7.0 8.5 137.5 95.0 550.0 475.0
165 4 3 1 Deadlift Close Grip Bench Volume 3 1 4 8.5 7 145.0 75.0 580.0 300.0
166 4 3 1 Deadlift Close Grip Bench Volume 4 2 4 8.5 7 145.0 75.0 580.0 300.0
167 4 3 1 Pause Deadlift Close Grip Bench Volume 1 3 5 4 7 122.5 75.0 612.5 300.0
168 4 3 2 Pause Deadlift Squat Volume 2 1 5 7 5.5 122.5 107.5 612.5 537.5
169 4 4 2 Squat Strength Volume 1 2 5 6.0 7.0 130.0 112.5 650.0 562.5
170 4 4 2 Squat Strength Volume 2 3 3 5 7.5 8.5 145.0 117.5 435.0 587.5
171 4 4 2 Squat Strength Volume 3 4 1 5 9.0 8.5 162.5 117.5 162.5 587.5
172 4 4 2 Squat Pause Squat Strength Volume 4 1 3 6 9.0 7 152.5 100.0 457.5 600.0
173 4 4 2 Bench Pause Squat Strength Volume 1 2 5 6 6.0 7 107.5 100.0 537.5 600.0
174 4 4 2 Bench Deadlift Strength Volume 2 1 3 4 7.5 5.5 120.0 360.0 480.0
175 4 4 2 Bench Deadlift Strength Volume 3 2 1 4 9.0 7.0 132.5 125.0 132.5 500.0
176 4 4 2 Bench Deadlift Strength Volume 4 3 3 4 9.0 8.5 125.0 132.5 375.0 530.0
177 4 4 2 Deadlift Strength Volume 1 4 5 4 6.0 8.5 130.0 132.5 650.0 530.0
178 4 4 2 Deadlift Pause Deadlift Strength Volume 2 1 3 5 7.5 7 145.0 112.5 435.0 562.5
179 4 4 2 Deadlift Pause Deadlift Strength Volume 3 2 1 5 9.0 7 162.5 112.5 162.5 562.5
180 4 4 3 Deadlift Bench Strength Volume 4 1 3 5 9.0 5.5 152.5 87.5 457.5 437.5
181 5 4 1 3 Squat Bench Volume 1 2 4 5 3.0 7.0 120.0 90.0 480.0 450.0
182 5 4 1 3 Squat Bench Volume 2 3 4 5 4.5 8.5 127.5 95.0 510.0 475.0
183 5 4 1 3 Squat Bench Volume 3 4 4 5 6.0 8.5 132.5 95.0 530.0 475.0
184 5 4 1 3 Squat Close Grip Bench Volume 4 1 4 6.0 7 132.5 75.0 530.0 300.0
185 5 4 1 3 Deadlift Close Grip Bench Strength Volume 1 2 5 4 3.0 7 115.0 75.0 575.0 300.0
186 5 4 1 3 Deadlift Close Grip Bench Strength Volume 2 3 3 4 4.5 7 132.5 75.0 397.5 300.0
187 5 4 1 3 Deadlift Strength Volume 3 1 1 4 6.0 5.5 147.5 120.0 147.5 480.0
188 5 4 1 3 Deadlift Strength Volume 4 2 2 4 6.0 7.0 142.5 125.0 285.0 500.0
189 5 4 2 3 Squat Deadlift Strength Volume 1 3 5 4 3.0 8.5 115.0 132.5 575.0 530.0
190 5 4 2 3 Squat Deadlift Strength Volume 2 4 3 4 4.5 8.5 132.5 397.5 530.0
191 5 4 2 3 Squat Pause Deadlift Strength Volume 3 1 1 5 6.0 7 147.5 112.5 147.5 562.5
192 5 4 2 3 Squat Pause Deadlift Strength Volume 4 2 2 5 6.0 7 142.5 112.5 285.0 562.5
193 5 4 2 4 Bench Squat Volume Strength 1 5 3.0 6.0 95.0 107.5 475.0 537.5
194 5 4 2 4 Bench Squat Volume Strength 2 5 3 4.5 7.5 100.0 120.0 500.0 360.0
195 5 4 2 4 Bench Squat Volume Strength 3 5 1 6.0 9.0 107.5 135.0 537.5 135.0
196 5 4 3 4 Bench Squat Strength 1 4 5 3 3.0 9.0 95.0 127.5 475.0 382.5
197 5 4 3 4 Bench Squat Strength 2 5 3 4.5 9.0 107.5 127.5 322.5 382.5
198 5 4 3 4 Bench Strength 3 1 1 5 6.0 120.0 87.5 120.0 437.5
199 5 4 3 4 Bench Strength 4 2 2 3 6.0 7.5 117.5 97.5 235.0 292.5
200 5 4 3 4 Deadlift Bench Volume Strength 1 3 4 1 3.0 9.0 120.0 110.0 480.0 110.0
201 5 4 3 4 Deadlift Bench Volume Strength 2 4 4 3 4.5 9.0 127.5 102.5 510.0 307.5
202 5 4 3 4 Deadlift Bench Volume Strength 3 5 4 3 6.0 9.0 132.5 102.5 530.0 307.5
203 5 4 3 4 Deadlift Volume Strength 4 1 4 5 6.0 132.5 117.5 530.0 587.5
204 5 4 4 Squat Deadlift Strength 1 2 5 3 6.0 7.5 130.0 132.5 650.0 397.5
205 5 4 4 Squat Deadlift Strength 2 3 3 1 7.0 9.0 142.5 147.5 427.5 147.5
206 5 4 4 Squat Deadlift Strength 3 4 1 4 7.0 9.0 152.5 135.0 152.5 540.0
207 5 4 1 Squat Strength Volume 4 1 1 5 8.5 3.0 160.0 97.5 160.0 487.5
208 5 4 1 Squat Strength Volume 5 2 1 5 10.0 4.5 170.0 102.5 170.0 512.5
209 5 4 1 Bench Squat Strength Volume 1 3 5 6.0 107.5 537.5
210 5 4 1 Bench Squat Strength Volume 2 4 3 5 7.0 6.0 117.5 107.5 352.5 537.5
211 5 4 1 Bench Strength Volume 3 1 1 5 7.0 3.0 125.0 77.5 125.0 387.5
212 5 4 1 Bench Strength Volume 4 2 1 5 8.5 4.5 132.5 82.5 132.5 412.5
213 5 4 1 Bench Strength Volume 5 3 1 5 10.0 6.0 140.0 87.5 140.0 437.5
214 5 4 1 Deadlift Bench Strength Volume 1 4 5 6.0 130.0 87.5 650.0 437.5
215 5 4 2 Deadlift Squat Strength Volume 2 1 3 5 7.0 3.0 142.5 97.5 427.5 487.5
216 5 4 2 Deadlift Squat Strength Volume 3 2 1 5 7.0 4.5 152.5 102.5 152.5 512.5
217 5 4 2 Deadlift Squat Strength Volume 4 3 1 5 8.5 6.0 160.0 107.5 160.0 537.5
218 5 4 2 Deadlift Squat Strength Volume 5 4 1 5 10.0 6.0 170.0 107.5 170.0 537.5
219 5 2 Deadlift Volume 1 4 3.0 110.0 440.0
220 5 2 Deadlift Volume 2 4 4.5 117.5 470.0
221 5 2 Deadlift Volume 3 4 6.0 122.5 490.0
222 5 2 Deadlift Volume 4 4 6.0 122.5 490.0
223 5 3 Bench Volume 1 5 3.0 77.5 387.5
224 5 3 Bench Volume 2 5 4.5 82.5 412.5
225 5 3 Bench Volume 3 5 6.0 87.5 437.5
226 5 3 Bench Volume 4 5 6.0 87.5 437.5
227 5 3 Deadlift Volume 1 4 3.0 110.0 440.0
228 5 3 Deadlift Volume 2 4 4.5 117.5 470.0
229 5 3 Deadlift Volume 3 4 6.0 122.5 490.0
230 5 3 Deadlift Volume 4 4 6.0 122.5 490.0
231 5 4 Squat Strength 1 5 6.0 107.5 537.5
232 5 4 Squat Strength 2 3 7.0 120.0 360.0
233 5 4 Squat Strength 3 1 7.0 127.5 127.5
234 5 4 Squat Strength 4 1 8.5 132.5 132.5
235 5 4 Squat Strength 5 1 10.0 142.5 142.5
236 5 4 Bench Strength 1 5 6.0 87.5 437.5
237 5 4 Bench Strength 2 3 7.0 97.5 292.5
238 5 4 Bench Strength 3 1 7.0 102.5 102.5
239 5 4 Bench Strength 4 1 8.5 107.5 107.5
240 5 4 Bench Strength 5 1 10.0 115.0 115.0
241 5 4 Deadlift Strength 1 5 6.0 117.5 587.5
242 5 4 Deadlift Strength 2 3 7.0 130.0 390.0
243 5 4 Deadlift Strength 3 1 7.0 137.5 137.5
244 5 4 Deadlift Strength 4 1 8.5 145.0 145.0
245 5 4 Deadlift Strength 5 1 10.0 155.0 155.0
+67 -8
View File
@@ -1,16 +1,44 @@
from language import Plan, LiftGoal
import argparse
import os
import sys
from datetime import date
sample_plan = Plan(squat_plan = [LiftGoal.VOLUME, LiftGoal.STRENGTH, LiftGoal.STRENGTH],
bench_plan = [LiftGoal.VOLUME, LiftGoal.STRENGTH, LiftGoal.STRENGTH],
deadlift_plan = [LiftGoal.STRENGTH, LiftGoal.VOLUME, LiftGoal.STRENGTH],
from language import Plan, Lift, LiftGoal
from language import opengym
sample_plan = Plan(squat_plan = [LiftGoal.VOLUME, LiftGoal.VOLUME, LiftGoal.STRENGTH],
bench_plan = [LiftGoal.VOLUME, LiftGoal.VOLUME, LiftGoal.STRENGTH],
deadlift_plan = [LiftGoal.VOLUME, LiftGoal.VOLUME, LiftGoal.STRENGTH],
squat_targets=(3100, 2000),
bench_targets=(2500, 1650),
deadlift_targets=(3100, 2000),
squat_1rm=170, bench_1rm=140, deadlift_1rm=170,
num_weeks=4, num_workouts=4
squat_1rm=142, bench_1rm=115, deadlift_1rm=155,
rpe_targets=[7.0, 7.5, 8.0, 8.5], num_workouts=4
)
for (i, week) in enumerate(sample_plan.generate_full_plan()):
# Accessories by day of the week, in every week but the taper/mock meet week:
# (name in opengym.ACCESSORY_IDS, sets, reps, insert before this exercise id, or None for the end).
BENCH = opengym.EXERCISE_IDS[Lift.BENCH]
ACCESSORIES = {
1: [("external rotation", 1, 10, BENCH), ("triceps pushdown", 2, 10, None), ("reverse fly", 2, 10, None)],
2: [("incline curl", 2, 10, None)],
3: [("triceps pushdown", 2, 10, None), ("lateral raise", 2, 10, None)],
}
parser = argparse.ArgumentParser()
parser.add_argument("--opengym", action="store_true", help="push the plan to OpenGym as a rotation")
parser.add_argument("--opengym-dry-run", action="store_true", help="show what --opengym would change, write nothing")
parser.add_argument("--opengym-replace", action="store_true", help="replace routines from an earlier push")
parser.add_argument("--opengym-migrate", nargs=2, metavar=("OLD_ID", "NEW_ID"),
help="re-file logged history from one exercise id to another (with --opengym or --opengym-dry-run)")
parser.add_argument("--opengym-accessories", action="store_true",
help="add ACCESSORIES to routines already pushed, leaving the queue alone")
parser.add_argument("--start", type=date.fromisoformat, default=date.today(), help="first day of the block")
parser.add_argument("--label", default="Powerlifting block", help="rotation name in OpenGym")
args = parser.parse_args()
full_plan = sample_plan.generate_full_plan()
for (i, week) in enumerate(full_plan):
print()
print(f"Week {i + 1}:")
for (j, day) in enumerate(week):
@@ -18,6 +46,37 @@ for (i, week) in enumerate(sample_plan.generate_full_plan()):
total_volume = sum(lift.total_volume for lift in day)
for lift in day:
print(f" {lift.lift}: {lift.sets}")
print(f" Total Volume: {total_volume * 2.2165:.2f} lbs")
print(f" Total Volume: {total_volume * 2.20462:.2f} lbs")
sample_plan.to_csv("sample_plan.csv")
if args.opengym or args.opengym_dry_run or args.opengym_accessories:
client = opengym.Client(os.environ.get("OPENGYM_URL") or opengym.resolve_base_url())
client.login(*opengym.rbw_credentials(os.environ.get("OPENGYM_RBW_ENTRY", "gym.rcjohnstone.com")))
routines = opengym.plan_to_routines(full_plan)
def change(state):
if args.opengym_accessories:
opengym.add_accessories(state["routines"], ACCESSORIES, state.get("exWeights") or {})
return
opengym.add_accessories(routines, ACCESSORIES, state.get("exWeights") or {})
if args.opengym_migrate:
n = opengym.migrate_exercise(state, *args.opengym_migrate)
print(f"migrate: {n} workouts re-filed from {args.opengym_migrate[0]} to {args.opengym_migrate[1]}")
print(opengym.describe_change(state, routines))
opengym.apply_plan(state, routines, args.start, args.label, args.opengym_replace)
print()
if args.opengym_dry_run:
state, rev = client.get_state()
change(state)
if args.opengym_accessories:
for r in state["routines"]:
print(r["name"], [e["id"] for e in r["ex"]])
print(f"dry run against rev {rev}: nothing written")
sys.exit(0)
rev = client.update_state(change)
if args.opengym_accessories:
print(f"added accessories; OpenGym is now at rev {rev}")
sys.exit(0)
print(f"pushed {len(routines)} routines starting {args.start}; OpenGym is now at rev {rev}")
+320
View File
@@ -0,0 +1,320 @@
"""Push a generated Plan to an OpenGym instance as a rotation of routines.
OpenGym keeps one state document per user. Writing means GET /api/data, change it, and
PUT it back with baseRev/baseWid; the server rejects the PUT with 409 if anything else
wrote in between. Entries left out of a PUT are recorded as deletions on every device, so
everything here only adds to or edits the state it just read.
"""
import json
import os
import subprocess
import time
import urllib.error
import urllib.request
from collections import Counter
from datetime import date
from itertools import groupby
from .lifts import Lift, LiftGoal, LiftSession
# OpenGym library exercise ids.
EXERCISE_IDS = {
Lift.SQUAT: "1435", # barbell low bar squat
Lift.HIGH_BAR_SQUAT: "1436", # barbell high bar squat
Lift.PAUSE_SQUAT: "5017", # pause squat
Lift.BENCH: "0025", # barbell bench press
Lift.CLOSE_GRIP_BENCH: "0030", # barbell close-grip bench press
Lift.LARSEN_PRESS: "6270", # barbell larsen press
Lift.DEADLIFT: "0032", # barbell deadlift
Lift.PAUSE_DEADLIFT: "5014", # barbell pause deadlift
}
# Accessories have no 1RM; their planned weight is the last one logged, if any.
ACCESSORY_IDS = {
"external rotation": "0235", # cable standing shoulder external rotation
"reverse fly": "0383", # dumbbell reverse fly
"lateral raise": "0334", # dumbbell lateral raise
"triceps pushdown": "0241", # cable triceps pushdown (v-bar)
"incline curl": "0318", # dumbbell incline curl
}
# Not in the library; added to customEx on first use.
HALT_DEADLIFT = {
"id": "stack-halt-deadlift",
"n": "halt deadlift (barbell)",
"bp": "upper legs",
"eq": "barbell",
"tg": "glutes",
"desc": "Deadlift from the floor to just above the knee, paused, then lowered.",
"custom": True,
}
ID_PREFIX = "stack-"
ROTATION_ID = "stack-rotation"
API_CONTAINER = "connor_opengym-api_1"
API_NETWORK = "connor_internal"
class OpenGymError(RuntimeError):
pass
def resolve_base_url() -> str:
"""Find the API container's address; Authelia blocks scripts on the public URL."""
fmt = f'{{{{(index .NetworkSettings.Networks "{API_NETWORK}").IPAddress}}}}'
out = subprocess.run(
["sudo", "podman", "inspect", API_CONTAINER, "--format", fmt],
capture_output=True, text=True,
)
ip = out.stdout.strip()
if out.returncode != 0 or not ip:
raise OpenGymError(
f"could not find the {API_CONTAINER} address ({out.stderr.strip()}); set OPENGYM_URL"
)
return f"http://{ip}:3000"
def rbw_credentials(entry: str) -> tuple[str, str]:
"""Read (username, password) for an rbw entry."""
def get(*args: str) -> str:
out = subprocess.run(["rbw", "get", *args, entry], capture_output=True, text=True)
if out.returncode != 0:
raise OpenGymError(f"rbw get {entry} failed: {out.stderr.strip()}")
return out.stdout.strip()
return get("--field", "username"), get()
class Client:
def __init__(self, base_url: str, token: str | None = None):
self.base_url = base_url.rstrip("/")
self.token = token
def _request(self, method: str, path: str, body: dict | None = None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(self.base_url + path, data=data, method=method)
req.add_header("Content-Type", "application/json")
if self.token:
req.add_header("Authorization", f"Bearer {self.token}")
try:
with urllib.request.urlopen(req, timeout=30) as resp:
return resp.status, resp.headers, json.loads(resp.read() or b"null")
except urllib.error.HTTPError as e:
return e.code, e.headers, json.loads(e.read() or b"null")
def login(self, identifier: str, password: str) -> None:
status, headers, body = self._request(
"POST", "/api/login/password", {"identifier": identifier, "password": password}
)
if status != 200:
raise OpenGymError(f"login failed: HTTP {status} {body}")
for cookie in headers.get_all("Set-Cookie") or []:
name, _, rest = cookie.partition("=")
if name.strip() in ("__Host-gymsid", "gymsid"):
self.token = rest.split(";", 1)[0]
return
raise OpenGymError("login succeeded but no session cookie came back")
def get_state(self) -> tuple[dict, int]:
status, _, body = self._request("GET", "/api/data")
if status != 200:
raise OpenGymError(f"GET /api/data failed: HTTP {status} {body}")
return body["state"], body["rev"]
def update_state(self, change, attempts: int = 3) -> int:
"""GET, apply change(state) in place, PUT with baseRev; retry from a fresh GET on 409.
The state as read is saved under ~/.local/state/stack/ before each PUT.
"""
backup_dir = os.path.join(
os.environ.get("XDG_STATE_HOME") or os.path.expanduser("~/.local/state"), "stack"
)
os.makedirs(backup_dir, exist_ok=True)
for _ in range(attempts):
state, rev = self.get_state()
backup = os.path.join(backup_dir, f"opengym-rev{rev}-{int(time.time())}.json")
with open(os.open(backup, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600), "w") as f:
json.dump({"state": state, "rev": rev}, f)
print(f"backup: {backup}")
change(state)
status, _, body = self._request(
"PUT", "/api/data", {"state": state, "baseRev": rev, "baseWid": state.get("_wid")}
)
if status == 200:
return body["rev"]
if status != 409:
raise OpenGymError(f"PUT /api/data failed: HTTP {status} {body}")
raise OpenGymError(f"PUT /api/data kept conflicting after {attempts} attempts")
def _rpe(x: float) -> str:
return f"{x:g}"
def _set_summary(session: LiftSession) -> str:
"""Per-set RPEs, which OpenGym has no per-set field for, e.g. '5@4, 3@5.5, 1@7, 2x3@7'."""
parts = []
for (reps, rpe), group in groupby((s.reps, s.rpe) for s in session.sets):
n = len(list(group))
parts.append(f"{n}x{reps}@{rpe:g}" if n > 1 else f"{reps}@{rpe:g}")
return ", ".join(parts)
def exercise_entry(session: LiftSession) -> dict:
"""One routine exercise with every set's reps and weight fixed and progression off."""
reps = [s.reps for s in session.sets]
top_rpe = max(s.rpe for s in session.sets)
rest = 240 if session.goal == LiftGoal.STRENGTH else 180
exercise_id = (
HALT_DEADLIFT["id"] if session.lift == Lift.HALT_DEADLIFT else EXERCISE_IDS[session.lift]
)
return {
"id": exercise_id,
"sets": len(session.sets),
"mode": "reps",
"reps": Counter(reps).most_common(1)[0][0],
"weight": session.sets[-1].weight,
"prog": "off",
"pyramid": reps,
"pyramidWeight": [s.weight for s in session.sets],
"restSec": rest,
"warmupSets": 0,
"rpeMin": top_rpe,
"rpeMax": top_rpe,
"note": f"{session.goal}: {_set_summary(session)} (reps@RPE)",
}
def plan_to_routines(full_plan: list[list[list[LiftSession]]]) -> list[dict]:
"""One routine per training day, in order, named 'W1 D2 - Squat / Bench'."""
routines = []
last_week = len(full_plan)
for w, week in enumerate(full_plan, 1):
for d, day in enumerate(week, 1):
main_lifts = [str(s.lift) for s in day if s.lift in (Lift.SQUAT, Lift.BENCH, Lift.DEADLIFT)]
title = "Mock meet" if w == last_week and d == len(week) else " / ".join(dict.fromkeys(main_lifts))
routines.append({
"id": f"{ID_PREFIX}w{w}d{d}",
"name": f"W{w} D{d} – {title}",
"emoji": "barbell",
"excludeFromProgression": True,
"ex": [exercise_entry(s) for s in day],
})
return routines
def accessory_entry(exercise_id: str, sets: int, reps: int, weight: float) -> dict:
return {
"id": exercise_id,
"sets": sets,
"mode": "reps",
"reps": reps,
"weight": weight,
"prog": "off",
"restSec": 90,
"warmupSets": 0,
"rpeMin": 7,
"rpeMax": 8,
"note": f"Accessory: {sets}x{reps}, 2-3 reps short of failure",
}
def add_accessories(
routines: list[dict], accessories: dict[int, list[tuple]], ex_weights: dict
) -> None:
"""Add accessories to routines in place, by day of the week, in every week but the last.
accessories maps day (1-based) to (name, sets, reps, before) tuples, where name is a key
of ACCESSORY_IDS and before is an exercise id to insert ahead of, or None for the end.
Exercises a routine already has are skipped, so this can run on routines pushed earlier.
"""
weeks = [_week_day(r)[0] for r in routines if _is_ours(r)]
last_week = max(weeks, default=0)
for routine in routines:
if not _is_ours(routine):
continue
week, day = _week_day(routine)
if week == last_week:
continue
for name, sets, reps, before in accessories.get(day, []):
exercise_id = ACCESSORY_IDS[name]
if any(e["id"] == exercise_id for e in routine["ex"]):
continue
weight = (ex_weights.get(exercise_id) or {}).get("w", 0)
entry = accessory_entry(exercise_id, sets, reps, weight)
ids = [e["id"] for e in routine["ex"]]
at = ids.index(before) if before in ids else len(ids)
routine["ex"].insert(at, entry)
def _week_day(routine: dict) -> tuple[int, int]:
"""(week, day) from an id made by plan_to_routines, e.g. stack-w2d3 -> (2, 3)."""
week, _, day = routine["id"].removeprefix(f"{ID_PREFIX}w").partition("d")
return int(week), int(day)
def _is_ours(routine: dict) -> bool:
return str(routine.get("id", "")).startswith(ID_PREFIX)
def apply_plan(state: dict, routines: list[dict], start: date, label: str, replace: bool) -> None:
"""Add the routines to state and queue them as the active rotation, in place."""
existing = [r for r in state.get("routines") or [] if _is_ours(r)]
if existing and not replace:
raise OpenGymError(
f"{len(existing)} routines from an earlier push are already there; use --opengym-replace"
)
state["routines"] = [r for r in state.get("routines") or [] if not _is_ours(r)] + routines
uses_halt = any(e["id"] == HALT_DEADLIFT["id"] for r in routines for e in r["ex"])
custom = state.setdefault("customEx", [])
if uses_halt and not any(c.get("id") == HALT_DEADLIFT["id"] for c in custom):
custom.append(dict(HALT_DEADLIFT))
ids = [r["id"] for r in routines]
state["rotation"] = {"id": ROTATION_ID, "sequence": ids, "label": label}
state["queue"] = {
"ids": ids,
"since": int(time.time() * 1000),
"startsOn": start.isoformat(),
"label": label,
"rotationId": ROTATION_ID,
}
state["scheduleMode"] = "rotation"
def describe_change(state: dict, routines: list[dict]) -> str:
"""What apply_plan would change, for a dry run."""
lines = []
ours = [r for r in state.get("routines") or [] if _is_ours(r)]
others = len(state.get("routines") or []) - len(ours)
lines.append(f"routines: {others} of yours kept, {len(ours)} from an earlier push replaced, {len(routines)} added")
if state.get("rotation") and state["rotation"].get("id") != ROTATION_ID:
lines.append(f"rotation: replaces '{state['rotation'].get('label')}'")
if state.get("scheduleMode") != "rotation":
lines.append(f"scheduleMode: {state.get('scheduleMode')} -> rotation")
return "\n".join(lines)
def migrate_exercise(state: dict, old_id: str, new_id: str) -> int:
"""Re-file logged history from old_id to new_id in place; returns workouts changed."""
changed = 0
for workout in state.get("workouts") or []:
entries = workout.get("entries") or []
if any(e.get("id") == old_id for e in entries):
if any(e.get("id") == new_id for e in entries):
raise OpenGymError(
f"workout {workout.get('id')} on {workout.get('d')} logs both {old_id} and {new_id}"
)
for e in entries:
if e.get("id") == old_id:
e["id"] = new_id
changed += 1
weights = state.get("exWeights") or {}
if old_id in weights:
old = weights.pop(old_id)
new = weights.get(new_id)
if new is None or old.get("d", "") > new.get("d", ""):
weights[new_id] = old
return changed
+32 -6
View File
@@ -4,7 +4,7 @@ import warnings
from itertools import product
import yaml
from pydantic import BaseModel, computed_field, model_validator
from pydantic import BaseModel, model_validator
from .lifts import Lift, LiftGoal, LiftSession, WorkoutItem, Sport
from .session import generate_lift_session, generate_mock_comp_session
@@ -31,8 +31,11 @@ class Plan(BaseModel):
# Fields with defaults
sport: Sport = Sport.POWERLIFTING
num_weeks: int = 4
num_workouts: int = 4
# One RPE per training week; the block length is len(rpe_targets). The taper/mock comp
# week is added after these. If omitted, num_weeks picks a default progression.
rpe_targets: list[float] | None = None
num_weeks: int | None = None
# Internal constants
_RPE_TARGETS = {
@@ -55,10 +58,33 @@ class Plan(BaseModel):
)
return self
@computed_field
def rpe_targets(self) -> list[float]:
"""Get RPE targets based on number of weeks."""
return self._RPE_TARGETS[self.num_weeks]
@model_validator(mode="after")
def resolve_rpe_targets(self) -> "Plan":
"""Fill in rpe_targets/num_weeks from each other and check the RPEs are usable."""
if self.rpe_targets is None:
weeks = 4 if self.num_weeks is None else self.num_weeks
if weeks not in self._RPE_TARGETS:
raise ValueError(
f"no default RPE progression for {weeks} weeks "
f"(defaults exist for {sorted(self._RPE_TARGETS)}); pass rpe_targets"
)
self.rpe_targets = list(self._RPE_TARGETS[weeks])
if not self.rpe_targets:
raise ValueError("rpe_targets must contain at least one week")
if self.num_weeks is None:
self.num_weeks = len(self.rpe_targets)
elif self.num_weeks != len(self.rpe_targets):
raise ValueError(
f"num_weeks={self.num_weeks} but rpe_targets has {len(self.rpe_targets)} entries"
)
# Ramp sets go down to RPE - 3 and the SBD day adds 0.5, and the RPE table covers
# 3.0-10.0 in half steps, so a week's RPE has to be a half step in 6.0-9.5.
for rpe in self.rpe_targets:
if rpe * 2 != int(rpe * 2) or not 6.0 <= rpe <= 9.5:
raise ValueError(
f"weekly RPE {rpe} must be a multiple of 0.5 between 6.0 and 9.5"
)
return self
def generate_week(self) -> list[list[WorkoutItem]]:
"""Generate optimal workout distribution via exhaustive search.