Various frontend improvements
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 16s
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 16s
This commit is contained in:
@ -48,6 +48,17 @@ def update_race_guess(race_name: str, user_name: str, pxx_select: str | None, dn
|
||||
return redirect("/race/Everyone")
|
||||
|
||||
|
||||
def delete_race_guess(race_name: str, user_name: str) -> Response:
|
||||
# Don't change guesses that are already over
|
||||
if race_has_result(race_name):
|
||||
return redirect(f"/race/{quote(user_name)}")
|
||||
|
||||
db.session.query(RaceGuess).filter_by(race_name=race_name, user_name=user_name).delete()
|
||||
db.session.commit()
|
||||
|
||||
return redirect("/race/Everyone")
|
||||
|
||||
|
||||
def find_or_create_team_winners(user_name: str) -> TeamWinners:
|
||||
# There can be a single TeamWinners at most, since user_name is the primary key
|
||||
team_winners: TeamWinners | None = db.session.query(TeamWinners).filter_by(user_name=user_name).first()
|
||||
@ -112,7 +123,9 @@ def find_or_create_season_guess(user_name: str) -> SeasonGuess:
|
||||
return season_guess
|
||||
|
||||
|
||||
def update_season_guess(user_name: str, guesses: List[str | None] | List[str], team_winner_guesses: List[str | None] | List[str], podium_driver_guesses: List[str]) -> Response:
|
||||
def update_season_guess(user_name: str, guesses: List[str | None], team_winner_guesses: List[str | None], podium_driver_guesses: List[str]) -> Response:
|
||||
# Pylance marks type errors here, but those are intended. Columns are marked nullable.
|
||||
|
||||
season_guess: SeasonGuess = find_or_create_season_guess(user_name)
|
||||
season_guess.hot_take = guesses[0]
|
||||
season_guess.p2_team_name = guesses[1]
|
||||
@ -125,7 +138,7 @@ def update_season_guess(user_name: str, guesses: List[str | None] | List[str], t
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return redirect(f"/season/{quote(user_name)}")
|
||||
return redirect(f"/season/Everyone")
|
||||
|
||||
|
||||
def find_or_create_race_result(race_name: str) -> RaceResult:
|
||||
@ -146,53 +159,30 @@ def find_or_create_race_result(race_name: str) -> RaceResult:
|
||||
return race_result
|
||||
|
||||
|
||||
def update_race_result(race_name: str, pxx_driver_names_list: List[str], dnf_driver_names_list: List[str], excluded_driver_names_list: List[str]) -> Response:
|
||||
def update_race_result(race_name: str, pxx_driver_names_list: List[str], first_dnf_driver_names_list: List[str], dnf_driver_names_list: List[str], excluded_driver_names_list: List[str]) -> Response:
|
||||
# Use strings as keys, as these dicts will be serialized to json
|
||||
# The pxx_driver_names_list contains all 20 drivers, so use that one to determine positions for dnf_driver_names and excluded_driver_names
|
||||
pxx_driver_names: Dict[str, str] = {
|
||||
str(position + 1): driver for position, driver in enumerate(pxx_driver_names_list)
|
||||
if driver not in dnf_driver_names_list and driver not in excluded_driver_names_list
|
||||
}
|
||||
dnf_driver_names: Dict[str, str] = {
|
||||
str(position + 1): driver for position, driver in enumerate(pxx_driver_names_list)
|
||||
if driver in dnf_driver_names_list and driver not in excluded_driver_names_list
|
||||
}
|
||||
|
||||
# Not counted drivers have to be at the end
|
||||
excluded_driver_names: Dict[str, str] = {
|
||||
str(position + 1): driver for position, driver in enumerate(pxx_driver_names_list)
|
||||
if driver in excluded_driver_names_list
|
||||
}
|
||||
|
||||
# All dictionaries should be disjunct and result in a complete list from P1-P20 if merged
|
||||
union: Dict[str, str] = pxx_driver_names | dnf_driver_names | excluded_driver_names
|
||||
if len(union) != 20 or not positions_are_contiguous(list(union.keys())) or "1" not in union or "20" not in union:
|
||||
if len(excluded_driver_names) > 0 and (not "20" in excluded_driver_names or not positions_are_contiguous(list(excluded_driver_names.keys()))):
|
||||
return redirect(f"/result/{quote(race_name)}")
|
||||
|
||||
# dnf_drivers have positions above excluded_drivers
|
||||
if len(excluded_driver_names) > 0:
|
||||
best_excluded_driver_position: int = min(map(int, excluded_driver_names.keys()))
|
||||
for position in dnf_driver_names.keys():
|
||||
if int(position) >= best_excluded_driver_position:
|
||||
return redirect(f"/result/{quote(race_name)}")
|
||||
|
||||
# pxx_drivers have positions above dnf_drivers
|
||||
if len(dnf_driver_names) > 0:
|
||||
best_dnf_driver_position: int = min(map(int, dnf_driver_names.keys()))
|
||||
for position in pxx_driver_names.keys():
|
||||
if int(position) >= best_dnf_driver_position:
|
||||
return redirect(f"/result/{quote(race_name)}")
|
||||
|
||||
# pxx_drivers have positions above excluded_drivers
|
||||
if len(excluded_driver_names) > 0:
|
||||
best_excluded_driver_position: int = min(map(int, excluded_driver_names.keys()))
|
||||
for position in pxx_driver_names.keys():
|
||||
if int(position) >= best_excluded_driver_position:
|
||||
return redirect(f"/result/{quote(race_name)}")
|
||||
|
||||
# First DNF drivers have to be contained in DNF drivers
|
||||
for driver_name in first_dnf_driver_names_list:
|
||||
if driver_name not in dnf_driver_names_list:
|
||||
dnf_driver_names_list.append(driver_name)
|
||||
|
||||
race_result: RaceResult = find_or_create_race_result(race_name)
|
||||
race_result.pxx_driver_names = pxx_driver_names
|
||||
race_result.dnf_driver_names = dnf_driver_names
|
||||
race_result.excluded_driver_names = excluded_driver_names
|
||||
race_result.first_dnf_driver_names = first_dnf_driver_names_list
|
||||
race_result.dnf_driver_names = dnf_driver_names_list
|
||||
race_result.excluded_driver_names = excluded_driver_names_list
|
||||
|
||||
db.session.commit()
|
||||
|
||||
|
22
formula10.py
22
formula10.py
@ -5,7 +5,7 @@ from werkzeug import Response
|
||||
from model import Team, db
|
||||
from file_utils import reload_static_data, reload_dynamic_data, export_dynamic_data
|
||||
from template_model import TemplateModel
|
||||
from backend_model import update_race_guess, update_race_result, update_season_guess, update_user
|
||||
from backend_model import delete_race_guess, update_race_guess, update_race_result, update_season_guess, update_user
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@ -19,11 +19,12 @@ db.init_app(app)
|
||||
# TODO
|
||||
# General
|
||||
|
||||
# - Show place when entering race result (would require updating the drag'n'drop code...)
|
||||
# - Choose "place to guess" late before the race? Make a page for this
|
||||
# - Make user order changeable using drag'n'drop?
|
||||
|
||||
# - Show place when entering race result (would require updating the drag'n'drop code...)
|
||||
# - Show cards of previous race results, like with season guesses?
|
||||
# - Make the season card grid left-aligned? So e.g. 2 cards are not spread over the whole screen with large gaps?
|
||||
# - Choose "place to guess" late before the race?
|
||||
|
||||
# Statistics
|
||||
# - Auto calculate points
|
||||
@ -89,6 +90,14 @@ def race_guess_post(race_name: str, user_name: str) -> Response:
|
||||
return update_race_guess(race_name, user_name, pxx, dnf)
|
||||
|
||||
|
||||
@app.route("/race-guess-delete/<race_name>/<user_name>", methods=["POST"])
|
||||
def race_guess_delete_post(race_name: str, user_name: str) -> Response:
|
||||
race_name = unquote(race_name)
|
||||
user_name = unquote(user_name)
|
||||
|
||||
return delete_race_guess(race_name, user_name)
|
||||
|
||||
|
||||
@app.route("/season")
|
||||
def season_root() -> Response:
|
||||
return redirect("/season/Everyone")
|
||||
@ -139,11 +148,12 @@ def result_active_race(race_name: str) -> str:
|
||||
@app.route("/result-enter/<race_name>", methods=["POST"])
|
||||
def result_enter_post(race_name: str) -> Response:
|
||||
race_name = unquote(race_name)
|
||||
pxxs: List[str] = request.form.getlist("pxxdrivers")
|
||||
pxxs: List[str] = request.form.getlist("pxx-drivers")
|
||||
first_dnfs: List[str] = request.form.getlist("first-dnf-drivers")
|
||||
dnfs: List[str] = request.form.getlist("dnf-drivers")
|
||||
excludes: List[str] = request.form.getlist("exclude-drivers")
|
||||
excluded: List[str] = request.form.getlist("excluded-drivers")
|
||||
|
||||
return update_race_result(race_name, pxxs, dnfs, excludes)
|
||||
return update_race_result(race_name, pxxs, first_dnfs, dnfs, excluded)
|
||||
|
||||
|
||||
@app.route("/user")
|
||||
|
139
model.py
139
model.py
@ -71,7 +71,7 @@ class Driver(db.Model):
|
||||
return driver
|
||||
|
||||
name: Mapped[str] = mapped_column(String(32), primary_key=True)
|
||||
abbr: Mapped[str] = mapped_column(String(3))
|
||||
abbr: Mapped[str] = mapped_column(String(4))
|
||||
team_name: Mapped[str] = mapped_column(ForeignKey("team.name"))
|
||||
country_code: Mapped[str] = mapped_column(String(2)) # alpha-2 code
|
||||
|
||||
@ -118,7 +118,7 @@ class RaceResult(db.Model):
|
||||
"""
|
||||
__tablename__ = "raceresult"
|
||||
__allow_unmapped__ = True # TODO: Used for json conversion, move this to some other class instead
|
||||
__csv_header__ = ["race_name", "pxx_driver_names_json", "dnf_driver_names_json", "excluded_driver_names_json"]
|
||||
__csv_header__ = ["race_name", "pxx_driver_names_json", "first_dnf_driver_names_json", "dnf_driver_names_json", "excluded_driver_names_json"]
|
||||
|
||||
def __init__(self, race_name: str):
|
||||
self.race_name = race_name # Primary key
|
||||
@ -127,20 +127,23 @@ class RaceResult(db.Model):
|
||||
def from_csv(row: List[str]):
|
||||
race_result: RaceResult = RaceResult(str(row[0]))
|
||||
race_result.pxx_driver_names_json = str(row[1])
|
||||
race_result.dnf_driver_names_json = str(row[2])
|
||||
race_result.excluded_driver_names_json = str(row[3])
|
||||
race_result.first_dnf_driver_names_json = str(row[2])
|
||||
race_result.dnf_driver_names_json = str(row[3])
|
||||
race_result.excluded_driver_names_json = str(row[4])
|
||||
return race_result
|
||||
|
||||
def to_csv(self) -> List[Any]:
|
||||
return [
|
||||
self.race_name,
|
||||
self.pxx_driver_names_json,
|
||||
self.first_dnf_driver_names_json,
|
||||
self.dnf_driver_names_json,
|
||||
self.excluded_driver_names_json
|
||||
]
|
||||
|
||||
race_name: Mapped[str] = mapped_column(ForeignKey("race.name"), primary_key=True)
|
||||
pxx_driver_names_json: Mapped[str] = mapped_column(String(1024), nullable=True)
|
||||
first_dnf_driver_names_json: Mapped[str] = mapped_column(String(1024), nullable=True)
|
||||
dnf_driver_names_json: Mapped[str] = mapped_column(String(1024), nullable=True)
|
||||
excluded_driver_names_json: Mapped[str] = mapped_column(String(1024), nullable=True)
|
||||
|
||||
@ -153,26 +156,35 @@ class RaceResult(db.Model):
|
||||
self.pxx_driver_names_json = json.dumps(new_pxx_driver_names)
|
||||
|
||||
@property
|
||||
def dnf_driver_names(self) -> Dict[str, str]:
|
||||
def first_dnf_driver_names(self) -> List[str]:
|
||||
return json.loads(self.first_dnf_driver_names_json)
|
||||
|
||||
@first_dnf_driver_names.setter
|
||||
def first_dnf_driver_names(self, new_first_dnf_driver_names: List[str]):
|
||||
self.first_dnf_driver_names_json = json.dumps(new_first_dnf_driver_names)
|
||||
|
||||
@property
|
||||
def dnf_driver_names(self) -> List[str]:
|
||||
return json.loads(self.dnf_driver_names_json)
|
||||
|
||||
@dnf_driver_names.setter
|
||||
def dnf_driver_names(self, new_dnf_driver_names: Dict[str, str]):
|
||||
def dnf_driver_names(self, new_dnf_driver_names: List[str]):
|
||||
self.dnf_driver_names_json = json.dumps(new_dnf_driver_names)
|
||||
|
||||
@property
|
||||
def excluded_driver_names(self) -> Dict[str, str]:
|
||||
def excluded_driver_names(self) -> List[str]:
|
||||
return json.loads(self.excluded_driver_names_json)
|
||||
|
||||
@excluded_driver_names.setter
|
||||
def excluded_driver_names(self, new_excluded_driver_names: Dict[str, str]):
|
||||
def excluded_driver_names(self, new_excluded_driver_names: List[str]):
|
||||
self.excluded_driver_names_json = json.dumps(new_excluded_driver_names)
|
||||
|
||||
# Relationships
|
||||
race: Mapped["Race"] = relationship("Race", foreign_keys=[race_name])
|
||||
_pxx_drivers: Dict[str, Driver] | None = None
|
||||
_dnf_drivers: Dict[str, Driver] | None = None
|
||||
_excluded_drivers: Dict[str, Driver] | None = None
|
||||
_first_dnf_drivers: List[Driver] | None = None
|
||||
_dnf_drivers: List[Driver] | None = None
|
||||
_excluded_drivers: List[Driver] | None = None
|
||||
|
||||
@property
|
||||
def pxx_drivers(self) -> Dict[str, Driver]:
|
||||
@ -187,70 +199,81 @@ class RaceResult(db.Model):
|
||||
|
||||
return self._pxx_drivers
|
||||
|
||||
@property
|
||||
def dnf_drivers(self) -> Dict[str, Driver]:
|
||||
if self._dnf_drivers is None:
|
||||
self._dnf_drivers = dict()
|
||||
for position, driver_name in self.dnf_driver_names.items():
|
||||
driver: Driver | None = db.session.query(Driver).filter_by(name=driver_name).first()
|
||||
if driver is None:
|
||||
raise Exception(f"Error: Couldn't find driver with id {driver_name}")
|
||||
|
||||
self._dnf_drivers[position] = driver
|
||||
|
||||
return self._dnf_drivers
|
||||
|
||||
@property
|
||||
def excluded_drivers(self) -> Dict[str, Driver]:
|
||||
if self._excluded_drivers is None:
|
||||
self._excluded_drivers = dict()
|
||||
for position, driver_name in self.excluded_driver_names.items():
|
||||
driver: Driver | None = db.session.query(Driver).filter_by(name=driver_name).first()
|
||||
if driver is None:
|
||||
raise Exception(f"Error: Couldn't find driver with id {driver_name}")
|
||||
|
||||
self._excluded_drivers[position] = driver
|
||||
|
||||
return self._excluded_drivers
|
||||
|
||||
def pxx(self, offset: int = 0) -> Driver | None:
|
||||
def pxx_driver(self, offset: int = 0) -> Driver | None:
|
||||
pxx_num: str = str(self.race.pxx + offset)
|
||||
|
||||
if pxx_num not in self.pxx_drivers:
|
||||
none_driver: Driver | None = db.session.query(Driver).filter_by(name="NONE").first()
|
||||
raise Exception(f"Position {pxx_num} not found in RaceResult.pxx_drivers")
|
||||
|
||||
if self.pxx_drivers[pxx_num].name in self.excluded_driver_names:
|
||||
none_driver: Driver | None = db.session.query(Driver).filter_by(name="None").first()
|
||||
if none_driver is None:
|
||||
raise Exception("NONE-driver not found in database")
|
||||
raise Exception(f"NONE-driver not found in database")
|
||||
|
||||
return none_driver
|
||||
|
||||
|
||||
return self.pxx_drivers[pxx_num]
|
||||
|
||||
@property
|
||||
def dnf(self) -> Driver:
|
||||
none_driver: Driver | None = db.session.query(Driver).filter_by(name="NONE").first()
|
||||
if none_driver is None:
|
||||
raise Exception("NONE-driver not found in database")
|
||||
def pxx_driver_position_string(self, driver_name: str) -> str:
|
||||
for position, driver in self.pxx_driver_names.items():
|
||||
if driver == driver_name and driver not in self.excluded_driver_names:
|
||||
return f"P{position}"
|
||||
|
||||
return sorted(self.dnf_drivers.items(), reverse=True)[0][1] if len(self.dnf_drivers) > 0 else none_driver
|
||||
|
||||
def single_position(self, position: str) -> Driver:
|
||||
if position in self.pxx_drivers:
|
||||
return self.pxx_drivers[position]
|
||||
|
||||
if position in self.dnf_drivers:
|
||||
return self.dnf_drivers[position]
|
||||
|
||||
if position in self.excluded_drivers:
|
||||
return self.excluded_drivers[position]
|
||||
|
||||
raise Exception(f"Driver for position {position} not found in pxx/dnf/excluded")
|
||||
return "NC"
|
||||
|
||||
@property
|
||||
def all_positions(self) -> List[Driver]:
|
||||
return [
|
||||
self.single_position(str(position)) for position in range(1, 21)
|
||||
self.pxx_drivers[str(position)] for position in range(1, 21)
|
||||
]
|
||||
|
||||
@property
|
||||
def first_dnf_drivers(self) -> List[Driver]:
|
||||
if self._first_dnf_drivers is None:
|
||||
self._first_dnf_drivers = list()
|
||||
for driver_name in self.first_dnf_driver_names:
|
||||
driver: Driver | None = db.session.query(Driver).filter_by(name=driver_name).first()
|
||||
if driver is None:
|
||||
raise Exception(f"Error: Couldn't find driver with id {driver_name}")
|
||||
|
||||
self._first_dnf_drivers.append(driver)
|
||||
|
||||
if len(self._first_dnf_drivers) == 0:
|
||||
none_driver: Driver | None = db.session.query(Driver).filter_by(name="None").first()
|
||||
if none_driver is None:
|
||||
raise Exception("NONE-driver not found in database")
|
||||
|
||||
self._first_dnf_drivers.append(none_driver)
|
||||
|
||||
return self._first_dnf_drivers
|
||||
|
||||
@property
|
||||
def dnf_drivers(self) -> List[Driver]:
|
||||
if self._dnf_drivers is None:
|
||||
self._dnf_drivers = list()
|
||||
for driver_name in self.dnf_driver_names:
|
||||
driver: Driver | None = db.session.query(Driver).filter_by(name=driver_name).first()
|
||||
if driver is None:
|
||||
raise Exception(f"Error: Couldn't find driver with id {driver_name}")
|
||||
|
||||
self._dnf_drivers.append(driver)
|
||||
|
||||
return self._dnf_drivers
|
||||
|
||||
@property
|
||||
def excluded_drivers(self) -> List[Driver]:
|
||||
if self._excluded_drivers is None:
|
||||
self._excluded_drivers = list()
|
||||
for driver_name in self.excluded_driver_names:
|
||||
driver: Driver | None = db.session.query(Driver).filter_by(name=driver_name).first()
|
||||
if driver is None:
|
||||
raise Exception(f"Error: Couldn't find driver with id {driver_name}")
|
||||
|
||||
self._excluded_drivers.append(driver)
|
||||
|
||||
return self._excluded_drivers
|
||||
|
||||
|
||||
class RaceGuess(db.Model):
|
||||
"""
|
||||
|
@ -215,7 +215,7 @@ class TemplateModel:
|
||||
"""
|
||||
Returns a list of all drivers in the database, excluding the NONE driver.
|
||||
"""
|
||||
predicate: Callable[[Driver], bool] = lambda driver: driver.name != "NONE"
|
||||
predicate: Callable[[Driver], bool] = lambda driver: driver.name != "None"
|
||||
return find_multiple(predicate, self.all_drivers())
|
||||
|
||||
@overload
|
||||
|
@ -41,7 +41,7 @@
|
||||
<option value="{{ driver.name }}">{{ driver.abbr }}</option>
|
||||
{% endif %}
|
||||
|
||||
{% if (include_none == true) and (driver.abbr == "NON") %}
|
||||
{% if (include_none == true) and (driver.abbr == "None") %}
|
||||
<option disabled>──────────</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@ -102,34 +102,34 @@
|
||||
|
||||
{#@formatter:off#}
|
||||
{% macro pxx_guess_colorization(driver_abbr='', result=none) -%}
|
||||
{% if (driver_abbr == result.pxx(-3).abbr) and (driver_abbr != "NON") %}fw-bold
|
||||
{% elif (driver_abbr == result.pxx(-2).abbr) and (driver_abbr != "NON") %}text-danger fw-bold
|
||||
{% elif (driver_abbr == result.pxx(-1).abbr) and (driver_abbr != "NON") %}text-warning fw-bold
|
||||
{% elif (driver_abbr == result.pxx(0).abbr) %}text-success fw-bold
|
||||
{% elif (driver_abbr == result.pxx(1).abbr) and (driver_abbr != "NON") %}text-warning fw-bold
|
||||
{% elif (driver_abbr == result.pxx(2).abbr) and (driver_abbr != "NON") %}text-danger fw-bold
|
||||
{% elif (driver_abbr == result.pxx(3).abbr) and (driver_abbr != "NON") %}fw-bold{% endif %}
|
||||
{% if (driver_abbr == result.pxx_driver(-3).abbr) and (driver_abbr != "None") %}fw-bold
|
||||
{% elif (driver_abbr == result.pxx_driver(-2).abbr) and (driver_abbr != "None") %}text-danger fw-bold
|
||||
{% elif (driver_abbr == result.pxx_driver(-1).abbr) and (driver_abbr != "None") %}text-warning fw-bold
|
||||
{% elif (driver_abbr == result.pxx_driver(0).abbr) %}text-success fw-bold
|
||||
{% elif (driver_abbr == result.pxx_driver(1).abbr) and (driver_abbr != "None") %}text-warning fw-bold
|
||||
{% elif (driver_abbr == result.pxx_driver(2).abbr) and (driver_abbr != "None") %}text-danger fw-bold
|
||||
{% elif (driver_abbr == result.pxx_driver(3).abbr) and (driver_abbr != "None") %}fw-bold{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro pxx_points_tooltip_text(driver_abbr='', result=none) -%}
|
||||
{% if (driver_abbr == result.pxx(-3).abbr) and (driver_abbr != "NON") %}1 Point
|
||||
{% elif (driver_abbr == result.pxx(-2).abbr) and (driver_abbr != "NON") %}3 Points
|
||||
{% elif (driver_abbr == result.pxx(-1).abbr) and (driver_abbr != "NON") %}6 Points
|
||||
{% elif (driver_abbr == result.pxx(0).abbr) %}10 Points
|
||||
{% elif (driver_abbr == result.pxx(1).abbr) and (driver_abbr != "NON") %}6 Points
|
||||
{% elif (driver_abbr == result.pxx(2).abbr) and (driver_abbr != "NON") %}3 Points
|
||||
{% elif (driver_abbr == result.pxx(3).abbr) and (driver_abbr != "NON") %}1 Point
|
||||
{% if (driver_abbr == result.pxx_driver(-3).abbr) and (driver_abbr != "None") %}1 Point
|
||||
{% elif (driver_abbr == result.pxx_driver(-2).abbr) and (driver_abbr != "None") %}3 Points
|
||||
{% elif (driver_abbr == result.pxx_driver(-1).abbr) and (driver_abbr != "None") %}6 Points
|
||||
{% elif (driver_abbr == result.pxx_driver(0).abbr) %}10 Points
|
||||
{% elif (driver_abbr == result.pxx_driver(1).abbr) and (driver_abbr != "None") %}6 Points
|
||||
{% elif (driver_abbr == result.pxx_driver(2).abbr) and (driver_abbr != "None") %}3 Points
|
||||
{% elif (driver_abbr == result.pxx_driver(3).abbr) and (driver_abbr != "None") %}1 Point
|
||||
{% else %}0 Points{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro pxx_standing_tooltip_text(result=none) -%}
|
||||
P{{ result.race.pxx - 3 }}: {{ result.pxx(-3).abbr }}
|
||||
P{{ result.race.pxx - 2 }}: {{ result.pxx(-2).abbr }}
|
||||
P{{ result.race.pxx - 1 }}: {{ result.pxx(-1).abbr }}
|
||||
P{{ result.race.pxx }}: {{ result.pxx(0).abbr }}
|
||||
P{{ result.race.pxx + 1 }}: {{ result.pxx(1).abbr }}
|
||||
P{{ result.race.pxx + 2 }}: {{ result.pxx(2).abbr }}
|
||||
P{{ result.race.pxx + 3 }}: {{ result.pxx(3).abbr }}
|
||||
P{{ result.race.pxx - 3 }}: {{ result.pxx_driver(-3).abbr }}
|
||||
P{{ result.race.pxx - 2 }}: {{ result.pxx_driver(-2).abbr }}
|
||||
P{{ result.race.pxx - 1 }}: {{ result.pxx_driver(-1).abbr }}
|
||||
P{{ result.race.pxx }}: {{ result.pxx_driver(0).abbr }}
|
||||
P{{ result.race.pxx + 1 }}: {{ result.pxx_driver(1).abbr }}
|
||||
P{{ result.race.pxx + 2 }}: {{ result.pxx_driver(2).abbr }}
|
||||
P{{ result.race.pxx + 3 }}: {{ result.pxx_driver(3).abbr }}
|
||||
{% endmacro %}
|
||||
{#@formatter:on#}
|
||||
|
||||
|
@ -82,28 +82,37 @@
|
||||
{{ driver.name }}
|
||||
|
||||
<div class="d-inline-block float-end">
|
||||
{# Driver DNFed #}
|
||||
{# Driver DNFed at first #}
|
||||
<div class="form-check form-check-reverse d-inline-block">
|
||||
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
||||
id="first-dnf-{{ driver.name }}" name="first-dnf-drivers"
|
||||
{% if (active_result is not none) and (driver in active_result.first_dnf_drivers) %}checked{% endif %}>
|
||||
<label for="first-dnf-{{ driver.name }}"
|
||||
class="form-check-label text-muted">1. DNF</label>
|
||||
</div>
|
||||
|
||||
{# Driver DNFed #}
|
||||
<div class="form-check form-check-reverse d-inline-block mx-2">
|
||||
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
||||
id="dnf-{{ driver.name }}" name="dnf-drivers"
|
||||
{% if (active_result is not none) and (driver in active_result.dnf_drivers.values()) %}checked{% endif %}>
|
||||
{% if (active_result is not none) and (driver in active_result.dnf_drivers) %}checked{% endif %}>
|
||||
<label for="dnf-{{ driver.name }}"
|
||||
class="form-check-label text-muted">DNF</label>
|
||||
</div>
|
||||
|
||||
{# Driver Excluded #}
|
||||
<div class="form-check form-check-reverse d-inline-block mx-2">
|
||||
<div class="form-check form-check-reverse d-inline-block">
|
||||
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
||||
id="exclude-{{ driver.name }}" name="exclude-drivers"
|
||||
{% if (active_result is not none) and (driver in active_result.excluded_drivers.values()) %}checked{% endif %}>
|
||||
id="exclude-{{ driver.name }}" name="excluded-drivers"
|
||||
{% if (active_result is not none) and (driver in active_result.excluded_drivers) %}checked{% endif %}>
|
||||
<label for="exclude-{{ driver.name }}"
|
||||
class="form-check-label text-muted" data-bs-toggle="tooltip"
|
||||
title="Exclude driver from points calculation">Exclude</label>
|
||||
title="Driver is not counted for standing">NC</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Standing order #}
|
||||
<input type="hidden" name="pxxdrivers" value="{{ driver.name }}"></li>
|
||||
<input type="hidden" name="pxx-drivers" value="{{ driver.name }}"></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
|
@ -70,8 +70,7 @@
|
||||
|
||||
<tr class="table-danger">
|
||||
<td class="text-nowrap">
|
||||
<span class="fw-bold">{{ current_race.number }}:</span> <a href="/result"
|
||||
class="link-dark">{{ current_race.name }}</a><br>
|
||||
<span class="fw-bold">{{ current_race.number }}:</span> {{ current_race.name }}<br>
|
||||
<small><span class="fw-bold">Guess:</span> P{{ current_race.pxx }}</small>
|
||||
</td>
|
||||
|
||||
@ -82,10 +81,10 @@
|
||||
{% if user_guess is not none %}
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item" style="background-color: inherit;">
|
||||
P{{ current_race.pxx }}: {{ user_guess.pxx.abbr }}
|
||||
{{ user_guess.pxx.abbr }}
|
||||
</li>
|
||||
<li class="list-group-item" style="background-color: inherit;">
|
||||
DNF: {{ user_guess.dnf.abbr }}
|
||||
{{ user_guess.dnf.abbr }}
|
||||
</li>
|
||||
</ul>
|
||||
{% else %}
|
||||
@ -120,6 +119,9 @@
|
||||
|
||||
<input type="submit" class="btn btn-danger mt-2 w-100" value="Save">
|
||||
</form>
|
||||
<form action="/race-guess-delete/{{ current_race.name_sanitized }}/{{ active_user.name_sanitized }}" method="post">
|
||||
<input type="submit" class="btn btn-dark mt-2 w-100" value="Delete">
|
||||
</form>
|
||||
</td>
|
||||
|
||||
<td> </td>
|
||||
@ -130,8 +132,7 @@
|
||||
{% for past_result in model.all_race_results() %}
|
||||
<tr>
|
||||
<td class="text-nowrap">
|
||||
<span class="fw-bold">{{ past_result.race.number }}:</span> <a
|
||||
href="/result/{{ past_result.race.name_sanitized }}" class="link-dark">{{ past_result.race.name }}</a><br>
|
||||
<span class="fw-bold">{{ past_result.race.number }}:</span> {{ past_result.race.name }}<br>
|
||||
<small><span class="fw-bold">Guessed:</span> P{{ past_result.race.pxx }}</small>
|
||||
</td>
|
||||
|
||||
@ -149,12 +150,12 @@
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item {{ pxx_guess_colorization(user_guess.pxx.abbr, past_result) }}">
|
||||
<span data-bs-toggle="tooltip" title="{{ pxx_points_tooltip_text(user_guess.pxx.abbr, past_result) }}">
|
||||
P{{ past_result.race.pxx }}: {{ user_guess.pxx.abbr }}
|
||||
{{ user_guess.pxx.abbr }}{% if user_guess.pxx.abbr != "None" %} ({{ past_result.pxx_driver_position_string(user_guess.pxx.name) }}){% endif %}
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item {% if user_guess.dnf.abbr == past_result.dnf.abbr %}text-success fw-bold{% endif %}">
|
||||
<span data-bs-toggle="tooltip" title="{% if user_guess.dnf.abbr == past_result.dnf.abbr %}10 Points{% else %}0 Points{% endif %}">
|
||||
DNF: {{ user_guess.dnf.abbr }}
|
||||
<li class="list-group-item {% if user_guess.dnf.name in past_result.first_dnf_driver_names %}text-success fw-bold{% endif %}">
|
||||
<span data-bs-toggle="tooltip" title="{% if user_guess.dnf.name in past_result.first_dnf_driver_names %}10 Points{% else %}0 Points{% endif %}">
|
||||
{{ user_guess.dnf.abbr }}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
@ -168,11 +169,11 @@
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<span data-bs-toggle="tooltip" title="{{ pxx_standing_tooltip_text(past_result) }}">
|
||||
P{{ past_result.race.pxx }}: {{ past_result.pxx().abbr }}
|
||||
P{{ past_result.race.pxx }}: {{ past_result.pxx_driver().abbr }}
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item {% if past_result.dnf.abbr == 'NON' %}text-muted{% endif %}">
|
||||
DNF: {{ past_result.dnf.abbr }}</li>
|
||||
<li class="list-group-item">
|
||||
DNF: {% for dnf_driver in past_result.first_dnf_drivers %}{{ dnf_driver.abbr }} {% endfor %}</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -82,7 +82,7 @@
|
||||
</div>
|
||||
|
||||
{# Most Gained + Lost #}
|
||||
<div class="input-group mt-2">
|
||||
<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(user_guess.gained_driver.abbr if user_guess is not none else "",
|
||||
"gainedselect", "Most WDC places gained:", false) }}
|
||||
{{ driver_select_with_preselect(user_guess.lost_driver.abbr if user_guess is not none else "",
|
||||
|
Reference in New Issue
Block a user