forked from Hanabi/hanabi-league
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_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'
|
||||
|
|
35
database.py
35
database.py
|
@ -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.
|
||||
|
@ -28,4 +30,33 @@ class DBConnectionManager:
|
|||
|
||||
|
||||
# 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.
|
||||
*/
|
||||
|
||||
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. */
|
||||
|
|
Loading…
Reference in a new issue