From 8e1b84467bc0a06df4e61b5e257d059f80ff231f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Ke=C3=9Fler?= Date: Sun, 24 Dec 2023 23:33:36 +0100 Subject: [PATCH] Add player tab featuring table of players --- install/database_schema.sql | 2 +- src/render_site.py | 85 ++++++++++++++++++++++++++++++++++++- templates/main.html | 20 +++++++++ templates/stats_table.html | 34 ++++++++++++++- 4 files changed, 138 insertions(+), 3 deletions(-) diff --git a/install/database_schema.sql b/install/database_schema.sql index f022b57..8ac697a 100644 --- a/install/database_schema.sql +++ b/install/database_schema.sql @@ -346,7 +346,7 @@ CREATE TABLE user_statistics ( games_played INTEGER, games_won INTEGER, games_lost INTEGER GENERATED ALWAYS AS (games_played - games_won) STORED, - winrate REAL GENERATED ALWAYS AS (CASE WHEN games_played != 0 THEN CAST(games_won AS REAL)/ games_played ELSE NULL END) STORED, + winrate REAL GENERATED ALWAYS AS (CASE WHEN games_played != 0 THEN 100 * CAST(games_won AS REAL)/ games_played ELSE NULL END) STORED, total_bdr INTEGER, /** Number of critical cards that were either discarded or misplayed */ total_crits_lots INTEGER, diff --git a/src/render_site.py b/src/render_site.py index 3d86ee6..79eae9c 100644 --- a/src/render_site.py +++ b/src/render_site.py @@ -135,6 +135,23 @@ class PlayerRow: stats: PlayerStats +@dataclass +class PlayerOverview: + name: str + novar_rating: float + novar_rank: int + novar_games_played: int + novar_winrate: float + novar_cur_streak: int + novar_max_streak: int + cs_rating: float + cs_rank: int + cs_games_played: float + cs_winrate: float + cs_cur_streak: int + cs_max_streak: int + + def get_games() -> List[GameRow]: cur = conn_manager.get_connection().cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute( @@ -424,6 +441,69 @@ def get_player_stats() -> Dict[str, Dict[int, PlayerStats]]: return ret +def get_player_list() -> List[Dict]: + cur = conn_manager.get_connection().cursor(cursor_factory=psycopg2.extras.DictCursor) + cur.execute( + "WITH users_rated AS (" + " SELECT " + " users.*," + " COALESCE(novar_rating, novar_base_ratings.rating) AS novar_rating," + " COALESCE(cs_rating, novar_base_ratings.rating) AS cs_rating" + " FROM users " + " INNER JOIN user_base_ratings AS novar_base_ratings" + " ON users.id = novar_base_ratings.user_id AND novar_base_ratings.rating_type = 0" + " INNER JOIN user_base_ratings AS cs_base_ratings" + " ON users.id = cs_base_ratings.user_id AND cs_base_ratings.rating_type = 1" + " LEFT JOIN LATERAL (" + " SELECT value_after AS novar_rating" + " FROM user_ratings" + " WHERE user_ratings.user_id = users.id AND user_ratings.rating_type = 0" + " ORDER BY league_id DESC" + " LIMIT 1" + " ) ON TRUE " + " LEFT JOIN LATERAL (" + " SELECT value_after AS cs_rating" + " FROM user_ratings" + " WHERE user_ratings.user_id = users.id AND user_ratings.rating_type = 1" + " ORDER BY league_id DESC" + " LIMIT 1" + " ) ON TRUE " + ")" + "SELECT" + " player_name AS name," + " novar_rating," + " rank() over (ORDER BY novar_rating DESC) AS novar_rank," + " novar_statistics.games_played AS novar_games_played," + " COALESCE(novar_statistics.winrate, 0) AS novar_winrate," + " novar_statistics.current_streak AS novar_cur_streak," + " novar_statistics.maximum_streak AS novar_max_streak, " + " cs_rating," + " rank() over (ORDER BY cs_rating DESC) AS cs_rank," + " cs_statistics.games_played AS cs_games_played," + " COALESCE(cs_statistics.winrate, 0) AS cs_winrate," + " cs_statistics.current_streak AS cs_cur_streak," + " cs_statistics.maximum_streak AS cs_max_streak " + "FROM" + " users_rated " + "LEFT OUTER JOIN user_statistics AS novar_statistics" + " ON novar_statistics.user_id = users_rated.id AND novar_statistics.variant_type = 0 " + "LEFT OUTER JOIN user_statistics AS cs_statistics" + " ON cs_statistics.user_id = users_rated.id AND cs_statistics.variant_type = 1" + ) + res = [] + for row in cur.fetchall(): + # Note: We convert to the dataclass here and then back to the dictionary + # This ensures that all attributes set in the dataclass are in fact specified. + overview = PlayerOverview(**row) + overview.novar_rating = round(overview.novar_rating, 1) + overview.cs_rating = round(overview.cs_rating, 1) + overview.novar_winrate = round(overview.novar_winrate, 1) + overview.cs_winrate = round(overview.cs_winrate, 1) + res.append(dataclasses.asdict(overview)) + print(res) + return res + + def get_total_games(): cur = conn_manager.get_new_cursor() cur.execute("SELECT COUNT(league_id) FROM games") @@ -463,6 +543,7 @@ def render_main_site(env: jinja2.Environment, out_dir: Path): rating_lists = get_rating_lists() streak_lists = get_streak_list() leaders = get_leaders(rating_lists, streak_lists) + players = get_player_list() variant_rows: List[VariantRow] = get_variant_rows() @@ -479,7 +560,8 @@ def render_main_site(env: jinja2.Environment, out_dir: Path): total_players=get_num_players(), latest_run=datetime.datetime.now().isoformat(), variants_with_player_nums=variant_rows, - unique_variants=build_unique_variants(variant_rows) + unique_variants=build_unique_variants(variant_rows), + players=players # variants=variants, ) @@ -559,5 +641,6 @@ def render_all(): if __name__ == "__main__": + get_player_list() render_all() diff --git a/templates/main.html b/templates/main.html index ecb43f6..989a066 100644 --- a/templates/main.html +++ b/templates/main.html @@ -1,4 +1,5 @@ {% extends "layout.html" %} +{% from "stats_table.html" import player_table_js %} {% block navbar %}