wger-project / wger

Self hosted FLOSS fitness/workout, nutrition and weight tracker
https://wger.de
GNU Affero General Public License v3.0
3.18k stars 584 forks source link

Support lifting programs, especially those that don't fit within a week #848

Open ChemiKyle opened 3 years ago

ChemiKyle commented 3 years ago

Use case

Programs whose workouts don't fit within a single week schedule are tedious to configure.
Programs like Stronglifts and Starting Strength are simple enough to just deal with the tedium, but programs like GZCL and 531 - which follow an ABCD cyclic pattern of workouts - but are intended to use a 3 day schedule require 4 individual workout weeks to be made in this example, despite only having 4 unique days.

Proposal

Ideally, one should be able to specify N workouts cycling over M days (specified weekdays is simplest, though I imagine some PPL routines may deviate from MWF).

Again, using GZCL as an example, there are 4 unique workout weeks: ABC, DAB, CDA, BCD. Ideally there would be an interface much like the "Workout" creation that allows defining an arbitrary number of training days, perhaps assigning them an integer instead of a weekday. The next step would be to pick the days of the week to schedule.

This could fall under a new option, "Training programs".

If it needed to fit within the existing framework, a "training program" could be an advanced option while creating a schedule; the requisite number of "workouts" could be autogenerated until workout "A" appears again on the first scheduled day[^1]. Each of these workouts would then be added in to a looped schedule, each workout's "Weeks" set to 1.[^2]

Consideration of full periodization support

Periodization support would be necessary to really ship a "Training programs" feature but may be beyond the scope of this issue.

Non-responsive undulation can be handled by the user, but even simple progressive overload is dependent on user logging.
When initiating a log on a known workout, the previous log for the same workout would be checked if it matched the specified number of reps and requisite weight increase for all sets of the specified exercise. If it did not, the autofilled values would fall back to the next stopping point. An issue here is the programs I've used as examples increase set count the next exercise after a failure, managing this on the frontend may be as simple as generating an additional <div class ="form-row form-row"> on the log page, but I haven't dug into the backend much.

[^1]: I'm sure this can be pre-calculated, there must be some formula for a set of N ordered elements mapping to a set of M ordered elements having P permutations, but my combinatorics knowledge has left its pigeonhole.

[^2]: I'm poking around the database (much easier thanks to the docker setup!) trying to implement this, albeit outside wger itself. I do this sort of thing for a living in R; I'll try to keep my script general if anyone might find it useful. I'm not familiar with django, but may be able to translate to python3 with standard lib (might need pandas) if that'd be helpful to the dev team.

rolandgeider commented 3 years ago

This is a good idea and something I've also been thinking about. What I personally do when I have something like your GZCL is adding all four days and keeping track of the days (which is easy, but not optimal, that's something computers are very good at).

The same for any kind of planned weight change we might need (what is the smallest unit by which you can increase the weight? what happens when you are not able to lift the planned weight one week? What if you do more?)

In general, I would prefer to only have one point to add such routines in the app, the current schedules are only tacked on and if we implement this would be removed. It might be enough to hide this behind some switch that shows the advanced planning. Or we save some program templates so that you could select 531 and just "fill in" some values, the rest of the planing is already there.

manfre commented 3 years ago

For adaptive programs, the weight and/or reps can vary based upon the previous workout of that type. E.g. With SBS there is a percentage change to the training max (up or down) based upon how many reps in the final set compared to the target number of reps. For 531, you increase training max only if you hit a minimum number or reps in the 1+ AMRAP set.

