diff --git a/src/schedule.erl b/src/schedule.erl index 9567de2..cc2bba7 100644 --- a/src/schedule.erl +++ b/src/schedule.erl @@ -4,8 +4,12 @@ -export([start_link/0, init/1, handle_cast/2, handle_call/3]). +-export([set_test_config/0]). + -include("erlk.hrl"). +-record(division, {teams = [], practice = 0, qualification = 0}). +-record(config, {divisions = []}). -record(state, {database_file = none, database = none, teams = [], config = none}). start_link() -> @@ -39,49 +43,41 @@ fill_padding_schedule(Matches, Teams) -> {Schedule, _} = lists:mapfoldl(fun fill_padding_match/2, TeamsRandom, Matches), Schedule. -hash_teams_list(Teams) -> - [ Y || <> <= crypto:hash(sha, Teams), Y <- integer_to_list(X,16)]. - init_db(Database) -> ok = sqlite3:create_table(Database, teams, [{name, text, [not_null]}, {inspection, blob}], [{primary_key, [name]}]), ok = sqlite3:create_table(Database, matches, [{division, integer, [not_null]}, {round, text, [not_null]}, - {size, integer, [not_null]}, - {teams, text, [not_null]}, {number, integer, [not_null]}, - {seed, integer, [not_null]}, + {genid, text, [not_null]}, {red_1, text, [not_null]}, {red_2, text, [not_null]}, {blue_1, text, [not_null]}, {blue_2, text, [not_null]}], - [{primary_key, [division, round, size, teams, number, seed]}, + [{primary_key, [division, round, number, genid]}, {check, "round IN ('practice', 'qualification', 'elimination')"}, {foreign_key, {[blue_1], teams, [name], "ON DELETE RESTRICT"}}, {foreign_key, {[blue_2], teams, [name], "ON DELETE RESTRICT"}}, {foreign_key, {[red_1], teams, [name], "ON DELETE RESTRICT"}}, {foreign_key, {[red_2], teams, [name], "ON DELETE RESTRICT"}}]), + ok = sqlite3:create_table(Database, match_scores, [{division, integer, [not_null]}, {round, text, [not_null]}, - {size, integer, [not_null]}, - {teams, text, [not_null]}, {number, integer, [not_null]}, - {seed, integer, [not_null]}, + {genid, text, [not_null]}, {instance, integer, [not_null]}, {score, blob, [not_null]}], - [{primary_key, [division, round, size, teams, number, seed, instance]}, - {foreign_key, {[division, round, size, teams, number, seed], matches, [division, round, size, teams, number, seed], "ON DELETE CASCADE"}}]), + [{primary_key, [division, round, number, genid, instance]}, + {foreign_key, {[division, round, number, genid], matches, [division, round, number, genid], "ON DELETE CASCADE"}}]), ok = sqlite3:create_table(Database, match_states, [{division, integer, [not_null]}, {round, text, [not_null]}, - {size, integer, [not_null]}, - {teams, text, [not_null]}, {number, integer, [not_null]}, - {seed, integer, [not_null]}, + {genid, text, [not_null]}, {time, integer, [not_null]}, {state, blob, [not_null]}], - [{primary_key, [division, round, size, teams, number, seed, time]}, - {foreign_key, {[division, round, size, teams, number, seed], matches, [division, round, size, teams, number, seed], "ON DELETE CASCADE"}}]), + [{primary_key, [division, round, number, genid, time]}, + {foreign_key, {[division, round, number, genid], matches, [division, round, number, genid], "ON DELETE CASCADE"}}]), % TODO: finals % ok = sqlite3:create_table(Database, finals, [], [{primary_key, []}]), @@ -111,21 +107,19 @@ first_error([]) -> ok; first_error([ok | Rest]) -> first_error(Rest); first_error([Error | _]) -> Error. -delete_matches(Database, Division, Size, Seed, TeamsHash, Round) -> - SQL = io_lib:format("DELETE FROM matches WHERE division = ~p AND size = ~p AND seed = ~p AND teams = '~s' AND round = '~p'", [Division, Size, Seed, TeamsHash, Round]), +delete_matches(Database, Division, Round, GenID) -> + SQL = io_lib:format("DELETE FROM matches WHERE division = ~p AND genid = ~p AND round = '~p'", [Division, GenID, Round]), sqlite3:sql_exec_script(Database, SQL). -write_matches(Database, Division, Size, Seed, TeamsHash, Round, MatchTeams) -> +write_matches(Database, Division, Round, GenID, Matches) -> Schedule = [[{division, Division}, {round, atom_to_list(Round)}, - {size, Size}, - {teams, TeamsHash}, {number, N}, - {seed, Seed}, + {genid, GenID}, {blue_1, B1}, {blue_2, B2}, {red_1, R1}, - {red_2, R2}] || {N, [B1, B2, R1, R2]} <- lists:enumerate(MatchTeams)], + {red_2, R2}] || {N, [B1, B2, R1, R2]} <- lists:enumerate(Matches)], [ok | Resp] = sqlite3:write_many(Database, matches, Schedule), lists:last(Resp). @@ -152,6 +146,28 @@ pick_matches(Teams, Matches) -> pick_matches(Teams) -> pick_matches(Teams, []). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(TEST_CONFIG, #config{divisions = [#division{teams = ["A", "B", "C", "D"], practice = 1}]}). +set_test_config() -> + gen_server:call(?MODULE, {set_config, ?TEST_CONFIG}). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +handle_call({set_config, Config}, _, State) when is_record(Config, config) -> + {reply, ok, State#state{config = Config}}; +handle_call({new_schedule, Division, Round, Seed}, _, State) -> + DivInfo = lists:nth(Division, State#state.config#config.divisions), + Teams = DivInfo#division.teams, + Matches = case Round of + practice when DivInfo#division.practice =/= 0 -> + DivInfo#division.practice; + qualification when DivInfo#division.qualification =/= 0-> + DivInfo#division.qualification + end, + MatchTeams = create_schedule(Seed, Matches, Teams), + GenID = lists:sublist([ Y || <> <= crypto:hash(sha, Teams ++ [Seed, Matches]), + Y <- integer_to_list(X, 16)], 4), + ok = write_matches(State#state.database, Division, Round, GenID, MatchTeams), + {reply, GenID, State}; handle_call({new_db, DatabaseFile}, _, State) -> ok = if State#state.database =:= none -> ok; true -> sqlite3:close(State#state.database) @@ -183,61 +199,6 @@ handle_cast({delete_teams, Removed}, State) -> handle_cast({add_teams, Teams}, State) -> ok = first_error(add_teams(State#state.database, Teams)), {noreply, State#state{teams = Teams}}; -handle_cast({new_schedule, Division, Size, Seed, Teams, Round}, State) -> - MatchTeams = create_schedule(Seed, Size, Teams), - ok = write_matches(State#state.database, Division, Size, Seed, hash_teams_list(Teams), Round, MatchTeams), - {noreply, State}; -handle_cast({delete_schedule, Division, Size, Seed, TeamsHash, Round}, State) -> - ok = first_error(delete_matches(State#state.database, Division, Size, Seed, TeamsHash, Round)), +handle_cast({delete_schedule, Division, Round, GenID}, State) -> + ok = first_error(delete_matches(State#state.database, Division, Round, GenID)), {noreply, State}. - -get_matches_from_db(Database, Division, Round, Seed, Size, TeamsHash) -> - if Size == 0 -> []; - Size > 0 -> SQL = io_lib:format("SELECT number, blue_1, blue_2, red_1, red_2 FROM - matches WHERE - division = '~p' AND - round = '~p' AND - size = '~p' AND - seed = '~p' AND - teams = '~s';", - [Division, Round, Size, Seed, TeamsHash]), - [{columns, Columns}, {rows, Rows}] = sqlite3:sql_exec(Database, SQL), - [] - end. - -parse_division(Division) -> - TeamsBinary = maps:get(<<"teams">>, Division), - TeamsList = [binary_to_list(X) || X <- TeamsBinary], - TeamsHash = schedule:hash_teams_list(TeamsList), - - PracticeRounds = maps:get(<<"practice_rounds">>, Division), - PracticeSeed = maps:get(<<"practice_seed">>, Division), - - QualificationRounds = maps:get(<<"qualification_rounds">>, Division), - QualificationSeed = maps:get(<<"qualification_seed">>, Division), - - #division_config{ - fields = [binary_to_list(X) || X <- maps:get(<<"fields">>, Division)], - elimination_alliances = maps:get(<<"elimination_alliances">>, Division), - practice_rounds = PracticeRounds, - practice_seed = PracticeSeed, - qualification_rounds = QualificationRounds, - qualification_seed = QualificationSeed, - teams = TeamsList, - teams_hash = TeamsHash - }. - -parse_event_config(ConfigJSON) -> - Config = jsone:decode(list_to_binary(ConfigJSON)), - Divisions = lists:map(fun parse_division/1, maps:get(<<"divisions">>, Config)), - TestingFields = [binary_to_list(X) || X <- maps:get(<<"testing_fields">>, Config)], - SkillsFields = [binary_to_list(X) || X <- maps:get(<<"skills_fields">>, Config)], - FinalsFields = [binary_to_list(X) || X <- maps:get(<<"finals_fields">>, Config)], - - #event_config{ - database_file = binary_to_list(maps:get(<<"database">>, Config)), - finals_fields = FinalsFields, - skills_fields = SkillsFields, - testing_fields = TestingFields, - divisions = Divisions - }.