forked from Hanabi/hanabi-league
Add player tab featuring table of players
This commit is contained in:
parent
d3f0f56244
commit
8e1b84467b
4 changed files with 138 additions and 3 deletions
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% extends "layout.html" %}
|
||||
{% from "stats_table.html" import player_table_js %}
|
||||
|
||||
{% block navbar %}
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
|
@ -18,6 +19,9 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link" id="leaderboards-1-tab" data-toggle="tab" href="#leaderboards-1">Clue Starved</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="players-tab" data-toggle="tab" href="#players">Players</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="variants-dropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Variants
|
||||
|
@ -105,6 +109,22 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="tab-pane fade" id="players">
|
||||
<div class="container my-5">
|
||||
<div id="table-players"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let player_tabledata = [
|
||||
{% for player_row in players %}
|
||||
{{- player_row -}},
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
{{ player_table_js("player_tabledata", "players") }}
|
||||
</script>
|
||||
|
||||
|
||||
<!-- Variants -->
|
||||
<div class="tab-pane fade" id="variants">
|
||||
|
|
|
@ -100,4 +100,36 @@ var table_{{div_id}} = new Tabulator("#table-{{div_id}}", {
|
|||
{title: "Result", field: "game_outcomes"}
|
||||
],
|
||||
});
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro player_table_js(data, div_id) %}
|
||||
var table_{{div_id}} = new Tabulator("#table-{{div_id}}", {
|
||||
data: {{data}},
|
||||
layout: "fitDataStretch",
|
||||
initialSort: [
|
||||
{column: "name", dir: "asc"},
|
||||
],
|
||||
columns: [
|
||||
{title: "Player", field: "name", formatter: "link", formatterParams:{
|
||||
urlPrefix: "/player/",
|
||||
}},
|
||||
{title: "No Variant", columns: [
|
||||
{title: "ELO", field: "novar_rating"},
|
||||
{title: "Rank", field: "novar_rank"},
|
||||
{title: "Games", field: "novar_games_played"},
|
||||
{title: "Win%", field: "novar_winrate"},
|
||||
{title: "Streak", field: "novar_cur_streak"},
|
||||
{title: "Max Streak", field: "novar_max_streak"},
|
||||
]},
|
||||
{title: "Clue Starved", columns: [
|
||||
{title: "ELO", field: "cs_rating"},
|
||||
{title: "Rank", field: "cs_rank"},
|
||||
{title: "Games", field: "cs_games_played"},
|
||||
{title: "Win%", field: "cs_winrate"},
|
||||
{title: "Streak", field: "cs_cur_streak"},
|
||||
{title: "Max Streak", field: "cs_max_streak"}
|
||||
]},
|
||||
]
|
||||
});
|
||||
{% endmacro %}
|
||||
|
|
Loading…
Reference in a new issue