Split base model from template + points model
This commit is contained in:
@ -17,6 +17,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.statistics_controller
|
||||
import formula10.controller.rules_controller
|
||||
import formula10.controller.admin_controller
|
||||
import formula10.controller.error_controller
|
||||
@ -25,11 +26,14 @@ import formula10.controller.error_controller
|
||||
# TODO
|
||||
# General
|
||||
|
||||
# Create a model baseclass that contains the cached teams/drivers/races etc., so the points + template model can be derived from it
|
||||
|
||||
# Statistics
|
||||
# - Auto calculate points
|
||||
# - Display points somewhere in race table?
|
||||
# - Highlight currently correct values for some season guesses (e.g. current most dnfs)
|
||||
# - Display points somewhere in race table? Below the name in the table header.
|
||||
# - Highlight currently correct values for some season guesses (e.g. current most dnfs, team winners, podiums)
|
||||
# - Generate static diagram using chart.js + templating the js (funny yikes)
|
||||
# - Which driver was voted most for dnf?
|
||||
|
||||
# Possible but probably not
|
||||
# - Show cards of previous race results, like with season guesses?
|
||||
|
@ -5,7 +5,7 @@ from werkzeug import Response
|
||||
|
||||
from formula10.database.update_queries import update_race_result, update_user
|
||||
from formula10.database.import_export import export_dynamic_data, reload_static_data
|
||||
from formula10.frontend.template_model import TemplateModel
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
from formula10 import app
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@ from typing import cast
|
||||
from flask import redirect, render_template, session
|
||||
from werkzeug import Response
|
||||
|
||||
from formula10.frontend.template_model import TemplateModel
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
from formula10 import app
|
||||
|
||||
def error_redirect(error_message: str) -> Response:
|
||||
|
@ -3,7 +3,7 @@ from flask import redirect, render_template, request
|
||||
from werkzeug import Response
|
||||
|
||||
from formula10.database.update_queries import delete_race_guess, update_race_guess
|
||||
from formula10.frontend.template_model import TemplateModel
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
from formula10 import app
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
from flask import render_template
|
||||
|
||||
from formula10 import app
|
||||
from formula10.frontend.template_model import TemplateModel
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
|
||||
@app.route("/rules")
|
||||
def rules_root() -> str:
|
||||
|
@ -5,8 +5,8 @@ from werkzeug import Response
|
||||
|
||||
from formula10.database.model.db_team import DbTeam
|
||||
from formula10.database.update_queries import update_season_guess
|
||||
from formula10.frontend.model.team import NONE_TEAM
|
||||
from formula10.frontend.template_model import TemplateModel
|
||||
from formula10.domain.model.team import NONE_TEAM
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
from formula10 import app, db
|
||||
|
||||
|
||||
|
9
formula10/controller/statistics_controller.py
Normal file
9
formula10/controller/statistics_controller.py
Normal file
@ -0,0 +1,9 @@
|
||||
from flask import render_template
|
||||
from formula10 import app
|
||||
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)
|
||||
|
||||
return render_template("statistics.jinja", model=model)
|
0
formula10/database/__init__.py
Normal file
0
formula10/database/__init__.py
Normal file
0
formula10/database/model/__init__.py
Normal file
0
formula10/database/model/__init__.py
Normal file
@ -3,7 +3,7 @@ from typing import Any, Callable, Iterable, List, TypeVar, overload
|
||||
|
||||
from formula10.database.model.db_race import DbRace
|
||||
from formula10 import db
|
||||
from formula10.frontend.model.race import Race
|
||||
from formula10.domain.model.race import Race
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
0
formula10/domain/__init__.py
Normal file
0
formula10/domain/__init__.py
Normal file
118
formula10/domain/domain_model.py
Normal file
118
formula10/domain/domain_model.py
Normal file
@ -0,0 +1,118 @@
|
||||
from typing import Callable, List
|
||||
from sqlalchemy import desc
|
||||
|
||||
from formula10.database.model.db_driver import DbDriver
|
||||
from formula10.database.model.db_race import DbRace
|
||||
from formula10.database.model.db_race_guess import DbRaceGuess
|
||||
from formula10.database.model.db_race_result import DbRaceResult
|
||||
from formula10.database.model.db_season_guess import DbSeasonGuess
|
||||
from formula10.database.model.db_team import DbTeam
|
||||
from formula10.database.model.db_user import DbUser
|
||||
from formula10.database.validation import find_multiple_strict
|
||||
from formula10.domain.model.driver import NONE_DRIVER, Driver
|
||||
from formula10.domain.model.race import Race
|
||||
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.team import NONE_TEAM, Team
|
||||
from formula10.domain.model.user import User
|
||||
from formula10 import db
|
||||
|
||||
|
||||
class Model():
|
||||
_all_users: List[User] | None = None
|
||||
_all_race_results: List[RaceResult] | None = None
|
||||
_all_race_guesses: List[RaceGuess] | None = None
|
||||
_all_season_guesses: List[SeasonGuess] | None = None
|
||||
_all_races: List[Race] | None = None
|
||||
_all_drivers: List[Driver] | None = None
|
||||
_all_teams: List[Team] | None = None
|
||||
|
||||
def all_users(self) -> List[User]:
|
||||
"""
|
||||
Returns a list of all users in the database.
|
||||
"""
|
||||
if self._all_users is None:
|
||||
self._all_users = [
|
||||
User.from_db_user(db_user)
|
||||
for db_user in db.session.query(DbUser).filter_by(enabled=True).all()
|
||||
]
|
||||
|
||||
return self._all_users
|
||||
|
||||
def all_race_results(self) -> List[RaceResult]:
|
||||
"""
|
||||
Returns a list of all race results in the database, in descending order (most recent first).
|
||||
"""
|
||||
if self._all_race_results is None:
|
||||
self._all_race_results = [
|
||||
RaceResult.from_db_race_result(db_race_result)
|
||||
for db_race_result in db.session.query(DbRaceResult).join(DbRaceResult.race).order_by(desc(DbRace.number)).all()
|
||||
]
|
||||
|
||||
return self._all_race_results
|
||||
|
||||
def all_race_guesses(self) -> List[RaceGuess]:
|
||||
"""
|
||||
Returns a list of all race guesses in the database.
|
||||
"""
|
||||
if self._all_race_guesses is None:
|
||||
self._all_race_guesses = [
|
||||
RaceGuess.from_db_race_guess(db_race_guess)
|
||||
for db_race_guess in db.session.query(DbRaceGuess).join(DbRaceGuess.user).filter_by(enabled=True).all() # Ignore disabled users
|
||||
]
|
||||
|
||||
return self._all_race_guesses
|
||||
|
||||
def all_season_guesses(self) -> List[SeasonGuess]:
|
||||
if self._all_season_guesses is None:
|
||||
self._all_season_guesses = [
|
||||
SeasonGuess.from_db_season_guess(db_season_guess)
|
||||
for db_season_guess in db.session.query(DbSeasonGuess).join(DbSeasonGuess.user).filter_by(enabled=True).all() # Ignore disabled users
|
||||
]
|
||||
|
||||
return self._all_season_guesses
|
||||
|
||||
def all_races(self) -> List[Race]:
|
||||
"""
|
||||
Returns a list of all races in the database.
|
||||
"""
|
||||
if self._all_races is None:
|
||||
self._all_races = [
|
||||
Race.from_db_race(db_race)
|
||||
for db_race in db.session.query(DbRace).order_by(desc(DbRace.number)).all()
|
||||
]
|
||||
|
||||
return self._all_races
|
||||
|
||||
def all_drivers(self, *, include_none: bool) -> List[Driver]:
|
||||
"""
|
||||
Returns a list of all drivers in the database.
|
||||
"""
|
||||
if self._all_drivers is None:
|
||||
self._all_drivers = [
|
||||
Driver.from_db_driver(db_driver)
|
||||
for db_driver in db.session.query(DbDriver).all()
|
||||
]
|
||||
|
||||
if include_none:
|
||||
return self._all_drivers
|
||||
else:
|
||||
predicate: Callable[[Driver], bool] = lambda driver: driver != NONE_DRIVER
|
||||
return find_multiple_strict(predicate, self._all_drivers)
|
||||
|
||||
def all_teams(self, *, include_none: bool) -> List[Team]:
|
||||
"""
|
||||
Returns a list of all teams in the database.
|
||||
"""
|
||||
if self._all_teams is None:
|
||||
self._all_teams = [
|
||||
Team.from_db_team(db_team)
|
||||
for db_team in db.session.query(DbTeam).all()
|
||||
]
|
||||
|
||||
if include_none:
|
||||
return self._all_teams
|
||||
else:
|
||||
predicate: Callable[[Team], bool] = lambda team: team != NONE_TEAM
|
||||
return find_multiple_strict(predicate, self._all_teams)
|
0
formula10/domain/model/__init__.py
Normal file
0
formula10/domain/model/__init__.py
Normal file
@ -1,7 +1,7 @@
|
||||
from urllib.parse import quote
|
||||
|
||||
from formula10.database.model.db_driver import DbDriver
|
||||
from formula10.frontend.model.team import NONE_TEAM, Team
|
||||
from formula10.domain.model.team import NONE_TEAM, Team
|
||||
|
||||
|
||||
class Driver():
|
@ -1,7 +1,7 @@
|
||||
from formula10.database.model.db_race_guess import DbRaceGuess
|
||||
from formula10.frontend.model.driver import Driver
|
||||
from formula10.frontend.model.race import Race
|
||||
from formula10.frontend.model.user import User
|
||||
from formula10.domain.model.driver import Driver
|
||||
from formula10.domain.model.race import Race
|
||||
from formula10.domain.model.user import User
|
||||
|
||||
|
||||
class RaceGuess():
|
@ -3,8 +3,8 @@ from typing import Dict, List
|
||||
|
||||
from formula10.database.common_queries import find_single_driver_strict
|
||||
from formula10.database.model.db_race_result import DbRaceResult
|
||||
from formula10.frontend.model.driver import NONE_DRIVER, Driver
|
||||
from formula10.frontend.model.race import Race
|
||||
from formula10.domain.model.driver import NONE_DRIVER, Driver
|
||||
from formula10.domain.model.race import Race
|
||||
|
||||
|
||||
class RaceResult:
|
@ -2,9 +2,9 @@ import json
|
||||
from typing import List
|
||||
from formula10.database.common_queries import find_single_driver_strict
|
||||
from formula10.database.model.db_season_guess import DbSeasonGuess
|
||||
from formula10.frontend.model.driver import Driver
|
||||
from formula10.frontend.model.team import Team
|
||||
from formula10.frontend.model.user import User
|
||||
from formula10.domain.model.driver import Driver
|
||||
from formula10.domain.model.team import Team
|
||||
from formula10.domain.model.user import User
|
||||
|
||||
|
||||
class SeasonGuess():
|
5
formula10/domain/points_model.py
Normal file
5
formula10/domain/points_model.py
Normal file
@ -0,0 +1,5 @@
|
||||
from formula10.domain.domain_model import Model
|
||||
|
||||
class PointsModel(Model):
|
||||
def __init__(self):
|
||||
Model.__init__(self)
|
@ -1,37 +1,21 @@
|
||||
from typing import List, Callable, Dict, overload
|
||||
from sqlalchemy import desc
|
||||
|
||||
from formula10.database.model.db_driver import DbDriver
|
||||
from formula10.database.model.db_race import DbRace
|
||||
from formula10.database.model.db_race_guess import DbRaceGuess
|
||||
from formula10.database.model.db_race_result import DbRaceResult
|
||||
from formula10.database.model.db_season_guess import DbSeasonGuess
|
||||
from formula10.database.model.db_team import DbTeam
|
||||
from formula10.database.model.db_user import DbUser
|
||||
from formula10.frontend.model.driver import NONE_DRIVER, Driver
|
||||
from formula10.frontend.model.race import Race
|
||||
from formula10.frontend.model.race_guess import RaceGuess
|
||||
from formula10.frontend.model.race_result import RaceResult
|
||||
from formula10.frontend.model.season_guess import SeasonGuess
|
||||
from formula10.frontend.model.team import NONE_TEAM, Team
|
||||
from formula10.frontend.model.user import User
|
||||
from formula10.domain.domain_model import Model
|
||||
from formula10.domain.model.driver import NONE_DRIVER, Driver
|
||||
from formula10.domain.model.race import Race
|
||||
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.team import NONE_TEAM, Team
|
||||
from formula10.domain.model.user import User
|
||||
from formula10.database.validation import find_first_else_none, find_multiple_strict, find_single_strict, find_single_or_none_strict, race_has_started
|
||||
from formula10 import db
|
||||
|
||||
|
||||
class TemplateModel:
|
||||
class TemplateModel(Model):
|
||||
"""
|
||||
This class bundles all data required from inside a template.
|
||||
"""
|
||||
|
||||
_all_users: List[User] | None = None
|
||||
_all_race_results: List[RaceResult] | None = None
|
||||
_all_race_guesses: List[RaceGuess] | None = None
|
||||
_all_season_guesses: List[SeasonGuess] | None = None
|
||||
_all_races: List[Race] | None = None
|
||||
_all_drivers: List[Driver] | None = None
|
||||
_all_teams: List[Team] | None = None
|
||||
|
||||
active_user: User | None = None
|
||||
active_result: RaceResult | None = None
|
||||
|
||||
@ -57,18 +41,6 @@ class TemplateModel:
|
||||
def active_user_name_sanitized_or_everyone(self) -> str:
|
||||
return self.active_user.name_sanitized if self.active_user is not None else "Everyone"
|
||||
|
||||
def all_users(self) -> List[User]:
|
||||
"""
|
||||
Returns a list of all users in the database.
|
||||
"""
|
||||
if self._all_users is None:
|
||||
self._all_users = [
|
||||
User.from_db_user(db_user)
|
||||
for db_user in db.session.query(DbUser).filter_by(enabled=True).all()
|
||||
]
|
||||
|
||||
return self._all_users
|
||||
|
||||
def all_users_or_active_user(self) -> List[User]:
|
||||
if self.active_user is not None:
|
||||
return [self.active_user]
|
||||
@ -99,18 +71,6 @@ class TemplateModel:
|
||||
predicate: Callable[[User], bool] = lambda user: user.name == user_name
|
||||
return find_single_strict(predicate, self.all_users())
|
||||
|
||||
def all_race_results(self) -> List[RaceResult]:
|
||||
"""
|
||||
Returns a list of all race results in the database, in descending order (most recent first).
|
||||
"""
|
||||
if self._all_race_results is None:
|
||||
self._all_race_results = [
|
||||
RaceResult.from_db_race_result(db_race_result)
|
||||
for db_race_result in db.session.query(DbRaceResult).join(DbRaceResult.race).order_by(desc(DbRace.number)).all()
|
||||
]
|
||||
|
||||
return self._all_race_results
|
||||
|
||||
def race_result_by(self, *, race_name: str) -> RaceResult | None:
|
||||
"""
|
||||
Tries to obtain the race result corresponding to a race name.
|
||||
@ -118,18 +78,6 @@ class TemplateModel:
|
||||
predicate: Callable[[RaceResult], bool] = lambda result: result.race.name == race_name
|
||||
return find_single_or_none_strict(predicate, self.all_race_results())
|
||||
|
||||
def all_race_guesses(self) -> List[RaceGuess]:
|
||||
"""
|
||||
Returns a list of all race guesses in the database.
|
||||
"""
|
||||
if self._all_race_guesses is None:
|
||||
self._all_race_guesses = [
|
||||
RaceGuess.from_db_race_guess(db_race_guess)
|
||||
for db_race_guess in db.session.query(DbRaceGuess).join(DbRaceGuess.user).filter_by(enabled=True).all() # Ignore disabled users
|
||||
]
|
||||
|
||||
return self._all_race_guesses
|
||||
|
||||
@overload
|
||||
def race_guesses_by(self, *, user_name: str) -> List[RaceGuess]:
|
||||
"""
|
||||
@ -189,15 +137,6 @@ class TemplateModel:
|
||||
|
||||
raise Exception("race_guesses_by encountered illegal combination of arguments")
|
||||
|
||||
def all_season_guesses(self) -> List[SeasonGuess]:
|
||||
if self._all_season_guesses is None:
|
||||
self._all_season_guesses = [
|
||||
SeasonGuess.from_db_season_guess(db_season_guess)
|
||||
for db_season_guess in db.session.query(DbSeasonGuess).join(DbSeasonGuess.user).filter_by(enabled=True).all() # Ignore disabled users
|
||||
]
|
||||
|
||||
return self._all_season_guesses
|
||||
|
||||
@overload
|
||||
def season_guesses_by(self, *, user_name: str) -> SeasonGuess:
|
||||
"""
|
||||
@ -228,18 +167,6 @@ class TemplateModel:
|
||||
|
||||
raise Exception("season_guesses_by encountered illegal combination of arguments")
|
||||
|
||||
def all_races(self) -> List[Race]:
|
||||
"""
|
||||
Returns a list of all races in the database.
|
||||
"""
|
||||
if self._all_races is None:
|
||||
self._all_races = [
|
||||
Race.from_db_race(db_race)
|
||||
for db_race in db.session.query(DbRace).order_by(desc(DbRace.number)).all()
|
||||
]
|
||||
|
||||
return self._all_races
|
||||
|
||||
def first_race_without_result(self) -> Race | None:
|
||||
"""
|
||||
Returns the first race-object with no associated race result.
|
||||
@ -273,41 +200,9 @@ class TemplateModel:
|
||||
else:
|
||||
return self.all_races()[0].name_sanitized
|
||||
|
||||
def all_teams(self, *, include_none: bool) -> List[Team]:
|
||||
"""
|
||||
Returns a list of all teams in the database.
|
||||
"""
|
||||
if self._all_teams is None:
|
||||
self._all_teams = [
|
||||
Team.from_db_team(db_team)
|
||||
for db_team in db.session.query(DbTeam).all()
|
||||
]
|
||||
|
||||
if include_none:
|
||||
return self._all_teams
|
||||
else:
|
||||
predicate: Callable[[Team], bool] = lambda team: team != NONE_TEAM
|
||||
return find_multiple_strict(predicate, self._all_teams)
|
||||
|
||||
def none_team(self) -> Team:
|
||||
return NONE_TEAM
|
||||
|
||||
def all_drivers(self, *, include_none: bool) -> List[Driver]:
|
||||
"""
|
||||
Returns a list of all drivers in the database.
|
||||
"""
|
||||
if self._all_drivers is None:
|
||||
self._all_drivers = [
|
||||
Driver.from_db_driver(db_driver)
|
||||
for db_driver in db.session.query(DbDriver).all()
|
||||
]
|
||||
|
||||
if include_none:
|
||||
return self._all_drivers
|
||||
else:
|
||||
predicate: Callable[[Driver], bool] = lambda driver: driver != NONE_DRIVER
|
||||
return find_multiple_strict(predicate, self._all_drivers)
|
||||
|
||||
def all_drivers_or_active_result_standing_drivers(self) -> List[Driver]:
|
||||
return self.active_result.ordered_standing_list() if self.active_result is not None else self.all_drivers(include_none=False)
|
||||
|
33
formula10/templates/statistics.jinja
Normal file
33
formula10/templates/statistics.jinja
Normal file
@ -0,0 +1,33 @@
|
||||
{% 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>
|
||||
|
||||
{# Table that lists each users + Total Points (?), Race guesses points, Season guesses points (missing overtakes + hottake), number of guesses that yielded points, average points per guess #}
|
||||
</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