A Nix-friendly SQLite-enhanced fork of Flitter, a speedrunning split timer for Unix-style terminals
修订版 | 6df03b7fc8079ff67780c9c2c8b1c900b0701550 (tree) |
---|---|
时间 | 2023-05-24 14:53:04 |
作者 | Corbin <cds@corb...> |
Commiter | Corbin |
Start factoring SQLite access.
Also, reuse the existing Welford implementation. The SQLite user-defined
function bindings worked perfectly on the first try, and this was much
easier than requiring a special SQLite with extra extension functions.
@@ -121,16 +121,12 @@ let insert_runs db sexp = | ||
121 | 121 | let () = |
122 | 122 | match Array.to_list (Sys.get_argv ()) with |
123 | 123 | | _ :: db_path :: scms -> |
124 | - let db = db_open db_path in | |
125 | - print_string (sqlite_version_info () ^ "\n"); | |
126 | - Rc.check (exec db "pragma foreign_keys = on;"); | |
127 | - List.iter | |
128 | - ~f:(fun path -> | |
129 | - let sexp = Sexp.load_sexp_conv_exn path old_sexp_of_sexp in | |
130 | - insert_route db sexp; | |
131 | - print_string "Successfully created route!\n"; | |
132 | - insert_runs db sexp) | |
133 | - scms; | |
134 | - let _ = db_close db in | |
135 | - () | |
124 | + Storage.with_db db_path (fun db -> | |
125 | + List.iter | |
126 | + ~f:(fun path -> | |
127 | + let sexp = Sexp.load_sexp_conv_exn path old_sexp_of_sexp in | |
128 | + insert_route db sexp; | |
129 | + print_string "Successfully created route!\n"; | |
130 | + insert_runs db sexp) | |
131 | + scms) | |
136 | 132 | | _ -> print_string usage |
@@ -12,36 +12,48 @@ let check_select stmt vals f = | ||
12 | 12 | let () = |
13 | 13 | match Sys.get_argv () with |
14 | 14 | | [| _; db_path |] -> |
15 | - let db = db_open db_path in | |
16 | - Rc.check (exec db "pragma foreign_keys = on;"); | |
17 | - let stmt = prepare db "select count(*) from games;" in | |
18 | - check_select stmt [] (fun row -> | |
19 | - print_string | |
20 | - (sprintf "Number of games: %d\n" (Data.to_int_exn row.(0)))); | |
21 | - let stmt = prepare db "select * from games;" in | |
22 | - check_select stmt [] (fun row -> | |
23 | - print_string (sprintf "* %s\n" (Data.to_string_exn row.(0)))); | |
24 | - let _ = db_close db in | |
25 | - () | |
26 | - | [| _; db_path; game |] -> | |
27 | - let db = db_open db_path in | |
28 | - Rc.check (exec db "pragma foreign_keys = on;"); | |
29 | - let vals = [ Data.TEXT game ] in | |
30 | - let stmt = prepare db "select count(*) from routes where game = ?;" in | |
31 | - check_select stmt vals (fun row -> | |
32 | - print_string | |
33 | - (sprintf "Number of routes: %d\n" (Data.to_int_exn row.(0)))); | |
34 | - let stmt = | |
35 | - prepare db | |
36 | - "select category, penultimate, finish_line from routes where game = \ | |
37 | - ?;" | |
38 | - in | |
39 | - check_select stmt vals (fun row -> | |
40 | - print_string | |
41 | - (sprintf "* %s (%s, %s)\n" | |
42 | - (Data.to_string_exn row.(0)) | |
43 | - (Data.to_string_exn row.(1)) | |
44 | - (Data.to_string_exn row.(2)))); | |
45 | - let _ = db_close db in | |
46 | - () | |
15 | + Storage.with_db db_path (fun db -> | |
16 | + Aggregate.create_fun1 db "stdev" ~init:Welford.empty | |
17 | + ~step:(fun w d -> Welford.observe w (Data.to_int_exn d)) | |
18 | + ~final:(fun w -> Data.FLOAT (Welford.stddev w)); | |
19 | + let stmt = | |
20 | + prepare db | |
21 | + "with attempts as (select game, category, attempt, sum(duration) \ | |
22 | + as total from segments group by game, category, attempt) select \ | |
23 | + game, category, attempt, min(total), avg(total), stdev(total) \ | |
24 | + from attempts group by game, category;" | |
25 | + in | |
26 | + check_select stmt [] (fun row -> | |
27 | + print_string | |
28 | + (sprintf "%s (%s): min %s (attempt %d), avg %s ± %s\n" | |
29 | + (Data.to_string_exn row.(0)) | |
30 | + (Data.to_string_exn row.(1)) | |
31 | + (Duration.to_string (Data.to_int_exn row.(3)) 3) | |
32 | + (Data.to_int_exn row.(2)) | |
33 | + (Duration.to_string | |
34 | + (int_of_float (Data.to_float_exn row.(4))) | |
35 | + 3) | |
36 | + (Duration.to_string | |
37 | + (int_of_float (Data.to_float_exn row.(5))) | |
38 | + 3)))) | |
39 | + | [| _; db_path; game; category; attempt |] -> | |
40 | + Storage.with_db db_path (fun db -> | |
41 | + let vals = | |
42 | + [ | |
43 | + Data.TEXT game; | |
44 | + Data.TEXT category; | |
45 | + Data.opt_int (int_of_string_opt attempt); | |
46 | + ] | |
47 | + in | |
48 | + let stmt = | |
49 | + prepare db | |
50 | + "select starting_at, ending_at, duration from segments where \ | |
51 | + game = ? and category = ? and attempt = ?;" | |
52 | + in | |
53 | + check_select stmt vals (fun row -> | |
54 | + print_string | |
55 | + (sprintf "Segment: %s, %s, %s\n" | |
56 | + (Option.value (Data.to_string row.(0)) ~default:"(Start)") | |
57 | + (Data.to_string_exn row.(1)) | |
58 | + (Duration.to_string (Data.to_int_exn row.(2)) 3)))) | |
47 | 59 | | _ -> print_string usage |
@@ -0,0 +1,8 @@ | ||
1 | +open Sqlite3 | |
2 | + | |
3 | +let with_db db_path f = | |
4 | + let db = db_open db_path in | |
5 | + Rc.check (exec db "pragma foreign_keys = on;"); | |
6 | + f db; | |
7 | + let _ = db_close db in | |
8 | + () |