feat(logs): let a logged set attach to its routine - #3
Open
wromansky wants to merge 5 commits into
Open
Conversation
log_set could only record freestanding work. It sends exercise, repetitions, weight, date and rir — never routine, slot_entry or iteration, though POST /api/v2/workoutlog/ accepts all three. Sets logged through this server therefore came back with routine=null, and no routine report could attribute them. That breaks the loop the server is otherwise well set up for: build a routine, follow it, log against it, adjust it. Volume and comparison analytics cannot tell planned work from ad-hoc work, and "how am I tracking against my program" has no answer. log_set gains optional routine_id, slot_entry_id and iteration. Omitting them keeps the previous payload byte for byte, so nothing changes for existing callers. Passing slot_entry_id without routine_id is rejected up front rather than posting a log that wger cannot place. Supplying those ids meant knowing which slot entry today's exercise belongs to, which the existing tools only expose by walking routine -> days -> slots -> entries and matching by hand. get_workout_for_date does it in one call, using wger's own date-sequence-gym route: it returns the day, its iteration, and one entry per planned exercise carrying slot_entry_id alongside the planned sets, repetitions, weight and RiR. Feed those straight into log_set. A date the routine does not cover is reported as a rest day rather than an error, since asking "what am I doing today" on an off day is normal use. Adds 6 tests: slot-entry ids surface for today, an uncovered date and a rest day both report no planned work, the linkage reaches the payload, an unlinked call is unchanged, and slot_entry_id without routine_id is refused. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 13, 2026
get_workout_for_date returned exercise ids, so anything describing a plan to a human had to look each one up. Watching an assistant answer "what is in my routine", that meant list_slot_entry_configs per entry — which fans out to ten config endpoints each — followed by a full exerciseinfo record per exercise, just to recover names. Dozens of requests, and enough payload to exhaust a 32k-token context. Names live on translations rather than on the exercise, and neither plan serializer carries them, so the tool now resolves them itself: one exercise-translation lookup per distinct exercise, run concurrently, cached for the process since exercise names are static. The configured DEFAULT_LANGUAGE is mapped to wger's numeric language id (also cached) and preferred, with any other translation used rather than showing a bare id. Cost lands on the wire, not in the caller's context: the response gains one short string per exercise. A failed lookup leaves exercise_name null rather than failing the plan. The docstring now says plainly that this is the call for "what am I doing today" and "what is in this program", because the expensive walk is the obvious path when the cheap one looks incomplete. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
wger's gym mode enumerates sets, so an exercise prescribed for three sets appears three times. The docstring said one entry per exercise, which invites a caller to read the list as the exercise list and under-count the work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
get_workout_for_date passed wger's raw weight-unit id through. A plan is read by people and by language models, and "weight_unit: 1" invites both to guess. Observed: an assistant reported a planned set stored as 14 kg to its user as "14 lb" — it had been told the user trains in pounds, saw an opaque 1, and assumed. 14 kg is 31 lb, so the number it gave was wrong by more than double in a context where the user acts on it. Rendered as its code instead, falling back to the raw value for units this mapping does not know (wger also has Body Weight, Plates, km/h, mph). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PawelHaracz
approved these changes
Aug 13, 2026
Contributor
|
@wromansky please resolve conflicts |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The gap
log_setcan only record freestanding work. It sendsexercise,repetitions,weight,dateandrir— neverroutine,slot_entryoriteration, even thoughPOST /api/v2/workoutlog/accepts all three. Every set logged through this server comes back withroutine: null.That breaks the loop the rest of the server is well set up for: build a routine, follow it, log against it, adjust it.
volume_trendandcompare_periodscannot tell planned work from ad-hoc work, and "how am I tracking against my program" has no answer.Found while wiring this server to a self-hosted wger for a coaching agent: it could create routines and it could log sets, but the two were unrelated records.
The change
log_setgains optionalroutine_id,slot_entry_idanditeration. Omitting them reproduces the previous payload byte for byte. Passingslot_entry_idwithoutroutine_idis rejected up front rather than posting a log wger cannot place.New tool
get_workout_for_date(routine_id, workout_date=today), because supplying those ids means knowing which slot entry today's exercise belongs to — which the existing tools only expose by walkingroutine → days → slots → entriesand matching by hand. It uses wger's own/routine/{id}/date-sequence-gym/route.A date the routine does not cover is reported as a rest day with a note rather than an error — asking "what am I doing today" on an off day is normal use.
Three follow-up commits, each from watching it used
Exercise names. The tool first returned exercise ids, so an assistant asked "what is in my routine" answered by calling
list_slot_entry_configsper entry — which fans out to ten config endpoints each — plus a fullexerciseinforecord per exercise, purely to recover names. Dozens of requests, and enough payload to exhaust a 32k-token context. Names live on translations, not on the exercise (/exercise/{id}/is 306 bytes of ids and no name), and neither plan serializer carries them, so the tool now resolves them itself: oneexercise-translationlookup per distinct exercise, concurrent, cached for the process, preferringDEFAULT_LANGUAGEvia its numeric language id. The cost is on the wire, not in the caller's context — the response gains one short string per exercise. A failed lookup leavesexercise_namenull rather than failing the plan.weight_unitis rendered askg/lb, not a raw id. An assistant reported a set stored as 14 kg to its user as "14 lb" — it had been told the user trains in pounds, saw an opaque1, and asserted. 14 kg is 31 lb, so the number was wrong by more than double in a context where the user acts on it. Unknown units fall back to the raw value (wger also has Body Weight, Plates, km/h, mph).Docstring correction: gym mode enumerates sets, so a 3-set exercise appears three times. The original text said one entry per exercise, which invites a caller to read the list as the exercise list and under-count the work.
Tests
6 tests in
tests/test_routine_logging.py: slot-entry ids surface for today, an uncovered date and a rest day both report no planned work, the linkage reaches the POST payload, an unlinked call is unchanged, andslot_entry_idwithoutroutine_idis refused without touching the network. Names and the rendered unit are asserted alongside.Full suite passes (77) and
ruff checkis clean. The parsing was written against the running serializers —WorkoutDayDataGymModeSerializer→SlotDataSerializer→SetConfigDataSerializer— not against assumptions.Note
Independent of #2, #4 and #5. Touches
log_setalongside #6, which addsweight_unitto the same function; they merge cleanly in either order, but whichever lands second will want a trivial rebase.