Moved schedule to database and made schedule only handle generating from params and returning randomized schedule + genid
							parent
							
								
									7162a1de37
								
							
						
					
					
						commit
						6682f37635
					
				| @ -0,0 +1,166 @@ | ||||
| -module(database). | ||||
| 
 | ||||
| -behaviour(gen_server). | ||||
| 
 | ||||
| -export([start_link/0, init/1, handle_cast/2, handle_call/3, add_teams/1]). | ||||
| 
 | ||||
| -export([set_test_config/0, new/1, load/1]). | ||||
| 
 | ||||
| -record(division, {teams = [], practice = 0, qualification = 0}). | ||||
| -record(config, {divisions = []}). | ||||
| -record(state, {database_file = none, database = none, teams = [], config = none}). | ||||
| 
 | ||||
| start_link() -> | ||||
|   gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). | ||||
| 
 | ||||
| init([]) -> | ||||
|   {ok, #state{}}. | ||||
| 
 | ||||
| init_db(Database) -> | ||||
|   ok = sqlite3:create_table(Database, genids, [{genid, text, [not_null]}, | ||||
|                                                {type, text, [not_null]}, | ||||
|                                                {size, integer, [not_null]}, | ||||
|                                                {seed, integer, [not_null]}], | ||||
|                             [{primary_key, [genid]}, | ||||
|                              {check, "type IN ('division', 'final')"}]), | ||||
|   ok = sqlite3:create_table(Database, teams, [{name, text, [not_null]}, | ||||
|                                               {inspection, blob}], | ||||
|                             [{primary_key, [name]}]), | ||||
|   ok = sqlite3:create_table(Database, matches, [{genid, text, [not_null]}, | ||||
|                                                 {round, text, [not_null]}, | ||||
|                                                 {number, integer, [not_null]}, | ||||
|                                                 {red_1, text, [not_null]}, | ||||
|                                                 {red_2, text, [not_null]}, | ||||
|                                                 {blue_1, text, [not_null]}, | ||||
|                                                 {blue_2, text, [not_null]}], | ||||
|                             [{primary_key, [genid, round, number]}, | ||||
|                              {check, "round IN ('practice', 'qualification', 'elimination', 'final')"}, | ||||
|                              {foreign_key, {[genid], genids, [genid], "ON DELETE RESTRICT"}}, | ||||
|                              {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, [{genid, text, [not_null]}, | ||||
|                                                      {round, text, [not_null]}, | ||||
|                                                      {number, integer, [not_null]}, | ||||
|                                                      {instance, integer, [not_null]}, | ||||
|                                                      {score, blob, [not_null]}], | ||||
|                             [{primary_key, [genid, round, number, instance]}, | ||||
|                              {foreign_key, {[genid, round, number], matches, [genid, round, number], "ON DELETE CASCADE"}}]), | ||||
|   ok = sqlite3:create_table(Database, match_states, [{genid, text, [not_null]}, | ||||
|                                                      {round, text, [not_null]}, | ||||
|                                                      {number, integer, [not_null]}, | ||||
|                                                      {time, integer, [not_null]}, | ||||
|                                                      {state, blob, [not_null]}], | ||||
|                             [{primary_key, [genid, round, number, time]}, | ||||
|                              {foreign_key, {[genid, round, number], matches, [genid, round, number], "ON DELETE CASCADE"}}]), | ||||
| 
 | ||||
|   ok = sqlite3:create_table(Database, skills_scores, [{team, text, [not_null]}, | ||||
|                                                       {type, text, [not_null]}, | ||||
|                                                       {attempt, integer, [not_null]}, | ||||
|                                                       {score, blob, [not_null]}], | ||||
|                             [{primary_key, [team, type, attempt]}, | ||||
|                              {foreign_key, {[team], teams, [name], "ON DELETE CASCADE"}}]), | ||||
|   ok. | ||||
| 
 | ||||
| delete_teams(Database, Teams) -> | ||||
|   SQL = lists:append(["BEGIN; ", | ||||
|          lists:append([io_lib:format("DELETE FROM teams WHERE name = ~p; ", | ||||
|                                      [Team]) || Team <- Teams]), | ||||
|          "COMMIT;"]), | ||||
|   first_error(sqlite3:sql_exec_script(Database, SQL)). | ||||
| 
 | ||||
| add_teams(Database, Teams) -> | ||||
|   SQL = lists:append(["BEGIN; ", | ||||
|          lists:append([io_lib:format("INSERT INTO teams(name) VALUES('~s') ON CONFLICT(name) DO NOTHING; ", | ||||
|                                      [Team]) || Team <- Teams]), | ||||
|          "COMMIT;"]), | ||||
|   first_error(sqlite3:sql_exec_script(Database, SQL)). | ||||
| 
 | ||||
| first_error([]) -> ok; | ||||
| first_error([ok | Rest]) -> first_error(Rest); | ||||
| first_error([Error | _]) -> Error. | ||||
| 
 | ||||
| delete_matches(Database, GenID) -> | ||||
|   SQL = io_lib:format("DELETE FROM matches WHERE genid = ~p", [GenID]), | ||||
|   first_error(sqlite3:sql_exec_script(Database, SQL)). | ||||
| 
 | ||||
| write_matches(Database, Type, Teams, Seed, Size, Round, Matches) -> | ||||
|   GenID = schedule:make_genid(Type, Round, Teams, Seed, Size), | ||||
|   SQL = lists:append(["BEGIN; ", | ||||
|                       io_lib:format("INSERT INTO genids(genid, type, size, seed) VALUES('~s', '~s', ~p, ~p); ", | ||||
|                                     [GenID, atom_to_list(Type), Size, Seed]), | ||||
|                       lists:append([io_lib:format( | ||||
|                                       "INSERT INTO matches(genid, round, number, blue_1, blue_2, red_1, red_2) | ||||
|                                        VALUES ('~s', '~s', ~p, '~s', '~s', '~s', '~s'); ", | ||||
|                                        [GenID, atom_to_list(Round), N, B1, B2, R1, R2]) | ||||
|                                         || {N, [B1, B2, R1, R2]} <- lists:enumerate(Matches)]), | ||||
|                       "COMMIT;"]), | ||||
|   {first_error(sqlite3:sql_exec_script(Database, SQL)), GenID}. | ||||
| 
 | ||||
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||||
| -define(TEST_CONFIG, #config{divisions = [#division{teams = ["A", "B", "C", "D"], practice = 4}]}). | ||||
| set_test_config() -> | ||||
|   gen_server:call(?MODULE, {set_config, ?TEST_CONFIG}). | ||||
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||||
| 
 | ||||
| add_teams(Teams) -> | ||||
|   gen_server:call(?MODULE, {add_teams, Teams}). | ||||
| 
 | ||||
| new(DatabaseFile) -> | ||||
|   gen_server:call(?MODULE, {new_db, DatabaseFile}). | ||||
| 
 | ||||
| load(DatabaseFile) -> | ||||
|   gen_server:call(?MODULE, {load_db, DatabaseFile}). | ||||
| 
 | ||||
| handle_call({set_config, Config}, _, State) when is_record(Config, config) -> | ||||
|   {reply, ok, State#state{config = Config}}; | ||||
| handle_call({new_db, DatabaseFile}, _, State) -> | ||||
|   ok = if State#state.database =:= none -> ok; | ||||
|           true -> sqlite3:close(State#state.database) | ||||
|        end, | ||||
|   Exists = filelib:is_regular(DatabaseFile), | ||||
|   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}}; | ||||
| 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}}; | ||||
| handle_call({delete_teams, Removed}, _, State) -> | ||||
|   Teams = lists:filter(fun(X) -> lists:member(X, Removed) =:= false end, State#state.teams), | ||||
|   ok = delete_teams(State#state.database, Removed), | ||||
|   {reply, ok, State#state{teams = Teams}}; | ||||
| handle_call({add_teams, Teams}, _, State) -> | ||||
|   ok = add_teams(State#state.database, Teams), | ||||
|   {reply, ok, State#state{teams = Teams}}; | ||||
| handle_call({new_schedule, Division, Round, Seed}, _, State) -> | ||||
|   DivInfo = lists:nth(Division, State#state.config#config.divisions), | ||||
|   Teams = DivInfo#division.teams, | ||||
|   Size = case Round of | ||||
|       practice when DivInfo#division.practice =/= 0 -> | ||||
|         DivInfo#division.practice; | ||||
|       qualification when DivInfo#division.qualification =/= 0-> | ||||
|         DivInfo#division.qualification | ||||
|   end, | ||||
|   Matches = schedule:create(Seed, Size, Teams), | ||||
|   {ok, GenID} = write_matches(State#state.database, division, Teams, Seed, Size, Round, Matches), | ||||
|   {reply, GenID, State}; | ||||
| handle_call({delete_schedule, GenID}, _, State) -> | ||||
|   ok = delete_matches(State#state.database, GenID), | ||||
|   {reply, ok, State}. | ||||
| 
 | ||||
| handle_cast(_, State) -> | ||||
|   {noreply, State}. | ||||
		Loading…
	
		Reference in New Issue