Add DB initialization code. Fix DB schema
This commit is contained in:
parent
15a1b29b84
commit
8623b524de
3 changed files with 71 additions and 17 deletions
18
constants.py
18
constants.py
|
@ -7,3 +7,21 @@ DB_CONFIG_FILE_NAME = 'config.yaml'
|
||||||
DEFAULT_DB_NAME = 'hanabi-league'
|
DEFAULT_DB_NAME = 'hanabi-league'
|
||||||
DEFAULT_DB_USER = 'hanabi-league'
|
DEFAULT_DB_USER = 'hanabi-league'
|
||||||
DEFAULT_DB_PASS = 'hanabi-league'
|
DEFAULT_DB_PASS = 'hanabi-league'
|
||||||
|
|
||||||
|
DB_TABLE_NAMES = [
|
||||||
|
"users"
|
||||||
|
, "user_accounts"
|
||||||
|
, "downloads"
|
||||||
|
, "variants"
|
||||||
|
, "games"
|
||||||
|
, "game_participants"
|
||||||
|
, "game_actions"
|
||||||
|
, "seeds"
|
||||||
|
, "variant_base_ratings"
|
||||||
|
, "variant_ratings"
|
||||||
|
, "user_base_ratings"
|
||||||
|
, "user_ratings"
|
||||||
|
, "statistics"
|
||||||
|
]
|
||||||
|
|
||||||
|
DATABASE_SCHEMA_PATH = 'install/database_schema.sql'
|
||||||
|
|
35
database.py
35
database.py
|
@ -1,5 +1,7 @@
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
import psycopg2.extensions
|
||||||
|
|
||||||
|
import constants
|
||||||
from config import read_db_config
|
from config import read_db_config
|
||||||
from log_setup import logger
|
from log_setup import logger
|
||||||
|
|
||||||
|
@ -16,7 +18,7 @@ class DBConnectionManager:
|
||||||
))
|
))
|
||||||
logger.debug("Established database connection.")
|
logger.debug("Established database connection.")
|
||||||
|
|
||||||
def get_connection(self):
|
def get_connection(self) -> psycopg2.extensions.connection:
|
||||||
"""
|
"""
|
||||||
Get the database connection.
|
Get the database connection.
|
||||||
If not already connected, this reads the database config file and connects to the DB.
|
If not already connected, this reads the database config file and connects to the DB.
|
||||||
|
@ -28,4 +30,33 @@ class DBConnectionManager:
|
||||||
|
|
||||||
|
|
||||||
# Global instance that will hold our DB connection
|
# Global instance that will hold our DB connection
|
||||||
conn_manager = DBConnectionManager()
|
conn_manager = DBConnectionManager()
|
||||||
|
|
||||||
|
|
||||||
|
def get_existing_tables():
|
||||||
|
conn = conn_manager.get_connection()
|
||||||
|
cur = conn.cursor()
|
||||||
|
table_names = ", ".join("'{}'".format(tablename) for tablename in constants.DB_TABLE_NAMES)
|
||||||
|
cur.execute(
|
||||||
|
"SELECT tablename FROM pg_tables WHERE"
|
||||||
|
" schemaname = 'public' AND"
|
||||||
|
" tablename IN ({})".format(
|
||||||
|
table_names
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return [table for (table,) in cur.fetchall()]
|
||||||
|
|
||||||
|
|
||||||
|
def init_database(erase: bool = False):
|
||||||
|
tables = get_existing_tables()
|
||||||
|
|
||||||
|
if not erase and len(tables) > 0:
|
||||||
|
logger.error("Aborting database initialization: Tables {} already exist".format(", ".join(tables)))
|
||||||
|
return
|
||||||
|
conn = conn_manager.get_connection()
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
with open(constants.DATABASE_SCHEMA_PATH, "r") as f:
|
||||||
|
cur.execute(f.read())
|
||||||
|
conn.commit()
|
||||||
|
logger.verbose("Initialized DB tables.")
|
||||||
|
|
|
@ -65,25 +65,25 @@
|
||||||
* To be honest, I'd also like to apply all of the code to some non-league related games where I'd need this :D.
|
* To be honest, I'd also like to apply all of the code to some non-league related games where I'd need this :D.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DROP TABLE IF EXISTS users;
|
DROP TABLE IF EXISTS users CASCADE;
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
name TEXT NOT NULL
|
name TEXT NOT NULL,
|
||||||
discord_tag TEXT,
|
discord_tag TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS user_accounts;
|
DROP TABLE IF EXISTS user_accounts CASCADE;
|
||||||
CREATE TABLE user_accounts (
|
CREATE TABLE user_accounts (
|
||||||
/* The username from hanab.live. This will be used a) for associating games to one of our user_ids and b) show up in the player profile. */
|
/* The username from hanab.live. This will be used a) for associating games to one of our user_ids and b) show up in the player profile. */
|
||||||
username TEXT NOT NULL,
|
username TEXT PRIMARY KEY,
|
||||||
/**
|
/**
|
||||||
* Hanab.live stores a normalized username for each account to ensure that usernames are case-insensitive and don't have weird unicodes that resemble each other.
|
* Hanab.live stores a normalized username for each account to ensure that usernames are case-insensitive and don't have weird unicodes that resemble each other.
|
||||||
* It uses go-unidecode to generate these from the actual usernames, and I suggest we do the same.
|
* It uses go-unidecode to generate these from the actual usernames, and I suggest we do the same.
|
||||||
* The corresponding python package is 'unidecode', so we should just use that here.
|
* The corresponding python package is 'unidecode', so we should just use that here.
|
||||||
* This will then ensure that no two sign-ups correspond to the same hanab.live account.
|
* This will then ensure that no two sign-ups correspond to the same hanab.live account.
|
||||||
*/
|
*/
|
||||||
normalized_username TEXT NOT NULL UNIQUE
|
normalized_username TEXT NOT NULL UNIQUE,
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
@ -99,19 +99,18 @@ CREATE TABLE user_accounts (
|
||||||
DROP TABLE IF EXISTS downloads;
|
DROP TABLE IF EXISTS downloads;
|
||||||
CREATE TABLE downloads (
|
CREATE TABLE downloads (
|
||||||
/** Notice this has to be a hanab.live username, not a user_id */
|
/** Notice this has to be a hanab.live username, not a user_id */
|
||||||
username INTEGER NOT NULL,
|
username TEXT PRIMARY KEY REFERENCES user_accounts,
|
||||||
latest_game_id INTEGER NOT NULL,
|
latest_game_id INTEGER NOT NULL
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For completeness, I suggest we store the hanab.live variants here together with their names.
|
* For completeness, I suggest we store the hanab.live variants here together with their names.
|
||||||
* So this will just be a static table that we populate once.
|
* So this will just be a static table that we populate once.
|
||||||
*/
|
*/
|
||||||
DROP TABLE IF EXISTS variants;
|
DROP TABLE IF EXISTS variants CASCADE;
|
||||||
CREATE TABLE variants (
|
CREATE TABLE variants (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
name TEXT NOT NULL
|
name TEXT NOT NULL,
|
||||||
num_suits INTEGER NOT NULL,
|
num_suits INTEGER NOT NULL,
|
||||||
clue_starved BOOLEAN NOT NULL,
|
clue_starved BOOLEAN NOT NULL,
|
||||||
max_score SMALLINT NOT NULL
|
max_score SMALLINT NOT NULL
|
||||||
|
@ -122,7 +121,7 @@ CREATE TABLE variants (
|
||||||
* - Only download detailed data for each game once (The actions taken and the actual deck)
|
* - Only download detailed data for each game once (The actions taken and the actual deck)
|
||||||
* - We can evaluate statistics (also complicated ones) whenever we want, independent of the server
|
* - We can evaluate statistics (also complicated ones) whenever we want, independent of the server
|
||||||
*/
|
*/
|
||||||
DROP TABLE IF EXISTS games;
|
DROP TABLE IF EXISTS games CASCADE;
|
||||||
CREATE TABLE games (
|
CREATE TABLE games (
|
||||||
/** Here, we should use the same ids as the game ids from hanab.live */
|
/** Here, we should use the same ids as the game ids from hanab.live */
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
|
@ -141,6 +140,7 @@ CREATE TABLE games (
|
||||||
FOREIGN KEY (variant_id) REFERENCES variants (id)
|
FOREIGN KEY (variant_id) REFERENCES variants (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS game_participants CASCADE;
|
||||||
CREATE TABLE game_participants (
|
CREATE TABLE game_participants (
|
||||||
game_id INTEGER NOT NULL,
|
game_id INTEGER NOT NULL,
|
||||||
/**
|
/**
|
||||||
|
@ -223,7 +223,7 @@ CREATE TABLE seeds (
|
||||||
suit_index SMALLINT NOT NULL,
|
suit_index SMALLINT NOT NULL,
|
||||||
rank SMALLINT NOT NULL,
|
rank SMALLINT NOT NULL,
|
||||||
CONSTRAINT cards_unique UNIQUE (seed, card_index)
|
CONSTRAINT cards_unique UNIQUE (seed, card_index)
|
||||||
)
|
);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,14 +242,16 @@ CREATE TABLE seeds (
|
||||||
/**
|
/**
|
||||||
* Records the initial ratings of variants
|
* Records the initial ratings of variants
|
||||||
*/
|
*/
|
||||||
|
DROP TABLE IF EXISTS variant_base_ratings CASCADE;
|
||||||
CREATE TABLE variant_base_ratings (
|
CREATE TABLE variant_base_ratings (
|
||||||
variant_id SMALLINT PRIMARY KEY,
|
variant_id SMALLINT PRIMARY KEY,
|
||||||
rating REAL NOT NULL,
|
rating REAL NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a separate row for each elo update to one of the variants
|
* Store a separate row for each elo update to one of the variants
|
||||||
*/
|
*/
|
||||||
|
DROP TABLE IF EXISTS variant_ratings CASCADE;
|
||||||
CREATE TABLE variant_ratings (
|
CREATE TABLE variant_ratings (
|
||||||
/** This should reference the game that triggered the elo update */
|
/** This should reference the game that triggered the elo update */
|
||||||
league_id INTEGER PRIMARY KEY REFERENCES games,
|
league_id INTEGER PRIMARY KEY REFERENCES games,
|
||||||
|
@ -258,9 +260,10 @@ CREATE TABLE variant_ratings (
|
||||||
change REAL NOT NULL,
|
change REAL NOT NULL,
|
||||||
value_after REAL NOT NULL,
|
value_after REAL NOT NULL,
|
||||||
|
|
||||||
FOREIGN KEY (variant_id) REFERENCES variants (id),
|
FOREIGN KEY (variant_id) REFERENCES variants (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS user_base_ratings CASCADE;
|
||||||
CREATE TABLE user_base_ratings (
|
CREATE TABLE user_base_ratings (
|
||||||
user_id SMALLINT,
|
user_id SMALLINT,
|
||||||
/**
|
/**
|
||||||
|
@ -276,6 +279,7 @@ CREATE TABLE user_base_ratings (
|
||||||
FOREIGN KEY (user_id) REFERENCES users (id)
|
FOREIGN KEY (user_id) REFERENCES users (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS user_ratings CASCADE;
|
||||||
CREATE TABLE user_ratings (
|
CREATE TABLE user_ratings (
|
||||||
/** This should reference the game that triggered the elo update. I would use the league_id here for proper ordering. */
|
/** This should reference the game that triggered the elo update. I would use the league_id here for proper ordering. */
|
||||||
league_id INTEGER PRIMARY KEY REFERENCES games,
|
league_id INTEGER PRIMARY KEY REFERENCES games,
|
||||||
|
@ -298,6 +302,7 @@ CREATE TABLE user_ratings (
|
||||||
/* TABLES RELATED TO STATISTICS */
|
/* TABLES RELATED TO STATISTICS */
|
||||||
|
|
||||||
/** This is a rough outline, not really happy with it right now. */
|
/** This is a rough outline, not really happy with it right now. */
|
||||||
|
DROP TABLE IF EXISTS statistics CASCADE;
|
||||||
CREATE TABLE statistics (
|
CREATE TABLE statistics (
|
||||||
game_id INTEGER PRIMARY KEY,
|
game_id INTEGER PRIMARY KEY,
|
||||||
/** I'd say all of the following can just be null in case we have not evaluated them yet. */
|
/** I'd say all of the following can just be null in case we have not evaluated them yet. */
|
||||||
|
|
Loading…
Reference in a new issue