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.

Commit
a690684f8c1e11fbc265bd029efe64d902117ac2
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
ARCHITECTURE.org
index 5d25dc2e..fd4c3863 100644..100644
@@ -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
index 84a9dd14..ddf01fed 100644..100644
@@ -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
index 0b7089bb..e1af2073 100644..100644
@@ -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
index 9033b84e..fc9ba9ce 100644..100644
@@ -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
index 67ca518d..e04ae637 100644..100644
@@ -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 =