[OCaml] High Intensity Training Online
1
(** Parses a native Hevy CSV export into a {!Workout_import.batch}.
2
3
This is a web-layer adapter: it turns untrusted export text into the
4
application's import use case. It does not construct canonical evidence or
5
apply Heavy Duty judgment. It never depends on [hito.persistence]. The whole
6
export text is retained verbatim through
7
{!Hito_app.Workout_import.make_batch}, so provenance survives the parse.
8
9
The adapter enforces the strict native schema. The header must carry all 14
10
verified columns, in any order. Rows the adapter cannot promote — warmups,
11
non-strength data, unknown set types, malformed fields — become
12
{!Workout_import.warning} values, not sets. A row is dropped, never guessed
13
at. Rows group into workouts by the exact [(title, start_time, end_time)]
14
triple, in first-appearance order. *)
15
16
type error =
17
| Malformed_csv of { record : int; field : int; detail : string }
18
(** The CSV text does not conform to RFC4180. [record] and [field] are
19
1-based, as {!Csv.Failure} reports them. *)
20
| Missing_column of string
21
(** A required column is absent from the header. The first missing column
22
in canonical order is reported. *)
23
| Empty_file (** The export has no rows at all, not even a header. *)
24
25
val pp_error : Format.formatter -> error -> unit
26
27
val parse :
28
batch_id:Hito_app.Workout_import.batch_id ->
29
fingerprint:string ->
30
imported_at:Recovery.timestamp ->
31
utc_offset_seconds:int ->
32
string ->
33
(Hito_app.Workout_import.batch, error) result
34
(** [parse ~batch_id ~fingerprint ~imported_at ~utc_offset_seconds source]
35
parses [source] as a native Hevy export.
36
37
[utc_offset_seconds] is the offset of the wall-clock times in the export.
38
Hevy datetimes are local wall time, so UTC seconds = civil seconds - offset.
39
40
Returns [Error Empty_file] for empty input, [Error (Missing_column _)] when
41
the header lacks a required column, and [Error (Malformed_csv _)] when the
42
CSV text is not well formed. On success the batch retains [source] exactly.
43
Rows the adapter cannot use appear in {!Workout_import.batch_warnings} in
44
source order. *)
45