diff --git a/formula10/controller/admin_controller.py b/formula10/controller/admin_controller.py index d3c593d..2cd1a51 100644 --- a/formula10/controller/admin_controller.py +++ b/formula10/controller/admin_controller.py @@ -2,6 +2,7 @@ from typing import List from urllib.parse import unquote from flask import redirect, render_template, request from werkzeug import Response +from formula10.controller.error_controller import error_redirect from formula10.database.update_queries import update_race_result, update_user from formula10.domain.domain_model import Model @@ -31,17 +32,16 @@ def result_enter_post(race_name: str) -> Response: dnfs: List[str] = request.form.getlist("dnf-drivers") excluded: List[str] = request.form.getlist("excluded-drivers") + # Extra stats for points calculation fastest_lap: str | None = request.form.get("fastest-lap") sprint_pxxs: List[str] = request.form.getlist("sprint-pxx-drivers") sprint_dnf_drivers: List[str] = request.form.getlist("sprint-dnf-drivers") - # @todo Integrate these into update_race_result() - print("Fastest lap:", fastest_lap) - print("Sprint:", sprint_pxxs) - print("Sprint DNFs:", sprint_dnf_drivers) + if fastest_lap is None: + return error_redirect("Data was not saved, because fastest lap was not set.") 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, int(fastest_lap), sprint_pxxs, sprint_dnf_drivers) @app.route("/user") diff --git a/formula10/database/update_queries.py b/formula10/database/update_queries.py index 626e2ac..6bb0ad2 100644 --- a/formula10/database/update_queries.py +++ b/formula10/database/update_queries.py @@ -13,7 +13,6 @@ 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, race_has_started 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: @@ -129,8 +128,8 @@ def find_or_create_race_result(race_id: int) -> DbRaceResult: race_result.excluded_driver_ids_json = json.dumps(["9999"]) race_result.fastest_lap_id = 9999 - race_result.sprint_dnf_driver_ids_json = json.dumps(["9999"]) - race_result.sprint_points_json = json.dumps({"9999": "9999"}) + race_result.sprint_dnf_driver_ids_json = json.dumps([]) + race_result.sprint_points_json = json.dumps({}) db.session.add(race_result) db.session.commit() @@ -143,21 +142,22 @@ def find_or_create_race_result(race_id: int) -> DbRaceResult: 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], + fastest_lap_driver_id: int, sprint_pxx_driver_ids_list: List[str], sprint_dnf_driver_ids_list: List[str]) -> Response: 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!") # Use strings as keys, as these dicts will be serialized to json - pxx_driver_names: Dict[str, str] = { + pxx_driver_ids: Dict[str, str] = { str(position + 1): driver_id for position, driver_id in enumerate(pxx_driver_ids_list) } # Not counted drivers have to be at the end - excluded_driver_names: Dict[str, str] = { + excluded_driver_ids: Dict[str, str] = { str(position + 1): driver_id for position, driver_id in enumerate(pxx_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_ids) > 0 and (not "20" in excluded_driver_ids or not positions_are_contiguous(list(excluded_driver_ids.keys()))): 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 @@ -170,15 +170,19 @@ def update_race_result(race_id: int, pxx_driver_ids_list: List[str], first_dnf_d 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.pxx_driver_ids_json = json.dumps(pxx_driver_names) + race_result.pxx_driver_ids_json = json.dumps(pxx_driver_ids) 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.excluded_driver_ids_json = json.dumps(excluded_driver_ids_list) - # @todo Dummy values - race_result.fastest_lap_id = 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}) + # Extra stats for points calculation + sprint_pxx_driver_ids: Dict[str, str] = { + str(position + 1): driver_id for position, driver_id in enumerate(sprint_pxx_driver_ids_list) + } + + race_result.fastest_lap_id = fastest_lap_driver_id + race_result.sprint_dnf_driver_ids_json = json.dumps(sprint_dnf_driver_ids_list) + race_result.sprint_points_json = json.dumps(sprint_pxx_driver_ids) db.session.commit() diff --git a/formula10/domain/model/race_result.py b/formula10/domain/model/race_result.py index 03835a0..3528f99 100644 --- a/formula10/domain/model/race_result.py +++ b/formula10/domain/model/race_result.py @@ -174,6 +174,11 @@ class RaceResult: self.standing[str(position)] for position in range(1, 21) ] + def ordered_sprint_standing_list(self) -> List[Driver]: + return [ + self.sprint_standing[str(position)] for position in range(1, 21) + ] + def initial_dnf_string(self) -> str: if len(self.initial_dnf) == 0: return NONE_DRIVER.name diff --git a/formula10/domain/points_model.py b/formula10/domain/points_model.py index 9bd42b7..a7378cb 100644 --- a/formula10/domain/points_model.py +++ b/formula10/domain/points_model.py @@ -11,6 +11,8 @@ from formula10.domain.model.season_guess_result import SeasonGuessResult from formula10.domain.model.team import Team from formula10.domain.model.user import User +# Guess points + RACE_GUESS_OFFSET_POINTS: Dict[int, int] = { 3: 1, 2: 3, @@ -18,7 +20,6 @@ RACE_GUESS_OFFSET_POINTS: Dict[int, int] = { 0: 10 } RACE_GUESS_DNF_POINTS: int = 10 - SEASON_GUESS_HOT_TAKE_POINTS: int = 10 SEASON_GUESS_P2_POINTS: int = 10 SEASON_GUESS_OVERTAKES_POINTS: int = 10 @@ -30,6 +31,8 @@ SEASON_GUESS_TEAMWINNER_FALSE_POINTS: int = -3 SEASON_GUESS_PODIUMS_CORRECT_POINTS: int = 3 SEASON_GUESS_PODIUMS_FALSE_POINTS: int = -2 +# Driver points + DRIVER_RACE_POINTS: Dict[int, int] = { 1: 25, 2: 18, @@ -42,6 +45,19 @@ DRIVER_RACE_POINTS: Dict[int, int] = { 9: 2, 10: 1 } +DRIVER_SPRINT_POINTS: Dict[int, int] = { + 1: 8, + 2: 7, + 3: 6, + 4: 5, + 5: 4, + 6: 3, + 7: 2, + 8: 1 +} +DRIVER_FASTEST_LAP_POINTS: int = 1 + +# Last season results WDC_STANDING_2023: Dict[str, int] = { "Max Verstappen": 1, @@ -65,7 +81,6 @@ WDC_STANDING_2023: Dict[str, int] = { "Kevin Magnussen": 19, "Logan Sargeant": 21 } - WCC_STANDING_2023: Dict[str, int] = { "Red Bull": 1, "Mercedes": 2, @@ -133,7 +148,6 @@ class PointsModel(Model): return self._points_per_step - # @todo Doesn't include fastest lap + sprint points def driver_points_per_step(self) -> Dict[str, List[int]]: """ Returns a dictionary of lists, containing points per race for each driver. @@ -144,15 +158,19 @@ class PointsModel(Model): self._driver_points_per_step[driver.name] = [0] * (len(self.all_races()) + 1) # Start at index 1, like the race numbers for race_result in self.all_race_results(): - for position, driver in race_result.standing.items(): - driver_name: str = driver.name - race_number: int = race_result.race.number + race_number: int = race_result.race.number - self._driver_points_per_step[driver_name][race_number] = DRIVER_RACE_POINTS[int(position)] if int(position) in DRIVER_RACE_POINTS else 0 + for position, driver in race_result.standing.items(): + self._driver_points_per_step[driver.name][race_number] = DRIVER_RACE_POINTS[int(position)] if int(position) in DRIVER_RACE_POINTS else 0 + self._driver_points_per_step[driver.name][race_number] += DRIVER_FASTEST_LAP_POINTS if race_result.fastest_lap_driver == driver else 0 + + for position, driver in race_result.sprint_standing.items(): + driver_name: str = driver.name + + self._driver_points_per_step[driver_name][race_number] += DRIVER_SPRINT_POINTS[int(position)] if int(position) in DRIVER_SPRINT_POINTS else 0 return self._driver_points_per_step - # @todo Doesn't include fastest lap + sprint points def team_points_per_step(self) -> Dict[str, List[int]]: """ Returns a dictionary of lists, containing points per race for each team. @@ -163,15 +181,14 @@ class PointsModel(Model): self._team_points_per_step[team.name] = [0] * (len(self.all_races()) + 1) # Start at index 1, like the race numbers for race_result in self.all_race_results(): - for position, driver in race_result.standing.items(): + for driver in race_result.standing.values(): team_name: str = driver.team.name race_number: int = race_result.race.number - self._team_points_per_step[team_name][race_number] += DRIVER_RACE_POINTS[int(position)] if int(position) in DRIVER_RACE_POINTS else 0 + self._team_points_per_step[team_name][race_number] += self.driver_points_per_step()[driver.name][race_number] return self._team_points_per_step - # @todo Doesn't include sprint dnfs def dnfs(self) -> Dict[str, int]: if self._dnfs is None: self._dnfs = dict() @@ -183,6 +200,9 @@ class PointsModel(Model): for driver in race_result.all_dnfs: self._dnfs[driver.name] += 1 + for driver in race_result.sprint_dnfs: + self._dnfs[driver.name] += 1 + return self._dnfs # diff --git a/formula10/domain/template_model.py b/formula10/domain/template_model.py index 8d2bdca..e1433fe 100644 --- a/formula10/domain/template_model.py +++ b/formula10/domain/template_model.py @@ -96,6 +96,9 @@ class TemplateModel(Model): def all_drivers_or_active_result_standing_drivers(self) -> List[Driver]: return self.active_result.ordered_standing_list() if self.active_result is not None else self.all_drivers(include_none=False) + def all_drivers_or_active_result_sprint_standing_drivers(self) -> List[Driver]: + return self.active_result.ordered_sprint_standing_list() if self.active_result is not None else self.all_drivers(include_none=False) + def drivers_for_wdc_gained(self) -> List[Driver]: predicate: Callable[[Driver], bool] = lambda driver: driver.abbr not in self._wdc_gained_excluded_abbrs return find_multiple_strict(predicate, self.all_drivers(include_none=False)) diff --git a/formula10/templates/result.jinja b/formula10/templates/result.jinja index 3390b54..93ee95f 100644 --- a/formula10/templates/result.jinja +++ b/formula10/templates/result.jinja @@ -155,7 +155,7 @@ {# Place numbers #}