If there is a singular way of adding training sessions, these adaptive programs could potentially leverage that portion of the API in a pluggable way that essentially does an "add the next scheduled session". This is something that I need in a training program to accommodate my training not being bound to a weekly schedule. It cycles between 5 unique training days over a period of 8-12 days (depends on schedule and how I'm feeling).

I found wger today and am planning to get more familiar with the code to better refine my idea of how the above can fit in to the project. (Really happy to have found a Django based fitness project while determining if I needed to create one)

RlndVt commented 2 years ago

Proposal

[...] Ideally there would be an interface much like the "Workout" creation that allows defining an arbitrary number of training days, perhaps assigning them an integer instead of a weekday. The next step would be to pick the days of the week to schedule.

Emphasis mine.

Is it necessary to loop back to the day of the week? I would say it is preferable to just keep it ABCD or 1 2 3 4; and show which is the next workout.

Using a rigid 'days of the week' of the week system has a few issues:

1) For people with a more chaotic out-of-gym life; one week they might fit in one workout and another week four, and never on the same day of the week. 2) As OP mentioned, some lifting programs don't 'fit' in a week. If someone wants to use a ABCD schedule, but train thrice a week we get OP's scenario. But this is the case for any mismatch between scheduling days, and workout days (per week). 3) Missing a workout-day, should not equal missing a workout. e.g. Running a U/L or PPL scheme and missing one day would mean you skip a targeted area. And we all know you should never skip leg-day.

So in conclusion, I would suggest stepping away from days of the week for creating a program.

rolandgeider commented 2 years ago

I would also tend to specify how often / in which order / etc. one plans to do the training and then calculating the days of the week on the fly as some kind of nice to have info for the calendar, personal planning, etc. This would have the advantage that if you miss one day the next dates are still right, but we might also want to show a warning or something similar.

In any case, after finishing the exercise rewamp (whenever that might be), this is probably the next bigger feature I'd want to tackle

ChemiKyle commented 2 years ago

I think templates are a good idea as long as customizability remains. Perhaps an import/export feature would be a good addition here. Most programs are distributed via spreadsheets with macros which leaves a lot to be desired.

My first thought would be to store program data as JSON - this may not play nice with base SQLite, but there is an extension - for customizable progression based on performance. That opens up quite a can of worms, but I tossed a mockup down at the bottom.


Is it necessary to loop back to the day of the week? I would say it is preferable to just keep it ABCD or 1 2 3 4; and show which is the next workout.

I would also tend to specify how often / in which order / etc. one plans to do the training and then calculating the days of the week on the fly as some kind of nice to have info for the calendar, personal planning, etc

Indeed, auto calculating this may be best, perhaps setting an acceptable range of rest days and intended sessions per week? I definitely agree that if you miss a day it should not mean skipping that exercise.
Personally, knowing what exercise, weight, and reps I have to do next is what I'd like to know at a glance. Timestamps don't seem important outside of data entry.


Here's a bit of rough JSON off the top of my head for how a cookie-cutter GZCL "A" workout may look; it is perhaps over engineered - and overfit to linear progression GZCL - but I tried to imagine how undulating periodization and AMRAP varied progression may be handled generically.

Expand JSON mockup In prose, if you hit your rep/set goals, you increase weight by `weight_increment` for the next session of that workout. If you miss your reps for any set, your weight remains the same and you fall down to the next `scheme` for the next session of that workout (e.g. if I'm supposed to do 5x3 and I get 3, 3, 3, 2, 1, next session I do 6x2 at the same weight). Once you fail the last scheme, your next session is spent trying to find your 3 rep max. - The final set is AMRAP, if you double your goal reps, you also double your progression. - I think 531 the `scheme:weight_increment` would be 0, since progression is solely based on AMRAP. ```json [ { // workout day is an array of objects representing lifts and metadata "A": [ // Tier 1 lift: low rep, high weight { "lift_name": "back_squat", "scheme": [ // schemes are ordinal, if you fail progression criteria, you drop to the next scheme { "sets": 5, "reps": 3, // successfully complete this workout scheme this many times to enable weight increment, // possibly useful for programs that don't progress each sequential session "success_limit": 1, "weight_increment": 10.0 }, { "sets": 6, "reps": 2, "weight_increment": 10.0 }, { "sets": 10, "reps": 1, "weight_increment": 10.0 }, // after failing all the above schemes, GZCL has you retest to find your 3RM ], "progression_changes": { [ { "scheme": -1, "reps": NULL, "weight_increment": NULL }, { // (optional) success condition, hit double your rep target to increase rate of progression "set": "last", "reps": "double", // choose one of these, flat replacement of usual or multiplier on usual "weight_increment": 20.0, "bonus_weight_increment_multiplier": 2.0 }, { // (optional) failure condition, fail to meet reps on any set and you will fall to the next scheme "set": "any", "reps": "less", // optional smaller weight increment? "bonus_weight_increment": 0.0 }, ] } }, // Tier 2 lift: high rep, low weight { "lift_name": "bench_press", "scheme": [ { "sets": 3, "reps": 10, "weight_increment": 5.0 }, { "sets": 3, "reps": 8, "weight_increment": 5.0 }, { "sets": 3, "reps": 8, "weight_increment": 5.0 } // after failing the final scheme you simply start back from the top at a lower weight ] "progression_changes": { [ { // failure condition, fail to meet reps on any set and you will fall to the next scheme // fail to meet reps on the final scheme and you lower weight and restart "set": "any", "reps": "less", "scheme": -1, // end of scheme array // decrease "bonus_weight_increment": -20.0 } ] } } ] ```
BeatLink commented 2 years ago

