Update replaces a user-owned program and its children in one tx. sql.ErrNoRows if the program isn't theirs (nothing is mutated).
(uid, id int64, req models.CreateProgramRequest)
| 119 | p.CurrentDayIndex = idxByID[p.ID] |
| 120 | return nil |
| 121 | } |
| 122 | |
| 123 | // currentDayIndex computes, for every given program, which Days[] entry is due |
| 124 | // today, in a small constant number of queries regardless of how many programs |
| 125 | // are passed (a single query via a CTE, whether called with one program from |
| 126 | // hydrate or a whole page of them from List — see ProgramStore.List). The due day |
| 127 | // is derived from WHICH specific day the most recently started program workout |
| 128 | // was logged against (workouts.program_day_id), not from a blind workout count — |
| 129 | // a count is blind to repeats and out-of-order logging, silently desyncing |
| 130 | // "today's workout" from what was actually done. The due day is the next workout |
| 131 | // day after the last-logged one in cycle order, wrapping to the first workout day |
| 132 | // past the end; rest days are never "due" and are skipped over. Repeating the |
| 133 | // same day therefore doesn't skip anything (due stays the next day in sequence), |
| 134 | // and logging an out-of-cycle day deliberately moves the tracker to whatever |
| 135 | // follows THAT day — no "remaining incomplete days" bookkeeping, by design. |
| 136 | // |
| 137 | // Only workouts whose program_day_id maps to one of THIS program's current non-rest |
| 138 | // days can anchor the tracker to a specific day; rows carrying a day from some other |
| 139 | // program are ignored outright. Rows with a NULL day and no dropped mark — clients |
| 140 | // that predate day tracking — can't anchor, but each one newer than the anchor still |
| 141 | // ADVANCES the tracker by one workout day, count-based: without that fallback a user |
nothing calls this directly
no test coverage detected