[OCaml] High Intensity Training Online
refactor share strict-prior baselines
Keep native and imported progress comparisons on the same prior-record rule, including corrections at an equal timestamp.
Changed files
lib/core/progress.ml
@@ -168,17 +168,31 @@
168
168
current : Evidence.Stimulus.Effort.t;
169
169
}
170
170
171
Removed:
(* The latest observation strictly before the workout started. This prevents a
172
Removed:
correction of the saved workout from comparing an exercise against itself. *)
173
Removed:
let previous_effort log ~started exercise =
174
Removed:
Evidence.Log.observations log exercise
175
Removed:
|> List.filter (fun observation ->
171
Added:
(* The latest observation strictly before [at]. This prevents a correction from
172
Added:
comparing an exercise against itself when the recorded observation shares its
173
Added:
workout's start time. Callers scope the observation series by exercise before
174
Added:
they call this helper. *)
175
Added:
let latest_before ~at observations =
176
Added:
observations
177
Added:
|> List.filter (fun (observation : Evidence.Observation.t) ->
176
178
Recovery.duration_to_seconds
177
179
(Recovery.elapsed
178
180
~since:(Evidence.Observation.performed_at observation)
179
Removed:
~now:started)
181
Added:
~now:at)
180
182
> 0)
183
Added:
|> List.sort (fun a b ->
184
Added:
Int.compare
185
Added:
(Recovery.timestamp_to_unix_seconds
186
Added:
(Evidence.Observation.performed_at a))
187
Added:
(Recovery.timestamp_to_unix_seconds
188
Added:
(Evidence.Observation.performed_at b)))
181
189
|> last
190
Added:
191
Added:
(* The latest observation strictly before the workout started. This prevents a
192
Added:
correction of the saved workout from comparing an exercise against itself. *)
193
Added:
let previous_effort log ~started exercise =
194
Added:
Evidence.Log.observations log exercise
195
Added:
|> latest_before ~at:started
182
196
|> Option.map Evidence.Observation.effort
183
197
184
198
let workout_comparisons log workout =
@@ -221,16 +235,5 @@
221
235
let performed = Evidence.Observation.performed_at target in
222
236
observations
223
237
|> List.filter (fun (o : Evidence.Observation.t) ->
224
Removed:
Exercise.equal (Evidence.Observation.exercise o) exercise
225
Removed:
&& Recovery.duration_to_seconds
226
Removed:
(Recovery.elapsed
227
Removed:
~since:(Evidence.Observation.performed_at o)
228
Removed:
~now:performed)
229
Removed:
> 0)
230
Removed:
|> List.sort (fun a b ->
231
Removed:
Int.compare
232
Removed:
(Recovery.timestamp_to_unix_seconds
233
Removed:
(Evidence.Observation.performed_at a))
234
Removed:
(Recovery.timestamp_to_unix_seconds
235
Removed:
(Evidence.Observation.performed_at b)))
236
Removed:
|> last
238
Added:
Exercise.equal (Evidence.Observation.exercise o) exercise)
239
Added:
|> latest_before ~at:performed
test/test_progression.ml
@@ -311,6 +311,41 @@
311
311
let log_of workouts =
312
312
List.fold_left Evidence.Log.add Evidence.Log.empty workouts
313
313
314
Added:
let strict_prior_tests =
315
Added:
[
316
Added:
( "an observation at the target timestamp is not a baseline",
317
Added:
`Quick,
318
Added:
fun () ->
319
Added:
let target = seen ~on:3 12. 9 in
320
Added:
let same_time = seen ~on:3 14. 7 in
321
Added:
Alcotest.(check bool)
322
Added:
"no prior observation" true
323
Added:
(Option.is_none (Progress.previous_observation [ same_time ] target))
324
Added:
);
325
Added:
( "the latest earlier observation is the baseline",
326
Added:
`Quick,
327
Added:
fun () ->
328
Added:
let target = seen ~on:3 16. 6 in
329
Added:
let earliest = seen ~on:1 12. 8 in
330
Added:
let latest = seen ~on:2 14. 7 in
331
Added:
match Progress.previous_observation [ latest; earliest ] target with
332
Added:
| Some previous ->
333
Added:
Alcotest.(check (float 0.001))
334
Added:
"latest load" 14.
335
Added:
(Stimulus.Effort.load (Evidence.Observation.effort previous))
336
Added:
| None -> Alcotest.fail "expected a prior observation" );
337
Added:
( "a workout does not compare against another record at its start time",
338
Added:
`Quick,
339
Added:
fun () ->
340
Added:
let first = solo ~on:3 ~load:12. ~r:8 in
341
Added:
let second = solo ~on:3 ~load:14. ~r:7 in
342
Added:
Alcotest.(check int)
343
Added:
"no comparison at the same timestamp" 0
344
Added:
(List.length
345
Added:
(Progress.workout_comparisons (log_of [ first; second ]) second))
346
Added:
);
347
Added:
]
348
Added:
314
349
let workout_progress_tests =
315
350
[
316
351
( "a first workout has nothing to beat",
@@ -405,6 +440,7 @@
405
440
("progression.remedy", remedy_tests);
406
441
("progression.load", load_tests);
407
442
("progression.diagnostics", diagnostic_tests);
443
Added:
("progression.strict_prior", strict_prior_tests);
408
444
("progression.workout_progress", workout_progress_tests);
409
445
("progression.workout_changes", workout_change_tests);
410
446
]