My agentic slop goes here. Not intended for anyone else!
1open Cmdliner
2open Owntracks_to_clickhouse
3
4let process_file input_file output_file =
5 try
6 let records = Owntracks_parser.parse_file input_file in
7 match output_file with
8 | Some out_path ->
9 Clickhouse_formatter.write_jsonl_file out_path records;
10 Printf.printf "Converted %d records from %s to %s\n"
11 (List.length records) input_file out_path
12 | None ->
13 Clickhouse_formatter.print_jsonl records
14 with
15 | Sys_error msg ->
16 Printf.eprintf "Error: %s\n" msg;
17 exit 1
18 | e ->
19 Printf.eprintf "Unexpected error: %s\n" (Printexc.to_string e);
20 exit 1
21
22let process_directory dir_path output_file recursive =
23 let rec find_rec_files dir =
24 let entries = Sys.readdir dir in
25 Array.fold_left (fun acc entry ->
26 let full_path = Filename.concat dir entry in
27 if Sys.is_directory full_path then
28 if recursive then
29 acc @ find_rec_files full_path
30 else
31 acc
32 else if Filename.check_suffix entry ".rec" then
33 full_path :: acc
34 else
35 acc
36 ) [] entries
37 in
38
39 let rec_files = find_rec_files dir_path in
40 let all_records = List.fold_left (fun acc file ->
41 try
42 let records = Owntracks_parser.parse_file file in
43 Printf.printf "Processed %s: %d records\n" file (List.length records);
44 acc @ records
45 with e ->
46 Printf.eprintf "Warning: Failed to process %s: %s\n"
47 file (Printexc.to_string e);
48 acc
49 ) [] rec_files in
50
51 match output_file with
52 | Some out_path ->
53 Clickhouse_formatter.write_jsonl_file out_path all_records;
54 Printf.printf "Total: Converted %d records from %d files to %s\n"
55 (List.length all_records) (List.length rec_files) out_path
56 | None ->
57 Clickhouse_formatter.print_jsonl all_records
58
59let main input output recursive =
60 if Sys.is_directory input then
61 process_directory input output recursive
62 else
63 process_file input output
64
65let input_arg =
66 let doc = "Input OwnTracks .rec file or directory containing .rec files" in
67 Arg.(required & pos 0 (some string) None & info [] ~docv:"INPUT" ~doc)
68
69let output_arg =
70 let doc = "Output JSON lines file (if not specified, outputs to stdout)" in
71 Arg.(value & opt (some string) None & info ["o"; "output"] ~docv:"OUTPUT" ~doc)
72
73let recursive_arg =
74 let doc = "Recursively process directories for .rec files" in
75 Arg.(value & flag & info ["r"; "recursive"] ~doc)
76
77let main_term =
78 Term.(const main $ input_arg $ output_arg $ recursive_arg)
79
80let info =
81 let doc = "Convert OwnTracks .rec files to ClickHouse JSON lines" in
82 let man = [
83 `S Manpage.s_description;
84 `P "$(tname) converts OwnTracks recorder files (.rec) to JSON lines format suitable for importing into ClickHouse with geo data types.";
85 `P "Each location record is converted to a JSON object with the following fields:";
86 `P "- timestamp: ISO 8601 formatted timestamp";
87 `P "- timestamp_epoch: Unix timestamp";
88 `P "- point: [longitude, latitude] array for ClickHouse Point type";
89 `P "- latitude, longitude: Individual coordinate fields";
90 `P "- altitude, accuracy, battery: Optional fields from OwnTracks";
91 `P "- tracker_id: Device identifier from OwnTracks";
92 `S Manpage.s_examples;
93 `P "Convert a single file to stdout:";
94 `Pre " $(tname) path/to/file.rec";
95 `P "Convert a single file to output file:";
96 `Pre " $(tname) path/to/file.rec -o output.jsonl";
97 `P "Process all .rec files in a directory:";
98 `Pre " $(tname) path/to/directory -o output.jsonl";
99 `P "Process directory recursively:";
100 `Pre " $(tname) path/to/directory -r -o output.jsonl";
101 ] in
102 Cmd.info "owntracks2clickhouse" ~version:"1.0.0" ~doc ~man
103
104let cmd = Cmd.v info main_term
105
106let () = exit (Cmd.eval cmd)