Added calls to save score and state.

main
noah metz 2024-01-29 00:04:19 -07:00
parent 24e4ae69e0
commit b9da942482
1 changed files with 81 additions and 29 deletions

@ -3,18 +3,43 @@
-behaviour(gen_server).
-export([start_link/1, init/1, handle_cast/2, handle_call/3]).
-export([get_tick/0]).
-record(state, {database_file = none, database = none}).
-record(state, {database_file = none, database = none, tick_start = none}).
start_link(DatabaseFile) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [DatabaseFile], []).
open_db(DatabaseFile) ->
{ok, DB} = sqlite3:open(event_db, [{file, DatabaseFile}]),
ok = sqlite3:sql_exec(DB, "PRAGMA foreign_keys = ON;"),
HT = case sqlite3:sql_exec(DB, "SELECT tick FROM match_states;") of
[{columns, C1}, {rows, R1}] ->
if length(R1) == 0 -> 0;
true ->
T1 = get_column("tick", C1, R1),
lists:max(T1)
end;
_ -> 0
end,
HFT = case sqlite3:sql_exec(DB, "SELECT tick FROM finals_states;") of
[{columns, C2}, {rows, R2}] ->
if length(R2) == 0 -> 0;
true ->
T2 = get_column("tick", C2, R2),
lists:max(T2)
end;
_ -> 0
end,
HighestTick = lists:max([HT, HFT]),
TickStart = erlang:monotonic_time(millisecond) - HighestTick,
{TickStart, DB}.
init([DatabaseFile]) ->
Exists = filelib:is_regular(DatabaseFile),
if Exists =:= true ->
{ok, DB} = sqlite3:open(schedule_db, [{file, DatabaseFile}]),
ok = sqlite3:sql_exec(DB, "PRAGMA foreign_keys = ON;"),
{ok, #state{database_file = DatabaseFile, database = DB}};
{TickStart, DB} = open_db(DatabaseFile),
{ok, #state{database_file = DatabaseFile, database = DB, tick_start = TickStart}};
true ->
io:format("Failed to load database ~s~n", [DatabaseFile]),
{ok, #state{database_file = none, database = none}}
@ -53,6 +78,8 @@ init_db(Database) ->
{type, text, [not_null]},
{number, integer, [not_null]},
{instance, integer, [not_null]},
{red, integer, [not_null]},
{blue, integer, [not_null]},
{score, blob, [not_null]}],
[{primary_key, [division, type, number, instance]},
{foreign_key, {[division, type, number], matches, [division, type, number], "ON DELETE CASCADE"}}]),
@ -60,9 +87,9 @@ init_db(Database) ->
ok = sqlite3:create_table(Database, match_states, [{division, integer, [not_null]},
{type, text, [not_null]},
{number, integer, [not_null]},
{time, integer, [not_null]},
{state, blob, [not_null]}],
[{primary_key, [division, type, number, time]},
{tick, integer, [not_null]},
{state, text, [not_null]}],
[{primary_key, [division, type, number, tick]},
{foreign_key, {[division, type, number], matches, [division, type, number], "ON DELETE CASCADE"}}]),
ok = sqlite3:create_table(Database, finals, [{number, integer, [not_null]},
@ -79,14 +106,16 @@ init_db(Database) ->
ok = sqlite3:create_table(Database, finals_scores, [{number, integer, [not_null]},
{instance, integer, [not_null]},
{red, integer, [not_null]},
{blue, integer, [not_null]},
{score, blob, [not_null]}],
[{primary_key, [number, instance]},
{foreign_key, {[number], finals, [number], ""}}]),
ok = sqlite3:create_table(Database, finals_states, [{number, integer, [not_null]},
{time, integer, [not_null]},
{tick, integer, [not_null]},
{state, blob, [not_null]}],
[{primary_key, [number, time]},
[{primary_key, [number, tick]},
{foreign_key, {[number], finals, [number], ""}}]),
ok = sqlite3:create_table(Database, skills_scores, [{team, text, [not_null]},
@ -129,8 +158,8 @@ write_division_matches(Database, Division, Type, Matches) ->
[Division, atom_to_list(Type)]),
lists:append([io_lib:format(
"INSERT INTO matches(division, type, number, blue_1, blue_2, red_1, red_2) VALUES (~p, '~s', ~p, '~s', '~s', '~s', '~s'); ",
[Division, atom_to_list(Type), N, B1, B2, R1, R2])
|| {N, [B1, B2, R1, R2]} <- lists:enumerate(Matches)]),
[Division, atom_to_list(Type), N, B1, B2, R1, R2])
|| {N, [B1, B2, R1, R2]} <- lists:enumerate(Matches)]),
"COMMIT;"]),
first_error(sqlite3:sql_exec_script(Database, SQL)).
@ -147,6 +176,8 @@ get_column(Name, Columns, Rows) ->
Index = index_of(Name, Columns),
[element(Index, Row) || Row <- Rows].
handle_call(get_tick_start, _, State) ->
{reply, State#state.tick_start, State};
handle_call({new_db, DatabaseFile}, _, State) ->
ok = if State#state.database =:= none -> ok;
true -> sqlite3:close(State#state.database)
@ -155,21 +186,19 @@ handle_call({new_db, DatabaseFile}, _, State) ->
ok = if Exists -> file:delete(DatabaseFile);
true -> ok
end,
{ok, Database} = sqlite3:open(schedule_db, [{file, DatabaseFile}]),
ok = sqlite3:sql_exec(Database, "PRAGMA foreign_keys = ON;"),
{reply, init_db(Database), State#state{database_file = DatabaseFile, database = Database}};
{TickStart, Database} = open_db(DatabaseFile),
{reply, init_db(Database), State#state{database_file = DatabaseFile, database = Database, tick_start = TickStart}};
handle_call({load_db, DatabaseFile}, _, State) ->
true = filelib:is_regular(DatabaseFile),
{ok, Database} = if DatabaseFile == State#state.database_file ->
{ok, State#state.database};
true ->
ok = if State#state.database =:= none -> ok;
true -> sqlite3:close(State#state.database)
end,
{ok, DB} = sqlite3:open(schedule_db, [{file, DatabaseFile}]),
{sqlite3:sql_exec(DB, "PRAGMA foreign_keys = ON;"), DB}
end,
{reply, ok, State#state{database_file = DatabaseFile, database = Database}};
{TickStart, Database} = if DatabaseFile == State#state.database_file ->
{State#state.tick_start, State#state.database};
true ->
ok = if State#state.database =:= none -> ok;
true -> sqlite3:close(State#state.database)
end,
open_db(DatabaseFile)
end,
{reply, ok, State#state{database_file = DatabaseFile, database = Database, tick_start = TickStart}};
handle_call(close_db, _, State) ->
if State#state.database =:= none -> {reply, ok, State};
true ->
@ -193,19 +222,38 @@ handle_call({delete_teams, Removed}, _, State) ->
ok = delete_teams(State#state.database, Removed),
{reply, ok, State};
handle_call({match_score, Division, Round, Number, Red, Blue, Score}, _, State) ->
[{columns, Columns}, {rows, Rows}] = sqlite3:sql_exec(State#state.database,
io_lib:format(
"SELECT instance FROM match_scores WHERE Division = ~p AND type = '~s' AND number = ~p;",
[Division, atom_to_list(Round), Number])),
Instances = get_column("instance", Columns, Rows),
NextInstance = if length(Instances) == 0 -> 1;
true -> lists:max(Instances) + 1
end,
ok = first_error(sqlite3:sql_exec_script(State#state.database, lists:append(
[io_lib:format("INSERT INTO match_scores(division, type, number, instance, red, blue, score) VALUES(~p, '~s', ~p, ~p, ~p, ~p, '~s');", [Division, atom_to_list(Round), Number, NextInstance, Red, Blue, Score]),
io_lib:format("INSERT INTO match_states(division, type, number, tick, state) VALUES(~p, '~s', ~p, ~p, '~s');", [Division, atom_to_list(Round), Number, get_tick(State), "scored"])]))),
{reply, ok, State};
handle_call({match_state, Division, Round, Number, MatchState}, _, State) ->
{rowid, _} = sqlite3:sql_exec(State#state.database,
io_lib:format("INSERT INTO match_states(division, type, number, tick, state) VALUES(~p, '~s', ~p, ~p, '~s');",
[Division, atom_to_list(Round), Number, get_tick(State), MatchState])),
{reply, ok, State};
handle_call({add_division, PSize, QSize, ESize}, _, State) ->
[{columns, Columns}, {rows, Rows}] = sqlite3:sql_exec(State#state.database, "SELECT division FROM divisions;"),
Divisions = lists:sort(get_column("division", Columns, Rows)),
NextDivision = first_empty([0 | Divisions]),
{rowid, _} = sqlite3:sql_exec(State#state.database,
io_lib:format("INSERT INTO divisions(division, psize, qsize, esize) VALUES(~p, ~p, ~p, ~p);",
[NextDivision, PSize, QSize, ESize])),
io_lib:format("INSERT INTO divisions(division, psize, qsize, esize) VALUES(~p, ~p, ~p, ~p);",
[NextDivision, PSize, QSize, ESize])),
{reply, ok, State};
handle_call({edit_division, Division, PSize, QSize, ESize}, _, State) ->
ok = sqlite3:sql_exec(State#state.database,
io_lib:format(
"UPDATE divisions SET psize = ~p, qsize = ~p, esize = ~p WHERE division = ~p;",
[PSize, QSize, ESize, Division])),
io_lib:format(
"UPDATE divisions SET psize = ~p, qsize = ~p, esize = ~p WHERE division = ~p;",
[PSize, QSize, ESize, Division])),
{reply, ok, State};
handle_call({delete_division, Division}, _, State) ->
ok = first_error(sqlite3:sql_exec_script(State#state.database, io_lib:format("UPDATE teams SET division = NULL WHERE division = ~p; DELETE FROM divisions WHERE division = ~p;", [Division, Division]))),
@ -236,3 +284,7 @@ get_teams(Database) ->
handle_cast(_, State) ->
{noreply, State}.
get_tick(State) -> erlang:monotonic_time(millisecond) - State#state.tick_start.
get_tick() ->
erlang:monotonic_time(millisecond) - gen_server:call(?MODULE, get_tick_start).