Disable stuff no longer required
All checks were successful
Build Formula10 Docker Image / build-docker (push) Successful in 14s

This commit is contained in:
2024-03-09 20:30:35 +01:00
parent 4fc13471f5
commit 4b3f95764c
5 changed files with 88 additions and 112 deletions

View File

@ -28,25 +28,4 @@ import formula10.controller.leaderboard_controller
import formula10.controller.statistics_controller import formula10.controller.statistics_controller
import formula10.controller.rules_controller import formula10.controller.rules_controller
import formula10.controller.admin_controller import formula10.controller.admin_controller
import formula10.controller.error_controller import formula10.controller.error_controller
# TODO
# Large DB Update
# - Don't use names for frontend post requests, use IDs
# - For season guess calc there is missing: Fastest laps + sprint points + sprint DNFs (in race result)
# - Mask to allow changing usernames (easy if name is not used as ID)
# - Maybe even masks for races + drivers + teams?
# - DB fields for links to F1 site - NO: just hardcode them in with a dictionary
# Leaderboards/Points
# - Auto calculate season points (display season points in table + season guess card title?)
# Optimizations
# - Optimize PointsModel + TemplateModel. In case something is calculated often, cache it.
# - NEVER do manual DB queries, except in the DomainModel!
# General
# - Adapt diagram colors to team colors
# - Add links to the official F1 stats page (for quali/result), probably best to store entire link in DB (because they are not entirely regular)?
# - Unit testing (as much as possible, but especially points calculation)

View File

@ -1,39 +1,36 @@
from typing import List from flask import render_template, request
from urllib.parse import unquote
from flask import redirect, render_template, request
from werkzeug import Response from werkzeug import Response
from formula10.database.update_queries import update_race_result, update_user from formula10.database.update_queries import update_user
from formula10.domain.domain_model import Model
from formula10.domain.template_model import TemplateModel from formula10.domain.template_model import TemplateModel
from formula10 import app from formula10 import app
@app.route("/result") # @app.route("/result")
def result_root() -> Response: # def result_root() -> Response:
return redirect("/result/Current") # return redirect("/result/Current")
@app.route("/result/<race_name>") # @app.route("/result/<race_name>")
def result_active_race(race_name: str) -> str: # def result_active_race(race_name: str) -> str:
race_name = unquote(race_name) # race_name = unquote(race_name)
model = TemplateModel(active_user_name=None, # model = TemplateModel(active_user_name=None,
active_result_race_name=race_name) # active_result_race_name=race_name)
return render_template("result.jinja", model=model) # return render_template("result.jinja", model=model)
@app.route("/result-enter/<race_name>", methods=["POST"]) # @app.route("/result-enter/<race_name>", methods=["POST"])
def result_enter_post(race_name: str) -> Response: # def result_enter_post(race_name: str) -> Response:
race_name = unquote(race_name) # race_name = unquote(race_name)
pxxs: List[str] = request.form.getlist("pxx-drivers") # pxxs: List[str] = request.form.getlist("pxx-drivers")
first_dnfs: List[str] = request.form.getlist("first-dnf-drivers") # first_dnfs: List[str] = request.form.getlist("first-dnf-drivers")
dnfs: List[str] = request.form.getlist("dnf-drivers") # dnfs: List[str] = request.form.getlist("dnf-drivers")
excluded: List[str] = request.form.getlist("excluded-drivers") # excluded: List[str] = request.form.getlist("excluded-drivers")
# @todo Ugly # # @todo Ugly
race_id: int = Model().race_by(race_name=race_name).id # race_id: int = Model().race_by(race_name=race_name).id
return update_race_result(race_id, pxxs, first_dnfs, dnfs, excluded) # return update_race_result(race_id, pxxs, first_dnfs, dnfs, excluded)
@app.route("/user") @app.route("/user")

View File

View File

