Update race result standings handling
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:
@ -154,40 +154,51 @@ def find_or_create_race_result(race_name: str) -> RaceResult:
|
|||||||
|
|
||||||
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], 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
|
# Use strings as keys, as these dicts will be serialized to json
|
||||||
pxx_driver_names: Dict[str, str] = {str(position + 1): driver for position, driver in enumerate(pxx_driver_names_list)}
|
# The pxx_driver_names_list contains all 20 drivers, so use that one to determine positions for dnf_driver_names and excluded_driver_names
|
||||||
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}
|
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
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
# This one is only used for validation
|
# All dictionaries should be disjunct and result in a complete list from P1-P20 if merged
|
||||||
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}
|
union: Dict[str, str] = pxx_driver_names | dnf_driver_names | excluded_driver_names
|
||||||
best_excluded_driver_position: int = sorted(list(map(int, list(excluded_driver_names.keys()))))[0] if len(excluded_driver_names) > 0 else 21
|
if len(union) != 20 or not positions_are_contiguous(list(union.keys())) or "1" not in union or "20" not in union:
|
||||||
worst_dnf_driver_position: int = sorted(list(map(int, list(dnf_driver_names.keys()))), reverse=True)[0] if len(dnf_driver_names) > 0 else best_excluded_driver_position - 1
|
|
||||||
|
|
||||||
if not positions_are_contiguous(list(dnf_driver_names.keys())):
|
|
||||||
return redirect(f"/result/{quote(race_name)}")
|
return redirect(f"/result/{quote(race_name)}")
|
||||||
|
|
||||||
if not positions_are_contiguous(list(excluded_driver_names.keys())):
|
# dnf_drivers have positions above excluded_drivers
|
||||||
return redirect(f"/result/{quote(race_name)}")
|
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)}")
|
||||||
|
|
||||||
# DNF + exclude is exclusive
|
# pxx_drivers have positions above dnf_drivers
|
||||||
for driver_name in dnf_driver_names_list:
|
if len(dnf_driver_names) > 0:
|
||||||
if driver_name in excluded_driver_names_list:
|
best_dnf_driver_position: int = min(map(int, dnf_driver_names.keys()))
|
||||||
return redirect(f"/result/{quote(race_name)}")
|
for position in pxx_driver_names.keys():
|
||||||
|
if int(position) >= best_dnf_driver_position:
|
||||||
|
return redirect(f"/result/{quote(race_name)}")
|
||||||
|
|
||||||
# Excluded drivers occupy the last positions, if any are excluded
|
# pxx_drivers have positions above excluded_drivers
|
||||||
if len(excluded_driver_names_list) > 0 and not "20" in excluded_driver_names:
|
if len(excluded_driver_names) > 0:
|
||||||
return redirect(f"/result/{quote(race_name)}")
|
best_excluded_driver_position: int = min(map(int, excluded_driver_names.keys()))
|
||||||
|
for position in pxx_driver_names.keys():
|
||||||
# DNF drivers occupy the positions between finishing and excluded drivers
|
if int(position) >= best_excluded_driver_position:
|
||||||
if len(dnf_driver_names_list) > 0 and worst_dnf_driver_position + 1 != best_excluded_driver_position:
|
return redirect(f"/result/{quote(race_name)}")
|
||||||
print(dnf_driver_names, worst_dnf_driver_position)
|
|
||||||
print(excluded_driver_names, best_excluded_driver_position)
|
|
||||||
return redirect(f"/result/{quote(race_name)}")
|
|
||||||
|
|
||||||
|
|
||||||
race_result: RaceResult = find_or_create_race_result(race_name)
|
race_result: RaceResult = find_or_create_race_result(race_name)
|
||||||
race_result.pxx_driver_names = pxx_driver_names
|
race_result.pxx_driver_names = pxx_driver_names
|
||||||
race_result.dnf_driver_names = dnf_driver_names if len(dnf_driver_names_list) > 0 else {"20": "NONE"}
|
race_result.dnf_driver_names = dnf_driver_names
|
||||||
race_result.excluded_driver_names = excluded_driver_names_list
|
race_result.excluded_driver_names = excluded_driver_names
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|||||||
@ -19,9 +19,7 @@ db.init_app(app)
|
|||||||
# TODO
|
# TODO
|
||||||
# General
|
# General
|
||||||
|
|
||||||
# - A lot of validation (esp. in the model), each input should be checked (e.g. DNF, excluded order)...
|
# - Show place when entering race result (would require updating the drag'n'drop code...)
|
||||||
# - NONE driver handling: If PXX = NONE is selected, excluded drivers have to be taken into account
|
|
||||||
# - Show place when entering race result
|
|
||||||
|
|
||||||
# - Show cards of previous race results, like with season guesses?
|
# - 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?
|
# - Make the season card grid left-aligned? So e.g. 2 cards are not spread over the whole screen with large gaps?
|
||||||
|
|||||||
57
model.py
57
model.py
@ -161,18 +161,18 @@ class RaceResult(db.Model):
|
|||||||
self.dnf_driver_names_json = json.dumps(new_dnf_driver_names)
|
self.dnf_driver_names_json = json.dumps(new_dnf_driver_names)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def excluded_driver_names(self) -> List[str]:
|
def excluded_driver_names(self) -> Dict[str, str]:
|
||||||
return json.loads(self.excluded_driver_names_json)
|
return json.loads(self.excluded_driver_names_json)
|
||||||
|
|
||||||
@excluded_driver_names.setter
|
@excluded_driver_names.setter
|
||||||
def excluded_driver_names(self, new_excluded_driver_names: List[str]):
|
def excluded_driver_names(self, new_excluded_driver_names: Dict[str, str]):
|
||||||
self.excluded_driver_names_json = json.dumps(new_excluded_driver_names)
|
self.excluded_driver_names_json = json.dumps(new_excluded_driver_names)
|
||||||
|
|
||||||
# Relationships
|
# Relationships
|
||||||
race: Mapped["Race"] = relationship("Race", foreign_keys=[race_name])
|
race: Mapped["Race"] = relationship("Race", foreign_keys=[race_name])
|
||||||
_pxx_drivers: Dict[str, Driver] | None = None
|
_pxx_drivers: Dict[str, Driver] | None = None
|
||||||
_dnf_drivers: Dict[str, Driver] | None = None
|
_dnf_drivers: Dict[str, Driver] | None = None
|
||||||
_excluded_drivers: List[Driver] | None = None
|
_excluded_drivers: Dict[str, Driver] | None = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pxx_drivers(self) -> Dict[str, Driver]:
|
def pxx_drivers(self) -> Dict[str, Driver]:
|
||||||
@ -187,16 +187,6 @@ class RaceResult(db.Model):
|
|||||||
|
|
||||||
return self._pxx_drivers
|
return self._pxx_drivers
|
||||||
|
|
||||||
@property
|
|
||||||
def pxx_drivers_values(self) -> List[Driver]:
|
|
||||||
drivers: List[Driver] = list()
|
|
||||||
|
|
||||||
# I don't know what order dict.values() etc. will return...
|
|
||||||
for position in range(1, 21):
|
|
||||||
drivers.append(self.pxx_drivers[str(position)])
|
|
||||||
|
|
||||||
return drivers
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dnf_drivers(self) -> Dict[str, Driver]:
|
def dnf_drivers(self) -> Dict[str, Driver]:
|
||||||
if self._dnf_drivers is None:
|
if self._dnf_drivers is None:
|
||||||
@ -211,28 +201,55 @@ class RaceResult(db.Model):
|
|||||||
return self._dnf_drivers
|
return self._dnf_drivers
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def excluded_drivers(self) -> List[Driver]:
|
def excluded_drivers(self) -> Dict[str, Driver]:
|
||||||
if self._excluded_drivers is None:
|
if self._excluded_drivers is None:
|
||||||
self._excluded_drivers = list()
|
self._excluded_drivers = dict()
|
||||||
for driver_name in self.excluded_driver_names:
|
for position, driver_name in self.excluded_driver_names.items():
|
||||||
driver: Driver | None = db.session.query(Driver).filter_by(name=driver_name).first()
|
driver: Driver | None = db.session.query(Driver).filter_by(name=driver_name).first()
|
||||||
if driver is None:
|
if driver is None:
|
||||||
raise Exception(f"Error: Couldn't find driver with id {driver_name}")
|
raise Exception(f"Error: Couldn't find driver with id {driver_name}")
|
||||||
|
|
||||||
self._excluded_drivers.append(driver)
|
self._excluded_drivers[position] = driver
|
||||||
|
|
||||||
return self._excluded_drivers
|
return self._excluded_drivers
|
||||||
|
|
||||||
def pxx(self, offset: int = 0) -> Driver:
|
def pxx(self, offset: int = 0) -> Driver | None:
|
||||||
pxx_num: str = str(self.race.pxx + offset)
|
pxx_num: str = str(self.race.pxx + offset)
|
||||||
|
|
||||||
if pxx_num not in self.pxx_drivers:
|
if pxx_num not in self.pxx_drivers:
|
||||||
raise Exception(f"Error: Position {self.race.pxx} not contained in race result")
|
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")
|
||||||
|
|
||||||
|
return none_driver
|
||||||
|
|
||||||
return self.pxx_drivers[pxx_num]
|
return self.pxx_drivers[pxx_num]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dnf(self) -> Driver:
|
def dnf(self) -> Driver:
|
||||||
return sorted(self.dnf_drivers.items(), reverse=True)[0][1] # SortedList[FirstElement][KeyPairValue]
|
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")
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def all_positions(self) -> List[Driver]:
|
||||||
|
return [
|
||||||
|
self.single_position(str(position)) for position in range(1, 21)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class RaceGuess(db.Model):
|
class RaceGuess(db.Model):
|
||||||
|
|||||||
@ -98,23 +98,23 @@
|
|||||||
|
|
||||||
{#@formatter:off#}
|
{#@formatter:off#}
|
||||||
{% macro pxx_guess_colorization(driver_abbr='', result=none) -%}
|
{% macro pxx_guess_colorization(driver_abbr='', result=none) -%}
|
||||||
{% if driver_abbr == result.pxx(-3).abbr %}fw-bold
|
{% if (driver_abbr == result.pxx(-3).abbr) and (driver_abbr != "NON") %}fw-bold
|
||||||
{% elif driver_abbr == result.pxx(-2).abbr %}text-danger fw-bold
|
{% elif (driver_abbr == result.pxx(-2).abbr) and (driver_abbr != "NON") %}text-danger fw-bold
|
||||||
{% elif driver_abbr == result.pxx(-1).abbr %}text-warning 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(0).abbr) %}text-success fw-bold
|
||||||
{% elif driver_abbr == result.pxx(1).abbr %}text-warning fw-bold
|
{% elif (driver_abbr == result.pxx(1).abbr) and (driver_abbr != "NON") %}text-warning fw-bold
|
||||||
{% elif driver_abbr == result.pxx(2).abbr %}text-danger fw-bold
|
{% elif (driver_abbr == result.pxx(2).abbr) and (driver_abbr != "NON") %}text-danger fw-bold
|
||||||
{% elif driver_abbr == result.pxx(3).abbr %}fw-bold{% endif %}
|
{% elif (driver_abbr == result.pxx(3).abbr) and (driver_abbr != "NON") %}fw-bold{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro pxx_points_tooltip_text(driver_abbr='', result=none) -%}
|
{% macro pxx_points_tooltip_text(driver_abbr='', result=none) -%}
|
||||||
{% if driver_abbr == result.pxx(-3).abbr %}1 Point
|
{% if (driver_abbr == result.pxx(-3).abbr) and (driver_abbr != "NON") %}1 Point
|
||||||
{% elif driver_abbr == result.pxx(-2).abbr %}3 Points
|
{% elif (driver_abbr == result.pxx(-2).abbr) and (driver_abbr != "NON") %}3 Points
|
||||||
{% elif driver_abbr == result.pxx(-1).abbr %}6 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(0).abbr) %}10 Points
|
||||||
{% elif driver_abbr == result.pxx(1).abbr %}6 Points
|
{% elif (driver_abbr == result.pxx(1).abbr) and (driver_abbr != "NON") %}6 Points
|
||||||
{% elif driver_abbr == result.pxx(2).abbr %}3 Points
|
{% elif (driver_abbr == result.pxx(2).abbr) and (driver_abbr != "NON") %}3 Points
|
||||||
{% elif driver_abbr == result.pxx(3).abbr %}1 Point
|
{% elif (driver_abbr == result.pxx(3).abbr) and (driver_abbr != "NON") %}1 Point
|
||||||
{% else %}0 Points{% endif %}
|
{% else %}0 Points{% endif %}
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
|||||||
@ -65,17 +65,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</h5>
|
</h5>
|
||||||
|
|
||||||
<form action="/result-enter/
|
{# @formatter:off #}
|
||||||
|
<form action="/result-enter/{%- if active_result is not none %}{{ active_result.race.name }}{% else %}{{ current_race.name }}{% endif %}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{%- if active_result is not none %}{{ active_result.race.name }}{% else %}{{ current_race.name }}{% endif %}"
|
|
||||||
method="post">
|
method="post">
|
||||||
|
{# @formatter:on #}
|
||||||
<ul id="columns" class="list-group list-group-flush">
|
<ul id="columns" class="list-group list-group-flush">
|
||||||
|
|
||||||
{% if active_result is not none %}
|
{% if active_result is not none %}
|
||||||
{% set drivers = active_result.pxx_drivers_values %}
|
{% set drivers = active_result.all_positions %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% set drivers = model.all_drivers_except_none() %}
|
{% set drivers = model.all_drivers_except_none() %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -98,9 +95,10 @@
|
|||||||
<div class="form-check form-check-reverse d-inline-block mx-2">
|
<div class="form-check form-check-reverse d-inline-block mx-2">
|
||||||
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
<input type="checkbox" class="form-check-input" value="{{ driver.name }}"
|
||||||
id="exclude-{{ driver.name }}" name="exclude-drivers"
|
id="exclude-{{ driver.name }}" name="exclude-drivers"
|
||||||
{% if (active_result is not none) and (driver in active_result.excluded_drivers) %}checked{% endif %}>
|
{% if (active_result is not none) and (driver in active_result.excluded_drivers.values()) %}checked{% endif %}>
|
||||||
<label for="exclude-{{ driver.name }}"
|
<label for="exclude-{{ driver.name }}"
|
||||||
class="form-check-label text-muted" data-bs-toggle="tooltip" title="Exclude driver from points calculation">Exclude</label>
|
class="form-check-label text-muted" data-bs-toggle="tooltip"
|
||||||
|
title="Exclude driver from points calculation">Exclude</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user