diff --git a/install/database_schema.sql b/install/database_schema.sql index 297df32..9f40aab 100644 --- a/install/database_schema.sql +++ b/install/database_schema.sql @@ -340,13 +340,13 @@ CREATE TABLE user_statistics ( user_id INTEGER NOT NULL REFERENCES users (id), /** We track stats separately for each variant type */ variant_type SMALLINT NOT NULL, - current_streak INTEGER NOT NULL, - games_played INTEGER NOT NULL, - games_won INTEGER NOT NULL, - total_bdr INTEGER NOT NULL, + current_streak INTEGER, + games_played INTEGER, + games_won INTEGER, + total_bdr INTEGER, /** Number of critical cards that were either discarded or misplayed */ - total_crits_lots INTEGER NOT NULL, - total_game_moves INTEGER NOT NULL, + total_crits_lots INTEGER, + total_game_moves INTEGER, PRIMARY KEY (user_id, variant_type) ); diff --git a/src/statistics.py b/src/stats.py similarity index 52% rename from src/statistics.py rename to src/stats.py index 826047e..f3d573d 100644 --- a/src/statistics.py +++ b/src/stats.py @@ -2,7 +2,7 @@ import enum from typing import List, Tuple from hanabi import hanab_game - +import utils from database import conn_manager @@ -63,10 +63,50 @@ def analyze_game(instance: hanab_game.HanabiInstance, actions: List[hanab_game.A return GameAnalysisResult(outcomes, bdrs) -def update_user_statistics(user_ids: List[int]): +def update_user_statistics(): """ Update the cumulative user statistics for this user, assuming that the corresponding game statistics have been computed already. @param user_ids: @return: """ + # Note that some of these statistics could be computed by updating them on each new game insertion. + # However, it would be tedious to ensure that *every* new game triggers an update of these statistics. + # Also, this would be error-prone, since doing a mistake once means that values will be off forever + # (unless the DB is reset). + # Since it is cheap to accumulate some values over the whole DB, we therefore recreate the statistics as a whole, + # reusing only the individual results (that never change and therefore can only be missing, but never wrong) + cur = conn_manager.get_new_cursor() + + # Update total number of moves + for clue_starved in [True, False]: + # We insert 0 here to ensure that we have an entry for each player + # Note that this will immediately be changed by the next query in case it is nonzero, + # so the zero value never shows up in the database if it was nonzero before. + cur.execute( + "INSERT INTO user_statistics (user_id, variant_type, total_game_moves)" + " (" + " SELECT id, %s, %s FROM users" + " )" + "ON CONFLICT (user_id, variant_type) DO UPDATE " + "SET total_game_moves = EXCLUDED.total_game_moves", + (utils.get_rating_type(clue_starved), 0) + ) + cur.execute( + "INSERT INTO user_statistics (user_id, variant_type, total_game_moves)" + " (" + " SELECT users.id, %s, SUM(games.num_turns) FROM users " + " LEFT OUTER JOIN game_participants " + " ON game_participants.user_id = users.id " + " LEFT OUTER JOIN games " + " ON game_participants.game_id = games.id " + " LEFT OUTER JOIN variants" + " ON variants.id = games.variant_id " + " WHERE variants.clue_starved = %s OR variants.clue_starved IS NULL" + " GROUP BY users.id " + " ) " + "ON CONFLICT (user_id, variant_type) DO UPDATE " + "SET total_game_moves = EXCLUDED.total_game_moves", + (utils.get_rating_type(clue_starved), clue_starved) + ) + conn_manager.get_connection().commit()