Add initial (untested) race guess points calculation
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
from typing import Callable, List
|
from typing import Callable, Dict, List, overload
|
||||||
from sqlalchemy import desc
|
from sqlalchemy import desc
|
||||||
|
|
||||||
from formula10.database.model.db_driver import DbDriver
|
from formula10.database.model.db_driver import DbDriver
|
||||||
@ -8,7 +8,7 @@ from formula10.database.model.db_race_result import DbRaceResult
|
|||||||
from formula10.database.model.db_season_guess import DbSeasonGuess
|
from formula10.database.model.db_season_guess import DbSeasonGuess
|
||||||
from formula10.database.model.db_team import DbTeam
|
from formula10.database.model.db_team import DbTeam
|
||||||
from formula10.database.model.db_user import DbUser
|
from formula10.database.model.db_user import DbUser
|
||||||
from formula10.database.validation import find_multiple_strict
|
from formula10.database.validation import find_multiple_strict, find_single_or_none_strict, find_single_strict
|
||||||
from formula10.domain.model.driver import NONE_DRIVER, Driver
|
from formula10.domain.model.driver import NONE_DRIVER, Driver
|
||||||
from formula10.domain.model.race import Race
|
from formula10.domain.model.race import Race
|
||||||
from formula10.domain.model.race_guess import RaceGuess
|
from formula10.domain.model.race_guess import RaceGuess
|
||||||
@ -30,7 +30,7 @@ class Model():
|
|||||||
|
|
||||||
def all_users(self) -> List[User]:
|
def all_users(self) -> List[User]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all users in the database.
|
Returns a list of all enabled users.
|
||||||
"""
|
"""
|
||||||
if self._all_users is None:
|
if self._all_users is None:
|
||||||
self._all_users = [
|
self._all_users = [
|
||||||
@ -42,7 +42,7 @@ class Model():
|
|||||||
|
|
||||||
def all_race_results(self) -> List[RaceResult]:
|
def all_race_results(self) -> List[RaceResult]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all race results in the database, in descending order (most recent first).
|
Returns a list of all race results, in descending order (most recent first).
|
||||||
"""
|
"""
|
||||||
if self._all_race_results is None:
|
if self._all_race_results is None:
|
||||||
self._all_race_results = [
|
self._all_race_results = [
|
||||||
@ -54,7 +54,7 @@ class Model():
|
|||||||
|
|
||||||
def all_race_guesses(self) -> List[RaceGuess]:
|
def all_race_guesses(self) -> List[RaceGuess]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all race guesses in the database.
|
Returns a list of all race guesses (of enabled users).
|
||||||
"""
|
"""
|
||||||
if self._all_race_guesses is None:
|
if self._all_race_guesses is None:
|
||||||
self._all_race_guesses = [
|
self._all_race_guesses = [
|
||||||
@ -65,6 +65,9 @@ class Model():
|
|||||||
return self._all_race_guesses
|
return self._all_race_guesses
|
||||||
|
|
||||||
def all_season_guesses(self) -> List[SeasonGuess]:
|
def all_season_guesses(self) -> List[SeasonGuess]:
|
||||||
|
"""
|
||||||
|
Returns a list of all season guesses (of enabled users).
|
||||||
|
"""
|
||||||
if self._all_season_guesses is None:
|
if self._all_season_guesses is None:
|
||||||
self._all_season_guesses = [
|
self._all_season_guesses = [
|
||||||
SeasonGuess.from_db_season_guess(db_season_guess)
|
SeasonGuess.from_db_season_guess(db_season_guess)
|
||||||
@ -75,7 +78,7 @@ class Model():
|
|||||||
|
|
||||||
def all_races(self) -> List[Race]:
|
def all_races(self) -> List[Race]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all races in the database.
|
Returns a list of all races, in descending order (last race first).
|
||||||
"""
|
"""
|
||||||
if self._all_races is None:
|
if self._all_races is None:
|
||||||
self._all_races = [
|
self._all_races = [
|
||||||
@ -87,7 +90,7 @@ class Model():
|
|||||||
|
|
||||||
def all_drivers(self, *, include_none: bool) -> List[Driver]:
|
def all_drivers(self, *, include_none: bool) -> List[Driver]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all drivers in the database.
|
Returns a list of all drivers.
|
||||||
"""
|
"""
|
||||||
if self._all_drivers is None:
|
if self._all_drivers is None:
|
||||||
self._all_drivers = [
|
self._all_drivers = [
|
||||||
@ -103,7 +106,7 @@ class Model():
|
|||||||
|
|
||||||
def all_teams(self, *, include_none: bool) -> List[Team]:
|
def all_teams(self, *, include_none: bool) -> List[Team]:
|
||||||
"""
|
"""
|
||||||
Returns a list of all teams in the database.
|
Returns a list of all teams.
|
||||||
"""
|
"""
|
||||||
if self._all_teams is None:
|
if self._all_teams is None:
|
||||||
self._all_teams = [
|
self._all_teams = [
|
||||||
@ -116,3 +119,197 @@ class Model():
|
|||||||
else:
|
else:
|
||||||
predicate: Callable[[Team], bool] = lambda team: team != NONE_TEAM
|
predicate: Callable[[Team], bool] = lambda team: team != NONE_TEAM
|
||||||
return find_multiple_strict(predicate, self._all_teams)
|
return find_multiple_strict(predicate, self._all_teams)
|
||||||
|
|
||||||
|
#
|
||||||
|
# User queries
|
||||||
|
#
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def user_by(self, *, user_name: str) -> User:
|
||||||
|
"""
|
||||||
|
Tries to obtain the user object for a specific username.
|
||||||
|
"""
|
||||||
|
return self.user_by(user_name=user_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def user_by(self, *, user_name: str, ignore: List[str]) -> User | None:
|
||||||
|
"""
|
||||||
|
Tries to obtain the user object for a specific username, but ignores certain usernames.
|
||||||
|
"""
|
||||||
|
return self.user_by(user_name=user_name, ignore=ignore)
|
||||||
|
|
||||||
|
def user_by(self, *, user_name: str, ignore: List[str] | None = None) -> User | None:
|
||||||
|
if ignore is None:
|
||||||
|
ignore = []
|
||||||
|
|
||||||
|
if len(ignore) > 0 and user_name in ignore:
|
||||||
|
return None
|
||||||
|
|
||||||
|
predicate: Callable[[User], bool] = lambda user: user.name == user_name
|
||||||
|
return find_single_strict(predicate, self.all_users())
|
||||||
|
|
||||||
|
#
|
||||||
|
# Race result queries
|
||||||
|
#
|
||||||
|
|
||||||
|
def race_result_by(self, *, race_name: str) -> RaceResult | None:
|
||||||
|
"""
|
||||||
|
Tries to obtain the race result corresponding to a race name.
|
||||||
|
"""
|
||||||
|
predicate: Callable[[RaceResult], bool] = lambda result: result.race.name == race_name
|
||||||
|
return find_single_or_none_strict(predicate, self.all_race_results())
|
||||||
|
|
||||||
|
#
|
||||||
|
# Race guess queries
|
||||||
|
#
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def race_guesses_by(self, *, user_name: str) -> List[RaceGuess]:
|
||||||
|
"""
|
||||||
|
Returns a list of all race guesses made by a specific user.
|
||||||
|
"""
|
||||||
|
return self.race_guesses_by(user_name=user_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def race_guesses_by(self, *, race_name: str) -> List[RaceGuess]:
|
||||||
|
"""
|
||||||
|
Returns a list of all race guesses made for a specific race.
|
||||||
|
"""
|
||||||
|
return self.race_guesses_by(race_name=race_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def race_guesses_by(self, *, user_name: str, race_name: str) -> RaceGuess | None:
|
||||||
|
"""
|
||||||
|
Returns a single race guess by a specific user for a specific race, or None, if this guess doesn't exist.
|
||||||
|
"""
|
||||||
|
return self.race_guesses_by(user_name=user_name, race_name=race_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def race_guesses_by(self) -> Dict[str, Dict[str, RaceGuess]]:
|
||||||
|
"""
|
||||||
|
Returns a dictionary that maps race-ids to user-id - guess dictionaries.
|
||||||
|
"""
|
||||||
|
return self.race_guesses_by()
|
||||||
|
|
||||||
|
def race_guesses_by(self, *, user_name: str | None = None, race_name: str | None = None) -> RaceGuess | List[RaceGuess] | Dict[str, Dict[str, RaceGuess]] | None:
|
||||||
|
# List of all guesses by a single user
|
||||||
|
if user_name is not None and race_name is None:
|
||||||
|
predicate: Callable[[RaceGuess], bool] = lambda guess: guess.user.name == user_name
|
||||||
|
return find_multiple_strict(predicate, self.all_race_guesses())
|
||||||
|
|
||||||
|
# List of all guesses for a single race
|
||||||
|
if user_name is None and race_name is not None:
|
||||||
|
predicate: Callable[[RaceGuess], bool] = lambda guess: guess.race.name == race_name
|
||||||
|
return find_multiple_strict(predicate, self.all_race_guesses())
|
||||||
|
|
||||||
|
# Guess for a single race by a single user
|
||||||
|
if user_name is not None and race_name is not None:
|
||||||
|
predicate: Callable[[RaceGuess], bool] = lambda guess: guess.user.name == user_name and guess.race.name == race_name
|
||||||
|
return find_single_or_none_strict(predicate, self.all_race_guesses())
|
||||||
|
|
||||||
|
# Dict with all guesses
|
||||||
|
if user_name is None and race_name is None:
|
||||||
|
guesses_by: Dict[str, Dict[str, RaceGuess]] = dict()
|
||||||
|
guess: RaceGuess
|
||||||
|
|
||||||
|
for guess in self.all_race_guesses():
|
||||||
|
if guess.race.name not in guesses_by:
|
||||||
|
guesses_by[guess.race.name] = dict()
|
||||||
|
|
||||||
|
guesses_by[guess.race.name][guess.user.name] = guess
|
||||||
|
|
||||||
|
return guesses_by
|
||||||
|
|
||||||
|
raise Exception("race_guesses_by encountered illegal combination of arguments")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Season guess queries
|
||||||
|
#
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def season_guesses_by(self, *, user_name: str) -> SeasonGuess:
|
||||||
|
"""
|
||||||
|
Returns the season guess made by a specific user.
|
||||||
|
"""
|
||||||
|
return self.season_guesses_by(user_name=user_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def season_guesses_by(self) -> Dict[str, SeasonGuess]:
|
||||||
|
"""
|
||||||
|
Returns a dictionary of season guesses mapped to usernames.
|
||||||
|
"""
|
||||||
|
return self.season_guesses_by()
|
||||||
|
|
||||||
|
def season_guesses_by(self, *, user_name: str | None = None) -> SeasonGuess | Dict[str, SeasonGuess] | None:
|
||||||
|
if user_name is not None:
|
||||||
|
predicate: Callable[[SeasonGuess], bool] = lambda guess: guess.user.name == user_name
|
||||||
|
return find_single_or_none_strict(predicate, self.all_season_guesses())
|
||||||
|
|
||||||
|
if user_name is None:
|
||||||
|
guesses_by: Dict[str, SeasonGuess] = dict()
|
||||||
|
guess: SeasonGuess
|
||||||
|
|
||||||
|
for guess in self.all_season_guesses():
|
||||||
|
guesses_by[guess.user.name] = guess
|
||||||
|
|
||||||
|
return guesses_by
|
||||||
|
|
||||||
|
raise Exception("season_guesses_by encountered illegal combination of arguments")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Team queries
|
||||||
|
#
|
||||||
|
|
||||||
|
def none_team(self) -> Team:
|
||||||
|
return NONE_TEAM
|
||||||
|
|
||||||
|
#
|
||||||
|
# Driver queries
|
||||||
|
#
|
||||||
|
|
||||||
|
def none_driver(self) -> Driver:
|
||||||
|
return NONE_DRIVER
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def drivers_by(self, *, team_name: str) -> List[Driver]:
|
||||||
|
"""
|
||||||
|
Returns a list of all drivers driving for a certain team.
|
||||||
|
"""
|
||||||
|
return self.drivers_by(team_name=team_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def drivers_by(self) -> Dict[str, List[Driver]]:
|
||||||
|
"""
|
||||||
|
Returns a dictionary of drivers mapped to team names.
|
||||||
|
"""
|
||||||
|
return self.drivers_by()
|
||||||
|
|
||||||
|
def drivers_by(self, *, team_name: str | None = None) -> List[Driver] | Dict[str, List[Driver]]:
|
||||||
|
if team_name is not None:
|
||||||
|
predicate: Callable[[Driver], bool] = lambda driver: driver.team.name == team_name
|
||||||
|
return find_multiple_strict(predicate, self.all_drivers(include_none=False), 2)
|
||||||
|
|
||||||
|
if team_name is None:
|
||||||
|
drivers_by: Dict[str, List[Driver]] = dict()
|
||||||
|
driver: Driver
|
||||||
|
team: Team
|
||||||
|
|
||||||
|
for team in self.all_teams(include_none=False):
|
||||||
|
drivers_by[team.name] = []
|
||||||
|
for driver in self.all_drivers(include_none=False):
|
||||||
|
drivers_by[driver.team.name] += [driver]
|
||||||
|
|
||||||
|
return drivers_by
|
||||||
|
|
||||||
|
raise Exception("drivers_by encountered illegal combination of arguments")
|
||||||
|
|
||||||
|
#
|
||||||
|
# Race queries
|
||||||
|
#
|
||||||
|
|
||||||
|
def race_by(self, *, race_name: str) -> Race:
|
||||||
|
for race in self.all_races():
|
||||||
|
if race.name == race_name:
|
||||||
|
return race
|
||||||
|
|
||||||
|
raise Exception(f"Couldn't find race {race_name}")
|
@ -70,8 +70,8 @@ class RaceResult:
|
|||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
race: Race
|
race: Race
|
||||||
standing: Dict[str, Driver]
|
standing: Dict[str, Driver] # Always contains all 20 drivers, even if DNF'ed or excluded
|
||||||
initial_dnf: List[Driver]
|
initial_dnf: List[Driver] # initial_dnf is empty if no-one DNF'ed
|
||||||
all_dnfs: List[Driver]
|
all_dnfs: List[Driver]
|
||||||
standing_exclusions: List[Driver]
|
standing_exclusions: List[Driver]
|
||||||
|
|
||||||
@ -86,6 +86,16 @@ class RaceResult:
|
|||||||
|
|
||||||
return self.standing[position]
|
return self.standing[position]
|
||||||
|
|
||||||
|
def driver_standing_position(self, driver: Driver) -> int | None:
|
||||||
|
if driver == NONE_DRIVER:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for position, _driver in self.standing.items():
|
||||||
|
if driver == _driver and driver not in self.standing_exclusions:
|
||||||
|
return int(position)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def driver_standing_position_string(self, driver: Driver) -> str:
|
def driver_standing_position_string(self, driver: Driver) -> str:
|
||||||
if driver == NONE_DRIVER:
|
if driver == NONE_DRIVER:
|
||||||
return ""
|
return ""
|
||||||
|
@ -1,5 +1,159 @@
|
|||||||
|
from typing import Dict, List, overload
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from formula10.domain.domain_model import Model
|
from formula10.domain.domain_model import Model
|
||||||
|
from formula10.domain.model.driver import NONE_DRIVER
|
||||||
|
from formula10.domain.model.race_guess import RaceGuess
|
||||||
|
from formula10.domain.model.race_result import RaceResult
|
||||||
|
|
||||||
|
RACE_GUESS_OFFSET_POINTS: Dict[int, int] = {
|
||||||
|
3: 1,
|
||||||
|
2: 3,
|
||||||
|
1: 6,
|
||||||
|
0: 10
|
||||||
|
}
|
||||||
|
RACE_GUESS_DNF_POINTS: int = 10
|
||||||
|
|
||||||
|
SEASON_GUESS_HOT_TAKE_POINTS: int = 10
|
||||||
|
SEASON_GUESS_P2_POINTS: int = 10
|
||||||
|
SEASON_GUESS_OVERTAKES_POINTS: int = 10
|
||||||
|
SEASON_GUESS_DNF_POINTS: int = 10
|
||||||
|
SEASON_GUESS_GAINED_POINTS: int = 10
|
||||||
|
SEASON_GUESS_LOST_POINTS: int = 10
|
||||||
|
SEASON_GUESS_TEAMWINNER_CORRECT_POINTS: int = 3
|
||||||
|
SEASON_GUESS_TEAMWINNER_FALSE_POINTS: int = -3
|
||||||
|
SEASON_GUESS_PODIUMS_CORRECT_POINTS: int = 3
|
||||||
|
SEASON_GUESS_PODIUMS_FALSE_POINTS: int = -2
|
||||||
|
|
||||||
|
STANDING_2023: Dict[str, int] = {
|
||||||
|
"Max Verstappen": 1,
|
||||||
|
"Sergio Perez": 2,
|
||||||
|
"Lewis Hamilton": 3,
|
||||||
|
"Fernando Alonso": 4,
|
||||||
|
"Charles Leclerc": 5,
|
||||||
|
"Lando Norris": 6,
|
||||||
|
"Carlos Sainz": 7,
|
||||||
|
"George Russel": 8,
|
||||||
|
"Oscar Piastri": 9,
|
||||||
|
"Lance Stroll": 10,
|
||||||
|
"Pierre Gasly": 11,
|
||||||
|
"Esteban Ocon": 12,
|
||||||
|
"Alexander Albon": 13,
|
||||||
|
"Yuki Tsunoda": 14,
|
||||||
|
"Valtteri Bottas": 15,
|
||||||
|
"Nico Hulkenberg": 16,
|
||||||
|
"Daniel Ricciardo": 17,
|
||||||
|
"Zhou Guanyu": 18,
|
||||||
|
"Kevin Magnussen": 19,
|
||||||
|
"Logan Sargeant": 21
|
||||||
|
}
|
||||||
|
|
||||||
|
def standing_points(race_guess: RaceGuess, race_result: RaceResult) -> int:
|
||||||
|
guessed_driver_position: int | None = race_result.driver_standing_position(driver=race_guess.pxx_guess)
|
||||||
|
if guessed_driver_position is None:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
position_offset: int = abs(guessed_driver_position - race_guess.race.place_to_guess)
|
||||||
|
if position_offset not in RACE_GUESS_OFFSET_POINTS:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return RACE_GUESS_OFFSET_POINTS[position_offset]
|
||||||
|
|
||||||
|
def dnf_points(race_guess: RaceGuess, race_result: RaceResult) -> int:
|
||||||
|
if race_guess.dnf_guess in race_result.initial_dnf:
|
||||||
|
return RACE_GUESS_DNF_POINTS
|
||||||
|
|
||||||
|
if race_guess.dnf_guess == NONE_DRIVER and len(race_result.initial_dnf) == 0:
|
||||||
|
return RACE_GUESS_DNF_POINTS
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
class PointsModel(Model):
|
class PointsModel(Model):
|
||||||
|
"""
|
||||||
|
This class bundles all data + functionality required to do points calculations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_points_per_step: Dict[str, List[int]] | None = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Model.__init__(self)
|
Model.__init__(self)
|
||||||
|
|
||||||
|
def points_per_step(self) -> Dict[str, List[int]]:
|
||||||
|
"""
|
||||||
|
Returns a dictionary of lists, containing points per race for each user.
|
||||||
|
"""
|
||||||
|
if self._points_per_step is None:
|
||||||
|
self._points_per_step = dict()
|
||||||
|
for user in self.all_users():
|
||||||
|
self._points_per_step[user.name] = [0] * 24 # Start at index 1, like the race numbers
|
||||||
|
|
||||||
|
for race_guess in self.all_race_guesses():
|
||||||
|
user_name: str = race_guess.user.name
|
||||||
|
race_number: int = race_guess.race.number
|
||||||
|
race_result: RaceResult | None = self.race_result_by(race_name=race_guess.race.name)
|
||||||
|
|
||||||
|
if race_result is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self._points_per_step[user_name][race_number] = standing_points(race_guess, race_result) + dnf_points(race_guess, race_result)
|
||||||
|
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
return self._points_per_step
|
||||||
|
|
||||||
|
def points_per_step_cumulative(self) -> Dict[str, List[int]]:
|
||||||
|
"""
|
||||||
|
Returns a dictionary of lists, containing cumulative points per race for each user.
|
||||||
|
"""
|
||||||
|
points_per_step_cumulative: Dict[str, List[int]] = dict()
|
||||||
|
for user_name, points in self.points_per_step().items():
|
||||||
|
points_per_step_cumulative[user_name] = np.cumsum(points).tolist()
|
||||||
|
|
||||||
|
return points_per_step_cumulative
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def points_by(self, *, user_name: str) -> List[int]:
|
||||||
|
"""
|
||||||
|
Returns a list of points per race for a specific user.
|
||||||
|
"""
|
||||||
|
return self.points_by(user_name=user_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def points_by(self, *, race_name: str) -> Dict[str, int]:
|
||||||
|
"""
|
||||||
|
Returns a dictionary of points per user for a specific race.
|
||||||
|
"""
|
||||||
|
return self.points_by(race_name=race_name)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def points_by(self, *, user_name: str, race_name: str) -> int:
|
||||||
|
"""
|
||||||
|
Returns the points for a specific race for a specific user.
|
||||||
|
"""
|
||||||
|
return self.points_by(user_name=user_name, race_name=race_name)
|
||||||
|
|
||||||
|
def points_by(self, *, user_name: str | None = None, race_name: str | None = None) -> List[int] | Dict[str, int] | int:
|
||||||
|
if user_name is not None and race_name is None:
|
||||||
|
return self.points_per_step()[user_name]
|
||||||
|
|
||||||
|
if user_name is None and race_name is not None:
|
||||||
|
race_number: int = self.race_by(race_name=race_name).number
|
||||||
|
points_by_race: Dict[str, int] = dict()
|
||||||
|
|
||||||
|
for _user_name, points in self.points_per_step().items():
|
||||||
|
points_by_race[_user_name] = points[race_number]
|
||||||
|
|
||||||
|
return points_by_race
|
||||||
|
|
||||||
|
if user_name is not None and race_name is not None:
|
||||||
|
race_number: int = self.race_by(race_name=race_name).number
|
||||||
|
|
||||||
|
return self.points_per_step()[user_name][race_number]
|
||||||
|
|
||||||
|
raise Exception("points_by received an illegal combination of arguments")
|
||||||
|
|
||||||
|
def total_points_by(self, user_name: str) -> int:
|
||||||
|
"""
|
||||||
|
Returns the total number of points for a specific user.
|
||||||
|
"""
|
||||||
|
return sum(self.points_by(user_name=user_name))
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
from typing import List, Callable, Dict, overload
|
from typing import List, Callable
|
||||||
|
|
||||||
from formula10.domain.domain_model import Model
|
from formula10.domain.domain_model import Model
|
||||||
from formula10.domain.model.driver import NONE_DRIVER, Driver
|
from formula10.domain.model.driver import Driver
|
||||||
from formula10.domain.model.race import Race
|
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.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.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.database.validation import find_first_else_none, find_multiple_strict, race_has_started
|
||||||
|
|
||||||
|
|
||||||
class TemplateModel(Model):
|
class TemplateModel(Model):
|
||||||
"""
|
"""
|
||||||
This class bundles all data required from inside a template.
|
This class bundles all data + functionality required from inside a template.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
active_user: User | None = None
|
active_user: User | None = None
|
||||||
@ -23,6 +20,8 @@ class TemplateModel(Model):
|
|||||||
_wdc_gained_excluded_abbrs: List[str] = ["RIC"]
|
_wdc_gained_excluded_abbrs: List[str] = ["RIC"]
|
||||||
|
|
||||||
def __init__(self, *, active_user_name: str | None, active_result_race_name: str | None):
|
def __init__(self, *, active_user_name: str | None, active_result_race_name: str | None):
|
||||||
|
Model.__init__(self)
|
||||||
|
|
||||||
if active_user_name is not None:
|
if active_user_name is not None:
|
||||||
self.active_user = self.user_by(user_name=active_user_name, ignore=["Everyone"])
|
self.active_user = self.user_by(user_name=active_user_name, ignore=["Everyone"])
|
||||||
|
|
||||||
@ -47,126 +46,6 @@ class TemplateModel(Model):
|
|||||||
|
|
||||||
return self.all_users()
|
return self.all_users()
|
||||||
|
|
||||||
@overload
|
|
||||||
def user_by(self, *, user_name: str) -> User:
|
|
||||||
"""
|
|
||||||
Tries to obtain the user object for a specific username.
|
|
||||||
"""
|
|
||||||
return self.user_by(user_name=user_name)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def user_by(self, *, user_name: str, ignore: List[str]) -> User | None:
|
|
||||||
"""
|
|
||||||
Tries to obtain the user object for a specific username, but ignores certain usernames.
|
|
||||||
"""
|
|
||||||
return self.user_by(user_name=user_name, ignore=ignore)
|
|
||||||
|
|
||||||
def user_by(self, *, user_name: str, ignore: List[str] | None = None) -> User | None:
|
|
||||||
if ignore is None:
|
|
||||||
ignore = []
|
|
||||||
|
|
||||||
if len(ignore) > 0 and user_name in ignore:
|
|
||||||
return None
|
|
||||||
|
|
||||||
predicate: Callable[[User], bool] = lambda user: user.name == user_name
|
|
||||||
return find_single_strict(predicate, self.all_users())
|
|
||||||
|
|
||||||
def race_result_by(self, *, race_name: str) -> RaceResult | None:
|
|
||||||
"""
|
|
||||||
Tries to obtain the race result corresponding to a race name.
|
|
||||||
"""
|
|
||||||
predicate: Callable[[RaceResult], bool] = lambda result: result.race.name == race_name
|
|
||||||
return find_single_or_none_strict(predicate, self.all_race_results())
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def race_guesses_by(self, *, user_name: str) -> List[RaceGuess]:
|
|
||||||
"""
|
|
||||||
Returns a list of all race guesses made by a specific user.
|
|
||||||
"""
|
|
||||||
return self.race_guesses_by(user_name=user_name)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def race_guesses_by(self, *, race_name: str) -> List[RaceGuess]:
|
|
||||||
"""
|
|
||||||
Returns a list of all race guesses made for a specific race.
|
|
||||||
"""
|
|
||||||
return self.race_guesses_by(race_name=race_name)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def race_guesses_by(self, *, user_name: str, race_name: str) -> RaceGuess | None:
|
|
||||||
"""
|
|
||||||
Returns a single race guess by a specific user for a specific race, or None, if this guess doesn't exist.
|
|
||||||
"""
|
|
||||||
return self.race_guesses_by(user_name=user_name, race_name=race_name)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def race_guesses_by(self) -> Dict[str, Dict[str, RaceGuess]]:
|
|
||||||
"""
|
|
||||||
Returns a dictionary that maps race-ids to user-id - guess dictionaries.
|
|
||||||
"""
|
|
||||||
return self.race_guesses_by()
|
|
||||||
|
|
||||||
def race_guesses_by(self, *, user_name: str | None = None, race_name: str | None = None) -> RaceGuess | List[RaceGuess] | Dict[str, Dict[str, RaceGuess]] | None:
|
|
||||||
# List of all guesses by a single user
|
|
||||||
if user_name is not None and race_name is None:
|
|
||||||
predicate: Callable[[RaceGuess], bool] = lambda guess: guess.user.name == user_name
|
|
||||||
return find_multiple_strict(predicate, self.all_race_guesses())
|
|
||||||
|
|
||||||
# List of all guesses for a single race
|
|
||||||
if user_name is None and race_name is not None:
|
|
||||||
predicate: Callable[[RaceGuess], bool] = lambda guess: guess.race.name == race_name
|
|
||||||
return find_multiple_strict(predicate, self.all_race_guesses())
|
|
||||||
|
|
||||||
# Guess for a single race by a single user
|
|
||||||
if user_name is not None and race_name is not None:
|
|
||||||
predicate: Callable[[RaceGuess], bool] = lambda guess: guess.user.name == user_name and guess.race.name == race_name
|
|
||||||
return find_single_or_none_strict(predicate, self.all_race_guesses())
|
|
||||||
|
|
||||||
# Dict with all guesses
|
|
||||||
if user_name is None and race_name is None:
|
|
||||||
guesses_by: Dict[str, Dict[str, RaceGuess]] = dict()
|
|
||||||
guess: RaceGuess
|
|
||||||
|
|
||||||
for guess in self.all_race_guesses():
|
|
||||||
if guess.race.name not in guesses_by:
|
|
||||||
guesses_by[guess.race.name] = dict()
|
|
||||||
|
|
||||||
guesses_by[guess.race.name][guess.user.name] = guess
|
|
||||||
|
|
||||||
return guesses_by
|
|
||||||
|
|
||||||
raise Exception("race_guesses_by encountered illegal combination of arguments")
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def season_guesses_by(self, *, user_name: str) -> SeasonGuess:
|
|
||||||
"""
|
|
||||||
Returns the season guess made by a specific user.
|
|
||||||
"""
|
|
||||||
return self.season_guesses_by(user_name=user_name)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def season_guesses_by(self) -> Dict[str, SeasonGuess]:
|
|
||||||
"""
|
|
||||||
Returns a dictionary of season guesses mapped to usernames.
|
|
||||||
"""
|
|
||||||
return self.season_guesses_by()
|
|
||||||
|
|
||||||
def season_guesses_by(self, *, user_name: str | None = None) -> SeasonGuess | Dict[str, SeasonGuess] | None:
|
|
||||||
if user_name is not None:
|
|
||||||
predicate: Callable[[SeasonGuess], bool] = lambda guess: guess.user.name == user_name
|
|
||||||
return find_single_or_none_strict(predicate, self.all_season_guesses())
|
|
||||||
|
|
||||||
if user_name is None:
|
|
||||||
guesses_by: Dict[str, SeasonGuess] = dict()
|
|
||||||
guess: SeasonGuess
|
|
||||||
|
|
||||||
for guess in self.all_season_guesses():
|
|
||||||
guesses_by[guess.user.name] = guess
|
|
||||||
|
|
||||||
return guesses_by
|
|
||||||
|
|
||||||
raise Exception("season_guesses_by encountered illegal combination of arguments")
|
|
||||||
|
|
||||||
def first_race_without_result(self) -> Race | None:
|
def first_race_without_result(self) -> Race | None:
|
||||||
"""
|
"""
|
||||||
Returns the first race-object with no associated race result.
|
Returns the first race-object with no associated race result.
|
||||||
@ -200,48 +79,9 @@ class TemplateModel(Model):
|
|||||||
else:
|
else:
|
||||||
return self.all_races()[0].name_sanitized
|
return self.all_races()[0].name_sanitized
|
||||||
|
|
||||||
def none_team(self) -> Team:
|
|
||||||
return NONE_TEAM
|
|
||||||
|
|
||||||
def all_drivers_or_active_result_standing_drivers(self) -> List[Driver]:
|
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)
|
return self.active_result.ordered_standing_list() if self.active_result is not None else self.all_drivers(include_none=False)
|
||||||
|
|
||||||
def drivers_for_wdc_gained(self) -> List[Driver]:
|
def drivers_for_wdc_gained(self) -> List[Driver]:
|
||||||
predicate: Callable[[Driver], bool] = lambda driver: driver.abbr not in self._wdc_gained_excluded_abbrs
|
predicate: Callable[[Driver], bool] = lambda driver: driver.abbr not in self._wdc_gained_excluded_abbrs
|
||||||
return find_multiple_strict(predicate, self.all_drivers(include_none=False))
|
return find_multiple_strict(predicate, self.all_drivers(include_none=False))
|
||||||
|
|
||||||
def none_driver(self) -> Driver:
|
|
||||||
return NONE_DRIVER
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def drivers_by(self, *, team_name: str) -> List[Driver]:
|
|
||||||
"""
|
|
||||||
Returns a list of all drivers driving for a certain team.
|
|
||||||
"""
|
|
||||||
return self.drivers_by(team_name=team_name)
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def drivers_by(self) -> Dict[str, List[Driver]]:
|
|
||||||
"""
|
|
||||||
Returns a dictionary of drivers mapped to team names.
|
|
||||||
"""
|
|
||||||
return self.drivers_by()
|
|
||||||
|
|
||||||
def drivers_by(self, *, team_name: str | None = None) -> List[Driver] | Dict[str, List[Driver]]:
|
|
||||||
if team_name is not None:
|
|
||||||
predicate: Callable[[Driver], bool] = lambda driver: driver.team.name == team_name
|
|
||||||
return find_multiple_strict(predicate, self.all_drivers(include_none=False), 2)
|
|
||||||
|
|
||||||
if team_name is None:
|
|
||||||
drivers_by: Dict[str, List[Driver]] = dict()
|
|
||||||
driver: Driver
|
|
||||||
team: Team
|
|
||||||
|
|
||||||
for team in self.all_teams(include_none=False):
|
|
||||||
drivers_by[team.name] = []
|
|
||||||
for driver in self.all_drivers(include_none=False):
|
|
||||||
drivers_by[driver.team.name] += [driver]
|
|
||||||
|
|
||||||
return drivers_by
|
|
||||||
|
|
||||||
raise Exception("drivers_by encountered illegal combination of arguments")
|
|
||||||
|
Reference in New Issue
Block a user