Skip to content

Commit

Permalink
Add newtypes for primary keys (#512)
Browse files Browse the repository at this point in the history
Adds type safety
  • Loading branch information
evroon committed Feb 23, 2024
1 parent ed76eea commit b395d14
Show file tree
Hide file tree
Showing 55 changed files with 622 additions and 391 deletions.
9 changes: 5 additions & 4 deletions backend/bracket/logic/planning/matches.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
)
from bracket.sql.stages import get_full_tournament_details
from bracket.sql.tournaments import sql_get_tournament
from bracket.utils.id_types import CourtId, MatchId, TournamentId
from bracket.utils.types import assert_some


async def schedule_all_unscheduled_matches(tournament_id: int) -> None:
async def schedule_all_unscheduled_matches(tournament_id: TournamentId) -> None:
tournament = await sql_get_tournament(tournament_id)
stages = await get_full_tournament_details(tournament_id)
courts = await get_all_courts_in_tournament(tournament_id)
Expand Down Expand Up @@ -81,7 +82,7 @@ class MatchPosition(NamedTuple):
async def reorder_matches_for_court(
tournament: Tournament,
scheduled_matches: list[MatchPosition],
court_id: int,
court_id: CourtId,
) -> None:
matches_this_court = sorted(
(match_pos for match_pos in scheduled_matches if match_pos.match.court_id == court_id),
Expand All @@ -104,7 +105,7 @@ async def reorder_matches_for_court(


async def handle_match_reschedule(
tournament_id: int, body: MatchRescheduleBody, match_id: int
tournament_id: TournamentId, body: MatchRescheduleBody, match_id: MatchId
) -> None:
if body.old_position == body.new_position and body.old_court_id == body.new_court_id:
return
Expand Down Expand Up @@ -143,7 +144,7 @@ async def handle_match_reschedule(
await reorder_matches_for_court(tournament, scheduled_matches, body.old_court_id)


async def update_start_times_of_matches(tournament_id: int) -> None:
async def update_start_times_of_matches(tournament_id: TournamentId) -> None:
stages = await get_full_tournament_details(tournament_id)
tournament = await sql_get_tournament(tournament_id)
courts = await get_all_courts_in_tournament(tournament_id)
Expand Down
5 changes: 4 additions & 1 deletion backend/bracket/logic/planning/rounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from bracket.sql.stages import get_full_tournament_details
from bracket.sql.tournaments import sql_get_tournament
from bracket.utils.id_types import TournamentId
from bracket.utils.types import assert_some


Expand Down Expand Up @@ -36,7 +37,9 @@ def is_round_in_future(round_: RoundWithMatches) -> bool:


async def schedule_all_matches_for_swiss_round(
tournament_id: int, active_round: RoundWithMatches, adjust_to_time: datetime_utc | None = None
tournament_id: TournamentId,
active_round: RoundWithMatches,
adjust_to_time: datetime_utc | None = None,
) -> None:
courts = await get_all_courts_in_tournament(tournament_id)
stages = await get_full_tournament_details(tournament_id)
Expand Down
21 changes: 13 additions & 8 deletions backend/bracket/logic/ranking/elo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import math
from collections import defaultdict
from decimal import Decimal
from typing import TypeVar

from bracket.database import database
from bracket.models.db.match import MatchWithDetailsDefinitive
Expand All @@ -10,17 +11,21 @@
from bracket.sql.players import get_all_players_in_tournament, update_player_stats
from bracket.sql.stages import get_full_tournament_details
from bracket.sql.teams import update_team_stats
from bracket.utils.id_types import PlayerId, TeamId, TournamentId
from bracket.utils.types import assert_some

K = 32
D = 400


TeamIdOrPlayerId = TypeVar("TeamIdOrPlayerId", bound=PlayerId | TeamId)


def set_statistics_for_player_or_team(
team_index: int,
stats: defaultdict[int, PlayerStatistics],
stats: defaultdict[TeamIdOrPlayerId, PlayerStatistics],
match: MatchWithDetailsDefinitive,
team_or_player_id: int,
team_or_player_id: TeamIdOrPlayerId,
rating_team1_before: float,
rating_team2_before: float,
) -> None:
Expand Down Expand Up @@ -48,9 +53,9 @@ def set_statistics_for_player_or_team(

def determine_ranking_for_stage_items(
stage_items: list[StageItemWithRounds],
) -> tuple[defaultdict[int, PlayerStatistics], defaultdict[int, PlayerStatistics]]:
player_x_stats: defaultdict[int, PlayerStatistics] = defaultdict(PlayerStatistics)
team_x_stats: defaultdict[int, PlayerStatistics] = defaultdict(PlayerStatistics)
) -> tuple[defaultdict[PlayerId, PlayerStatistics], defaultdict[TeamId, PlayerStatistics]]:
player_x_stats: defaultdict[PlayerId, PlayerStatistics] = defaultdict(PlayerStatistics)
team_x_stats: defaultdict[TeamId, PlayerStatistics] = defaultdict(PlayerStatistics)
matches = [
match
for stage_item in stage_items
Expand Down Expand Up @@ -100,19 +105,19 @@ def determine_ranking_for_stage_items(

def determine_team_ranking_for_stage_item(
stage_item: StageItemWithRounds,
) -> list[tuple[int, PlayerStatistics]]:
) -> list[tuple[TeamId, PlayerStatistics]]:
_, team_ranking = determine_ranking_for_stage_items([stage_item])
return sorted(team_ranking.items(), key=lambda x: x[1].elo_score, reverse=True)


async def recalculate_ranking_for_tournament_id(tournament_id: int) -> None:
async def recalculate_ranking_for_tournament_id(tournament_id: TournamentId) -> None:
stages = await get_full_tournament_details(tournament_id)
stage_items = [stage_item for stage in stages for stage_item in stage.stage_items]
await recalculate_ranking_for_stage_items(tournament_id, stage_items)


async def recalculate_ranking_for_stage_items(
tournament_id: int, stage_items: list[StageItemWithRounds]
tournament_id: TournamentId, stage_items: list[StageItemWithRounds]
) -> None:
elo_per_player, elo_per_team = determine_ranking_for_stage_items(stage_items)

Expand Down
9 changes: 6 additions & 3 deletions backend/bracket/logic/scheduling/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
from bracket.models.db.util import StageWithStageItems
from bracket.sql.rounds import get_next_round_name, sql_create_round
from bracket.sql.stage_items import get_stage_item
from bracket.utils.id_types import StageId, TournamentId
from bracket.utils.types import assert_some


async def create_rounds_for_new_stage_item(tournament_id: int, stage_item: StageItem) -> None:
async def create_rounds_for_new_stage_item(
tournament_id: TournamentId, stage_item: StageItem
) -> None:
rounds_count: int
match stage_item.type:
case StageType.ROUND_ROBIN:
Expand All @@ -42,7 +45,7 @@ async def create_rounds_for_new_stage_item(tournament_id: int, stage_item: Stage
)


async def build_matches_for_stage_item(stage_item: StageItem, tournament_id: int) -> None:
async def build_matches_for_stage_item(stage_item: StageItem, tournament_id: TournamentId) -> None:
await create_rounds_for_new_stage_item(tournament_id, stage_item)
stage_item_with_rounds = await get_stage_item(tournament_id, assert_some(stage_item.id))

Expand All @@ -66,7 +69,7 @@ async def build_matches_for_stage_item(stage_item: StageItem, tournament_id: int


def determine_available_inputs(
stage_id: int,
stage_id: StageId,
teams: list[FullTeamWithPlayers],
stages: list[StageWithStageItems],
) -> list[StageItemInputOptionTentative | StageItemInputOptionFinal]:
Expand Down
3 changes: 2 additions & 1 deletion backend/bracket/logic/scheduling/elimination.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from bracket.sql.matches import sql_create_match
from bracket.sql.rounds import get_rounds_for_stage_item
from bracket.sql.tournaments import sql_get_tournament
from bracket.utils.id_types import TournamentId
from bracket.utils.types import assert_some


Expand Down Expand Up @@ -70,7 +71,7 @@ def determine_matches_subsequent_round(


async def build_single_elimination_stage_item(
tournament_id: int, stage_item: StageItemWithRounds
tournament_id: TournamentId, stage_item: StageItemWithRounds
) -> None:
rounds = await get_rounds_for_stage_item(tournament_id, stage_item.id)
tournament = await sql_get_tournament(tournament_id)
Expand Down
13 changes: 7 additions & 6 deletions backend/bracket/logic/scheduling/handle_stage_activation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
from bracket.sql.matches import sql_get_match, sql_update_team_ids_for_match
from bracket.sql.stage_items import get_stage_item
from bracket.sql.stages import get_full_tournament_details
from bracket.utils.id_types import MatchId, StageId, StageItemId, TeamId, TournamentId
from bracket.utils.types import assert_some


async def determine_team_id(
tournament_id: int,
winner_from_stage_item_id: int | None,
tournament_id: TournamentId,
winner_from_stage_item_id: StageItemId | None,
winner_position: int | None,
winner_from_match_id: int | None,
) -> int | None:
winner_from_match_id: MatchId | None,
) -> TeamId | None:
if winner_from_stage_item_id is not None and winner_position is not None:
stage_item = await get_stage_item(tournament_id, winner_from_stage_item_id)
assert stage_item is not None
Expand All @@ -37,7 +38,7 @@ async def determine_team_id(
raise ValueError("Unexpected match type")


async def set_team_ids_for_match(tournament_id: int, match: MatchWithDetails) -> None:
async def set_team_ids_for_match(tournament_id: TournamentId, match: MatchWithDetails) -> None:
team1_id = await determine_team_id(
tournament_id,
match.team1_winner_from_stage_item_id,
Expand All @@ -54,7 +55,7 @@ async def set_team_ids_for_match(tournament_id: int, match: MatchWithDetails) ->
await sql_update_team_ids_for_match(assert_some(match.id), team1_id, team2_id)


async def update_matches_in_activated_stage(tournament_id: int, stage_id: int) -> None:
async def update_matches_in_activated_stage(tournament_id: TournamentId, stage_id: StageId) -> None:
[stage] = await get_full_tournament_details(tournament_id, stage_id=stage_id)

for stage_item in stage.stage_items:
Expand Down
9 changes: 5 additions & 4 deletions backend/bracket/logic/scheduling/ladder_teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@
)
from bracket.models.db.team import FullTeamWithPlayers
from bracket.models.db.util import RoundWithMatches
from bracket.utils.id_types import TeamId
from bracket.utils.types import assert_some


def get_draft_round_team_ids(draft_round: RoundWithMatches) -> list[int]:
def get_draft_round_team_ids(draft_round: RoundWithMatches) -> list[TeamId]:
return [
team
team_id
for match in draft_round.matches
if isinstance(match, MatchWithDetailsDefinitive)
for team in match.team_ids
for team_id in match.team_ids
]


Expand All @@ -37,7 +38,7 @@ def get_previous_matches_hashes(rounds: list[RoundWithMatches]) -> frozenset[str


def get_number_of_teams_played_per_team(
rounds: list[RoundWithMatches], excluded_team_ids: frozenset[int]
rounds: list[RoundWithMatches], excluded_team_ids: frozenset[TeamId]
) -> dict[int, int]:
result: dict[int, int] = defaultdict(int)

Expand Down
5 changes: 4 additions & 1 deletion backend/bracket/logic/scheduling/round_robin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from bracket.models.db.util import StageItemWithRounds
from bracket.sql.matches import sql_create_match
from bracket.sql.tournaments import sql_get_tournament
from bracket.utils.id_types import TournamentId
from bracket.utils.types import assert_some


Expand Down Expand Up @@ -35,7 +36,9 @@ def get_round_robin_combinations(team_count: int) -> list[list[tuple[int, int]]]
return matches


async def build_round_robin_stage_item(tournament_id: int, stage_item: StageItemWithRounds) -> None:
async def build_round_robin_stage_item(
tournament_id: TournamentId, stage_item: StageItemWithRounds
) -> None:
matches = get_round_robin_combinations(len(stage_item.inputs))
tournament = await sql_get_tournament(tournament_id)

Expand Down
7 changes: 5 additions & 2 deletions backend/bracket/logic/scheduling/upcoming_matches.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
from bracket.sql.rounds import get_rounds_for_stage_item
from bracket.sql.stages import get_full_tournament_details
from bracket.sql.teams import get_teams_with_members
from bracket.utils.id_types import TournamentId
from bracket.utils.types import assert_some


async def get_upcoming_matches_for_swiss_round(
match_filter: MatchFilter, round_: Round, tournament_id: int
match_filter: MatchFilter, round_: Round, tournament_id: TournamentId
) -> list[SuggestedMatch]:
[stage] = await get_full_tournament_details(tournament_id, stage_item_id=round_.stage_item_id)
[stage] = await get_full_tournament_details(
tournament_id, stage_item_ids={round_.stage_item_id}
)
assert len(stage.stage_items) == 1
[stage_item] = stage.stage_items

Expand Down
7 changes: 4 additions & 3 deletions backend/bracket/logic/tournaments.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@
from bracket.sql.stages import get_full_tournament_details, sql_delete_stage
from bracket.sql.teams import sql_delete_teams_of_tournament
from bracket.sql.tournaments import sql_delete_tournament, sql_get_tournament
from bracket.utils.id_types import TournamentId
from bracket.utils.types import assert_some


async def get_tournament_logo_path(tournament_id: int) -> str | None:
async def get_tournament_logo_path(tournament_id: TournamentId) -> str | None:
tournament = await sql_get_tournament(tournament_id)
logo_path = f"static/{tournament.logo_path}" if tournament.logo_path else None
return logo_path if logo_path is not None and await aiofiles.os.path.exists(logo_path) else None


async def delete_tournament_logo(tournament_id: int) -> None:
async def delete_tournament_logo(tournament_id: TournamentId) -> None:
logo_path = await get_tournament_logo_path(tournament_id)
if logo_path is not None:
await aiofiles.os.remove(logo_path)


async def sql_delete_tournament_completely(tournament_id: int) -> None:
async def sql_delete_tournament_completely(tournament_id: TournamentId) -> None:
stages = await get_full_tournament_details(tournament_id)
await delete_tournament_logo(tournament_id)

Expand Down
3 changes: 2 additions & 1 deletion backend/bracket/models/db/club.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from heliclockter import datetime_utc

from bracket.models.db.shared import BaseModelORM
from bracket.utils.id_types import ClubId


class Club(BaseModelORM):
id: int | None = None
id: ClubId | None = None
name: str
created: datetime_utc

Expand Down
7 changes: 4 additions & 3 deletions backend/bracket/models/db/court.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from heliclockter import datetime_utc

from bracket.models.db.shared import BaseModelORM
from bracket.utils.id_types import CourtId, TournamentId


class Court(BaseModelORM):
id: int | None = None
id: CourtId | None = None
name: str
created: datetime_utc
tournament_id: int
tournament_id: TournamentId


class CourtBody(BaseModelORM):
Expand All @@ -16,4 +17,4 @@ class CourtBody(BaseModelORM):

class CourtToInsert(CourtBody):
created: datetime_utc
tournament_id: int
tournament_id: TournamentId
Loading

0 comments on commit b395d14

Please sign in to comment.