[OCaml] High Intensity Training Online
refactor return a result from Progress.assess
A record too young to judge is an ordinary state, not a violated precondition, yet assess raised Invalid Insufficient_data and the service caught the exception only to turn it back into Error. The core now returns the result directly and the exception is gone, so the service passes the judgment through unchanged. Recoverable outcomes travel as values; exceptions stay for broken authored plans.
Changed files
lib/app/service.ml
@@ -349,9 +349,7 @@
349
349
let progress t trainee exercise =
350
350
R.log t.repo trainee >>= fun log ->
351
351
R.imported_records t.repo trainee >|= fun records ->
352
Removed:
match Progress.assess (merged_observations ~log ~records exercise) with
353
Removed:
| assessment -> Ok assessment
354
Removed:
| exception Progress.Invalid error -> Error error
352
Added:
Progress.assess (merged_observations ~log ~records exercise)
355
353
356
354
(* The merged native and imported observation series for one exercise,
357
355
oldest first. The same merge the progress verdict is built on, exposed
lib/core/progress.ml
@@ -15,8 +15,6 @@
15
15
type assessment = Progressing | Stalled | Decompensating
16
16
type error = Insufficient_data
17
17
18
Removed:
exception Invalid of error
19
Removed:
20
18
let pp_assessment ppf = function
21
19
| Progressing -> Format.pp_print_string ppf "progressing"
22
20
| Stalled -> Format.pp_print_string ppf "stalled"
@@ -54,7 +52,7 @@
54
52
let assess observations =
55
53
let window = Recovery.duration_to_seconds stall_window in
56
54
match (observations, last observations) with
57
Removed:
| ([] | [ _ ]), _ | _, None -> raise (Invalid Insufficient_data)
55
Added:
| ([] | [ _ ]), _ | _, None -> Error Insufficient_data
58
56
| first :: _, Some latest -> (
59
57
(* After the window passes without progress, separate a halt from a loss.
60
58
Strength below the reference is decompensation. Strength that holds at
@@ -69,14 +67,14 @@
69
67
match last_advance observations with
70
68
| Some advance ->
71
69
if elapsed_between advance latest >= window then
72
Removed:
verdict ~reference:advance
73
Removed:
else Progressing
70
Added:
Ok (verdict ~reference:advance)
71
Added:
else Ok Progressing
74
72
| None ->
75
73
(* Never advanced. Only a stall once the record is long enough to say
76
74
so. Otherwise two sessions a day apart would condemn the routine. *)
77
75
if elapsed_between first latest >= window then
78
Removed:
verdict ~reference:first
79
Removed:
else raise (Invalid Insufficient_data))
76
Added:
Ok (verdict ~reference:first)
77
Added:
else Error Insufficient_data)
80
78
81
79
type remedy =
82
80
| Lay_off_then_reduce of {
lib/core/progress.mli
@@ -12,17 +12,16 @@
12
12
13
13
type error = Insufficient_data
14
14
15
Removed:
exception Invalid of error
16
Removed:
17
15
val pp_assessment : Format.formatter -> assessment -> unit
18
16
val pp_error : Format.formatter -> error -> unit
19
17
20
18
val stall_window : Recovery.duration
21
19
(** 14 days. *)
22
20
23
Removed:
val assess : Evidence.Observation.t list -> assessment
24
Removed:
(** Raises [Invalid Insufficient_data] when the record cannot support a
25
Removed:
judgment. *)
21
Added:
val assess : Evidence.Observation.t list -> (assessment, error) result
22
Added:
(** [Error Insufficient_data] when the record cannot yet support a judgment: too
23
Added:
few observations, or a record that never advanced but is younger than the
24
Added:
stall window. This is an ordinary state of a young record, not a fault. *)
26
25
27
26
type remedy =
28
27
| Lay_off_then_reduce of {
test/test_progression.ml
@@ -29,11 +29,9 @@
29
29
Evidence.Observation.make ~exercise:(get "laterals") ~effort:(move load r)
30
30
~performed_at:(day on)
31
31
32
Removed:
let raises_insufficient_data f =
33
Removed:
try
34
Removed:
let _ = f () in
35
Removed:
false
36
Removed:
with Progress.Invalid Progress.Insufficient_data -> true
32
Added:
(* A young record the judge cannot yet read answers [Error], an ordinary state,
33
Added:
not a raised fault. *)
34
Added:
let insufficient_data f = f () = Error Progress.Insufficient_data
37
35
38
36
let assess_tests =
39
37
[
@@ -46,14 +44,13 @@
46
44
fun () ->
47
45
Alcotest.(check bool)
48
46
"nothing" true
49
Removed:
(raises_insufficient_data (fun () -> Progress.assess []));
47
Added:
(insufficient_data (fun () -> Progress.assess []));
50
48
Alcotest.(check bool)
51
49
"a single session" true
52
Removed:
(raises_insufficient_data (fun () ->
53
Removed:
Progress.assess [ seen ~on:1 12. 8 ]));
50
Added:
(insufficient_data (fun () -> Progress.assess [ seen ~on:1 12. 8 ]));
54
51
Alcotest.(check bool)
55
52
"flat but recent" true
56
Removed:
(raises_insufficient_data (fun () ->
53
Added:
(insufficient_data (fun () ->
57
54
Progress.assess [ seen ~on:1 12. 8; seen ~on:3 12. 8 ])) );
58
55
( "a recent advance is progress",
59
56
`Quick,
@@ -62,7 +59,7 @@
62
59
"progressing" true
63
60
(Progress.assess
64
61
[ seen ~on:1 12. 8; seen ~on:3 12. 9; seen ~on:5 12. 9 ]
65
Removed:
= Progress.Progressing) );
62
Added:
= Ok Progress.Progressing) );
66
63
( "no advance for two weeks is a stall",
67
64
`Quick,
68
65
fun () ->
@@ -70,7 +67,7 @@
70
67
"stalled" true
71
68
(Progress.assess
72
69
[ seen ~on:1 12. 8; seen ~on:8 12. 8; seen ~on:16 12. 8 ]
73
Removed:
= Progress.Stalled) );
70
Added:
= Ok Progress.Stalled) );
74
71
( "an advance more than two weeks ago is also a stall",
75
72
`Quick,
76
73
fun () ->
@@ -78,7 +75,7 @@
78
75
"stalled" true
79
76
(Progress.assess
80
77
[ seen ~on:1 12. 8; seen ~on:3 12. 9; seen ~on:20 12. 9 ]
81
Removed:
= Progress.Stalled) );
78
Added:
= Ok Progress.Stalled) );
82
79
( "a two-week decline is decompensation",
83
80
`Quick,
84
81
fun () ->
@@ -86,7 +83,7 @@
86
83
"decompensating" true
87
84
(Progress.assess
88
85
[ seen ~on:1 12. 8; seen ~on:8 12. 8; seen ~on:16 10. 7 ]
89
Removed:
= Progress.Decompensating) );
86
Added:
= Ok Progress.Decompensating) );
90
87
]
91
88
92
89
let remedy_tests =
@@ -299,7 +296,7 @@
299
296
"and the routine is stalled" true
300
297
(Progress.assess
301
298
[ seen ~on:1 12. 8; seen ~on:8 12. 8; seen ~on:16 12. 8 ]
302
Removed:
= Progress.Stalled) );
299
Added:
= Ok Progress.Stalled) );
303
300
]
304
301
305
302
(* --- per-workout progress count --- *)