[OCaml] High Intensity Training Online
1
(** Deterministic native history for the local development account.
2
3
The fixture uses the application repository port, so it exercises the same
4
stored-workout path as a real trainee. It does not belong in production. *)
5
6
open Lwt.Infix
7
8
module Make (R : Repository.S) (H : Password_hash.S) = struct
9
module App_service = Service.Make (R) (H)
10
module Stimulus = Evidence.Stimulus
11
12
let day = 86_400
13
let fixture_days = 90
14
let cycle_days = 7
15
let session_offsets = [ 0; 2; 4 ]
16
17
let load_for exercise =
18
match (Exercise.id exercise :> string) with
19
| "dumbbell-flyes" -> 18.
20
| "incline-press" -> 55.
21
| "laterals" -> 10.
22
| "bent-over-laterals" -> 8.
23
| "lying-french-press" -> 25.
24
| "dips" -> 0.
25
| "pullovers" -> 20.
26
| "close-grip-pulldowns" -> 50.
27
| "bent-over-rows" -> 50.
28
| "shrugs" -> 60.
29
| "hyperextensions" -> 0.
30
| "curls" -> 30.
31
| "preacher-curls" -> 25.
32
| "leg-extensions" -> 40.
33
| "leg-presses" -> 120.
34
| "squats" -> 70.
35
| "leg-curls" -> 35.
36
| "calf-raises" -> 60.
37
| "sit-ups" -> 0.
38
| "straight-arm-pulldowns" -> 35.
39
| "reverse-pec-deck" -> 30.
40
| "pressdowns" -> 30.
41
| "triceps-machine" -> 35.
42
| "deadlifts" -> 90.
43
| _ -> 10.
44
45
let effort ~cycle exercise =
46
let load = load_for exercise in
47
let load = if Float.equal load 0. then 0. else load +. float_of_int cycle in
48
let reps = 6 + (cycle mod 5) in
49
match
50
Stimulus.Effort.make ~exercise ~load ~reps
51
~outcome:Stimulus.Positive_failure
52
with
53
| Ok effort -> effort
54
| Error error ->
55
invalid_arg
56
(Format.asprintf "invalid development fixture effort: %a"
57
Stimulus.Effort.pp_error error)
58
59
let stimulus ~cycle prescription =
60
let single exercise =
61
Stimulus.make (Stimulus.Single (effort ~cycle exercise))
62
in
63
match Prescription.Stimulus.delivery prescription with
64
| Prescription.Stimulus.Single exercise -> single exercise
65
| Prescription.Stimulus.Pre_exhaust { isolation; compound } ->
66
Stimulus.make
67
(Stimulus.Pair
68
{
69
first = effort ~cycle isolation;
70
second = effort ~cycle compound;
71
})
72
73
let complete_workout ~cycle ~started_at prescription =
74
let clearance = Option.get (Recovery.clear Recovery.Ready) in
75
let workout = Evidence.Workout.start prescription ~clearance ~started_at in
76
let workout =
77
Prescription.Workout.stimuli prescription
78
|> List.mapi (fun slot prescription -> (slot, prescription))
79
|> List.fold_left
80
(fun workout (slot, prescription) ->
81
Evidence.Workout.record_at workout ~slot
82
(stimulus ~cycle prescription))
83
workout
84
in
85
Evidence.Workout.finish workout
86
~ended_at:
87
(Recovery.timestamp_to_unix_seconds started_at + (45 * 60)
88
|> Recovery.timestamp_of_unix_seconds)
89
90
let seed_history repo trainee_id ~now routine =
91
let first =
92
Recovery.timestamp_to_unix_seconds now - (fixture_days * day)
93
|> Recovery.timestamp_of_unix_seconds
94
in
95
let sessions =
96
List.init 13 (fun cycle ->
97
List.map
98
(fun offset -> (cycle, (cycle * cycle_days) + offset, offset / 2))
99
session_offsets)
100
|> List.concat
101
in
102
Lwt_list.iter_s
103
(fun (cycle, offset_days, workout_index) ->
104
let started_at =
105
Recovery.timestamp_to_unix_seconds first + (offset_days * day)
106
|> Recovery.timestamp_of_unix_seconds
107
in
108
let prescription =
109
List.nth (Prescription.Routine.workouts routine) workout_index
110
in
111
let workout = complete_workout ~cycle ~started_at prescription in
112
R.finish_workout repo trainee_id workout >|= ignore)
113
sessions
114
115
let find_or_register service repo =
116
App_service.register service ~username:"test" ~password:"test" >>= function
117
| Ok trainee -> Lwt.return_some trainee
118
| Error App_service.Username_taken -> (
119
match Trainee.username "test" with
120
| Error _ -> Lwt.return_none
121
| Ok username -> R.find_trainee_by_username repo username)
122
| Error (App_service.Username_invalid error) ->
123
Lwt.fail_with
124
(Format.asprintf "cannot seed development user: %a"
125
Trainee.pp_username_error error)
126
127
let seed repo ~now =
128
let service = App_service.make ~repo in
129
find_or_register service repo >>= function
130
| None -> Lwt.return_unit
131
| Some trainee -> (
132
let trainee_id = trainee.Trainee.id in
133
R.set_active_routine repo trainee_id (Repository.routine_id "ideal")
134
>>= fun () ->
135
R.history repo trainee_id >>= function
136
| _ :: _ -> Lwt.return_unit
137
| [] -> (
138
match R.find_routine repo (Repository.routine_id "ideal") with
139
| None ->
140
Lwt.fail_with
141
"cannot seed development user: Ideal Routine missing"
142
| Some routine -> seed_history repo trainee_id ~now routine))
143
end
144