[OCaml] High Intensity Training Online
1
(** Recorded stimuli, workouts, and history. Recording never judges. *)
2
3
(** One performed drive to failure. *)
4
module Stimulus : sig
5
type extension = Forced_reps | Negatives | Rest_pause | Static_hold
6
7
type outcome =
8
| Positive_failure
9
| Beyond_failure of extension * extension list
10
(** Nonempty, in application order. *)
11
12
val extensions_of_outcome : outcome -> extension list
13
val pp_extension : Format.formatter -> extension -> unit
14
15
module Effort : sig
16
type t
17
18
val make :
19
exercise:Exercise.t -> load:float -> reps:int -> outcome:outcome -> t
20
21
val exercise : t -> Exercise.t
22
val load : t -> float
23
val reps : t -> int
24
val outcome : t -> outcome
25
val extensions : t -> extension list
26
val pp : Format.formatter -> t -> unit
27
end
28
29
type delivery =
30
| Single of Effort.t
31
| Pair of { first : Effort.t; second : Effort.t }
32
33
type t
34
35
val make : delivery -> t
36
(** Records the delivery as performed. *)
37
38
val delivery : t -> delivery
39
40
val efforts : t -> Effort.t list
41
(** Performance order. *)
42
43
val exercises : t -> Exercise.t list
44
val extensions : t -> extension list
45
val is_extended : t -> bool
46
val pp : Format.formatter -> t -> unit
47
end
48
49
(** Typed signals reported at a point in time. *)
50
module Feedback : sig
51
type level =
52
| Very_poor
53
| Poor
54
| Fair
55
| Good
56
| Very_good
57
(** A five-point subjective score. [Very_poor] is 1, [Very_good] is 5.
58
These are wellness and readiness self-reports, never a measure of
59
the working set's intensity, which is categorical (0% at rest, 100%
60
at failure). *)
61
62
val level_to_score : level -> int
63
(** The score in 1..5. *)
64
65
val level_of_score : int -> level option
66
(** [Some] for 1..5, [None] otherwise. *)
67
68
type signal =
69
| Sleep of level
70
| Appetite of level
71
| Readiness of level
72
| Motivation of level
73
| Difficulty of level
74
| Pain
75
| Injury
76
| Preparation_insufficient
77
78
type t
79
type error = Duplicate_signal of signal
80
81
exception Invalid of error
82
83
val make : reported_at:Recovery.timestamp -> signal list -> t
84
(** Raises [Invalid] when feedback repeats a signal category. *)
85
86
val reported_at : t -> Recovery.timestamp
87
val signals : t -> signal list
88
end
89
90
(** A performed or in-progress prescribed workout. *)
91
module Workout : sig
92
type t
93
type shape = As_single | As_pair
94
type completeness = Complete | Incomplete
95
96
type error =
97
| Not_prescribed of Exercise.id
98
| Delivery_mismatch of {
99
exercise : Exercise.id;
100
prescribed : shape;
101
logged : shape;
102
}
103
| No_such_slot of int
104
(** A slot-targeted operation named a slot the prescription does not
105
have. *)
106
107
val pp_error : Format.formatter -> error -> unit
108
109
exception Invalid of error
110
111
val start :
112
Prescription.Workout.t ->
113
clearance:Recovery.clearance ->
114
started_at:Recovery.timestamp ->
115
t
116
117
val add_stimulus : t -> Stimulus.t -> t
118
(** Records against the next matching unanswered slot, or as extra volume when
119
every matching slot is already answered. Raises [Invalid] when the
120
stimulus does not conform to any prescribed slot. *)
121
122
val replace_stimulus : t -> slot:int -> Stimulus.t -> t
123
(** Records the stimulus into a named prescription slot, replacing whatever
124
filled that slot before. A correction, not extra work: it targets the slot
125
and never appends volume. The stimulus must conform to that slot's
126
prescription. Raises [Invalid (No_such_slot _)] for an unknown slot, and
127
[Invalid] for a stimulus the slot does not call for. *)
128
129
val finish : t -> ended_at:Recovery.timestamp -> t
130
(** Sets [ended_at] once; later calls retain the first value. *)
131
132
val prescription : t -> Prescription.Workout.t
133
val clearance : t -> Recovery.clearance
134
val started_at : t -> Recovery.timestamp
135
val ended_at : t -> Recovery.timestamp option
136
val is_finished : t -> bool
137
val duration : t -> Recovery.duration option
138
val completeness : t -> completeness
139
140
val stimuli : t -> Stimulus.t list
141
(** Performance order. *)
142
143
val performed : t -> (int * Stimulus.t) list
144
(** Each recorded stimulus paired with the prescription slot it filled, in
145
performance order. A slot appears more than once only when extra volume
146
was recorded against it. *)
147
148
val record_at : t -> slot:int -> Stimulus.t -> t
149
(** Append a stimulus at a named slot, preserving any prior fill of that slot.
150
The faithful-replay primitive: it reconstructs {!performed} verbatim,
151
including recorded extra volume, without the slot-clearing that
152
{!replace_stimulus} performs. Validates the stimulus against the slot and
153
raises [Invalid] as {!replace_stimulus} does. *)
154
155
val filled_slots : t -> int
156
(** How many distinct prescription slots have a record. Never exceeds the
157
prescription length, even when extra volume was recorded. *)
158
159
val outstanding : t -> (int * Prescription.Stimulus.t) list
160
val unperformed : t -> Prescription.Stimulus.t list
161
val pp : Format.formatter -> t -> unit
162
end
163
164
(** Workout history. *)
165
module Log : sig
166
type t
167
168
val empty : t
169
val add : t -> Workout.t -> t
170
171
val workouts : t -> Workout.t list
172
(** Most recent first. *)
173
174
val last_prescription : t -> Prescription.Workout.t option
175
176
type observation = {
177
exercise : Exercise.t;
178
effort : Stimulus.Effort.t;
179
performed_at : Recovery.timestamp;
180
}
181
182
val observations : t -> Exercise.t -> observation list
183
(** Oldest first. *)
184
185
val readiness :
186
t ->
187
now:Recovery.timestamp ->
188
recommended:Recovery.duration ->
189
Recovery.readiness
190
(** [Ready] for an empty log or no finished workout. *)
191
192
val pp : Format.formatter -> t -> unit
193
end
194