Skip to content

Commit

Permalink
site: Move the leaderboard to its own blueprint
Browse files Browse the repository at this point in the history
In preparation for adding a second leaderboard, move the existing
leaderboard to its own blueprint.

Signed-off-by: Sean Anderson <[email protected]>
  • Loading branch information
Forty-Bot committed Apr 17, 2024
1 parent bca60b9 commit 6810213
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 94 deletions.
100 changes: 100 additions & 0 deletions trends/site/leaderboards.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (C) 2020-24 Sean Anderson <[email protected]>

import flask

from .util import get_db, get_filter_params, get_filter_clauses, get_order, get_pagination

leaderboards = flask.Blueprint('leaderboards', __name__)

@leaderboards.route('/')
def overview():
limit, offset = get_pagination()
filters = get_filter_params()
filter_clauses = get_filter_clauses(filters, 'classid', 'league', 'formatid', 'mapid')

# Since we are using a cube, we need to explicitly select the NULL rows
cube_clauses = []
grouping = 0b00000
for (name, column, group) in (
('map', 'mapid', 0b0001),
('class', 'classid', 0b0010),
('format', 'formatid', 0b0100),
('league', 'league', 0b1000),
):
if not filters[name]:
cube_clauses.append(f"AND {column} ISNULL")
grouping |= group
cube_clauses = '\n'.join(cube_clauses)

order, order_clause = get_order({
'duration': "duration",
'logs': "logs",
'winrate': "winrate",
'rating': "rating",
'k30': "k30",
'd30': "d30",
'a30': "a30",
'kd': "kd",
'kad': "kad",
'dpm': "dpm",
'dtm': "dtm",
'ddm': "ddm",
'dr': "dr",
'acc': "acc",
}, 'rating')

db = get_db()
leaderboard = db.cursor()
leaderboard.execute(f"""SELECT
name,
avatarhash,
steamid64,
duration,
logs,
winrate,
rating,
k30,
d30,
a30,
kd,
kad,
dpm,
dtm,
ddm,
dr,
acc
FROM (SELECT
playerid,
sum(duration) AS duration,
sum(wins + losses + ties) AS logs,
sum(0.5 * ties + wins) /
sum(wins + losses + ties) AS winrate,
(50 + sum(0.5 * ties + wins)) /
(100 + sum(wins + losses + ties)) AS rating,
sum(kills) * 30.0 * 60 / nullif(sum(duration), 0) AS k30,
sum(deaths) * 30.0 * 60 / nullif(sum(duration), 0) AS d30,
sum(assists) * 30.0 * 60 / nullif(sum(duration), 0) AS a30,
sum(kills) * 1.0 / nullif(sum(deaths), 0) AS kd,
(sum(kills) + sum(assists)) * 1.0 / nullif(sum(deaths), 0) AS kad,
sum(dmg) * 60.0 / nullif(sum(duration), 0) AS dpm,
sum(dt) * 60.0 / nullif(sum(duration), 0) AS dtm,
(sum(dmg) - sum(dt)) * 60.0 / nullif(sum(duration), 0) AS ddm,
sum(dmg) * 1.0 / nullif(sum(dt), 0) AS dr,
sum(hits) * 1.0 / nullif(sum(shots), 0) AS acc
FROM leaderboard_cube
WHERE grouping = %(grouping)s
{filter_clauses}
{cube_clauses}
GROUP BY playerid
ORDER BY {order_clause} NULLS LAST
LIMIT %(limit)s OFFSET %(offset)s
) AS leaderboard
LEFT JOIN player USING (playerid)
LEFT JOIN name USING (nameid)
ORDER BY {order_clause};""",
{ **filters, 'grouping': grouping, 'limit': limit, 'offset': offset })
resp = flask.make_response(flask.render_template("leaderboards/overview.html",
leaderboard=leaderboard.fetchall()))
resp.cache_control.max_age = 3600
return resp
90 changes: 1 addition & 89 deletions trends/site/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,95 +68,7 @@ def search():

@root.route('/leaderboard')
def leaderboard():
limit, offset = get_pagination()
filters = get_filter_params()
filter_clauses = get_filter_clauses(filters, 'classid', 'league', 'formatid', 'mapid')

# Since we are using a cube, we need to explicitly select the NULL rows
cube_clauses = []
grouping = 0b00000
for (name, column, group) in (
('map', 'mapid', 0b0001),
('class', 'classid', 0b0010),
('format', 'formatid', 0b0100),
('league', 'league', 0b1000),
):
if not filters[name]:
cube_clauses.append(f"AND {column} ISNULL")
grouping |= group
cube_clauses = '\n'.join(cube_clauses)

order, order_clause = get_order({
'duration': "duration",
'logs': "logs",
'winrate': "winrate",
'rating': "rating",
'k30': "k30",
'd30': "d30",
'a30': "a30",
'kd': "kd",
'kad': "kad",
'dpm': "dpm",
'dtm': "dtm",
'ddm': "ddm",
'dr': "dr",
'acc': "acc",
}, 'rating')

