Add statistics page
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 14s
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 14s
This commit is contained in:
@ -27,6 +27,7 @@ db.init_app(app)
|
||||
# NOTE: These imports are required to register the routes. They need to be imported after "app" is declared
|
||||
import formula10.controller.race_controller # type: ignore
|
||||
import formula10.controller.season_controller
|
||||
import formula10.controller.leaderboard_controller
|
||||
import formula10.controller.statistics_controller
|
||||
import formula10.controller.rules_controller
|
||||
import formula10.controller.admin_controller
|
||||
|
@ -58,7 +58,7 @@ def result_active_race(race_name: str) -> str:
|
||||
model = TemplateModel(active_user_name=None,
|
||||
active_result_race_name=race_name)
|
||||
|
||||
return render_template("enter.jinja", model=model)
|
||||
return render_template("result.jinja", model=model)
|
||||
|
||||
|
||||
@app.route("/result-enter/<race_name>", methods=["POST"])
|
||||
|
11
formula10/controller/leaderboard_controller.py
Normal file
11
formula10/controller/leaderboard_controller.py
Normal file
@ -0,0 +1,11 @@
|
||||
from flask import render_template
|
||||
from formula10 import app
|
||||
from formula10.domain.points_model import PointsModel
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
|
||||
@app.route("/graphs")
|
||||
def graphs_root() -> str:
|
||||
model = TemplateModel(active_user_name=None, active_result_race_name=None)
|
||||
points = PointsModel()
|
||||
|
||||
return render_template("leaderboard.jinja", model=model, points=points)
|
@ -1,10 +1,11 @@
|
||||
|
||||
from flask import render_template
|
||||
from formula10 import app
|
||||
from formula10.domain.points_model import PointsModel
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
|
||||
@app.route("/graphs")
|
||||
def graphs_root() -> str:
|
||||
@app.route("/stats")
|
||||
def stats_root() -> str:
|
||||
model = TemplateModel(active_user_name=None, active_result_race_name=None)
|
||||
points = PointsModel()
|
||||
|
||||
|
@ -7,6 +7,7 @@ from formula10.domain.model.race_guess import RaceGuess
|
||||
from formula10.domain.model.race_result import RaceResult
|
||||
from formula10.domain.model.season_guess import SeasonGuess
|
||||
from formula10.domain.model.season_guess_result import SeasonGuessResult
|
||||
from formula10.domain.model.team import Team
|
||||
from formula10.domain.model.user import User
|
||||
|
||||
RACE_GUESS_OFFSET_POINTS: Dict[int, int] = {
|
||||
@ -292,6 +293,14 @@ class PointsModel(Model):
|
||||
|
||||
return most_lost_names
|
||||
|
||||
def drivers_sorted_by_points(self) -> List[Driver]:
|
||||
comparator: Callable[[Driver], int] = lambda driver: self.wdc_standing_by_driver()[driver.name]
|
||||
return sorted(self.all_drivers(include_none=False), key=comparator)
|
||||
|
||||
def teams_sorted_by_points(self) -> List[Team]:
|
||||
comparator: Callable[[Team], int] = lambda team: self.wcc_standing_by_team()[team.name]
|
||||
return sorted(self.all_teams(include_none=False), key=comparator)
|
||||
|
||||
def points_per_step_cumulative(self) -> Dict[str, List[int]]:
|
||||
"""
|
||||
Returns a dictionary of lists, containing cumulative points per race for each user.
|
||||
|
@ -204,6 +204,7 @@
|
||||
{{ nav_selector(page="/race/" ~ model.active_user_name_sanitized_or_everyone(), text="Race Picks") }}
|
||||
{{ nav_selector(page="/season/" ~ model.active_user_name_sanitized_or_everyone(), text="Season Picks") }}
|
||||
{{ nav_selector(page="/graphs", text="Leaderboard") }}
|
||||
{{ nav_selector(page="/stats", text="Statistics") }}
|
||||
{{ nav_selector(page="/rules", text="Rules") }}
|
||||
</div>
|
||||
|
||||
|
59
formula10/templates/leaderboard.jinja
Normal file
59
formula10/templates/leaderboard.jinja
Normal file
@ -0,0 +1,59 @@
|
||||
{% extends 'base.jinja' %}
|
||||
|
||||
{% block title %}Formula 10 - Leaderboard{% endblock title %}
|
||||
|
||||
{% set active_page = "/graphs" %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Leaderboard</h5>
|
||||
<h6 class="card-subtitle">Points only include race picks</h6>
|
||||
|
||||
<table class="table table-bordered table-sm table-responsive mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">Place</th>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">User</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Points</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Total picks</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;" data-bs-toggle="tooltip" title="Any points count as correct">Correct picks</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Points per pick</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for user in points.users_sorted_by_points() %}
|
||||
{% set user_standing = points.user_standing()[user.name] %}
|
||||
<tr class="{% if user_standing == 1 %}table-danger{% endif %}">
|
||||
<td class="text-center text-nowrap">{{ user_standing }}</td>
|
||||
<td class="text-center text-nowrap">{{ user.name }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.total_points_by(user.name) }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.picks_count(user.name) }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.picks_with_points_count(user.name) }}</td>
|
||||
<td class="text-center text-nowrap">{{ "%0.2f" % points.points_per_pick(user.name) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# <div class="card mt-2">#}
|
||||
{# <div class="card-body">#}
|
||||
{# <h5 class="card-title">History</h5>#}
|
||||
|
||||
{# Line chart of point history with a line per user #}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
|
||||
{# <div class="card mt-2">#}
|
||||
{# <div class="card-body">#}
|
||||
{# <h5 class="card-title">Statistics</h5>#}
|
||||
|
||||
{# Various statistics: Driver voted most for DNF #}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
|
||||
{% endblock body %}
|
@ -1,59 +1,69 @@
|
||||
{% extends 'base.jinja' %}
|
||||
|
||||
{% block title %}Formula 10 - Leaderboard{% endblock title %}
|
||||
{% block title %}Formula 10 - Statistics{% endblock title %}
|
||||
|
||||
{% set active_page = "/graphs" %}
|
||||
{% set active_page = "/stats" %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Leaderboard</h5>
|
||||
<h6 class="card-subtitle">Points only include race picks</h6>
|
||||
<div class="grid" style="grid-template-columns: repeat(auto-fit, minmax(450px, 1fr));">
|
||||
|
||||
<table class="table table-bordered table-sm table-responsive mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">Place</th>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">User</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Points</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Total picks</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;" data-bs-toggle="tooltip" title="Any points count as correct">Correct picks</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Points per pick</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Drivers</h5>
|
||||
|
||||
<tbody>
|
||||
{% for user in points.users_sorted_by_points() %}
|
||||
{% set user_standing = points.user_standing()[user.name] %}
|
||||
<tr class="{% if user_standing == 1 %}table-danger{% endif %}">
|
||||
<td class="text-center text-nowrap">{{ user_standing }}</td>
|
||||
<td class="text-center text-nowrap">{{ user.name }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.total_points_by(user.name) }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.picks_count(user.name) }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.picks_with_points_count(user.name) }}</td>
|
||||
<td class="text-center text-nowrap">{{ "%0.2f" % points.points_per_pick(user.name) }}</td>
|
||||
<table class="table table-bordered table-sm table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">Place</th>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">Driver</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Points</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">DNFs</th>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for driver in points.drivers_sorted_by_points() %}
|
||||
{% set driver_standing = points.wdc_standing_by_driver()[driver.name] %}
|
||||
<tr class="{% if driver_standing == 1 %}table-danger{% endif %}">
|
||||
<td class="text-center text-nowrap">{{ driver_standing }}</td>
|
||||
<td class="text-center text-nowrap">{{ driver.name }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.wdc_points()[driver.name] }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.dnfs()[driver.name] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Constructors</h5>
|
||||
|
||||
<table class="table table-bordered table-sm table-responsive">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">Place</th>
|
||||
<th scope="col" class="text-center" style="min-width: 50px;">Team</th>
|
||||
<th scope="col" class="text-center" style="min-width: 100px;">Points</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for team in points.teams_sorted_by_points() %}
|
||||
{% set team_standing = points.wcc_standing_by_team()[team.name] %}
|
||||
<tr class="{% if team_standing == 1 %}table-danger{% endif %}">
|
||||
<td class="text-center text-nowrap">{{ team_standing }}</td>
|
||||
<td class="text-center text-nowrap">{{ team.name }}</td>
|
||||
<td class="text-center text-nowrap">{{ points.wcc_points()[team.name] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{# <div class="card mt-2">#}
|
||||
{# <div class="card-body">#}
|
||||
{# <h5 class="card-title">History</h5>#}
|
||||
|
||||
{# Line chart of point history with a line per user #}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
|
||||
{# <div class="card mt-2">#}
|
||||
{# <div class="card-body">#}
|
||||
{# <h5 class="card-title">Statistics</h5>#}
|
||||
|
||||
{# Various statistics: Driver voted most for DNF #}
|
||||
{# </div>#}
|
||||
{# </div>#}
|
||||
|
||||
{% endblock body %}
|
||||
|
Reference in New Issue
Block a user