[OCaml] High Intensity Training Online
refactor Own all user-facing phrasing in Present
Present existed to be the one place wording lives, yet it rendered six messages by calling core and app pp functions. A copy change or a future translation would have reached the entity layer, which Clean Architecture says no UI change may do. Present now matches every error variant itself: Workout.error, Feedback.level and signal, Trainee.username_error, and Service.error. The variants are closed, so a new case is a compile error here rather than a silent fall-through. Exercise ids resolve to catalog names for display. The overview's duplicate duration and readiness wording moves into Present so the recovery status has one voice. The core pp functions are unchanged and remain debugging output.
lib/web/handlers_kit.ml
@@ -49,26 +49,72 @@
49
49
50
50
Every domain and service error becomes a user-facing sentence here, so a
51
51
handler renders a message rather than deciding its wording. One module owns
52
Removed:
the phrasing, and adding an error variant surfaces as a missing case. *)
52
Added:
the phrasing, and adding an error variant surfaces as a missing case. The
53
Added:
core's [pp] functions are debugging output and are not used for copy: a
54
Added:
wording change must never reach the entity layer. *)
53
55
module Present = struct
54
56
let form_invalid = "The submitted form is not valid."
55
57
58
Added:
(* Hours, with one decimal only when the duration is not whole. *)
59
Added:
let duration_hours duration =
60
Added:
let hours =
61
Added:
float_of_int (Recovery.duration_to_seconds duration) /. 3600.
62
Added:
in
63
Added:
if Float.equal hours (Float.round hours) then Printf.sprintf "%.0fh" hours
64
Added:
else Printf.sprintf "%.1fh" hours
65
Added:
66
Added:
let recovery_status ~rested ~recommended =
67
Added:
Printf.sprintf "Recovery: %s of %s." (duration_hours rested)
68
Added:
(duration_hours recommended)
69
Added:
70
Added:
(* The catalog name when the id still resolves, else the id itself. *)
71
Added:
let exercise_name (id : Exercise.id) =
72
Added:
match Exercise.find_id (id :> string) with
73
Added:
| Some exercise -> Exercise.name exercise
74
Added:
| None -> (id :> string)
75
Added:
76
Added:
let username_error : Trainee.username_error -> string = function
77
Added:
| Trainee.Too_short ->
78
Added:
Printf.sprintf "A username needs at least %d characters."
79
Added:
Trainee.username_min_length
80
Added:
| Trainee.Too_long ->
81
Added:
Printf.sprintf "A username can have at most %d characters."
82
Added:
Trainee.username_max_length
83
Added:
56
84
let registration : Service.register_error -> string = function
57
Removed:
| `Username e -> Format.asprintf "%a" Trainee.pp_username_error e
85
Added:
| `Username e -> username_error e
58
86
| `Username_taken -> "That username is already registered."
59
87
60
88
let sign_in_failed = "That username and password do not match."
61
Removed:
let routine = Format.asprintf "%a" Service.pp_error
62
89
90
Added:
let routine : Service.error -> string = function
91
Added:
| Service.Unknown_routine -> "That routine is not in the catalogue."
92
Added:
| Service.Not_recovered Recovery.Ready ->
93
Added:
"Recovery is complete; begin the workout."
94
Added:
| Service.Not_recovered (Recovery.Recovering { rested; recommended }) ->
95
Added:
"Recovery is not complete. " ^ recovery_status ~rested ~recommended
96
Added:
97
Added:
let shape : Evidence.Workout.shape -> string = function
98
Added:
| Evidence.Workout.As_single -> "a single set"
99
Added:
| Evidence.Workout.As_pair -> "a pre-exhaust pair"
100
Added:
101
Added:
let workout_error : Evidence.Workout.error -> string = function
102
Added:
| Evidence.Workout.Not_prescribed id ->
103
Added:
Printf.sprintf "%s is not prescribed for this workout."
104
Added:
(exercise_name id)
105
Added:
| Evidence.Workout.Delivery_mismatch { exercise; prescribed; logged } ->
106
Added:
Printf.sprintf "%s is prescribed as %s but was logged as %s."
107
Added:
(exercise_name exercise) (shape prescribed) (shape logged)
108
Added:
| Evidence.Workout.No_such_slot slot ->
109
Added:
Printf.sprintf "Slot %d is not part of this workout." slot
110
Added:
63
111
let log_error : Service.log_error -> string = function
64
112
| Service.No_workout_in_progress -> "No workout is in progress."
65
Removed:
| Service.Rejected error ->
66
Removed:
Format.asprintf "%a" Evidence.Workout.pp_error error
113
Added:
| Service.Rejected error -> workout_error error
67
114
68
115
let edit_error : Service.edit_error -> string = function
69
116
| Service.Unknown_workout -> "That saved workout no longer exists."
70
Removed:
| Service.Rejected_edit error ->
71
Removed:
Format.asprintf "%a" Evidence.Workout.pp_error error
117
Added:
| Service.Rejected_edit error -> workout_error error
72
118
73
119
let unknown_routine = "That routine is not in the catalogue."
74
120
let unknown_record = "That saved workout no longer exists."
@@ -76,13 +122,22 @@
76
122
let slot_not_awaiting = "That slot is not awaiting a record."
77
123
78
124
let change_username : Service.change_username_error -> string = function
79
Removed:
| `Username e -> Format.asprintf "%a" Trainee.pp_username_error e
125
Added:
| `Username e -> username_error e
80
126
| `Username_taken -> "That username is already registered."
81
127
| `Unknown -> "That account no longer exists."
82
128
83
129
let change_password : Service.change_password_error -> string = function
84
130
| `Incorrect_password -> "The current password is not correct."
85
131
| `Unknown -> "That account no longer exists."
132
Added:
133
Added:
let feedback_level : Evidence.Feedback.level -> string = function
134
Added:
| Evidence.Feedback.Worse_than_usual -> "worse than usual"
135
Added:
| Evidence.Feedback.As_usual -> "as usual"
136
Added:
| Evidence.Feedback.Better_than_usual -> "better than usual"
137
Added:
138
Added:
let feedback_signal : Evidence.Feedback.signal -> string = function
139
Added:
| Evidence.Feedback.Motivation l -> "Motivation " ^ feedback_level l
140
Added:
| Evidence.Feedback.Difficulty l -> "Difficulty " ^ feedback_level l
86
141
87
142
let app_feedback_empty = "Enter feedback before submitting."
88
143
let username_changed = "Username changed."
lib/web/handlers_logbook.ml
@@ -79,12 +79,7 @@
79
79
leveled "difficulty" (fun l -> Evidence.Feedback.Difficulty l);
80
80
]
81
81
82
Removed:
let describe_signal (signal : Evidence.Feedback.signal) =
83
Removed:
let level l = Format.asprintf "%a" Evidence.Feedback.pp_level l in
84
Removed:
match signal with
85
Removed:
| Motivation l -> Printf.sprintf "Motivation %s" (level l)
86
Removed:
| Difficulty l -> Printf.sprintf "Difficulty %s" (level l)
87
Removed:
82
Added:
let describe_signal = Present.feedback_signal
88
83
let factor_fields = [ "motivation"; "difficulty" ]
89
84
90
85
let factor_score field (report : Evidence.Feedback.t) =
lib/web/handlers_overview.ml
@@ -45,11 +45,6 @@
45
45
(Prescription.Routine.workouts r);
46
46
}
47
47
48
Removed:
let duration_hours duration =
49
Removed:
let hours = float_of_int (Recovery.duration_to_seconds duration) /. 3600. in
50
Removed:
if Float.equal hours (Float.round hours) then Printf.sprintf "%.0fh" hours
51
Removed:
else Printf.sprintf "%.1fh" hours
52
Removed:
53
48
let home_view ~(routine : Repository.routine_id) ~routine_name ~next
54
49
~readiness : View_model.Home.t =
55
50
{
@@ -61,11 +56,7 @@
61
56
| Recovery.Ready -> View_model.Home.Ready
62
57
| Recovery.Recovering { rested; recommended } ->
63
58
View_model.Home.Recovering
64
Removed:
{
65
Removed:
status =
66
Removed:
Printf.sprintf "Recovery: %s of %s." (duration_hours rested)
67
Removed:
(duration_hours recommended);
68
Removed:
});
59
Added:
{ status = Present.recovery_status ~rested ~recommended });
69
60
}
70
61
71
62
let page t trainee request =