db = get_db()
leaderboard = db.cursor()
leaderboard.execute(f"""SELECT
name,
avatarhash,
steamid64,
duration,
logs,
winrate,
rating,
k30,
d30,
a30,
kd,
kad,
dpm,
dtm,
ddm,
dr,
acc
FROM (SELECT
playerid,
sum(duration) AS duration,
sum(wins + losses + ties) AS logs,
sum(0.5 * ties + wins) /
sum(wins + losses + ties) AS winrate,
(50 + sum(0.5 * ties + wins)) /
(100 + sum(wins + losses + ties)) AS rating,
sum(kills) * 30.0 * 60 / nullif(sum(duration), 0) AS k30,
sum(deaths) * 30.0 * 60 / nullif(sum(duration), 0) AS d30,
sum(assists) * 30.0 * 60 / nullif(sum(duration), 0) AS a30,
sum(kills) * 1.0 / nullif(sum(deaths), 0) AS kd,
(sum(kills) + sum(assists)) * 1.0 / nullif(sum(deaths), 0) AS kad,
sum(dmg) * 60.0 / nullif(sum(duration), 0) AS dpm,
sum(dt) * 60.0 / nullif(sum(duration), 0) AS dtm,
(sum(dmg) - sum(dt)) * 60.0 / nullif(sum(duration), 0) AS ddm,
sum(dmg) * 1.0 / nullif(sum(dt), 0) AS dr,
sum(hits) * 1.0 / nullif(sum(shots), 0) AS acc
FROM leaderboard_cube
WHERE grouping = %(grouping)s
{filter_clauses}
{cube_clauses}
GROUP BY playerid
ORDER BY {order_clause} NULLS LAST
LIMIT %(limit)s OFFSET %(offset)s
) AS leaderboard
LEFT JOIN player USING (playerid)
LEFT JOIN name USING (nameid)
ORDER BY {order_clause};""",
{ **filters, 'grouping': grouping, 'limit': limit, 'offset': offset })
resp = flask.make_response(flask.render_template("leaderboard.html",
leaderboard=leaderboard.fetchall()))
resp.cache_control.max_age = 3600
return resp
return flask.redirect(flask.url_for('leaderboards.overview'), 301)

@root.route('/logs')
def logs():
Expand Down
2 changes: 1 addition & 1 deletion trends/site/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</picture>
<h3><a href="{{ url_for('root.index') }}">Trends.tf</a></h3>
<a href="{{ url_for('root.logs') }}">Logs</a>
<a href="{{ url_for('root.leaderboard') }}">Leaderboards</a>
<a href="{{ url_for('leaderboards.overview') }}">Leaderboards</a>
<a href="{{ url_for('league.comps', league='etf2l') }}">ETF2L</a>
<a href="{{ url_for('league.comps', league='rgl') }}">RGL</a>
<form action="{{ url_for('root.search') }}" role="search">
Expand Down
2 changes: 1 addition & 1 deletion trends/site/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<h1>Team Fortress 2 Stats and Trends</h1>
<dl>
<dt>Players</dt>
<dd><a href="{{ url_for('.leaderboard') }}">
<dd><a href="{{ url_for('leaderboards.overview') }}">
{{ "{:,}".format(players) }}
</a></dt>
<dt>Logs</dt>
Expand Down
10 changes: 10 additions & 0 deletions trends/site/templates/leaderboards/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{# SPDX-License-Identifier: AGPL-3.0-only #}
{# Copyright (C) 2020-24 Sean Anderson <seanga2@gmail.com> #}
{% from "macros/nav.html" import navbar %}
{% extends "base.html" %}
{% block title %}Leaderboards - {{ super() }}{% endblock %}
{% block content %}
{{ super() }}
<h1>Leaderboards</h1>
{{ navbar(('.overview', "Overview"),) }}
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
{% from "macros/pagination.html" import navigation %}
{% from "macros/icons.html" import playerlink %}
{% from "macros/sort.html" import sort %}
{% extends "base.html" %}
{% block title %}Leaderboard - {{ super() }}{% endblock %}
{% extends "leaderboards/base.html" %}
{% block title %}Overview - {{ super() }}{% endblock %}
{% block content %}
{{ super() }}
<h1>Leaderboard</h1>
<h2>Overview</h2>
{{ filter('class', 'league', 'format', 'map') }}
{{ sort({
'logs': "Logs",
Expand Down
2 changes: 2 additions & 0 deletions trends/site/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import werkzeug.utils

from .api import api, json_handler
from .leaderboards import leaderboards
from .league import league
from .player import player
from .root import root, metrics_extension
Expand Down Expand Up @@ -190,6 +191,7 @@ def create_app():

app.register_error_handler(werkzeug.exceptions.HTTPException, html_handler)
app.register_blueprint(root)
app.register_blueprint(leaderboards, url_prefix='/leaderboards')
app.register_blueprint(player, url_prefix='/player/<int:steamid>')
app.register_blueprint(league, url_prefix='/league/<league>')
app.register_blueprint(api, url_prefix='/api/v1')
Expand Down

0 comments on commit 6810213

Please sign in to comment.