From 707c1814ef41eb9e69c7d2c7e5a4d6ace4ec8201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Wed, 20 Dec 2023 01:20:49 +0100 Subject: [PATCH] adjust database scheme: rename some fields and introduce rating type to variants --- install/database_schema.sql | 23 +++++++++++------------ src/database.py | 12 ++++++------ src/ratings.py | 17 ++++++++--------- src/render_site.py | 8 ++++---- src/stats.py | 12 ++++++------ 5 files changed, 35 insertions(+), 37 deletions(-) diff --git a/install/database_schema.sql b/install/database_schema.sql index f954db5..4cae8aa 100644 --- a/install/database_schema.sql +++ b/install/database_schema.sql @@ -113,7 +113,8 @@ CREATE TABLE variants ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, num_suits INTEGER NOT NULL, - clue_starved BOOLEAN NOT NULL + clue_starved BOOLEAN NOT NULL, + rating_type INTEGER NOT NULL ); /** @@ -262,30 +263,29 @@ CREATE TABLE variant_base_ratings ( 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 (league_id), + league_id INTEGER NOT NULL REFERENCES games (league_id), + variant_id SMALLINT NOT NULL REFERENCES variants (id), - variant_id SMALLINT NOT NULL, num_players SMALLINT NOT NULL, change REAL NOT NULL, value_after REAL NOT NULL, - FOREIGN KEY (variant_id) REFERENCES variants (id) + PRIMARY KEY (league_id, variant_id) ); DROP TABLE IF EXISTS user_base_ratings CASCADE; CREATE TABLE user_base_ratings ( - user_id INTEGER, + user_id INTEGER REFERENCES users (id), /** * Since a user has different ratings now, this should represent which of the ratings we mean. * For now, I suggest 0 = NoVar, 1 = ClueStarved. * I'd use an integer here to be open for more elos per player in future seasons */ - type SMALLINT NOT NULL, + rating_type SMALLINT NOT NULL, rating REAL NOT NULL, - PRIMARY KEY (user_id, type), - FOREIGN KEY (user_id) REFERENCES users (id) + PRIMARY KEY (user_id, rating_type) ); DROP TABLE IF EXISTS user_ratings CASCADE; @@ -297,14 +297,13 @@ CREATE TABLE user_ratings ( */ league_id INTEGER NOT NULL REFERENCES games (league_id), - user_id INTEGER NOT NULL, - type SMALLINT NOT NULL, + user_id INTEGER NOT NULL REFERENCES users (id), + rating_type SMALLINT NOT NULL, change REAL NOT NULL, value_after REAL NOT NULL, - FOREIGN KEY (user_id) REFERENCES users (id), - CONSTRAINT user_change_per_game_unique UNIQUE (league_id, user_id) + PRIMARY KEY (league_id, user_id) ); diff --git a/src/database.py b/src/database.py index a03bec9..cf7efea 100644 --- a/src/database.py +++ b/src/database.py @@ -78,9 +78,9 @@ def init_database(): def fetch_and_initialize_variants(): response = requests.get(constants.VARIANTS_JSON_URL) if not response.status_code == 200: - logger.error( - "Could not download variants.json file from github (tried url {})".format(constants.VARIANTS_JSON_URL)) - return + err_msg = "Could not download variants.json file from github (tried url {})".format(constants.VARIANTS_JSON_URL) + logger.error(err_msg) + raise ConnectionError(err_msg) variants = json.loads(response.text) config = config_manager.get_config() @@ -96,8 +96,8 @@ def fetch_and_initialize_variants(): continue cur = conn_manager.get_new_cursor() cur.execute( - "INSERT INTO variants (id, name, num_suits, clue_starved) VALUES (%s, %s, %s, %s)", - (variant_id, name, num_suits, clue_starved) + "INSERT INTO variants (id, name, num_suits, clue_starved, rating_type) VALUES (%s, %s, %s, %s, %s)", + (variant_id, name, num_suits, clue_starved, utils.get_rating_type(clue_starved)) ) conn_manager.get_connection().commit() @@ -226,7 +226,7 @@ def init_player_base_rating(player_name: str, base_rating: Optional[int] = None) try: psycopg2.extras.execute_values( cur, - "INSERT INTO user_base_ratings (user_id, type, rating) VALUES %s", + "INSERT INTO user_base_ratings (user_id, rating_type, rating) VALUES %s", vals ) conn_manager.get_connection().commit() diff --git a/src/ratings.py b/src/ratings.py index c41c075..7d3505c 100644 --- a/src/ratings.py +++ b/src/ratings.py @@ -68,7 +68,7 @@ def get_current_user_ratings(user_ids: List[int], rating_type: int) -> Dict[int, """ cur = conn_manager.get_new_cursor() cur.execute("SELECT user_id, rating FROM user_base_ratings " - "WHERE user_id IN ({}) AND type = %s".format(", ".join("%s" for _ in user_ids)), + "WHERE user_id IN ({}) AND rating_type = %s".format(", ".join("%s" for _ in user_ids)), user_ids + [rating_type] ) base_ratings = cur.fetchall() @@ -82,17 +82,17 @@ def get_current_user_ratings(user_ids: List[int], rating_type: int) -> Dict[int, # even though we do not retrieve any values from the subclause-table cur.execute("SELECT user_ratings.user_id, value_after FROM user_ratings " "INNER JOIN (" - " SELECT user_id, type, MAX(league_id) AS max_league_id" + " SELECT user_id, rating_type, MAX(league_id) AS max_league_id" " FROM user_ratings " - " GROUP BY (user_id, type)" + " GROUP BY (user_id, rating_type)" " ) AS latest_user_ratings " " ON" " user_ratings.league_id = latest_user_ratings.max_league_id" " AND user_ratings.user_id = latest_user_ratings.user_id" - " AND user_ratings.type = latest_user_ratings.type " + " AND user_ratings.rating_type = latest_user_ratings.rating_type " "WHERE " " user_ratings.user_id IN ({})" - " AND user_ratings.type = %s" + " AND user_ratings.rating_type = %s" .format(", ".join("%s" for _ in user_ids)) , user_ids + [rating_type] ) @@ -153,14 +153,14 @@ def process_rating_of_next_game() -> bool: # Fetch data on the game played cur.execute( - "SELECT games.league_id, games.num_players, games.score, variants.num_suits, variants.clue_starved, variants.id " + "SELECT games.league_id, games.num_players, games.score, variants.num_suits, variants.clue_starved, variants.rating_type, variants.id " "FROM games " "INNER JOIN variants " " ON games.variant_id = variants.id " "WHERE games.id = %s", (game_id,) ) - league_id, num_players, score, num_suits, clue_starved, variant_id = cur.fetchone() + league_id, num_players, score, num_suits, clue_starved, rating_type, variant_id = cur.fetchone() # Fetch game participants and how many games they played each so far cur.execute("SELECT game_participants.user_id, COUNT(games.id) " @@ -190,7 +190,6 @@ def process_rating_of_next_game() -> bool: raise ValueError(err_msg) # Fetch current ratings of variant and players involved - rating_type = utils.get_rating_type(clue_starved) user_ratings = get_current_user_ratings(list(games_played.keys()), rating_type) variant_rating = get_current_variant_rating(variant_id, num_players) @@ -211,7 +210,7 @@ def process_rating_of_next_game() -> bool: # This updates the player rating. psycopg2.extras.execute_values( cur, - "INSERT INTO user_ratings (league_id, user_id, type, change, value_after) " + "INSERT INTO user_ratings (league_id, user_id, rating_type, change, value_after) " "VALUES %s", user_ratings_vals ) diff --git a/src/render_site.py b/src/render_site.py index 9b2fde2..6385830 100644 --- a/src/render_site.py +++ b/src/render_site.py @@ -225,11 +225,11 @@ def get_rating_lists() -> Dict[int, List[PlayerEntry]]: " COALESCE(value_after, rating) AS current_rating" " FROM users " " LEFT OUTER JOIN user_ratings " - " ON user_ratings.user_id = users.id AND user_ratings.type = %s " + " ON user_ratings.user_id = users.id AND user_ratings.rating_type = %s " " LEFT OUTER JOIN user_accounts " " ON users.id = user_accounts.user_id " " INNER JOIN user_base_ratings " - " ON users.id = user_base_ratings.user_id AND user_base_ratings.type = %s" + " ON users.id = user_base_ratings.user_id AND user_base_ratings.rating_type = %s" " GROUP BY (user_accounts.user_id, player_name, value_after, league_id, rating) " " ORDER BY user_accounts.user_id, league_id DESC" " ) AS ratings " @@ -381,11 +381,11 @@ def get_player_stats() -> Dict[str, Dict[int, PlayerStats]]: " COALESCE(value_after, rating) AS current_rating" " FROM users " " LEFT OUTER JOIN user_ratings " - " ON user_ratings.user_id = users.id AND user_ratings.type = %s " + " ON user_ratings.user_id = users.id AND user_ratings.rating_type = %s " " LEFT OUTER JOIN user_accounts " " ON users.id = user_accounts.user_id " " INNER JOIN user_base_ratings " - " ON users.id = user_base_ratings.user_id AND user_base_ratings.type = %s" + " ON users.id = user_base_ratings.user_id AND user_base_ratings.rating_type = %s" " GROUP BY (user_accounts.user_id, player_name, value_after, league_id, rating) " " ORDER BY user_accounts.user_id, league_id DESC" " ) AS ratings " diff --git a/src/stats.py b/src/stats.py index e7846ba..45fd037 100644 --- a/src/stats.py +++ b/src/stats.py @@ -155,7 +155,7 @@ def update_user_statistics(): " (" " SELECT" " users.id," - " CASE WHEN clue_starved THEN %s ELSE %s END," + " rating_type," " SUM(games.num_turns)," " COUNT(*)," # This counts the number of rows (per user id), so the number of played game " COUNT(*) FILTER ( WHERE variants.num_suits * 5 = games.score )," # Same, but only count wins now @@ -170,7 +170,7 @@ def update_user_statistics(): " ON variants.id = games.variant_id " " LEFT OUTER JOIN game_statistics" " ON games.id = game_statistics.game_id" - " GROUP BY users.id, clue_starved " + " GROUP BY users.id, rating_type " " ) " "ON CONFLICT (user_id, variant_type) DO UPDATE " "SET" @@ -198,7 +198,7 @@ def update_user_statistics(): " (" " SELECT" " user_id," - " CASE WHEN clue_starved THEN %s ELSE %s END," + " rating_type," " COALESCE(MAX(streak_length), 0) AS maximum_streak," " COALESCE((ARRAY_AGG(streak_length ORDER BY league_id DESC))[1], 0) AS current_streak," " (" @@ -215,15 +215,15 @@ def update_user_statistics(): " WHEN num_suits * 5 = score" " THEN" " COUNT(*)" - " OVER (PARTITION BY user_id, clue_starved, group_id ORDER BY league_id ASC)" + " OVER (PARTITION BY user_id, rating_type, group_id ORDER BY league_id ASC)" " END" " AS streak_length " " FROM" " (" " SELECT" " users.id AS user_id," - " variants.clue_starved," " variants.num_suits," + " variants.rating_type," " games.score," " games.league_id," # This count function is the tricky part that labels each game with the group_id of the streak it belongs to @@ -241,7 +241,7 @@ def update_user_statistics(): " ON variants.id = games.variant_id " " ) AS games_grouped_by_streak " " ) AS games_with_streaks " - " GROUP BY user_id, clue_starved" + " GROUP BY user_id, rating_type" " )" "ON CONFLICT (user_id, variant_type) DO UPDATE " "SET (maximum_streak, current_streak, maximum_streak_last_game) = (EXCLUDED.maximum_streak, EXCLUDED.current_streak, EXCLUDED.maximum_streak_last_game)",