Add statistics page
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 14s

This commit is contained in:
2024-03-02 22:18:28 +01:00
parent 09203d9390
commit 0ea6568082
9 changed files with 141 additions and 49 deletions

View File

@ -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

View File

@ -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"])

View 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)

View File

@ -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()

View File

@ -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.

View File

@ -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>

View 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 %}

View File

@ -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 %}