Display current season guess state
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:
@ -31,7 +31,10 @@ import formula10.controller.error_controller
|
||||
|
||||
|
||||
# TODO
|
||||
# Statistics
|
||||
# Leaderboard
|
||||
|
||||
# - For season guess calc there is missing: Fastest laps + sprint points + sprint DNFs (in race result)
|
||||
|
||||
# - Display total points somewhere in race table? Below the name in the table header.
|
||||
# - Auto calculate season points
|
||||
# - Highlight currently correct values for some season guesses (e.g. current most dnfs, team winners, podiums)
|
||||
@ -39,6 +42,9 @@ import formula10.controller.error_controller
|
||||
# - Interesting stats:
|
||||
# - Which driver was voted most for dnf (top 5)?
|
||||
|
||||
# Statistics
|
||||
# - Display stats: Driver standing, Team standing, DNFs, Fastest laps
|
||||
|
||||
# General
|
||||
# - Decouple names from IDs + Fix Valtteri/Russel spelling errors
|
||||
# - Unit testing (as much as possible, but especially points calculation)
|
||||
|
@ -6,6 +6,7 @@ from werkzeug import Response
|
||||
from formula10.database.model.db_team import DbTeam
|
||||
from formula10.database.update_queries import update_season_guess
|
||||
from formula10.domain.model.team import NONE_TEAM
|
||||
from formula10.domain.points_model import PointsModel
|
||||
from formula10.domain.template_model import TemplateModel
|
||||
from formula10 import app, db
|
||||
|
||||
@ -20,8 +21,9 @@ def season_active_user(user_name: str) -> str:
|
||||
user_name = unquote(user_name)
|
||||
model = TemplateModel(active_user_name=user_name,
|
||||
active_result_race_name=None)
|
||||
points = PointsModel()
|
||||
|
||||
return render_template("season.jinja", model=model)
|
||||
return render_template("season.jinja", model=model, points=points)
|
||||
|
||||
|
||||
@app.route("/season-guess/<user_name>", methods=["POST"])
|
||||
|
@ -1,10 +1,12 @@
|
||||
from typing import Callable, Dict, List, overload
|
||||
from typing import Callable, Dict, List, Tuple, overload
|
||||
import numpy as np
|
||||
|
||||
from formula10.domain.domain_model import Model
|
||||
from formula10.domain.model.driver import NONE_DRIVER
|
||||
from formula10.domain.model.driver import NONE_DRIVER, Driver
|
||||
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.user import User
|
||||
|
||||
RACE_GUESS_OFFSET_POINTS: Dict[int, int] = {
|
||||
@ -26,6 +28,19 @@ SEASON_GUESS_TEAMWINNER_FALSE_POINTS: int = -3
|
||||
SEASON_GUESS_PODIUMS_CORRECT_POINTS: int = 3
|
||||
SEASON_GUESS_PODIUMS_FALSE_POINTS: int = -2
|
||||
|
||||
DRIVER_RACE_POINTS: Dict[int, int] = {
|
||||
1: 25,
|
||||
2: 18,
|
||||
3: 15,
|
||||
4: 12,
|
||||
5: 10,
|
||||
6: 8,
|
||||
7: 6,
|
||||
8: 4,
|
||||
9: 2,
|
||||
10: 1
|
||||
}
|
||||
|
||||
STANDING_2023: Dict[str, int] = {
|
||||
"Max Verstappen": 1,
|
||||
"Sergio Perez": 2,
|
||||
@ -34,14 +49,14 @@ STANDING_2023: Dict[str, int] = {
|
||||
"Charles Leclerc": 5,
|
||||
"Lando Norris": 6,
|
||||
"Carlos Sainz": 7,
|
||||
"George Russel": 8,
|
||||
"George Russel": 8, # @todo typo
|
||||
"Oscar Piastri": 9,
|
||||
"Lance Stroll": 10,
|
||||
"Pierre Gasly": 11,
|
||||
"Esteban Ocon": 12,
|
||||
"Alexander Albon": 13,
|
||||
"Yuki Tsunoda": 14,
|
||||
"Valtteri Bottas": 15,
|
||||
"Valteri Bottas": 15, # @todo typo
|
||||
"Nico Hulkenberg": 16,
|
||||
"Daniel Ricciardo": 17,
|
||||
"Zhou Guanyu": 18,
|
||||
@ -75,6 +90,9 @@ class PointsModel(Model):
|
||||
"""
|
||||
|
||||
_points_per_step: Dict[str, List[int]] | None = None
|
||||
_wdc_points: Dict[str, int] | None = None
|
||||
_wcc_points: Dict[str, int] | None = None
|
||||
_dnfs: Dict[str, int] | None = None
|
||||
|
||||
def __init__(self):
|
||||
Model.__init__(self)
|
||||
@ -100,6 +118,104 @@ class PointsModel(Model):
|
||||
|
||||
return self._points_per_step
|
||||
|
||||
# @todo Doesn't include fastest lap + sprint points
|
||||
def wdc_points(self) -> Dict[str, int]:
|
||||
if self._wdc_points is None:
|
||||
self._wdc_points = dict()
|
||||
|
||||
for driver in self.all_drivers(include_none=False):
|
||||
self._wdc_points[driver.name] = 0
|
||||
|
||||
for race_result in self.all_race_results():
|
||||
for position, driver in race_result.standing.items():
|
||||
self._wdc_points[driver.name] += DRIVER_RACE_POINTS[int(position)] if int(position) in DRIVER_RACE_POINTS else 0
|
||||
|
||||
|
||||
return self._wdc_points
|
||||
|
||||
def wcc_points(self) -> Dict[str, int]:
|
||||
if self._wcc_points is None:
|
||||
self._wcc_points = dict()
|
||||
|
||||
for team in self.all_teams(include_none=False):
|
||||
self._wcc_points[team.name] = 0
|
||||
|
||||
for race_result in self.all_race_results():
|
||||
for driver in race_result.standing.values():
|
||||
self._wcc_points[driver.team.name] += self.wdc_points()[driver.name]
|
||||
|
||||
return self._wcc_points
|
||||
|
||||
# @todo Doesn't include sprint dnfs
|
||||
def dnfs(self) -> Dict[str, int]:
|
||||
if self._dnfs is None:
|
||||
self._dnfs = dict()
|
||||
|
||||
for driver in self.all_drivers(include_none=False):
|
||||
self._dnfs[driver.name] = 0
|
||||
|
||||
for race_result in self.all_race_results():
|
||||
for driver in race_result.all_dnfs:
|
||||
self._dnfs[driver.name] += 1
|
||||
|
||||
return self._dnfs
|
||||
|
||||
def wdc_diff_2023(self) -> Dict[str, int]:
|
||||
diff: Dict[str, int] = dict()
|
||||
|
||||
for driver in self.all_drivers(include_none=False):
|
||||
diff[driver.name] = STANDING_2023[driver.name] - self.wdc_standing_by_driver()[driver.name]
|
||||
|
||||
return diff
|
||||
|
||||
def wdc_standing_by_position(self) -> Dict[int, str]:
|
||||
standing: Dict[int, str] = dict()
|
||||
|
||||
comparator: Callable[[Tuple[str, int]], int] = lambda item: item[1]
|
||||
for position, (driver_name, _) in enumerate(sorted(self.wdc_points().items(), key=comparator)):
|
||||
standing[position] = driver_name
|
||||
|
||||
return standing
|
||||
|
||||
# @note Doesn't handle shared places (also applies to the following 3 method)
|
||||
def wdc_standing_by_driver(self) -> Dict[str, int]:
|
||||
standing: Dict[str, int] = dict()
|
||||
|
||||
comparator: Callable[[Tuple[str, int]], int] = lambda item: item[1]
|
||||
for position, (driver_name, _) in enumerate(sorted(self.wdc_points().items(), key=comparator)):
|
||||
standing[driver_name] = position
|
||||
|
||||
return standing
|
||||
|
||||
def wcc_standing_by_position(self) -> Dict[int, str]:
|
||||
standing: Dict[int, str] = dict()
|
||||
|
||||
comparator: Callable[[Tuple[str, int]], int] = lambda item: item[1]
|
||||
for position, (team_name, _) in enumerate(sorted(self.wcc_points().items(), key=comparator)):
|
||||
standing[position] = team_name
|
||||
|
||||
return standing
|
||||
|
||||
def wcc_standing_by_team(self) -> Dict[str, int]:
|
||||
standing: Dict[str, int] = dict()
|
||||
|
||||
comparator: Callable[[Tuple[str, int]], int] = lambda item: item[1]
|
||||
for position, (team_name, _) in enumerate(sorted(self.wcc_points().items(), key=comparator)):
|
||||
standing[team_name] = position
|
||||
|
||||
return standing
|
||||
|
||||
def most_dnfs_name(self) -> str:
|
||||
most_dnfs: Tuple[str, int] | None = None
|
||||
for driver_name, dnfs in self.dnfs().items():
|
||||
if most_dnfs is None or dnfs > most_dnfs[1]:
|
||||
most_dnfs = (driver_name, dnfs)
|
||||
|
||||
if most_dnfs is None:
|
||||
raise Exception("Failed to find driver with most dnfs")
|
||||
|
||||
return most_dnfs[0]
|
||||
|
||||
def points_per_step_cumulative(self) -> Dict[str, List[int]]:
|
||||
"""
|
||||
Returns a dictionary of lists, containing cumulative points per race for each user.
|
||||
@ -171,7 +287,7 @@ class PointsModel(Model):
|
||||
last_points: int = 0
|
||||
for user in self.users_sorted_by_points():
|
||||
if self.total_points_by(user.name) < last_points:
|
||||
position = position + 1
|
||||
position += 1
|
||||
|
||||
standing[user.name] = position
|
||||
|
||||
@ -192,9 +308,9 @@ class PointsModel(Model):
|
||||
continue
|
||||
|
||||
if standing_points(race_guess, race_result) > 0:
|
||||
count = count + 1
|
||||
count += 1
|
||||
if dnf_points(race_guess, race_result) > 0:
|
||||
count = count + 1
|
||||
count += 1
|
||||
|
||||
return count
|
||||
|
||||
@ -203,3 +319,68 @@ class PointsModel(Model):
|
||||
return 0.0
|
||||
|
||||
return self.total_points_by(user_name) / self.picks_count(user_name)
|
||||
|
||||
#
|
||||
# Season guess evaluation
|
||||
#
|
||||
|
||||
def hot_take_correct(self, user_name: str) -> bool:
|
||||
season_guess_result: SeasonGuessResult | None = self.season_guess_result_by(user_name=user_name)
|
||||
|
||||
return season_guess_result.hot_take_correct if season_guess_result is not None else False
|
||||
|
||||
def p2_constructor_correct(self, user_name: str) -> bool:
|
||||
season_guess: SeasonGuess | None = self.season_guesses_by(user_name=user_name)
|
||||
|
||||
if season_guess is None or season_guess.p2_wcc is None:
|
||||
return False
|
||||
|
||||
if 2 in self.wcc_standing_by_position():
|
||||
return self.wcc_standing_by_position()[2] == season_guess.p2_wcc.name
|
||||
|
||||
return False
|
||||
|
||||
def overtakes_correct(self, user_name: str) -> bool:
|
||||
season_guess_result: SeasonGuessResult | None = self.season_guess_result_by(user_name=user_name)
|
||||
|
||||
return season_guess_result.overtakes_correct if season_guess_result is not None else False
|
||||
|
||||
def dnfs_correct(self, user_name: str) -> bool:
|
||||
season_guess: SeasonGuess | None = self.season_guesses_by(user_name=user_name)
|
||||
|
||||
if season_guess is None or season_guess.most_dnfs is None:
|
||||
return False
|
||||
|
||||
return season_guess.most_dnfs.name == self.most_dnfs_name()
|
||||
|
||||
def most_gained_correct(self, user_name: str) -> bool:
|
||||
season_guess: SeasonGuess | None = self.season_guesses_by(user_name=user_name)
|
||||
|
||||
if season_guess is None or season_guess.most_wdc_gained is None:
|
||||
return False
|
||||
|
||||
comparator: Callable[[Tuple[str, int]], int] = lambda item: item[1]
|
||||
return season_guess.most_wdc_gained.name == max(self.wdc_diff_2023().items(), key=comparator)[0]
|
||||
|
||||
def most_lost_correct(self, user_name: str) -> bool:
|
||||
season_guess: SeasonGuess | None = self.season_guesses_by(user_name=user_name)
|
||||
|
||||
if season_guess is None or season_guess.most_wdc_lost is None:
|
||||
return False
|
||||
|
||||
comparator: Callable[[Tuple[str, int]], int] = lambda item: item[1]
|
||||
return season_guess.most_wdc_lost.name == min(self.wdc_diff_2023().items(), key=comparator)[0]
|
||||
|
||||
def is_team_winner(self, driver: Driver) -> bool:
|
||||
teammates: List[Driver] = self.drivers_by(team_name=driver.team.name)
|
||||
teammate: Driver = teammates[0] if teammates[1] == driver else teammates[1]
|
||||
|
||||
return self.wdc_standing_by_driver()[driver.name] >= self.wdc_standing_by_driver()[teammate.name]
|
||||
|
||||
def has_podium(self, driver: Driver) -> bool:
|
||||
for race_result in self.all_race_results():
|
||||
position: int | None = race_result.driver_standing_position(driver)
|
||||
if position is not None and position <= 3:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -24,9 +24,9 @@
|
||||
{% endmacro %}
|
||||
|
||||
{# Simple driver select for forms #}
|
||||
{% macro driver_select(name, label, include_none, drivers=none, disabled=false) %}
|
||||
{% macro driver_select(name, label, include_none, drivers=none, disabled=false, border="") %}
|
||||
<div class="form-floating">
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
<option value="" selected disabled hidden></option>
|
||||
|
||||
{% if drivers == none %}
|
||||
@ -42,9 +42,9 @@
|
||||
{% endmacro %}
|
||||
|
||||
{# Driver select for forms where a value might be preselected #}
|
||||
{% macro driver_select_with_preselect(driver_match, name, label, include_none, drivers=none, disabled=false) %}
|
||||
{% macro driver_select_with_preselect(driver_match, name, label, include_none, drivers=none, disabled=false, border="") %}
|
||||
<div class="form-floating">
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
{# Use namespace wrapper to persist scope between loop iterations #}
|
||||
{% set user_has_chosen = namespace(driverpre=false) %}
|
||||
|
||||
@ -75,9 +75,9 @@
|
||||
{% endmacro %}
|
||||
|
||||
{# Simple team select for forms #}
|
||||
{% macro team_select(name, label, include_none, teams=none, disabled=false) %}
|
||||
{% macro team_select(name, label, include_none, teams=none, disabled=false, border="") %}
|
||||
<div class="form-floating">
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
<option value="" selected disabled hidden></option>
|
||||
|
||||
{% if teams == none %}
|
||||
@ -93,9 +93,9 @@
|
||||
{% endmacro %}
|
||||
|
||||
{# Team select for forms where a value might be preselected #}
|
||||
{% macro team_select_with_preselect(team_match, name, label, include_none, teams=none, disabled=false) %}
|
||||
{% macro team_select_with_preselect(team_match, name, label, include_none, teams=none, disabled=false, border="") %}
|
||||
<div class="form-floating">
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" aria-label="{{ name }}" {% if disabled %}disabled="disabled"{% endif %}>
|
||||
{# Use namespace wrapper to persist scope between loop iterations #}
|
||||
{% set user_has_chosen = namespace(teampre=false) %}
|
||||
|
||||
|
@ -79,7 +79,7 @@
|
||||
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
||||
id="first-dnf-{{ driver.name }}" name="first-dnf-drivers"
|
||||
{% if (model.active_result is not none) and (driver in model.active_result.initial_dnf) %}checked{% endif %}
|
||||
{% if race_result_open == false %}disabled="disabled"{% endif %}>
|
||||
{% if race_result_open == false %}readonly="readonly"{% endif %}>
|
||||
<label for="first-dnf-{{ driver.name }}"
|
||||
class="form-check-label text-muted">1. DNF</label>
|
||||
</div>
|
||||
@ -89,7 +89,7 @@
|
||||
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
||||
id="dnf-{{ driver.name }}" name="dnf-drivers"
|
||||
{% if (model.active_result is not none) and (driver in model.active_result.all_dnfs) %}checked{% endif %}
|
||||
{% if race_result_open == false %}disabled="disabled"{% endif %}>
|
||||
{% if race_result_open == false %}readonly="readonly"{% endif %}>
|
||||
<label for="dnf-{{ driver.name }}"
|
||||
class="form-check-label text-muted">DNF</label>
|
||||
</div>
|
||||
@ -99,7 +99,7 @@
|
||||
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
||||
id="exclude-{{ driver.name }}" name="excluded-drivers"
|
||||
{% if (model.active_result is not none) and (driver in model.active_result.standing_exclusions) %}checked{% endif %}
|
||||
{% if race_result_open == false %}disabled="disabled"{% endif %}>
|
||||
{% if race_result_open == false %}readonly="readonly"{% endif %}>
|
||||
<label for="exclude-{{ driver.name }}"
|
||||
class="form-check-label text-muted" data-bs-toggle="tooltip"
|
||||
title="Driver is not counted for standing">NC</label>
|
||||
|
@ -129,7 +129,7 @@
|
||||
{# Delete guess #}
|
||||
<form action="{{ action_delete_href }}" method="post">
|
||||
<input type="submit" class="btn btn-dark mt-2 w-100" value="Delete"
|
||||
{% if race_guess_open == false %}disabled{% endif %}>
|
||||
{% if race_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
</form>
|
||||
</td>
|
||||
|
||||
|
@ -38,9 +38,11 @@
|
||||
|
||||
{# Hot Take #}
|
||||
<div class="form-floating">
|
||||
<textarea class="form-control" id="hot-take-input-{{ user.name }}" name="hottakeselect"
|
||||
style="height: 150px"
|
||||
{% if season_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
<textarea
|
||||
class="form-control {% if points.hot_take_correct(user.name) %}border-success{% endif %}"
|
||||
id="hot-take-input-{{ user.name }}" name="hottakeselect"
|
||||
style="height: 150px"
|
||||
{% if season_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
{%- if user_guess is not none -%}{{ user_guess.hot_take_string() }}{%- endif -%}
|
||||
</textarea>
|
||||
|
||||
@ -49,20 +51,31 @@
|
||||
|
||||
{# P2 Constructor #}
|
||||
<div class="mt-2">
|
||||
{{ team_select_with_preselect(team_match=user_guess.p2_wcc, name="p2select", label="P2 in WCC:", include_none=false, disabled=not season_guess_open) }}
|
||||
{{ team_select_with_preselect(team_match=user_guess.p2_wcc, name="p2select", label="P2 in WCC:",
|
||||
include_none=false, disabled=not season_guess_open,
|
||||
border=("border-success" if points.p2_constructor_correct(user.name) else "")) }}
|
||||
</div>
|
||||
|
||||
{# Most Overtakes + DNFs #}
|
||||
<div class="input-group mt-2">
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_overtakes, name="overtakeselect", label="Most overtakes:", include_none=false, disabled=not season_guess_open) }}
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_dnfs, name="dnfselect", label="Most DNFs:", include_none=false, disabled=not season_guess_open) }}
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_overtakes, name="overtakeselect",
|
||||
label="Most overtakes:", include_none=false, disabled=not season_guess_open,
|
||||
border=("border-success" if points.overtakes_correct(user.name) else "")) }}
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_dnfs, name="dnfselect", label="Most DNFs:",
|
||||
include_none=false, disabled=not season_guess_open,
|
||||
border=("border-success" if points.dnfs_correct(user.name) else "")) }}
|
||||
</div>
|
||||
|
||||
{# Most Gained + Lost #}
|
||||
<div class="input-group mt-2" data-bs-toggle="tooltip"
|
||||
title="Which driver will gain/lose the most places in comparison to last season's results?">
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_wdc_gained, name="gainedselect", label="Most WDC places gained:", include_none=false, drivers=model.drivers_for_wdc_gained(), disabled=not season_guess_open) }}
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_wdc_lost, name="lostselect", label="Most WDC places lost:", include_none=false, disabled=not season_guess_open) }}
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_wdc_gained, name="gainedselect",
|
||||
label="Most WDC places gained:", include_none=false, drivers=model.drivers_for_wdc_gained(),
|
||||
disabled=not season_guess_open,
|
||||
border=("border-success" if points.most_gained_correct(user.name) else "")) }}
|
||||
{{ driver_select_with_preselect(driver_match=user_guess.most_wdc_lost, name="lostselect",
|
||||
label="Most WDC places lost:", include_none=false, disabled=not season_guess_open,
|
||||
border=("border-success" if points.most_lost_correct(user.name) else "")) }}
|
||||
</div>
|
||||
|
||||
{# Team-internal Winners #}
|
||||
@ -82,7 +95,7 @@
|
||||
value="{{ driver_a.name }}"
|
||||
{% if (user_guess is not none) and (driver_a in user_guess.team_winners) %}checked="checked"{% endif %}
|
||||
{% if season_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
<label class="form-check-label"
|
||||
<label class="form-check-label {% if (user_guess is not none) and (driver_a in user_guess.team_winners) and points.is_team_winner(driver_a) %}text-success{% endif %}"
|
||||
for="teamwinner-{{ team.name }}-1-{{ user.name }}">{{ driver_a.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -95,7 +108,7 @@
|
||||
value="{{ driver_b.name }}"
|
||||
{% if (user_guess is not none) and (driver_b in user_guess.team_winners) %}checked="checked"{% endif %}
|
||||
{% if season_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
<label class="form-check-label"
|
||||
<label class="form-check-label {% if (user_guess is not none) and (driver_b in user_guess.team_winners) and points.is_team_winner(driver_b) %}text-success{% endif %}"
|
||||
for="teamwinner-{{ team.name }}-2-{{ user.name }}">{{ driver_b.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -118,7 +131,7 @@
|
||||
value="{{ driver_a.name }}"
|
||||
{% if (user_guess is not none) and (driver_a in user_guess.podiums) %}checked="checked"{% endif %}
|
||||
{% if season_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
<label class="form-check-label"
|
||||
<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{% endif %}"
|
||||
for="podium-{{ driver_a.name }}-{{ user.name }}">{{ driver_a.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -131,7 +144,7 @@
|
||||
value="{{ driver_b.name }}"
|
||||
{% if (user_guess is not none) and (driver_b in user_guess.podiums) %}checked="checked"{% endif %}
|
||||
{% if season_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
<label class="form-check-label"
|
||||
<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{% endif %}"
|
||||
for="podium-{{ driver_b.name }}-{{ user.name }}">{{ driver_b.name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@ -139,7 +152,7 @@
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn btn-danger mt-2 w-100" value="Save"
|
||||
{% if season_guess_open == false %}disabled{% endif %}>
|
||||
{% if season_guess_open == false %}disabled="disabled"{% endif %}>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user