Date-lock race+season guesses + use errorpage more often
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 17s
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 17s
This commit is contained in:
@ -3,13 +3,14 @@ from typing import Dict, List, cast
|
||||
from urllib.parse import quote
|
||||
from flask import redirect
|
||||
from werkzeug import Response
|
||||
from formula10.controller.error_controller import error_redirect
|
||||
|
||||
from formula10.database.common_queries import race_has_result, user_exists_and_disabled, user_exists_and_enabled
|
||||
from formula10.database.model.db_race_guess import DbRaceGuess
|
||||
from formula10.database.model.db_race_result import DbRaceResult
|
||||
from formula10.database.model.db_season_guess import DbSeasonGuess
|
||||
from formula10.database.model.db_user import DbUser
|
||||
from formula10.database.validation import any_is_none, positions_are_contiguous
|
||||
from formula10.database.validation import any_is_none, positions_are_contiguous, race_has_started
|
||||
from formula10 import db
|
||||
|
||||
|
||||
@ -34,17 +35,17 @@ def find_or_create_race_guess(user_name: str, race_name: str) -> DbRaceGuess:
|
||||
|
||||
def update_race_guess(race_name: str, user_name: str, pxx_select: str | None, dnf_select: str | None) -> Response:
|
||||
if any_is_none(pxx_select, dnf_select):
|
||||
return redirect(f"/race/{quote(user_name)}")
|
||||
return error_redirect(f"Picks for race \"{race_name}\" were not saved, because you did not fill all the fields.")
|
||||
|
||||
if race_has_started(race_name=race_name):
|
||||
return error_redirect(f"No picks for race \"{race_name}\" can be entered, as this race has already started.")
|
||||
|
||||
if race_has_result(race_name):
|
||||
return error_redirect(f"No picks for race \"{race_name}\" can be entered, as this race has already finished.")
|
||||
|
||||
pxx_driver_name: str = cast(str, pxx_select)
|
||||
dnf_driver_name: str = cast(str, dnf_select)
|
||||
|
||||
# TODO: Date-lock this. Otherwise there is a period of time after the race
|
||||
# but before the result where guesses can still be entered
|
||||
# We can't guess for races that are already over
|
||||
if race_has_result(race_name):
|
||||
return redirect(f"/race/{quote(user_name)}")
|
||||
|
||||
race_guess: DbRaceGuess = find_or_create_race_guess(user_name, race_name)
|
||||
race_guess.pxx_driver_name = pxx_driver_name
|
||||
race_guess.dnf_driver_name = dnf_driver_name
|
||||
@ -56,8 +57,11 @@ def update_race_guess(race_name: str, user_name: str, pxx_select: str | None, dn
|
||||
|
||||
def delete_race_guess(race_name: str, user_name: str) -> Response:
|
||||
# Don't change guesses that are already over
|
||||
if race_has_started(race_name=race_name):
|
||||
return error_redirect(f"No picks for race \"{race_name}\" can be deleted, as this race has already started.")
|
||||
|
||||
if race_has_result(race_name):
|
||||
return redirect(f"/race/{quote(user_name)}")
|
||||
return error_redirect(f"No picks for race \"{race_name}\" can be deleted, as this race has already finished.")
|
||||
|
||||
db.session.query(DbRaceGuess).filter_by(race_name=race_name, user_name=user_name).delete()
|
||||
db.session.commit()
|
||||
@ -87,6 +91,9 @@ def find_or_create_season_guess(user_name: str) -> DbSeasonGuess:
|
||||
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.
|
||||
|
||||
if race_has_started(race_name="Bahrain"):
|
||||
return error_redirect("No season picks can be entered, as the season has already begun!")
|
||||
|
||||
season_guess: DbSeasonGuess = find_or_create_season_guess(user_name)
|
||||
season_guess.hot_take = guesses[0] # type: ignore
|
||||
season_guess.p2_team_name = guesses[1] # type: ignore
|
||||
@ -125,6 +132,9 @@ def find_or_create_race_result(race_name: str) -> DbRaceResult:
|
||||
|
||||
|
||||
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:
|
||||
if not race_has_started(race_name=race_name):
|
||||
return error_redirect("No race result can be entered, as the race has not begun!")
|
||||
|
||||
# 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)
|
||||
@ -136,7 +146,7 @@ def update_race_result(race_name: str, pxx_driver_names_list: List[str], first_d
|
||||
if driver in excluded_driver_names_list
|
||||
}
|
||||
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)}")
|
||||
return error_redirect("Race result was not saved, as excluded drivers must be contiguous and at the end of the field!")
|
||||
|
||||
# First DNF drivers have to be contained in DNF drivers
|
||||
for driver_name in first_dnf_driver_names_list:
|
||||
@ -145,7 +155,7 @@ def update_race_result(race_name: str, pxx_driver_names_list: List[str], first_d
|
||||
|
||||
# There can't be dnfs but no initial dnfs
|
||||
if len(dnf_driver_names_list) > 0 and len(first_dnf_driver_names_list) == 0:
|
||||
return redirect(f"/result/{quote(race_name)}")
|
||||
return error_redirect("Race result was not saved, as there cannot be DNFs without (an) initial DNF(s)!")
|
||||
|
||||
race_result: DbRaceResult = find_or_create_race_result(race_name)
|
||||
race_result.pxx_driver_names_json = json.dumps(pxx_driver_names)
|
||||
@ -159,18 +169,21 @@ def update_race_result(race_name: str, pxx_driver_names_list: List[str], first_d
|
||||
|
||||
|
||||
def update_user(user_name: str | None, add: bool = False, delete: bool = False) -> Response:
|
||||
if user_name is None or len(user_name) < 3:
|
||||
return redirect("/user")
|
||||
if user_name is None:
|
||||
return error_redirect("Invalid request: Cannot add/delete user because it is \"None\"!")
|
||||
|
||||
if not add and not delete:
|
||||
return redirect("/user")
|
||||
return error_redirect("Invalid request: Can either add or delete user!")
|
||||
|
||||
if add and delete:
|
||||
return redirect("/user")
|
||||
return error_redirect("Invalid request: Can either add or delete user!")
|
||||
|
||||
if add:
|
||||
if len(user_name) < 3:
|
||||
return error_redirect(f"User \"{user_name}\" was not added, because the username must contain at least 3 characters!")
|
||||
|
||||
if user_exists_and_enabled(user_name):
|
||||
return redirect("/user")
|
||||
return error_redirect(f"User \"{user_name}\" was not added, because it already exists!")
|
||||
|
||||
elif user_exists_and_disabled(user_name):
|
||||
disabled_user: DbUser | None = db.session.query(DbUser).filter_by(name=user_name, enabled=False).first()
|
||||
@ -188,7 +201,10 @@ def update_user(user_name: str | None, add: bool = False, delete: bool = False)
|
||||
return redirect("/user")
|
||||
|
||||
if delete:
|
||||
if user_exists_and_enabled(user_name):
|
||||
if user_exists_and_disabled(user_name):
|
||||
return error_redirect(f"User \"{user_name}\" was not deleted, because it does not exist!")
|
||||
|
||||
elif user_exists_and_enabled(user_name):
|
||||
enabled_user: DbUser | None = db.session.query(DbUser).filter_by(name=user_name, enabled=True).first()
|
||||
if enabled_user is None:
|
||||
raise Exception("update_user couldn't disable user")
|
||||
@ -196,6 +212,9 @@ def update_user(user_name: str | None, add: bool = False, delete: bool = False)
|
||||
enabled_user.enabled = False
|
||||
db.session.commit()
|
||||
|
||||
else:
|
||||
return error_redirect(f"User \"{user_name}\" was not deleted, because it does not exist!")
|
||||
|
||||
return redirect("/user")
|
||||
|
||||
raise Exception("update_user received illegal combination of arguments")
|
||||
|
@ -1,4 +1,9 @@
|
||||
from typing import Any, Callable, Iterable, List, TypeVar
|
||||
from datetime import datetime
|
||||
from typing import Any, Callable, Iterable, List, TypeVar, overload
|
||||
|
||||
from formula10.database.model.db_race import DbRace
|
||||
from formula10 import db
|
||||
from formula10.frontend.model.race import Race
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
@ -21,6 +26,28 @@ def positions_are_contiguous(positions: List[str]) -> bool:
|
||||
# [2, 3, 4, 5]: 2 + 3 == 5
|
||||
return positions_sorted[0] + len(positions_sorted) - 1 == positions_sorted[-1]
|
||||
|
||||
@overload
|
||||
def race_has_started(*, race: Race) -> bool:
|
||||
return race_has_started(race=race)
|
||||
|
||||
@overload
|
||||
def race_has_started(*, race_name: str) -> bool:
|
||||
return race_has_started(race_name=race_name)
|
||||
|
||||
def race_has_started(*, race: Race | None = None, race_name: str | None = None) -> bool:
|
||||
if race is None and race_name is not None:
|
||||
_race: DbRace | None = db.session.query(DbRace).filter_by(name=race_name).first()
|
||||
|
||||
if _race is None:
|
||||
raise Exception(f"Couldn't obtain race {race_name} to check date")
|
||||
|
||||
return datetime.now() > _race.date
|
||||
|
||||
if race is not None and race_name is None:
|
||||
return datetime.now() > race.date
|
||||
|
||||
raise Exception("race_has_started received illegal arguments")
|
||||
|
||||
|
||||
def find_first_else_none(predicate: Callable[[_T], bool], iterable: Iterable[_T]) -> _T | None:
|
||||
"""
|
||||
|
Reference in New Issue
Block a user