Here is my spin on how this would be implemented.

Workouts would be organized in two stages: Sessions and Schedules.

Sessions would be a group of exercises along with all of their details (reps, weights, sets, etc). Eg Push, Pull, Legs or Workout A and Workout B

Schedules would be groups of sessions organized so that sessions can be added to particular days of particular weeks.

Hopefully the below hierarchy helps explain somewhat

It is essentially the existing system but rather than breaking each workout into particular days, it would be the schedule that would be broken up into days after the weeks. Workouts would just be self contained sessions, devoid of scheduling information.

So for example, a user may create 4 seperate sessions: Push, Pull, Legs and Cardio. The user could then combine these sessions to form a schedule, so for example,

Week 1 Monday - Push, Cardio Tuesday - Cardio Wednesday - Pull, Cardio Thursday - Cardio Friday - Legs, Cardio

Another Example would be an alternating programme

In this case, the user would create 2 sessions, Workouts A and B. Then it could be implemented as follows:

Week 1 Monday - A Tuesday Wednesday - B Thursday Friday - A

Week 2 Monday - B Tuesday Wednesday - A Thursday Friday - B

Loop Weeks: On

BeatLink commented 1 year ago

Hi. I wanted to find out if there was any progress with this design change. Perhaps we could get a poll for the various ideas?

rolandgeider commented 1 year ago

Nothing spefic yet, but you are right that we should probably start thinking about specific things to do.

Somebody in another issue suggested taking some inspiration from https://www.liftosaur.com/ about handling progression

BeatLink commented 1 year ago

I took a look at that app before and it would be awesome if this could be integrated into wger!

The first step I think though would be decoupling the workouts from specific weekdays though. Once you have self contained workouts decoupled from scheduling information, you could then look at integrating the scripting syntax from liftosaur.

Any thoughts on the structure I proposed here https://github.com/wger-project/wger/issues/848#issuecomment-1067492323?

IsotoxalDev commented 1 year ago

This!! This is something i need as well

BeatLink commented 1 year ago

I've created an entity relation diagram to detail my suggested structure. Maybe it could give some guidance with refactoring the models:

irontech-entity-diagram drawio

Its still a work in progress but if you need help explaining any part let me know

rolandgeider commented 1 year ago

something like that, yeah. Something that we still need to consider for the data model is handling progression, e.g. like in the JSON on this comment

lediable commented 1 year ago

Any update on this feature?

rolandgeider commented 11 months ago

status update, I've started doodling and thinking about this and besides the usual unplanned stuff that suddenly needs to be done, I'll try to actually (and finally) work on this

RlndVt commented 11 months ago

I've created an entity relation diagram to detail my suggested structure. Maybe it could give some guidance with refactoring the models:

irontech-entity-diagram drawio

Its still a work in progress but if you need help explaining any part let me know

A small change suggestion:

image

The 'workouts' or structure (what you called schedule) should not be coupled to a 'day of the week'.