Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add newtypes for primary keys #512

Merged
merged 3 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading