Compare commits

...

5 Commits

Author SHA1 Message Date
b485791d25 Bug: Update method signature in single-user raceguess page
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 15s
2024-12-08 20:01:30 +01:00
e8c8e35e05 Disable info cards regarding season picks
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 11s
2024-12-08 19:57:24 +01:00
15305b2f3e Bug: Fix statistics issues caused by driver substitutions
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 16s
2024-12-08 19:52:02 +01:00
0dc4b22c72 Commented out leaderboard diagram extension for season points
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 16s
2024-12-08 19:38:31 +01:00
cef40a9e8b Implement season guess evaluation
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 15s
2024-12-08 19:25:06 +01:00
4 changed files with 195 additions and 93 deletions

View File

@ -1,5 +1,5 @@
import json import json
from typing import Any, Callable, Dict, List, overload from typing import Any, Callable, Dict, List, overload, Tuple
import numpy as np import numpy as np
from formula10 import cache from formula10 import cache
@ -11,6 +11,7 @@ from formula10.domain.model.season_guess import SeasonGuess
from formula10.domain.model.season_guess_result import SeasonGuessResult from formula10.domain.model.season_guess_result import SeasonGuessResult
from formula10.domain.model.team import Team from formula10.domain.model.team import Team
from formula10.domain.model.user import User from formula10.domain.model.user import User
from formula10.database.validation import find_single_or_none_strict
# Guess points # Guess points
@ -68,6 +69,7 @@ WDC_STANDING_2023: Dict[str, int] = {
"Kevin Magnussen": 19, "Kevin Magnussen": 19,
"Logan Sargeant": 21, "Logan Sargeant": 21,
} }
WCC_STANDING_2023: Dict[str, int] = { WCC_STANDING_2023: Dict[str, int] = {
"Red Bull": 1, "Red Bull": 1,
"Mercedes": 2, "Mercedes": 2,
@ -81,6 +83,39 @@ WCC_STANDING_2023: Dict[str, int] = {
"Haas": 10, "Haas": 10,
} }
# In case a substitute driver is driving, those points have to be subtracted from the actual driver
# (Driver_ID, Race_ID, Points)
WDC_SUBSTITUTE_POINTS: List[Tuple[int, int, int]] = [
(15, 2, 6), # Bearman raced for Sainz in Saudi Arabia
(8, 17, 1), # Bearman raced for Magnussen in Azerbaijan
]
WDC_STANDING_2024: Dict[str, int] = {
"Max Verstappen": 1,
"Lando Norris": 2,
"Charles Leclerc": 3,
"Oscar Piastri": 4,
"Carlos Sainz": 5,
"George Russell": 6,
"Lewis Hamilton": 7,
"Sergio Perez": 8,
"Fernando Alonso": 9,
"Pierre Gasly": 10,
"Nico Hulkenberg": 11,
"Yuki Tsunoda": 12,
"Lance Stroll": 13,
"Esteban Ocon": 14,
"Kevin Magnussen": 15,
"Alexander Albon": 16,
"Daniel Ricciardo": 17,
"Oliver Bearman": 18,
"Franco Colapinto": 19,
"Zhou Guanyu": 20,
"Liam Lawson": 21,
"Valtteri Bottas": 22,
"Logan Sargeant": 23,
"Jack Doohan": 24
}
def standing_points(race_guess: RaceGuess, race_result: RaceResult) -> int: def standing_points(race_guess: RaceGuess, race_result: RaceResult) -> int:
guessed_driver_position: int | None = race_result.driver_standing_position( guessed_driver_position: int | None = race_result.driver_standing_position(
@ -106,6 +141,16 @@ def dnf_points(race_guess: RaceGuess, race_result: RaceResult) -> int:
return 0 return 0
def substitute_points(driver: Driver, race_number: int) -> int:
predicate: Callable[[Tuple[int, int, int]], bool] = lambda substitution: driver.id == substitution[0] and race_number == substitution[1]
substitution: Tuple[int, int, int] = find_single_or_none_strict(predicate, WDC_SUBSTITUTE_POINTS)
if substitution is not None:
return substitution[2]
else:
return 0
class PointsModel(Model): class PointsModel(Model):
""" """
This class bundles all data + functionality required to do points calculations. This class bundles all data + functionality required to do points calculations.
@ -124,7 +169,7 @@ class PointsModel(Model):
points_per_step = dict() points_per_step = dict()
for user in self.all_users(): for user in self.all_users():
points_per_step[user.name] = [0] * ( points_per_step[user.name] = [0] * (
len(self.all_races()) + 1 len(self.all_races()) + 1
) # Start at index 1, like the race numbers ) # Start at index 1, like the race numbers
for race_guess in self.all_race_guesses(): for race_guess in self.all_race_guesses():
@ -152,10 +197,10 @@ class PointsModel(Model):
""" """
driver_points_per_step = dict() driver_points_per_step = dict()
for driver in self.all_drivers( for driver in self.all_drivers(
include_none=False, include_inactive=include_inactive include_none=False, include_inactive=include_inactive
): ):
driver_points_per_step[driver.name] = [0] * ( driver_points_per_step[driver.name] = [0] * (
len(self.all_races()) + 1 len(self.all_races()) + 1
) # Start at index 1, like the race numbers ) # Start at index 1, like the race numbers
for race_result in self.all_race_results(): for race_result in self.all_race_results():
@ -170,8 +215,10 @@ class PointsModel(Model):
driver_points_per_step[driver.name][race_number] += ( driver_points_per_step[driver.name][race_number] += (
DRIVER_FASTEST_LAP_POINTS DRIVER_FASTEST_LAP_POINTS
if race_result.fastest_lap_driver == driver if race_result.fastest_lap_driver == driver
and int(position) <= 10
else 0 else 0
) )
driver_points_per_step[driver.name][race_number] -= substitute_points(driver, race_number)
for position, driver in race_result.sprint_standing.items(): for position, driver in race_result.sprint_standing.items():
driver_name: str = driver.name driver_name: str = driver.name
@ -192,7 +239,7 @@ class PointsModel(Model):
team_points_per_step = dict() team_points_per_step = dict()
for team in self.all_teams(include_none=False): for team in self.all_teams(include_none=False):
team_points_per_step[team.name] = [0] * ( team_points_per_step[team.name] = [0] * (
len(self.all_races()) + 1 len(self.all_races()) + 1
) # Start at index 1, like the race numbers ) # Start at index 1, like the race numbers
for race_result in self.all_race_results(): for race_result in self.all_race_results():
@ -237,7 +284,7 @@ class PointsModel(Model):
""" """
points_per_step_cumulative: Dict[str, List[int]] = dict() points_per_step_cumulative: Dict[str, List[int]] = dict()
for driver_name, points in self.driver_points_per_step( for driver_name, points in self.driver_points_per_step(
include_inactive=True include_inactive=True
).items(): ).items():
points_per_step_cumulative[driver_name] = np.cumsum(points).tolist() points_per_step_cumulative[driver_name] = np.cumsum(points).tolist()
@ -245,7 +292,7 @@ class PointsModel(Model):
@overload @overload
def driver_points_by( def driver_points_by(
self, *, driver_name: str, include_inactive: bool self, *, driver_name: str, include_inactive: bool
) -> List[int]: ) -> List[int]:
""" """
Returns a list of points per race for a specific driver. Returns a list of points per race for a specific driver.
@ -256,7 +303,7 @@ class PointsModel(Model):
@overload @overload
def driver_points_by( def driver_points_by(
self, *, race_name: str, include_inactive: bool self, *, race_name: str, include_inactive: bool
) -> Dict[str, int]: ) -> Dict[str, int]:
""" """
Returns a dictionary of points per driver for a specific race. Returns a dictionary of points per driver for a specific race.
@ -267,7 +314,7 @@ class PointsModel(Model):
@overload @overload
def driver_points_by( def driver_points_by(
self, *, driver_name: str, race_name: str, include_inactive: bool self, *, driver_name: str, race_name: str, include_inactive: bool
) -> int: ) -> int:
""" """
Returns the points for a specific race for a specific driver. Returns the points for a specific race for a specific driver.
@ -282,11 +329,11 @@ class PointsModel(Model):
timeout=None, args_to_ignore=["self"] timeout=None, args_to_ignore=["self"]
) # Cleanup when adding/updating race results ) # Cleanup when adding/updating race results
def driver_points_by( def driver_points_by(
self, self,
*, *,
driver_name: str | None = None, driver_name: str | None = None,
race_name: str | None = None, race_name: str | None = None,
include_inactive: bool include_inactive: bool
) -> List[int] | Dict[str, int] | int: ) -> List[int] | Dict[str, int] | int:
if driver_name is not None and race_name is None: if driver_name is not None and race_name is None:
return self.driver_points_per_step(include_inactive=include_inactive)[ return self.driver_points_per_step(include_inactive=include_inactive)[
@ -298,7 +345,7 @@ class PointsModel(Model):
points_by_race: Dict[str, int] = dict() points_by_race: Dict[str, int] = dict()
for _driver_name, points in self.driver_points_per_step( for _driver_name, points in self.driver_points_per_step(
include_inactive=include_inactive include_inactive=include_inactive
).items(): ).items():
points_by_race[_driver_name] = points[race_number] points_by_race[_driver_name] = points[race_number]
@ -340,23 +387,31 @@ class PointsModel(Model):
def wdc_standing_by_position(self) -> Dict[int, List[str]]: def wdc_standing_by_position(self) -> Dict[int, List[str]]:
standing: Dict[int, List[str]] = dict() standing: Dict[int, List[str]] = dict()
for position in range( if WDC_STANDING_2024 is None:
1, len(self.all_drivers(include_none=False, include_inactive=True)) + 1 for position in range(
): 1, len(self.all_drivers(include_none=False, include_inactive=True)) + 1
standing[position] = list() ):
standing[position] = list()
position: int = 1 position: int = 1
last_points: int = 0 last_points: int = 0
for driver in self.drivers_sorted_by_points(include_inactive=True): for driver in self.drivers_sorted_by_points(include_inactive=True):
points: int = self.total_driver_points_by(driver.name) points: int = self.total_driver_points_by(driver.name)
if points < last_points: if points < last_points:
# If multiple drivers have equal points, a place is shared. # If multiple drivers have equal points, a place is shared.
# In this case, the next driver does not occupy the immediate next position. # In this case, the next driver does not occupy the immediate next position.
position += len(standing[position]) position += len(standing[position])
standing[position].append(driver.name) standing[position].append(driver.name)
last_points = points last_points = points
if WDC_STANDING_2024 is not None:
for position in range(1, len(WDC_STANDING_2024) + 1):
standing[position] = list()
for driver, position in WDC_STANDING_2024.items():
standing[position] += [driver]
return standing return standing
@ -366,32 +421,36 @@ class PointsModel(Model):
def wdc_standing_by_driver(self) -> Dict[str, int]: def wdc_standing_by_driver(self) -> Dict[str, int]:
standing: Dict[str, int] = dict() standing: Dict[str, int] = dict()
position: int = 1 if WDC_STANDING_2024 is None:
last_points: int = 0 position: int = 1
last_points: int = 0
for driver in self.drivers_sorted_by_points(include_inactive=True): for driver in self.drivers_sorted_by_points(include_inactive=True):
points: int = self.total_driver_points_by(driver.name) points: int = self.total_driver_points_by(driver.name)
if points < last_points: if points < last_points:
drivers_with_this_position = 0 drivers_with_this_position = 0
for _driver, _position in standing.items(): for _driver, _position in standing.items():
if _position == position: if _position == position:
drivers_with_this_position += 1 drivers_with_this_position += 1
# If multiple drivers have equal points, a place is shared. # If multiple drivers have equal points, a place is shared.
# In this case, the next driver does not occupy the immediate next position. # In this case, the next driver does not occupy the immediate next position.
position += drivers_with_this_position position += drivers_with_this_position
standing[driver.name] = position standing[driver.name] = position
last_points = points last_points = points
return standing return standing
if WDC_STANDING_2024 is not None:
return WDC_STANDING_2024
def wdc_diff_2023_by(self, driver_name: str) -> int: def wdc_diff_2023_by(self, driver_name: str) -> int:
if not driver_name in WDC_STANDING_2023: if not driver_name in WDC_STANDING_2023:
return 0 return 0
return ( return (
WDC_STANDING_2023[driver_name] - self.wdc_standing_by_driver()[driver_name] WDC_STANDING_2023[driver_name] - self.wdc_standing_by_driver()[driver_name]
) )
@cache.cached( @cache.cached(
@ -583,7 +642,7 @@ class PointsModel(Model):
timeout=None, args_to_ignore=["self"] timeout=None, args_to_ignore=["self"]
) # Cleanup when adding/updating race results or users ) # Cleanup when adding/updating race results or users
def points_by( def points_by(
self, *, user_name: str | None = None, race_name: str | None = None self, *, user_name: str | None = None, race_name: str | None = None
) -> List[int] | Dict[str, int] | int: ) -> List[int] | Dict[str, int] | int:
if user_name is not None and race_name is None: if user_name is not None and race_name is None:
return self.points_per_step()[user_name] return self.points_per_step()[user_name]
@ -604,30 +663,64 @@ class PointsModel(Model):
raise Exception("points_by received an illegal combination of arguments") raise Exception("points_by received an illegal combination of arguments")
def total_points_by(self, user_name: str) -> int: def season_points_by(self, *, user_name: str) -> int:
"""
Returns the number of points from seasonguesses for a specific user.
"""
big_picks = (int(self.hot_take_correct(user_name=user_name)) * 10
+ int(self.p2_constructor_correct(user_name=user_name)) * 10
+ int(self.overtakes_correct(user_name=user_name)) * 10
+ int(self.dnfs_correct(user_name=user_name)) * 10
+ int(self.most_gained_correct(user_name=user_name)) * 10
+ int(self.most_lost_correct(user_name=user_name)) * 10)
small_picks = 0
guess: SeasonGuess = self.season_guesses_by(user_name=user_name)
for driver in guess.team_winners:
if self.is_team_winner(driver):
small_picks += 3
else:
small_picks -= 3
# NOTE: Not picked drivers that had a podium are also wrong
for driver in self.all_drivers(include_none=False, include_inactive=True):
if driver in guess.podiums and self.has_podium(driver):
small_picks += 3
elif driver in guess.podiums and not self.has_podium(driver):
small_picks -=2
elif driver not in guess.podiums and self.has_podium(driver):
small_picks -=2
return big_picks + small_picks
def total_points_by(self, *, user_name: str, include_season: bool) -> int:
""" """
Returns the total number of points for a specific user. Returns the total number of points for a specific user.
""" """
return sum(self.points_by(user_name=user_name)) if include_season:
return sum(self.points_by(user_name=user_name)) + self.season_points_by(user_name=user_name)
else:
return sum(self.points_by(user_name=user_name))
def users_sorted_by_points(self) -> List[User]: def users_sorted_by_points(self, *, include_season: bool) -> List[User]:
""" """
Returns the list of users, sorted by their points from race guesses (in descending order). Returns the list of users, sorted by their points from race guesses (in descending order).
""" """
comparator: Callable[[User], int] = lambda user: self.total_points_by(user.name) comparator: Callable[[User], int] = lambda user: self.total_points_by(user_name=user.name, include_season=include_season)
return sorted(self.all_users(), key=comparator, reverse=True) return sorted(self.all_users(), key=comparator, reverse=True)
@cache.cached( @cache.cached(
timeout=None, key_prefix="points_user_standing" timeout=None, key_prefix="points_user_standing"
) # Cleanup when adding/updating race results or users ) # Cleanup when adding/updating race results or users
def user_standing(self) -> Dict[str, int]: def user_standing(self, *, include_season: bool) -> Dict[str, int]:
standing: Dict[str, int] = dict() standing: Dict[str, int] = dict()
position: int = 1 position: int = 1
last_points: int = 0 last_points: int = 0
for user in self.users_sorted_by_points(): for user in self.users_sorted_by_points(include_season=include_season):
if self.total_points_by(user.name) < last_points: if self.total_points_by(user_name=user.name, include_season=include_season) < last_points:
users_with_this_position = 0 users_with_this_position = 0
for _user, _position in standing.items(): for _user, _position in standing.items():
if _position == position: if _position == position:
@ -639,7 +732,7 @@ class PointsModel(Model):
standing[user.name] = position standing[user.name] = position
last_points = self.total_points_by(user.name) last_points = self.total_points_by(user_name=user.name, include_season=include_season)
return standing return standing
@ -671,7 +764,7 @@ class PointsModel(Model):
if self.picks_count(user_name) == 0: if self.picks_count(user_name) == 0:
return 0.0 return 0.0
return self.total_points_by(user_name) / self.picks_count(user_name) return self.total_points_by(user_name=user_name, include_season=False) / self.picks_count(user_name)
# #
# Season guess evaluation # Season guess evaluation
@ -738,12 +831,11 @@ class PointsModel(Model):
teammates: List[Driver] = self.drivers_by( teammates: List[Driver] = self.drivers_by(
team_name=driver.team.name, include_inactive=True team_name=driver.team.name, include_inactive=True
) )
teammate: Driver = teammates[0] if teammates[1] == driver else teammates[1]
return ( # Min - Highest position is the lowest place number
self.wdc_standing_by_driver()[driver.name] winner: Driver = min(teammates, key=lambda driver: self.wdc_standing_by_driver()[driver.name])
<= self.wdc_standing_by_driver()[teammate.name]
) return driver == winner
@cache.memoize( @cache.memoize(
timeout=None, args_to_ignore=["self"] timeout=None, args_to_ignore=["self"]
@ -765,11 +857,11 @@ class PointsModel(Model):
data["labels"] = [0] + [ data["labels"] = [0] + [
race.name for race in sorted(self.all_races(), key=lambda race: race.number) race.name for race in sorted(self.all_races(), key=lambda race: race.number)
] ] # + ["Season"]
data["datasets"] = [ data["datasets"] = [
{ {
"data": self.points_per_step_cumulative()[user.name], "data": self.points_per_step_cumulative()[user.name], # + [self.total_points_by(user_name=user.name, include_season=True)],
"label": user.name, "label": user.name,
"fill": False, "fill": False,
} }

View File

@ -6,15 +6,15 @@
{% block body %} {% block body %}
<div class="card shadow-sm mb-2"> {# <div class="card shadow-sm mb-2">#}
<div class="card-header"> {# <div class="card-header">#}
Note {# Note#}
</div> {# </div>#}
{##}
<div class="card-body"> {# <div class="card-body">#}
Points only include race picks. {# Points only include race picks.#}
</div> {# </div>#}
</div> {# </div>#}
<div class="card shadow-sm mb-2"> <div class="card shadow-sm mb-2">
<div class="card-header"> <div class="card-header">
@ -29,6 +29,8 @@
<th scope="col" class="text-center" style="min-width: 50px;">Place</th> <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: 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;">Points</th>
<th scope="col" class="text-center" style="min-width: 100px;">Points (Race)</th>
<th scope="col" class="text-center" style="min-width: 100px;">Points (Season)</th>
<th scope="col" class="text-center" style="min-width: 100px;">Total picks</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" <th scope="col" class="text-center" style="min-width: 100px;" data-bs-toggle="tooltip"
title="Any points count as correct">Correct picks title="Any points count as correct">Correct picks
@ -38,12 +40,14 @@
</thead> </thead>
<tbody> <tbody>
{% for user in points.users_sorted_by_points() %} {% for user in points.users_sorted_by_points(include_season=True) %}
{% set user_standing = points.user_standing()[user.name] %} {% set user_standing = points.user_standing(include_season=True)[user.name] %}
<tr class="{% if user_standing == 1 %}table-danger{% endif %}"> <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_standing }}</td>
<td class="text-center text-nowrap">{{ user.name }}</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.total_points_by(user_name=user.name, include_season=True) }}</td>
<td class="text-center text-nowrap">{{ points.total_points_by(user_name=user.name, include_season=False) }}</td>
<td class="text-center text-nowrap">{{ points.season_points_by(user_name=user.name) }}</td>
<td class="text-center text-nowrap">{{ points.picks_count(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">{{ points.picks_with_points_count(user.name) }}</td>
<td class="text-center text-nowrap">{{ "%0.2f" % points.points_per_pick(user.name) }}</td> <td class="text-center text-nowrap">{{ "%0.2f" % points.points_per_pick(user.name) }}</td>
@ -57,7 +61,7 @@
<div class="card shadow-sm mb-2"> <div class="card shadow-sm mb-2">
<div class="card-header"> <div class="card-header">
History History (Race)
</div> </div>
<div class="card-body"> <div class="card-body">

View File

@ -39,13 +39,13 @@
{# Link should only be visible if all users are visible #} {# Link should only be visible if all users are visible #}
{% if model.active_user is not none %} {% if model.active_user is not none %}
<td class="text-center text-nowrap" style="min-width: 100px;">{{ model.active_user.name }} <td class="text-center text-nowrap" style="min-width: 100px;">{{ model.active_user.name }}
({{ points.total_points_by(model.active_user.name) }}) ({{ points.total_points_by(user_name=model.active_user.name, include_season=False) }})
</td> </td>
{% else %} {% else %}
{% for user in model.all_users() %} {% for user in model.all_users() %}
<td class="text-center text-nowrap" style="min-width: 100px;"> <td class="text-center text-nowrap" style="min-width: 100px;">
<a href="/race/{{ user.name_sanitized }}" class="link-dark">{{ user.name }} <a href="/race/{{ user.name_sanitized }}" class="link-dark">{{ user.name }}
({{ points.total_points_by(user.name) }})</a> ({{ points.total_points_by(user_name=user.name, include_season=False) }})</a>
</td> </td>
{% endfor %} {% endfor %}
{% endif %} {% endif %}

View File

@ -10,16 +10,16 @@
{% block body %} {% block body %}
<div class="card shadow-sm mb-2"> {# <div class="card shadow-sm mb-2">#}
<div class="card-header"> {# <div class="card-header">#}
Note {# Note#}
</div> {# </div>#}
{##}
<div class="card-body"> {# <div class="card-body">#}
Picks that match the current standings are marked in green, except for the hot-take and overtake picks, as {# Picks that match the current standings are marked in green, except for the hot-take and overtake picks, as#}
those are not evaluated automatically.<br> {# those are not evaluated automatically.<br>#}
</div> {# </div>#}
</div> {# </div>#}
<div class="grid card-grid"> <div class="grid card-grid">
@ -95,8 +95,10 @@
winners:</h6> winners:</h6>
<div class="grid mt-2 container" style="row-gap: 0;"> <div class="grid mt-2 container" style="row-gap: 0;">
{% for team in model.all_teams(include_none=false) %} {% for team in model.all_teams(include_none=false) %}
{% set driver_a = model.drivers_by(team_name=team.name, include_inactive=False)[0] %} {# HACK: Choosing 0 and 1 will chose the drivers with the lowest IDs (although there could be others). #}
{% set driver_b = model.drivers_by(team_name=team.name, include_inactive=False)[1] %} {# This means the drivers chosen from at the start of the season will be visible. #}
{% set driver_a = model.drivers_by(team_name=team.name, include_inactive=True)[0] %}
{% set driver_b = model.drivers_by(team_name=team.name, include_inactive=True)[1] %}
<div class="g-col-6"> <div class="g-col-6">
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
@ -133,8 +135,10 @@
title="Which driver will reach at least a single podium?">Drivers with podium(s):</h6> title="Which driver will reach at least a single podium?">Drivers with podium(s):</h6>
<div class="grid mt-2 container" style="row-gap: 0;"> <div class="grid mt-2 container" style="row-gap: 0;">
{% for team in model.all_teams(include_none=false) %} {% for team in model.all_teams(include_none=false) %}
{% set driver_a = model.drivers_by(team_name=team.name, include_inactive=False)[0] %} {# HACK: Choosing 0 and 1 will chose the drivers with the lowest IDs (although there could be others). #}
{% set driver_b = model.drivers_by(team_name=team.name, include_inactive=False)[1] %} {# This means the drivers chosen from at the start of the season will be visible. #}
{% set driver_a = model.drivers_by(team_name=team.name, include_inactive=True)[0] %}
{% set driver_b = model.drivers_by(team_name=team.name, include_inactive=True)[1] %}
<div class="g-col-6"> <div class="g-col-6">
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
@ -145,7 +149,8 @@
{% if (user_guess is not none) and (driver_a in user_guess.podiums) %}checked="checked"{% endif %} {% if (user_guess is not none) and (driver_a in user_guess.podiums) %}checked="checked"{% endif %}
{% if season_guess_open == false %}disabled="disabled"{% endif %}> {% if season_guess_open == false %}disabled="disabled"{% endif %}>
<label class="form-check-label {% if (user_guess is not none) and (driver_a in user_guess.podiums) and points.has_podium(driver_a) %}text-success <label class="form-check-label {% if (user_guess is not none) and (driver_a in user_guess.podiums) and points.has_podium(driver_a) %}text-success
{% elif (user_guess is not none) and (driver_a in user_guess.podiums) and (not points.has_podium(driver_a)) %}text-danger{% endif %}" {% elif (user_guess is not none) and (driver_a in user_guess.podiums) and (not points.has_podium(driver_a)) %}text-danger
{% elif (user_guess is not none) and (driver_a not in user_guess.podiums) and (points.has_podium(driver_a)) %}text-danger{% endif %}"
for="podium-{{ driver_a.id }}-{{ user.id }}">{{ driver_a.name }}</label> for="podium-{{ driver_a.id }}-{{ user.id }}">{{ driver_a.name }}</label>
</div> </div>
</div> </div>
@ -159,7 +164,8 @@
{% if (user_guess is not none) and (driver_b in user_guess.podiums) %}checked="checked"{% endif %} {% if (user_guess is not none) and (driver_b in user_guess.podiums) %}checked="checked"{% endif %}
{% if season_guess_open == false %}disabled="disabled"{% endif %}> {% if season_guess_open == false %}disabled="disabled"{% endif %}>
<label class="form-check-label {% if (user_guess is not none) and (driver_b in user_guess.podiums) and points.has_podium(driver_b) %}text-success <label class="form-check-label {% if (user_guess is not none) and (driver_b in user_guess.podiums) and points.has_podium(driver_b) %}text-success
{% elif (user_guess is not none) and (driver_b in user_guess.podiums) and (not points.has_podium(driver_b)) %}text-danger{% endif %}" {% elif (user_guess is not none) and (driver_b in user_guess.podiums) and (not points.has_podium(driver_b)) %}text-danger
{% elif (user_guess is not none) and (driver_b not in user_guess.podiums) and (points.has_podium(driver_b)) %}text-danger{% endif %}"
for="podium-{{ driver_b.id }}-{{ user.id }}">{{ driver_b.name }}</label> for="podium-{{ driver_b.id }}-{{ user.id }}">{{ driver_b.name }}</label>
</div> </div>
</div> </div>