[OCaml] High Intensity Training Online
feat Remove the feedback-over-time graph
Drop the inline-SVG chart of feedback readings from the logbook, along with the factor_scores field it alone consumed on the view model, the controller projection that filled it, and its stylesheet rules. Past reports still read as a plain list. Some of the removed CSS still named the four signals dropped earlier, so this also clears that residue.
Changed files
ARCHITECTURE.org
@@ -274,8 +274,7 @@
274
274
factor left untouched is absent from the post, so a partial report needs no
275
275
Skip, and nothing is stored before Submit, so Close is a plain link with no
276
276
state to discard. An empty submission is refused with a message rather than
277
Removed:
stored as an empty report. The logbook draws a small inline-SVG time graph of
278
Removed:
the readings, one line per factor, once two reports carry a factor.
277
Added:
stored as an empty report. Past reports read as a plain list.
279
278
280
279
** Progress — the judgment
281
280
lib/presenter/pages_logbook.ml
@@ -93,107 +93,6 @@
93
93
];
94
94
]
95
95
96
Removed:
(* A small inline-SVG line chart of leveled feedback over time, one polyline per
97
Removed:
factor. No script and no dependency: the logbook records, and this only draws
98
Removed:
what it already holds. Shown once at least two reports carry a leveled
99
Removed:
factor, since a single point is not a trend. *)
100
Removed:
let feedback_graph (reports : View_model.Feedback.report list) =
101
Removed:
let chronological =
102
Removed:
List.sort
103
Removed:
(fun (a : View_model.Feedback.report) (b : View_model.Feedback.report) ->
104
Removed:
Int.compare a.at_unix b.at_unix)
105
Removed:
reports
106
Removed:
in
107
Removed:
let count = List.length chronological in
108
Removed:
let score_of field (report : View_model.Feedback.report) =
109
Removed:
match List.assoc_opt field report.factor_scores with
110
Removed:
| Some s -> s
111
Removed:
| None -> None
112
Removed:
in
113
Removed:
let series =
114
Removed:
List.map
115
Removed:
(fun (field, label) ->
116
Removed:
( label,
117
Removed:
List.mapi (fun i report -> (i, score_of field report)) chronological
118
Removed:
))
119
Removed:
feedback_factors
120
Removed:
in
121
Removed:
(* A factor charts only if it holds at least two scored points. *)
122
Removed:
let plottable (_, points) =
123
Removed:
List.length (List.filter (fun (_, s) -> Option.is_some s) points) >= 2
124
Removed:
in
125
Removed:
let series = List.filter plottable series in
126
Removed:
if count < 2 || series = [] then []
127
Removed:
else
128
Removed:
let width = 480 and height = 180 in
129
Removed:
let pad_left = 28 and pad_right = 12 and pad_top = 12 and pad_bottom = 24 in
130
Removed:
let plot_w = width - pad_left - pad_right in
131
Removed:
let plot_h = height - pad_top - pad_bottom in
132
Removed:
let x_of i =
133
Removed:
if count = 1 then pad_left + (plot_w / 2)
134
Removed:
else pad_left + (i * plot_w / (count - 1))
135
Removed:
in
136
Removed:
(* Score 1..5 maps low-to-high, so 5 sits at the top. *)
137
Removed:
let y_of score = pad_top + ((5 - score) * plot_h / 4) in
138
Removed:
let polyline label points =
139
Removed:
let coords =
140
Removed:
List.filter_map
141
Removed:
(fun (i, s) ->
142
Removed:
Option.map (fun s -> Printf.sprintf "%d,%d" (x_of i) (y_of s)) s)
143
Removed:
points
144
Removed:
in
145
Removed:
tag "polyline"
146
Removed:
[
147
Removed:
class_ "feedback-graph-line";
148
Removed:
Dream_html.string_attr "points" "%s" (String.concat " " coords);
149
Removed:
Dream_html.string_attr "fill" "none";
150
Removed:
Dream_html.string_attr "data-factor" "%s" label;
151
Removed:
]
152
Removed:
[]
153
Removed:
in
154
Removed:
let axis =
155
Removed:
List.map
156
Removed:
(fun score ->
157
Removed:
let y = y_of score in
158
Removed:
tag "text"
159
Removed:
[
160
Removed:
class_ "feedback-graph-tick";
161
Removed:
Dream_html.string_attr "x" "%d" (pad_left - 6);
162
Removed:
Dream_html.string_attr "y" "%d" (y + 3);
163
Removed:
Dream_html.string_attr "text-anchor" "end";
164
Removed:
]
165
Removed:
[ txt "%d" score ])
166
Removed:
[ 1; 2; 3; 4; 5 ]
167
Removed:
in
168
Removed:
let legend =
169
Removed:
tag "ul"
170
Removed:
[ class_ "feedback-graph-legend" ]
171
Removed:
(List.map
172
Removed:
(fun (label, _) ->
173
Removed:
tag "li"
174
Removed:
[ Dream_html.string_attr "data-factor" "%s" label ]
175
Removed:
[ txt "%s" label ])
176
Removed:
series)
177
Removed:
in
178
Removed:
[
179
Removed:
tag "figure"
180
Removed:
[ class_ "feedback-graph" ]
181
Removed:
[
182
Removed:
tag "figcaption" [] [ txt "Feedback over time" ];
183
Removed:
tag "svg"
184
Removed:
[
185
Removed:
class_ "feedback-graph-svg";
186
Removed:
Dream_html.string_attr "viewBox" "0 0 %d %d" width height;
187
Removed:
Dream_html.string_attr "role" "img";
188
Removed:
Dream_html.string_attr "aria-label"
189
Removed:
"Subjective feedback scores over time, one line per factor";
190
Removed:
]
191
Removed:
(axis
192
Removed:
@ List.map (fun (label, points) -> polyline label points) series);
193
Removed:
legend;
194
Removed:
];
195
Removed:
]
196
Removed:
197
96
let feedback_list (reports : View_model.Feedback.report list) =
198
97
if reports = [] then []
199
98
else
@@ -299,5 +198,5 @@
299
198
[ txt "Record feedback" ];
300
199
feedback_modal request ~open_:feedback_open;
301
200
]
302
Removed:
@ feedback_graph feedback @ feedback_list feedback);
201
Added:
@ feedback_list feedback);
303
202
]
lib/presenter/view_model.ml
@@ -34,10 +34,6 @@
34
34
type report = {
35
35
at_unix : int; (** Report time in Unix seconds, for chronological order. *)
36
36
signals : string list; (** Each signal already described, for the list. *)
37
Removed:
factor_scores : (string * int option) list;
38
Removed:
(** Score per leveled factor field, aligned with the factor order the
39
Removed:
presenter graphs. [None] where the report gave that factor no level.
40
Removed:
Flags such as pain carry no score and are absent here. *)
41
37
}
42
38
(** One subjective-feedback report, ready to render. Carries no [Feedback]
43
39
variant and no timestamp type. *)
lib/web/assets/hito.css
@@ -816,35 +816,6 @@
816
816
list-style: disc;
817
817
}
818
818
819
Removed:
/* The feedback time graph. A quiet inline chart on the paper surface, one
820
Removed:
coloured line per subjective factor, with a small legend. */
821
Removed:
.feedback-graph {
822
Removed:
margin: 1.25rem 0 0;
823
Removed:
}
824
Removed:
.feedback-graph figcaption {
825
Removed:
font: 600 var(--font-size-base)/1.2 var(--sans);
826
Removed:
margin-bottom: 0.5rem;
827
Removed:
}
828
Removed:
.feedback-graph-svg {
829
Removed:
display: block;
830
Removed:
width: 100%;
831
Removed:
max-width: 32rem;
832
Removed:
height: auto;
833
Removed:
border: 1px solid var(--rule);
834
Removed:
border-radius: var(--radius);
835
Removed:
background: var(--paper-raised);
836
Removed:
}
837
Removed:
.feedback-graph-tick {
838
Removed:
font: var(--font-size-small)/1 var(--mono);
839
Removed:
fill: var(--muted-ink);
840
Removed:
}
841
Removed:
.feedback-graph-line {
842
Removed:
stroke-width: 2;
843
Removed:
stroke-linejoin: round;
844
Removed:
stroke-linecap: round;
845
Removed:
}
846
Removed:
/* One hue per factor, drawn in document order to match the legend. */
847
Removed:
848
819
.settings-section {
849
820
max-width: 32rem;
850
821
margin-top: 1.25rem;
@@ -854,37 +825,6 @@
854
825
background: var(--paper-raised);
855
826
}
856
827
.settings-form { margin-top: 1rem; }
857
Removed:
.feedback-graph-line[data-factor="Sleep"] { stroke: var(--oxblood); }
858
Removed:
.feedback-graph-line[data-factor="Appetite"] { stroke: var(--brass); }
859
Removed:
.feedback-graph-line[data-factor="Readiness"] { stroke: #2f5d50; }
860
Removed:
.feedback-graph-line[data-factor="Motivation"] { stroke: #3a4a7a; }
861
Removed:
.feedback-graph-line[data-factor="Perceived difficulty"] { stroke: var(--ink); }
862
Removed:
.feedback-graph-legend {
863
Removed:
display: flex;
864
Removed:
flex-wrap: wrap;
865
Removed:
gap: 0.75rem;
866
Removed:
margin: 0.5rem 0 0;
867
Removed:
padding: 0;
868
Removed:
list-style: none;
869
Removed:
font: var(--font-size-small)/1.2 var(--sans);
870
Removed:
}
871
Removed:
.feedback-graph-legend li {
872
Removed:
display: inline-flex;
873
Removed:
align-items: center;
874
Removed:
gap: 0.35rem;
875
Removed:
}
876
Removed:
.feedback-graph-legend li::before {
877
Removed:
content: "";
878
Removed:
width: 0.9rem;
879
Removed:
height: 0.2rem;
880
Removed:
border-radius: var(--radius);
881
Removed:
background: var(--ink);
882
Removed:
}
883
Removed:
.feedback-graph-legend li[data-factor="Sleep"]::before { background: var(--oxblood); }
884
Removed:
.feedback-graph-legend li[data-factor="Appetite"]::before { background: var(--brass); }
885
Removed:
.feedback-graph-legend li[data-factor="Readiness"]::before { background: #2f5d50; }
886
Removed:
.feedback-graph-legend li[data-factor="Motivation"]::before { background: #3a4a7a; }
887
Removed:
.feedback-graph-legend li[data-factor="Perceived difficulty"]::before { background: var(--ink); }
888
828
889
829
.warn {
890
830
max-width: 47rem;
lib/web/handlers_logbook.ml
@@ -31,22 +31,7 @@
31
31
]
32
32
33
33
let describe_signal = Present.feedback_signal
34
Removed:
let factor_fields = [ "motivation"; "difficulty" ]
35
34
36
Removed:
let factor_score field (report : Feedback.t) =
37
Removed:
let score = function
38
Removed:
| Feedback.Worse_than_usual -> 1
39
Removed:
| As_usual -> 2
40
Removed:
| Better_than_usual -> 3
41
Removed:
in
42
Removed:
let of_signal (signal : Feedback.signal) =
43
Removed:
match (field, signal) with
44
Removed:
| "motivation", Motivation l | "difficulty", Difficulty l ->
45
Removed:
Some (score l)
46
Removed:
| _ -> None
47
Removed:
in
48
Removed:
List.find_map of_signal (Feedback.signals report)
49
Removed:
50
35
let entry_view log (record : Repository.record) : View_model.Logbook_entry.t =
51
36
let workout = record.Repository.workout in
52
37
let tm =
@@ -69,8 +54,6 @@
69
54
{
70
55
at_unix = Recovery.timestamp_to_unix_seconds (Feedback.reported_at report);
71
56
signals = List.map describe_signal (Feedback.signals report);
72
Removed:
factor_scores =
73
Removed:
List.map (fun f -> (f, factor_score f report)) factor_fields;
74
57
}
75
58
76
59
let index t trainee request =