View raw

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