@ -1,19 +1,15 @@
import json import json
from typing import Dict, List, cast from typing import List, cast
from urllib.parse import quote
from flask import redirect from flask import redirect
from werkzeug import Response from werkzeug import Response
from formula10.controller.error_controller import error_redirect 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.common_queries import race_has_result, user_exists_and_disabled, user_exists_and_enabled
from formula10.database.model.db_race import DbRace
from formula10.database.model.db_race_guess import DbRaceGuess 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_season_guess import DbSeasonGuess
from formula10.database.model.db_user import DbUser from formula10.database.model.db_user import DbUser
from formula10.database.validation import any_is_none, positions_are_contiguous, race_has_started from formula10.database.validation import any_is_none, race_has_started
from formula10 import ENABLE_TIMING, db from formula10 import ENABLE_TIMING, db
from formula10.domain.model.driver import NONE_DRIVER
def find_or_create_race_guess(user_id: int, race_id: int) -> DbRaceGuess: def find_or_create_race_guess(user_id: int, race_id: int) -> DbRaceGuess:
@ -116,77 +112,77 @@ def update_season_guess(user_id: int, guesses: List[str | None], team_winner_gue
return redirect(f"/season/Everyone") return redirect(f"/season/Everyone")
def find_or_create_race_result(race_id: int) -> DbRaceResult: # def find_or_create_race_result(race_id: int) -> DbRaceResult:
# There can be a single RaceResult at most, since race_name is the primary key # # There can be a single RaceResult at most, since race_name is the primary key
race_result: DbRaceResult | None = db.session.query(DbRaceResult).filter_by(race_id=race_id).first() # race_result: DbRaceResult | None = db.session.query(DbRaceResult).filter_by(race_id=race_id).first()
if race_result is not None: # if race_result is not None:
return race_result # return race_result
race_result = DbRaceResult(race_id=race_id) # race_result = DbRaceResult(race_id=race_id)
race_result.pxx_driver_ids_json = json.dumps(["9999"]) # race_result.pxx_driver_ids_json = json.dumps(["9999"])
race_result.first_dnf_driver_ids_json = json.dumps(["9999"]) # race_result.first_dnf_driver_ids_json = json.dumps(["9999"])
race_result.dnf_driver_ids_json = json.dumps(["9999"]) # race_result.dnf_driver_ids_json = json.dumps(["9999"])
race_result.excluded_driver_ids_json = json.dumps(["9999"]) # race_result.excluded_driver_ids_json = json.dumps(["9999"])
race_result.fastest_lap_id = 9999 # race_result.fastest_lap_id = 9999
race_result.sprint_dnf_driver_ids_json = json.dumps(["9999"]) # race_result.sprint_dnf_driver_ids_json = json.dumps(["9999"])
race_result.sprint_points_json = json.dumps({"9999": "9999"}) # race_result.sprint_points_json = json.dumps({"9999": "9999"})
db.session.add(race_result) # db.session.add(race_result)
db.session.commit() # db.session.commit()
# Double check if database insertion worked and obtain any values set by the database # # Double check if database insertion worked and obtain any values set by the database
race_result = db.session.query(DbRaceResult).filter_by(race_id=race_id).first() # race_result = db.session.query(DbRaceResult).filter_by(race_id=race_id).first()
if race_result is None: # if race_result is None:
raise Exception("Failed adding RaceResult to the database") # raise Exception("Failed adding RaceResult to the database")
return race_result # return race_result
def update_race_result(race_id: int, pxx_driver_ids_list: List[str], first_dnf_driver_ids_list: List[str], dnf_driver_ids_list: List[str], excluded_driver_ids_list: List[str]) -> Response: # def update_race_result(race_id: int, pxx_driver_ids_list: List[str], first_dnf_driver_ids_list: List[str], dnf_driver_ids_list: List[str], excluded_driver_ids_list: List[str]) -> Response:
if ENABLE_TIMING and not race_has_started(race_id=race_id): # if ENABLE_TIMING and not race_has_started(race_id=race_id):
return error_redirect("No race result can be entered, as the race has not begun!") # 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 # # Use strings as keys, as these dicts will be serialized to json
pxx_driver_names: Dict[str, str] = { # pxx_driver_names: Dict[str, str] = {
str(position + 1): driver_id for position, driver_id in enumerate(pxx_driver_ids_list) # str(position + 1): driver_id for position, driver_id in enumerate(pxx_driver_ids_list)
} # }
# Not counted drivers have to be at the end # # Not counted drivers have to be at the end
excluded_driver_names: Dict[str, str] = { # excluded_driver_names: Dict[str, str] = {
str(position + 1): driver_id for position, driver_id in enumerate(pxx_driver_ids_list) # str(position + 1): driver_id for position, driver_id in enumerate(pxx_driver_ids_list)
if driver_id in excluded_driver_ids_list # if driver_id in excluded_driver_ids_list
} # }
if len(excluded_driver_names) > 0 and (not "20" in excluded_driver_names or not positions_are_contiguous(list(excluded_driver_names.keys()))): # if len(excluded_driver_names) > 0 and (not "20" in excluded_driver_names or not positions_are_contiguous(list(excluded_driver_names.keys()))):
return error_redirect("Race result was not saved, as excluded drivers must be contiguous and at the end of the field!") # 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 # # First DNF drivers have to be contained in DNF drivers
for driver_id in first_dnf_driver_ids_list: # for driver_id in first_dnf_driver_ids_list:
if driver_id not in dnf_driver_ids_list: # if driver_id not in dnf_driver_ids_list:
dnf_driver_ids_list.append(driver_id) # dnf_driver_ids_list.append(driver_id)
# There can't be dnfs but no initial dnfs # # There can't be dnfs but no initial dnfs
if len(dnf_driver_ids_list) > 0 and len(first_dnf_driver_ids_list) == 0: # if len(dnf_driver_ids_list) > 0 and len(first_dnf_driver_ids_list) == 0:
return error_redirect("Race result was not saved, as there cannot be DNFs without (an) initial DNF(s)!") # 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_id) # race_result: DbRaceResult = find_or_create_race_result(race_id)
race_result.pxx_driver_ids_json = json.dumps(pxx_driver_names) # race_result.pxx_driver_ids_json = json.dumps(pxx_driver_names)
race_result.first_dnf_driver_ids_json = json.dumps(first_dnf_driver_ids_list) # race_result.first_dnf_driver_ids_json = json.dumps(first_dnf_driver_ids_list)
race_result.dnf_driver_ids_json = json.dumps(dnf_driver_ids_list) # race_result.dnf_driver_ids_json = json.dumps(dnf_driver_ids_list)
race_result.excluded_driver_ids_json = json.dumps(excluded_driver_ids_list) # race_result.excluded_driver_ids_json = json.dumps(excluded_driver_ids_list)
# @todo Dummy values # # @todo Dummy values
race_result.fastest_lap_id = NONE_DRIVER.id # race_result.fastest_lap_id = NONE_DRIVER.id
race_result.sprint_dnf_driver_ids_json = json.dumps([NONE_DRIVER.id]) # race_result.sprint_dnf_driver_ids_json = json.dumps([NONE_DRIVER.id])
race_result.sprint_points_json = json.dumps({NONE_DRIVER.id: 0}) # race_result.sprint_points_json = json.dumps({NONE_DRIVER.id: 0})
db.session.commit() # db.session.commit()
race: DbRace | None = db.session.query(DbRace).filter_by(id=race_id).first() # race: DbRace | None = db.session.query(DbRace).filter_by(id=race_id).first()
if race is None: # if race is None:
raise Exception(f"Could not find DbRace with id {race_id}") # raise Exception(f"Could not find DbRace with id {race_id}")
return redirect(f"/result/{quote(race.name)}") # return redirect(f"/result/{quote(race.name)}")
def update_user(user_name: str | None, add: bool = False, delete: bool = False) -> Response: def update_user(user_name: str | None, add: bool = False, delete: bool = False) -> Response:

View File

@ -26,7 +26,8 @@
{# Simple driver select for forms #} {# Simple driver select for forms #}
{% macro driver_select(name, label, include_none, drivers=none, disabled=false, border="") %} {% macro driver_select(name, label, include_none, drivers=none, disabled=false, border="") %}
<div class="form-floating"> <div class="form-floating">
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" 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> <option value="" selected disabled hidden></option>
{% if drivers == none %} {% if drivers == none %}
@ -44,7 +45,8 @@
{# Driver select for forms where a value might be preselected #} {# 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, border="") %} {% macro driver_select_with_preselect(driver_match, name, label, include_none, drivers=none, disabled=false, border="") %}
<div class="form-floating"> <div class="form-floating">
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" 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 #} {# Use namespace wrapper to persist scope between loop iterations #}
{% set user_has_chosen = namespace(driverpre=false) %} {% set user_has_chosen = namespace(driverpre=false) %}
@ -77,7 +79,8 @@
{# Simple team select for forms #} {# Simple team select for forms #}
{% macro team_select(name, label, include_none, teams=none, disabled=false, border="") %} {% macro team_select(name, label, include_none, teams=none, disabled=false, border="") %}
<div class="form-floating"> <div class="form-floating">
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" 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> <option value="" selected disabled hidden></option>
{% if teams == none %} {% if teams == none %}
@ -95,7 +98,8 @@
{# Team select for forms where a value might be preselected #} {# 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, border="") %} {% macro team_select_with_preselect(team_match, name, label, include_none, teams=none, disabled=false, border="") %}
<div class="form-floating"> <div class="form-floating">
<select name="{{ name }}" id="{{ name }}" class="form-select {{ border }}" 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 #} {# Use namespace wrapper to persist scope between loop iterations #}
{% set user_has_chosen = namespace(teampre=false) %} {% set user_has_chosen = namespace(teampre=false) %}
@ -218,7 +222,7 @@
<div class="flex-grow-1"></div> <div class="flex-grow-1"></div>
<div class="navbar-nav"> <div class="navbar-nav">
{{ nav_selector(page="/result", text="Enter Race Result") }} {# {{ nav_selector(page="/result", text="Enter Race Result") }} #}
{{ nav_selector(page="/user", text="Manage Users") }} {{ nav_selector(page="/user", text="Manage Users") }}
</div> </div>
</div> </div>