Add DB initialization code. Fix DB schema

This commit is contained in:
Maximilian Keßler 2023-11-22 16:00:26 +01:00
parent 15a1b29b84
commit 8623b524de
Signed by: max
GPG Key ID: BCC5A619923C0BA5
3 changed files with 71 additions and 17 deletions

View File

@ -7,3 +7,21 @@ DB_CONFIG_FILE_NAME = 'config.yaml'
DEFAULT_DB_NAME = 'hanabi-league'
DEFAULT_DB_USER = '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'

View File

@ -1,5 +1,7 @@
import psycopg2
import psycopg2.extensions
import constants
from config import read_db_config
from log_setup import logger
@ -16,7 +18,7 @@ class DBConnectionManager:
))
logger.debug("Established database connection.")
def get_connection(self):
def get_connection(self) -> psycopg2.extensions.connection:
"""
Get the database connection.
If not already connected, this reads the database config file and connects to the DB.
@ -29,3 +31,32 @@ class DBConnectionManager:
# Global instance that will hold our DB connection
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.")

View File

@ -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.
*/
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS users CASCADE;
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
discord_tag TEXT,
name TEXT NOT NULL,
discord_tag TEXT
);
DROP TABLE IF EXISTS user_accounts;
DROP TABLE IF EXISTS user_accounts CASCADE;
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. */
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.
* 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.
* 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,
FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);
@ -99,19 +99,18 @@ CREATE TABLE user_accounts (
DROP TABLE IF EXISTS downloads;
CREATE TABLE downloads (
/** Notice this has to be a hanab.live username, not a user_id */
username INTEGER NOT NULL,
latest_game_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
username TEXT PRIMARY KEY REFERENCES user_accounts,
latest_game_id INTEGER NOT NULL
);
/**
* 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.
*/
DROP TABLE IF EXISTS variants;
DROP TABLE IF EXISTS variants CASCADE;
CREATE TABLE variants (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
name TEXT NOT NULL,
num_suits INTEGER NOT NULL,
clue_starved BOOLEAN 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)
* - 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 (
/** Here, we should use the same ids as the game ids from hanab.live */
id INTEGER PRIMARY KEY,
@ -141,6 +140,7 @@ CREATE TABLE games (
FOREIGN KEY (variant_id) REFERENCES variants (id)
);
DROP TABLE IF EXISTS game_participants CASCADE;
CREATE TABLE game_participants (
game_id INTEGER NOT NULL,
/**
@ -223,7 +223,7 @@ CREATE TABLE seeds (
suit_index SMALLINT NOT NULL,
rank SMALLINT NOT NULL,
CONSTRAINT cards_unique UNIQUE (seed, card_index)
)
);
/**
@ -242,14 +242,16 @@ CREATE TABLE seeds (
/**
* Records the initial ratings of variants
*/
DROP TABLE IF EXISTS variant_base_ratings CASCADE;
CREATE TABLE variant_base_ratings (
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
*/
DROP TABLE IF EXISTS variant_ratings CASCADE;
CREATE TABLE variant_ratings (
/** This should reference the game that triggered the elo update */
league_id INTEGER PRIMARY KEY REFERENCES games,
@ -258,9 +260,10 @@ CREATE TABLE variant_ratings (
change 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 (
user_id SMALLINT,
/**
@ -276,6 +279,7 @@ CREATE TABLE user_base_ratings (
FOREIGN KEY (user_id) REFERENCES users (id)
);
DROP TABLE IF EXISTS user_ratings CASCADE;
CREATE TABLE user_ratings (
/** 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,
@ -298,6 +302,7 @@ CREATE TABLE user_ratings (
/* TABLES RELATED TO STATISTICS */
/** This is a rough outline, not really happy with it right now. */
DROP TABLE IF EXISTS statistics CASCADE;
CREATE TABLE statistics (
game_id INTEGER PRIMARY KEY,
/** I'd say all of the following can just be null in case we have not evaluated them yet. */