Pydantic v2 migration (#252)

Upgrade Pydantic to V2.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Erik Vroon
2024-02-09 11:51:14 +01:00
committed by GitHub
parent ea7ff4c374
commit 61611066cd
57 changed files with 361 additions and 315 deletions

View File

@@ -42,6 +42,8 @@ jobs:
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Run mypy
run: pipenv run mypy --version && pipenv run mypy .

View File

@@ -9,15 +9,16 @@ aiopg = ">=1.4.0"
alembic = ">=1.9.1"
bcrypt = "4.1.2"
click = ">=8.1.3"
databases = {extras = ["asyncpg"], version = ">=0.7.0"}
databases = {extras = ["asyncpg"], version = "<=0.7.0"}
fastapi = ">=0.88.0"
fastapi-cache2 = ">=0.2.0"
fastapi-sso = ">=0.6.4"
gunicorn = ">=20.1.0"
heliclockter = ">=1.0.4"
heliclockter = ">=1.3.0"
parameterized = ">=0.8.1"
passlib = ">=1.7.4"
pydantic = "<2.0.0"
pydantic = "<3.0.0"
pydantic_settings = "2.1.0"
pyjwt = ">=2.6.0"
python-dotenv = ">=0.21.0"
python-multipart = ">=0.0.5"
@@ -32,9 +33,10 @@ uvicorn = ">=0.20.0"
[dev-packages]
aioresponses = ">=0.7.4"
mypy = ">=1.3.1"
pluggy = "<=1.3.0"
pylint = ">=2.15.10"
pytest = "8.0.0"
pytest-asyncio = "0.20.3"
pytest = "<=7.4.3"
pytest-asyncio = "<=0.21.1"
pytest-cov = ">=4.0.0"
pytest-xdist = ">=3.2.1"
ruff = ">=0.0.292"

View File

@@ -1,9 +1,11 @@
import logging
import os
from enum import auto
from typing import Annotated
import sentry_sdk
from pydantic import BaseSettings, PostgresDsn
from pydantic import Field, PostgresDsn
from pydantic_settings import BaseSettings, SettingsConfigDict
from bracket.utils.types import EnumAutoStr
@@ -39,31 +41,29 @@ class Config(BaseSettings):
class CIConfig(Config):
class Config:
env_file = "ci.env"
model_config = SettingsConfigDict(env_file="ci.env")
class DevelopmentConfig(Config):
admin_email = "test@example.org"
admin_password = "aeGhoe1ahng2Aezai0Dei6Aih6dieHoo"
allow_insecure_http_sso = True
jwt_secret = "7495204c062787f257b12d03b88d80da1d338796a6449666eb634c9efbbf5fa7"
admin_email: Annotated[str, Field("test@example.org")]
admin_password: Annotated[str, Field("aeGhoe1ahng2Aezai0Dei6Aih6dieHoo")]
allow_insecure_http_sso: Annotated[bool, Field(True)]
jwt_secret: Annotated[
str, Field("7495204c062787f257b12d03b88d80da1d338796a6449666eb634c9efbbf5fa7")
]
class Config:
env_file = "dev.env"
model_config = SettingsConfigDict(env_file="dev.env")
class ProductionConfig(Config):
class Config:
env_file = "prod.env"
model_config = SettingsConfigDict(env_file="prod.env")
class DemoConfig(Config):
class Config:
env_file = "demo.env"
model_config = SettingsConfigDict(env_file="demo.env")
environment = Environment(os.getenv("ENVIRONMENT", "CI").upper())
environment = Environment(os.getenv("ENVIRONMENT", "DEVELOPMENT").upper())
config: Config
match environment:

View File

@@ -1,8 +1,27 @@
from typing import Any
import sqlalchemy
from databases import Database
from heliclockter import datetime_utc
from bracket.config import config
database = Database(config.pg_dsn)
engine = sqlalchemy.create_engine(config.pg_dsn)
def datetime_decoder(value: str) -> datetime_utc:
value = value.split(".")[0].replace("+00", "+00:00")
return datetime_utc.fromisoformat(value)
async def asyncpg_init(connection: Any) -> None:
for timestamp_type in ("timestamp", "timestamptz"):
await connection.set_type_codec(
timestamp_type,
encoder=datetime_utc.isoformat,
decoder=datetime_decoder,
schema="pg_catalog",
)
database = Database(str(config.pg_dsn), init=asyncpg_init)
engine = sqlalchemy.create_engine(str(config.pg_dsn))

View File

@@ -103,7 +103,7 @@ async def todo_schedule_all_matches(tournament_id: int) -> None:
matches_per_team: dict[int | None, list[Match]] = defaultdict(list)
matches_to_schedule = [
match.copy(update={"court_id": None, "position_in_schedule": None})
match.model_copy(update={"court_id": None, "position_in_schedule": None})
for stage in stages
for stage_item in stage.stage_items
for round_ in stage_item.rounds
@@ -156,7 +156,7 @@ async def iterative_scheduling(
start_time = tournament.start_time
position_in_schedule = 0
updated_match = match.copy(
updated_match = match.model_copy(
update={
"start_time": start_time,
"position_in_schedule": position_in_schedule,
@@ -238,7 +238,7 @@ async def handle_match_reschedule(
)
scheduled_matches.append(
MatchPosition(
match=match_pos.match.copy(update={"court_id": body.new_court_id}),
match=match_pos.match.model_copy(update={"court_id": body.new_court_id}),
position=body.new_position + offset,
)
)

View File

@@ -36,7 +36,7 @@ def get_active_and_next_rounds(
async def schedule_all_matches_for_swiss_round(
tournament_id: int, active_round: RoundWithMatches, adjust_to_time: datetime_utc | None
tournament_id: int, 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)
@@ -72,7 +72,7 @@ async def schedule_all_matches_for_swiss_round(
)
if timing_difference_minutes != 0:
last_match_adjusted = last_match.match.copy(
last_match_adjusted = last_match.match.model_copy(
update={
"custom_margin_minutes": last_match.match.margin_minutes
+ timing_difference_minutes

View File

@@ -129,5 +129,5 @@ async def recalculate_ranking_for_stage_items(
query=players.update().where(
(players.c.id == player.id) & (players.c.tournament_id == tournament_id)
),
values=PlayerStatistics().dict(),
values=PlayerStatistics().model_dump(),
)

View File

@@ -18,7 +18,6 @@ from bracket.models.db.stage_item_inputs import (
)
from bracket.models.db.team import FullTeamWithPlayers
from bracket.models.db.util import StageWithStageItems
from bracket.schema import rounds
from bracket.sql.rounds import get_next_round_name
from bracket.sql.stage_items import get_stage_item
from bracket.utils.types import assert_some
@@ -39,12 +38,15 @@ async def create_rounds_for_new_stage_item(tournament_id: int, stage_item: Stage
now = datetime_utc.now()
for _ in range(rounds_count):
await database.execute(
query=rounds.insert(),
query="""
INSERT INTO rounds (created, stage_item_id, name, is_draft, is_active)
VALUES (:created, :stage_item_id, :name, :is_draft, :is_active)
""",
values=RoundToInsert(
created=now,
stage_item_id=assert_some(stage_item.id),
name=await get_next_round_name(tournament_id, assert_some(stage_item.id)),
).dict(),
).model_dump(),
)

View File

@@ -12,16 +12,16 @@ from bracket.utils.types import assert_some
class MatchBase(BaseModelORM):
id: int | None = None
created: datetime_utc
start_time: datetime_utc | None
start_time: datetime_utc | None = None
duration_minutes: int
margin_minutes: int
custom_duration_minutes: int | None
custom_margin_minutes: int | None
position_in_schedule: int | None
custom_duration_minutes: int | None = None
custom_margin_minutes: int | None = None
position_in_schedule: int | None = None
round_id: int
team1_score: int
team2_score: int
court_id: int | None
court_id: int | None = None
@property
def end_time(self) -> datetime_utc:
@@ -32,14 +32,14 @@ class MatchBase(BaseModelORM):
class Match(MatchBase):
team1_id: int | None
team2_id: int | None
team1_winner_position: int | None
team1_winner_from_stage_item_id: int | None
team2_winner_from_stage_item_id: int | None
team2_winner_position: int | None
team1_winner_from_match_id: int | None
team2_winner_from_match_id: int | None
team1_id: int | None = None
team2_id: int | None = None
team1_winner_position: int | None = None
team1_winner_from_stage_item_id: int | None = None
team2_winner_from_stage_item_id: int | None = None
team2_winner_position: int | None = None
team1_winner_from_match_id: int | None = None
team2_winner_from_match_id: int | None = None
def get_winner_index(self) -> int | None:
if self.team1_score == self.team2_score:
@@ -49,7 +49,7 @@ class Match(MatchBase):
class MatchWithDetails(Match):
court: Court | None
court: Court | None = None
def get_match_hash(team_1_id: int | None, team_2_id: int | None) -> str:
@@ -59,7 +59,7 @@ def get_match_hash(team_1_id: int | None, team_2_id: int | None) -> str:
class MatchWithDetailsDefinitive(Match):
team1: FullTeamWithPlayers
team2: FullTeamWithPlayers
court: Court | None
court: Court | None = None
@property
def teams(self) -> list[FullTeamWithPlayers]:
@@ -84,29 +84,29 @@ class MatchBody(BaseModelORM):
round_id: int
team1_score: int = 0
team2_score: int = 0
court_id: int | None
custom_duration_minutes: int | None
custom_margin_minutes: int | None
court_id: int | None = None
custom_duration_minutes: int | None = None
custom_margin_minutes: int | None = None
class MatchCreateBodyFrontend(BaseModelORM):
round_id: int
court_id: int | None
team1_id: int | None
team2_id: int | None
team1_winner_from_stage_item_id: int | None
team1_winner_position: int | None
team1_winner_from_match_id: int | None
team2_winner_from_stage_item_id: int | None
team2_winner_position: int | None
team2_winner_from_match_id: int | None
court_id: int | None = None
team1_id: int | None = None
team2_id: int | None = None
team1_winner_from_stage_item_id: int | None = None
team1_winner_position: int | None = None
team1_winner_from_match_id: int | None = None
team2_winner_from_stage_item_id: int | None = None
team2_winner_position: int | None = None
team2_winner_from_match_id: int | None = None
class MatchCreateBody(MatchCreateBodyFrontend):
duration_minutes: int
margin_minutes: int
custom_duration_minutes: int | None
custom_margin_minutes: int | None
custom_duration_minutes: int | None = None
custom_margin_minutes: int | None = None
class MatchRescheduleBody(BaseModelORM):

View File

@@ -19,7 +19,7 @@ class RoundUpdateBody(BaseModelORM):
class RoundCreateBody(BaseModelORM):
name: str | None
name: str | None = None
stage_item_id: int

View File

@@ -1,19 +1,11 @@
from datetime import datetime
from typing import Any
from heliclockter import datetime_utc
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
class BaseModelORM(BaseModel):
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
def dict(self, **kwargs: Any) -> Any:
def model_dump(self, **kwargs: Any) -> Any:
kwargs["exclude_none"] = True
result = super().dict(**kwargs)
for k, v in result.items():
if isinstance(v, datetime_utc):
result[k] = datetime.fromisoformat(v.isoformat())
return result
return super().model_dump(**kwargs)

View File

@@ -2,7 +2,7 @@ from enum import auto
from typing import Any
from heliclockter import datetime_utc
from pydantic import Field, root_validator
from pydantic import Field, model_validator
from bracket.models.db.shared import BaseModelORM
from bracket.models.db.stage_item_inputs import StageItemInputCreateBody
@@ -37,12 +37,12 @@ class StageItemUpdateBody(BaseModelORM):
class StageItemActivateNextBody(BaseModelORM):
adjust_to_time: datetime_utc | None
adjust_to_time: datetime_utc | None = None
class StageItemCreateBody(BaseModelORM):
stage_id: int
name: str | None
name: str | None = None
type: StageType
team_count: int = Field(ge=2, le=64)
inputs: list[StageItemInputCreateBody]
@@ -50,7 +50,7 @@ class StageItemCreateBody(BaseModelORM):
def get_name_or_default_name(self) -> str:
return self.name if self.name is not None else self.type.value.replace("_", " ").title()
@root_validator
@model_validator(mode="before")
def handle_inputs_length(cls, values: Any) -> Any:
if ("inputs" in values and "team_count" in values) and (
len(values["inputs"]) != values["team_count"]

View File

@@ -4,17 +4,17 @@ from bracket.models.db.shared import BaseModelORM
class StageItemInputBase(BaseModelORM):
id: int | None
id: int | None = None
slot: int
tournament_id: int
stage_item_id: int | None
stage_item_id: int | None = None
class StageItemInputGeneric(BaseModel):
team_id: int | None
winner_from_stage_item_id: int | None
winner_position: int | None
winner_from_match_id: int | None
team_id: int | None = None
winner_from_stage_item_id: int | None = None
winner_position: int | None = None
winner_from_match_id: int | None = None
def __hash__(self) -> int:
return (

View File

@@ -3,9 +3,10 @@ from __future__ import annotations
# ruff: noqa: TCH001,TCH002
import json
from decimal import Decimal
from typing import Annotated
from heliclockter import datetime_utc
from pydantic import BaseModel, Field, validator
from pydantic import BaseModel, Field, StringConstraints, field_validator
from bracket.models.db.player import Player
from bracket.models.db.players import START_ELO
@@ -40,7 +41,7 @@ class TeamWithPlayers(BaseModel):
def player_ids(self) -> list[int]:
return [assert_some(player.id) for player in self.players]
@validator("players", pre=True)
@field_validator("players", mode="before")
def handle_players(values: list[Player]) -> list[Player]: # type: ignore[misc]
if isinstance(values, str):
values_json = json.loads(values)
@@ -76,9 +77,9 @@ class FullTeamWithPlayers(TeamWithPlayers, Team):
class TeamBody(BaseModelORM):
name: str = Field(..., min_length=1, max_length=30)
name: Annotated[str, StringConstraints(min_length=1, max_length=30)]
active: bool
player_ids: list[int] = Field(..., unique_items=True)
player_ids: set[int]
class TeamMultiBody(BaseModelORM):

View File

@@ -13,8 +13,8 @@ class Tournament(BaseModelORM):
duration_minutes: int = Field(..., ge=1)
margin_minutes: int = Field(..., ge=0)
dashboard_public: bool
dashboard_endpoint: str | None
logo_path: str | None
dashboard_endpoint: str | None = None
logo_path: str | None = None
players_can_be_in_multiple_teams: bool
auto_assign_courts: bool
@@ -23,7 +23,7 @@ class TournamentUpdateBody(BaseModelORM):
start_time: datetime_utc
name: str
dashboard_public: bool
dashboard_endpoint: str | None
dashboard_endpoint: str | None = None
players_can_be_in_multiple_teams: bool
auto_assign_courts: bool
duration_minutes: int = Field(..., ge=1)

View File

@@ -4,7 +4,7 @@ from __future__ import annotations
import json
from typing import Any
from pydantic import root_validator, validator
from pydantic import field_validator, model_validator
from bracket.models.db.match import Match, MatchWithDetails, MatchWithDetailsDefinitive
from bracket.models.db.round import Round
@@ -17,7 +17,7 @@ from bracket.utils.types import assert_some
class RoundWithMatches(Round):
matches: list[MatchWithDetailsDefinitive | MatchWithDetails]
@validator("matches", pre=True)
@field_validator("matches", mode="before")
def handle_matches(values: list[Match]) -> list[Match]: # type: ignore[misc]
if values == [None]:
return []
@@ -37,7 +37,7 @@ class StageItemWithRounds(StageItem):
inputs: list[StageItemInput]
type_name: str
@root_validator(pre=True)
@model_validator(mode="before")
def fill_type_name(cls, values: Any) -> Any:
match values["type"]:
case str() as type_:
@@ -47,7 +47,7 @@ class StageItemWithRounds(StageItem):
return values
@validator("rounds", "inputs", pre=True)
@field_validator("rounds", "inputs", mode="before")
def handle_empty_list_elements(values: list[Any] | None) -> list[Any]: # type: ignore[misc]
if values is None:
return []
@@ -57,7 +57,7 @@ class StageItemWithRounds(StageItem):
class StageWithStageItems(Stage):
stage_items: list[StageItemWithRounds]
@validator("stage_items", pre=True)
@field_validator("stage_items", mode="before")
def handle_stage_items(values: list[StageItemWithRounds]) -> list[StageItemWithRounds]: # type: ignore[misc]
if isinstance(values, str):
values_json = json.loads(values)

View File

@@ -86,7 +86,7 @@ async def check_jwt_and_get_user(token: str) -> UserPublic | None:
if user is None:
return None
return UserPublic.parse_obj(user.dict())
return UserPublic.model_validate(user.model_dump())
async def user_authenticated(token: str = Depends(oauth2_scheme)) -> UserPublic:
@@ -98,7 +98,7 @@ async def user_authenticated(token: str = Depends(oauth2_scheme)) -> UserPublic:
headers={"WWW-Authenticate": "Bearer"},
)
return UserPublic.parse_obj(user.dict())
return UserPublic.model_validate(user.model_dump())
async def user_authenticated_for_tournament(
@@ -113,7 +113,7 @@ async def user_authenticated_for_tournament(
headers={"WWW-Authenticate": "Bearer"},
)
return UserPublic.parse_obj(user.dict())
return UserPublic.model_validate(user.model_dump())
async def user_authenticated_for_club(
@@ -128,7 +128,7 @@ async def user_authenticated_for_club(
headers={"WWW-Authenticate": "Bearer"},
)
return UserPublic.parse_obj(user.dict())
return UserPublic.model_validate(user.model_dump())
async def user_authenticated_or_public_dashboard(

View File

@@ -88,10 +88,10 @@ async def create_court(
last_record_id = await database.execute(
query=courts.insert(),
values=CourtToInsert(
**court_body.dict(),
**court_body.model_dump(),
created=datetime_utc.now(),
tournament_id=tournament_id,
).dict(),
).model_dump(),
)
return SingleCourtResponse(
data=assert_some(

View File

@@ -79,7 +79,7 @@ async def create_match(
) -> SingleMatchResponse:
tournament = await sql_get_tournament(tournament_id)
body_with_durations = MatchCreateBody(
**match_body.dict(),
**match_body.model_dump(),
duration_minutes=tournament.duration_minutes,
margin_minutes=tournament.margin_minutes,
)

View File

@@ -1,7 +1,6 @@
from typing import Generic, TypeVar
from pydantic import BaseModel
from pydantic.generics import GenericModel
from bracket.models.db.club import Club
from bracket.models.db.court import Court
@@ -24,7 +23,7 @@ class SuccessResponse(BaseModel):
success: bool = True
class DataResponse(GenericModel, Generic[DataT]):
class DataResponse(BaseModel, Generic[DataT]):
data: DataT

View File

@@ -36,7 +36,7 @@ async def update_player_by_id(
query=players.update().where(
(players.c.id == player_id) & (players.c.tournament_id == tournament_id)
),
values=player_body.dict(),
values=player_body.model_dump(),
)
return SinglePlayerResponse(
data=assert_some(

View File

@@ -84,7 +84,7 @@ async def create_round(
created=datetime_utc.now(),
stage_item_id=round_body.stage_item_id,
name=await get_next_round_name(tournament_id, round_body.stage_item_id),
).dict(),
).model_dump(),
)
await set_round_active_or_draft(round_id, tournament_id, is_active=False, is_draft=True)

View File

@@ -22,7 +22,7 @@ from bracket.utils.types import assert_some
router = APIRouter()
async def update_team_members(team_id: int, tournament_id: int, player_ids: list[int]) -> None:
async def update_team_members(team_id: int, tournament_id: int, player_ids: set[int]) -> None:
[team] = await get_teams_with_members(tournament_id, team_id=team_id)
# Add members to the team
@@ -47,7 +47,9 @@ async def update_team_members(team_id: int, tournament_id: int, player_ids: list
async def get_teams(
tournament_id: int, _: UserPublic = Depends(user_authenticated_or_public_dashboard)
) -> TeamsWithPlayersResponse:
return TeamsWithPlayersResponse.parse_obj({"data": await get_teams_with_members(tournament_id)})
return TeamsWithPlayersResponse.model_validate(
{"data": await get_teams_with_members(tournament_id)}
)
@router.put("/tournaments/{tournament_id}/teams/{team_id}", response_model=SingleTeamResponse)
@@ -61,7 +63,7 @@ async def update_team_by_id(
query=teams.update().where(
(teams.c.id == team.id) & (teams.c.tournament_id == tournament_id)
),
values=team_body.dict(exclude={"player_ids"}),
values=team_body.model_dump(exclude={"player_ids"}),
)
await update_team_members(assert_some(team.id), tournament_id, team_body.player_ids)
await recalculate_ranking_for_tournament_id(tournament_id)
@@ -118,10 +120,10 @@ async def create_team(
last_record_id = await database.execute(
query=teams.insert(),
values=TeamToInsert(
**team_to_insert.dict(exclude={"player_ids"}),
**team_to_insert.model_dump(exclude={"player_ids"}),
created=datetime_utc.now(),
tournament_id=tournament_id,
).dict(),
).model_dump(),
)
await update_team_members(last_record_id, tournament_id, team_to_insert.player_ids)
@@ -148,7 +150,7 @@ async def create_multiple_teams(
active=team_body.active,
created=datetime_utc.now(),
tournament_id=tournament_id,
).dict(),
).model_dump(),
)
return SuccessResponse()

View File

@@ -81,7 +81,7 @@ async def update_tournament_by_id(
) -> SuccessResponse:
await database.execute(
query=tournaments.update().where(tournaments.c.id == tournament_id),
values=tournament_body.dict(),
values=tournament_body.model_dump(),
)
await update_start_times_of_matches(tournament_id)
return SuccessResponse()
@@ -115,9 +115,9 @@ async def create_tournament(
await database.execute(
query=tournaments.insert(),
values=TournamentToInsert(
**tournament_to_insert.dict(),
**tournament_to_insert.model_dump(),
created=datetime_utc.now(),
).dict(),
).model_dump(),
)
return SuccessResponse()

View File

@@ -14,7 +14,7 @@ async def create_club(club: ClubCreateBody, user_id: int) -> Club:
if result is None:
raise ValueError("Could not create club")
club_created = Club.parse_obj(result._mapping)
club_created = Club.model_validate(dict(result._mapping))
query_many_to_many = """
INSERT INTO users_x_clubs (club_id, user_id, relation)
@@ -36,7 +36,7 @@ async def sql_update_club(club_id: int, club: ClubUpdateBody) -> Club | None:
RETURNING *
"""
result = await database.fetch_one(query=query, values={"name": club.name, "club_id": club_id})
return Club.parse_obj(result) if result is not None else None
return Club.model_validate(result) if result is not None else None
async def sql_delete_club(club_id: int) -> None:
@@ -63,7 +63,7 @@ async def get_clubs_for_user_id(user_id: int) -> list[Club]:
WHERE uxc.user_id = :user_id
"""
results = await database.fetch_all(query=query, values={"user_id": user_id})
return [Club.parse_obj(result._mapping) for result in results]
return [Club.model_validate(dict(result._mapping)) for result in results]
async def todo_get_club_for_user_id(club_id: int, user_id: int) -> Club | None:
@@ -74,4 +74,4 @@ async def todo_get_club_for_user_id(club_id: int, user_id: int) -> Club | None:
AND club_id = :club_id
"""
result = await database.fetch_one(query=query, values={"user_id": user_id, "club_id": club_id})
return Club.parse_obj(result._mapping) if result is not None else None
return Club.model_validate(dict(result._mapping)) if result is not None else None

View File

@@ -10,7 +10,7 @@ async def get_all_courts_in_tournament(tournament_id: int) -> list[Court]:
ORDER BY name
"""
result = await database.fetch_all(query=query, values={"tournament_id": tournament_id})
return [Court.parse_obj(x._mapping) for x in result]
return [Court.model_validate(dict(x._mapping)) for x in result]
async def update_court(tournament_id: int, court_id: int, court_body: CourtBody) -> list[Court]:
@@ -24,7 +24,7 @@ async def update_court(tournament_id: int, court_id: int, court_body: CourtBody)
query=query,
values={"tournament_id": tournament_id, "court_id": court_id, "name": court_body.name},
)
return [Court.parse_obj(x._mapping) for x in result]
return [Court.model_validate(dict(x._mapping)) for x in result]
async def sql_delete_court(tournament_id: int, court_id: int) -> None:

View File

@@ -70,12 +70,12 @@ async def sql_create_match(match: MatchCreateBody) -> Match:
)
RETURNING *
"""
result = await database.fetch_one(query=query, values=match.dict())
result = await database.fetch_one(query=query, values=match.model_dump())
if result is None:
raise ValueError("Could not create stage")
return Match.parse_obj(result._mapping)
return Match.model_validate(dict(result._mapping))
async def sql_update_match(match_id: int, match: MatchBody, tournament: Tournament) -> None:
@@ -107,7 +107,7 @@ async def sql_update_match(match_id: int, match: MatchBody, tournament: Tourname
query=query,
values={
"match_id": match_id,
**match.dict(),
**match.model_dump(),
"duration_minutes": duration_minutes,
"margin_minutes": margin_minutes,
},
@@ -115,7 +115,7 @@ async def sql_update_match(match_id: int, match: MatchBody, tournament: Tourname
async def sql_update_team_ids_for_match(
match_id: int, team1_id: int | None, team2_id: int | None
match_id: int, team1_id: int | None, team2_id: int | None = None
) -> None:
query = """
UPDATE matches
@@ -205,4 +205,4 @@ async def sql_get_match(match_id: int) -> Match:
if result is None:
raise ValueError("Could not create stage")
return Match.parse_obj(result._mapping)
return Match.model_validate(dict(result._mapping))

View File

@@ -20,7 +20,7 @@ async def get_all_players_in_tournament(
query += "AND players.team_id IS NULL"
result = await database.fetch_all(query=query, values={"tournament_id": tournament_id})
return [Player.parse_obj(x._mapping) for x in result]
return [Player.model_validate(dict(x._mapping)) for x in result]
async def update_player_stats(
@@ -67,10 +67,10 @@ async def insert_player(player_body: PlayerBody, tournament_id: int) -> None:
await database.execute(
query=players.insert(),
values=PlayerToInsert(
**player_body.dict(),
**player_body.model_dump(),
created=datetime_utc.now(),
tournament_id=tournament_id,
elo_score=Decimal(START_ELO),
swiss_score=Decimal("0.0"),
).dict(),
).model_dump(),
)

View File

@@ -67,4 +67,4 @@ async def sql_create_stage_item_input(
if result is None:
raise ValueError("Could not create stage")
return StageItemInputBase.parse_obj(result._mapping)
return StageItemInputBase.model_validate(dict(result._mapping))

View File

@@ -25,7 +25,7 @@ async def sql_create_stage_item(tournament_id: int, stage_item: StageItemCreateB
if result is None:
raise ValueError("Could not create stage")
stage_item_result = StageItem.parse_obj(result._mapping)
stage_item_result = StageItem.model_validate(dict(result._mapping))
for input_ in stage_item.inputs:
await sql_create_stage_item_input(tournament_id, stage_item_result.id, input_)

View File

@@ -106,7 +106,7 @@ async def get_full_tournament_details(
}
)
result = await database.fetch_all(query=query, values=values)
return [StageWithStageItems.parse_obj(x._mapping) for x in result]
return [StageWithStageItems.model_validate(dict(x._mapping)) for x in result]
async def sql_delete_stage(tournament_id: int, stage_id: int) -> None:
@@ -141,7 +141,7 @@ async def sql_create_stage(tournament_id: int) -> Stage:
if result is None:
raise ValueError("Could not create stage")
return Stage.parse_obj(result._mapping)
return Stage.model_validate(dict(result._mapping))
async def get_next_stage_in_tournament(

View File

@@ -14,7 +14,7 @@ async def get_team_by_id(team_id: int, tournament_id: int) -> Team | None:
result = await database.fetch_one(
query=query, values={"team_id": team_id, "tournament_id": tournament_id}
)
return Team.parse_obj(result._mapping) if result is not None else None
return Team.model_validate(dict(result._mapping)) if result is not None else None
async def get_teams_with_members(
@@ -37,7 +37,7 @@ async def get_teams_with_members(
"""
values = dict_without_none({"tournament_id": tournament_id, "team_id": team_id})
result = await database.fetch_all(query=query, values=values)
return [FullTeamWithPlayers.parse_obj(x._mapping) for x in result]
return [FullTeamWithPlayers.model_validate(dict(x._mapping)) for x in result]
async def update_team_stats(

View File

@@ -23,7 +23,7 @@ async def sql_get_tournament(tournament_id: int) -> Tournament:
"""
result = await database.fetch_one(query=query, values={"tournament_id": tournament_id})
assert result is not None
return Tournament.parse_obj(result._mapping)
return Tournament.model_validate(dict(result._mapping))
async def sql_get_tournament_by_endpoint_name(endpoint_name: str) -> Tournament:
@@ -35,7 +35,7 @@ async def sql_get_tournament_by_endpoint_name(endpoint_name: str) -> Tournament:
"""
result = await database.fetch_one(query=query, values={"endpoint_name": endpoint_name})
assert result is not None
return Tournament.parse_obj(result._mapping)
return Tournament.model_validate(dict(result._mapping))
async def sql_get_tournaments(
@@ -54,7 +54,7 @@ async def sql_get_tournaments(
params = {**params, "endpoint_name": endpoint_name}
result = await database.fetch_all(query=query, values=params)
return [Tournament.parse_obj(x._mapping) for x in result]
return [Tournament.model_validate(dict(x._mapping)) for x in result]
async def sql_delete_tournament(tournament_id: int) -> None:

View File

@@ -1,5 +1,3 @@
from datetime import datetime
from bracket.database import database
from bracket.models.db.user import User, UserInDB, UserPublic, UserToUpdate
from bracket.schema import users
@@ -61,7 +59,7 @@ async def get_user_by_id(user_id: int) -> UserPublic | None:
WHERE id = :user_id
"""
result = await database.fetch_one(query=query, values={"user_id": user_id})
return UserPublic.parse_obj(result._mapping) if result is not None else None
return UserPublic.model_validate(dict(result._mapping)) if result is not None else None
async def get_expired_demo_users() -> list[UserPublic]:
@@ -72,7 +70,7 @@ async def get_expired_demo_users() -> list[UserPublic]:
AND created <= NOW() - INTERVAL '30 minutes'
"""
result = await database.fetch_all(query=query)
return [UserPublic.parse_obj(demo_user._mapping) for demo_user in result]
return [UserPublic.model_validate(demo_user._mapping) for demo_user in result]
async def create_user(user: User) -> User:
@@ -87,11 +85,11 @@ async def create_user(user: User) -> User:
"password_hash": user.password_hash,
"name": user.name,
"email": user.email,
"created": datetime.fromisoformat(user.created.isoformat()),
"created": user.created,
"account_type": user.account_type.value,
},
)
return User.parse_obj(assert_some(result)._mapping)
return User.model_validate(dict(assert_some(result)._mapping))
async def delete_user(user_id: int) -> None:

View File

@@ -1,6 +1,7 @@
from collections.abc import Mapping
from typing import Any
from heliclockter import datetime_tz
from pydantic import BaseModel
from bracket.utils.types import EnumAutoStr
@@ -9,7 +10,9 @@ from bracket.utils.types import EnumAutoStr
def _map_to_str(value: Any) -> Any:
match value:
case EnumAutoStr():
return value.name
return value.value
case datetime_tz():
return value.isoformat()
return value
@@ -17,4 +20,4 @@ def to_string_mapping(obj: BaseModel) -> Mapping[str, Any]:
"""
Turns a pydantic object into a string mapping to be used as database query
"""
return {key: _map_to_str(value) for key, value in obj.dict().items()}
return {key: _map_to_str(value) for key, value in obj.model_dump(exclude_none=True).items()}

View File

@@ -2,6 +2,7 @@ from databases import Database
from sqlalchemy import Table
from sqlalchemy.sql import Select
from bracket.config import Environment, environment
from bracket.utils.conversion import to_string_mapping
from bracket.utils.logging import logger
from bracket.utils.types import BaseModelT, assert_some
@@ -11,7 +12,7 @@ async def fetch_one_parsed(
database: Database, model: type[BaseModelT], query: Select
) -> BaseModelT | None:
record = await database.fetch_one(query)
return model.parse_obj(record._mapping) if record is not None else None
return model.model_validate(dict(record._mapping)) if record is not None else None
async def fetch_one_parsed_certain(
@@ -24,21 +25,24 @@ async def fetch_all_parsed(
database: Database, model: type[BaseModelT], query: Select
) -> list[BaseModelT]:
records = await database.fetch_all(query)
return [model.parse_obj(record._mapping) for record in records]
return [model.model_validate(dict(record._mapping)) for record in records]
async def insert_generic(
database: Database, data_model: BaseModelT, table: Table, return_type: type[BaseModelT]
) -> tuple[int, BaseModelT]:
assert environment is not Environment.PRODUCTION, "Below code can allow SQL injection"
try:
last_record_id: int = await database.execute(
query=table.insert(),
values=to_string_mapping(data_model), # type: ignore[arg-type]
mapping = to_string_mapping(data_model)
values = ", ".join([f"'{x}'" for x in mapping.values()])
query = (
f"INSERT INTO {table.name} ({', '.join(mapping.keys())}) VALUES ({values}) RETURNING *"
)
last_record_id: int = await database.execute(query)
row_inserted = await fetch_one_parsed(
database, return_type, table.select().where(table.c.id == last_record_id)
)
assert isinstance(row_inserted, return_type)
assert isinstance(row_inserted, return_type), f"Unexpected type: {row_inserted}"
return last_record_id, row_inserted
except Exception:
logger.exception(f"Could not insert {type(data_model).__name__}")

View File

@@ -136,7 +136,7 @@ async def sql_create_dev_db() -> int:
async def insert_dummy(obj_to_insert: BaseModelT, update_data: dict[str, Any] = {}) -> int:
record_id, _ = await insert_generic(
database,
obj_to_insert.copy(update=update_data),
obj_to_insert.model_copy(update=update_data),
table_lookup[type(obj_to_insert)],
type(obj_to_insert),
)

View File

@@ -9,6 +9,9 @@ filterwarnings = [
'ignore:The SelectBase.c and SelectBase.columns attributes are deprecated.*:DeprecationWarning',
'ignore:pkg_resources is deprecated as an API.*:DeprecationWarning',
'ignore:Deprecated call to `pkg_resources.declare_namespace(.*)`.*:DeprecationWarning',
'ignore:.*:pytest.PytestDeprecationWarning',
'ignore:.*pytest-asyncio detected an unclosed event loop.*:DeprecationWarning',
'ignore:.*The event_loop fixture provided by pytest-asyncio has been redefined.*:DeprecationWarning',
]
[tool.mypy]

View File

@@ -33,24 +33,26 @@ async def test_activate_next_stage(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with (
inserted_court(DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_court(
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
),
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted_1,
inserted_stage(
DUMMY_STAGE2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted_2,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_1,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_2,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_3,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_4,
):
tournament_id = assert_some(auth_context.tournament.id)
@@ -112,7 +114,7 @@ async def test_activate_next_stage(
assert match1.team2.id == team_inserted_2.id
await sql_update_match(
assert_some(match1.id),
MatchBody(**match1.copy(update={"team2_score": 42}).dict()),
MatchBody(**match1.model_copy(update={"team2_score": 42}).model_dump()),
auth_context.tournament,
)

View File

@@ -65,7 +65,7 @@ async def test_auth_on_protected_endpoint(startup_and_shutdown_uvicorn_server: N
"id": user_inserted.id,
"email": user_inserted.email,
"name": user_inserted.name,
"created": "2200-01-01T00:00:00+00:00",
"created": "2200-01-01T00:00:00Z",
"account_type": UserAccountType.REGULAR.value,
}
}
@@ -83,7 +83,7 @@ async def test_not_authenticated_for_tournament(
) -> None:
async with inserted_club(DUMMY_CLUB) as club_inserted:
async with inserted_tournament(
DUMMY_TOURNAMENT.copy(update={"club_id": club_inserted.id})
DUMMY_TOURNAMENT.model_copy(update={"club_id": club_inserted.id})
) as tournament_inserted:
response = JsonDict(
await send_auth_request(

View File

@@ -14,7 +14,7 @@ async def test_clubs_endpoint(
assert await send_auth_request(HTTPMethod.GET, "clubs", auth_context, {}) == {
"data": [
{
"created": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"id": auth_context.club.id,
"name": "Some Cool Club",
}

View File

@@ -13,15 +13,15 @@ async def test_courts_endpoint(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
):
async with inserted_court(
DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court_inserted:
assert await send_tournament_request(HTTPMethod.GET, "courts", auth_context, {}) == {
"data": [
{
"created": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"id": court_inserted.id,
"name": "Court 1",
"tournament_id": auth_context.tournament.id,
@@ -43,10 +43,10 @@ async def test_delete_court(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
):
async with inserted_court(
DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court_inserted:
assert (
await send_tournament_request(
@@ -62,10 +62,10 @@ async def test_update_court(
) -> None:
body = {"name": "Some new name"}
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
):
async with inserted_court(
DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court_inserted:
response = await send_tournament_request(
HTTPMethod.PUT, f"courts/{court_inserted.id}", auth_context, json=body

View File

@@ -20,15 +20,15 @@ async def test_available_inputs(
) -> None:
async with (
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted,
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted_1,
# inserted_stage(
# DUMMY_STAGE2.copy(update={'tournament_id': auth_context.tournament.id})
# DUMMY_STAGE2.model_copy(update={'tournament_id': auth_context.tournament.id})
# ) as stage_inserted_2,
inserted_stage_item(DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted_1.id})),
inserted_stage_item(DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted_1.id})),
):
response = await send_tournament_request(
HTTPMethod.GET, f"stages/{stage_inserted_1.id}/available_inputs", auth_context

View File

@@ -1,3 +1,5 @@
from decimal import Decimal
from bracket.database import database
from bracket.models.db.match import Match
from bracket.models.db.stage_item import StageType
@@ -37,22 +39,22 @@ async def test_create_match(
) -> None:
async with (
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(update={"stage_item_id": stage_item_inserted.id})
DUMMY_ROUND1.model_copy(update={"stage_item_id": stage_item_inserted.id})
) as round_inserted,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team1_inserted,
inserted_court(
DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court1_inserted,
inserted_team(
DUMMY_TEAM2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team2_inserted,
):
body = {
@@ -74,25 +76,25 @@ async def test_delete_match(
) -> None:
async with (
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(update={"stage_item_id": stage_item_inserted.id})
DUMMY_ROUND1.model_copy(update={"stage_item_id": stage_item_inserted.id})
) as round_inserted,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team1_inserted,
inserted_team(
DUMMY_TEAM2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team2_inserted,
inserted_court(
DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court1_inserted,
inserted_match(
DUMMY_MATCH1.copy(
DUMMY_MATCH1.model_copy(
update={
"round_id": round_inserted.id,
"team1_id": team1_inserted.id,
@@ -116,25 +118,25 @@ async def test_update_match(
) -> None:
async with (
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(update={"stage_item_id": stage_item_inserted.id})
DUMMY_ROUND1.model_copy(update={"stage_item_id": stage_item_inserted.id})
) as round_inserted,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team1_inserted,
inserted_team(
DUMMY_TEAM2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team2_inserted,
inserted_court(
DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court1_inserted,
inserted_match(
DUMMY_MATCH1.copy(
DUMMY_MATCH1.model_copy(
update={
"round_id": round_inserted.id,
"team1_id": team1_inserted.id,
@@ -177,7 +179,7 @@ async def test_upcoming_matches_endpoint(
) -> None:
async with (
inserted_stage(
DUMMY_STAGE1.copy(
DUMMY_STAGE1.model_copy(
update={
"is_active": True,
"tournament_id": auth_context.tournament.id,
@@ -185,10 +187,12 @@ async def test_upcoming_matches_endpoint(
)
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id, "type": StageType.SWISS})
DUMMY_STAGE_ITEM1.model_copy(
update={"stage_id": stage_inserted.id, "type": StageType.SWISS}
)
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(
DUMMY_ROUND1.model_copy(
update={
"is_draft": True,
"stage_item_id": stage_item_inserted.id,
@@ -196,36 +200,36 @@ async def test_upcoming_matches_endpoint(
)
) as round_inserted,
inserted_team(
DUMMY_TEAM1.copy(
update={"tournament_id": auth_context.tournament.id, "elo_score": 1150}
DUMMY_TEAM1.model_copy(
update={"tournament_id": auth_context.tournament.id, "elo_score": Decimal("1150.0")}
)
) as team1_inserted,
inserted_team(
DUMMY_TEAM2.copy(
update={"tournament_id": auth_context.tournament.id, "elo_score": 1350}
DUMMY_TEAM2.model_copy(
update={"tournament_id": auth_context.tournament.id, "elo_score": Decimal("1350.0")}
)
) as team2_inserted,
inserted_player_in_team(
DUMMY_PLAYER1.copy(
update={"elo_score": 1100, "tournament_id": auth_context.tournament.id}
DUMMY_PLAYER1.model_copy(
update={"elo_score": Decimal("1100.0"), "tournament_id": auth_context.tournament.id}
),
assert_some(team1_inserted.id),
) as player_inserted_1,
inserted_player_in_team(
DUMMY_PLAYER2.copy(
update={"elo_score": 1300, "tournament_id": auth_context.tournament.id}
DUMMY_PLAYER2.model_copy(
update={"elo_score": Decimal("1300.0"), "tournament_id": auth_context.tournament.id}
),
assert_some(team2_inserted.id),
) as player_inserted_2,
inserted_player_in_team(
DUMMY_PLAYER3.copy(
update={"elo_score": 1200, "tournament_id": auth_context.tournament.id}
DUMMY_PLAYER3.model_copy(
update={"elo_score": Decimal("1200.0"), "tournament_id": auth_context.tournament.id}
),
assert_some(team1_inserted.id),
) as player_inserted_3,
inserted_player_in_team(
DUMMY_PLAYER4.copy(
update={"elo_score": 1400, "tournament_id": auth_context.tournament.id}
DUMMY_PLAYER4.model_copy(
update={"elo_score": Decimal("1400.0"), "tournament_id": auth_context.tournament.id}
),
assert_some(team2_inserted.id),
) as player_inserted_4,
@@ -244,10 +248,10 @@ async def test_upcoming_matches_endpoint(
"id": player_inserted_1.id,
"active": True,
"name": "Player 01",
"created": "2022-01-11T04:32:11+00:00",
"created": "2022-01-11T04:32:11Z",
"tournament_id": auth_context.tournament.id,
"elo_score": 1100,
"swiss_score": 0,
"elo_score": "1100",
"swiss_score": "0",
"wins": 0,
"draws": 0,
"losses": 0,
@@ -256,17 +260,17 @@ async def test_upcoming_matches_endpoint(
"id": player_inserted_3.id,
"active": True,
"name": "Player 03",
"created": "2022-01-11T04:32:11+00:00",
"created": "2022-01-11T04:32:11Z",
"tournament_id": auth_context.tournament.id,
"elo_score": 1200,
"swiss_score": 0,
"elo_score": "1200",
"swiss_score": "0",
"wins": 0,
"draws": 0,
"losses": 0,
},
],
"swiss_score": 0.0,
"elo_score": 1150.0,
"swiss_score": "0.0",
"elo_score": "1150.0",
"wins": 0,
"draws": 0,
"losses": 0,
@@ -279,10 +283,10 @@ async def test_upcoming_matches_endpoint(
"id": player_inserted_2.id,
"active": True,
"name": "Player 02",
"created": "2022-01-11T04:32:11+00:00",
"created": "2022-01-11T04:32:11Z",
"tournament_id": auth_context.tournament.id,
"elo_score": 1300,
"swiss_score": 0,
"elo_score": "1300",
"swiss_score": "0",
"wins": 0,
"draws": 0,
"losses": 0,
@@ -291,23 +295,23 @@ async def test_upcoming_matches_endpoint(
"id": player_inserted_4.id,
"active": True,
"name": "Player 04",
"created": "2022-01-11T04:32:11+00:00",
"created": "2022-01-11T04:32:11Z",
"tournament_id": auth_context.tournament.id,
"elo_score": 1400,
"swiss_score": 0,
"elo_score": "1400",
"swiss_score": "0",
"wins": 0,
"draws": 0,
"losses": 0,
},
],
"swiss_score": 0.0,
"elo_score": 1350.0,
"swiss_score": "0.0",
"elo_score": "1350.0",
"wins": 0,
"draws": 0,
"losses": 0,
},
"elo_diff": 200,
"swiss_diff": 0,
"elo_diff": "200",
"swiss_diff": "0",
"is_recommended": True,
"player_behind_schedule_count": 0,
}

View File

@@ -13,19 +13,19 @@ async def test_players_endpoint(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
):
async with inserted_player(
DUMMY_PLAYER1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_PLAYER1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as player_inserted:
assert await send_tournament_request(HTTPMethod.GET, "players", auth_context, {}) == {
"data": [
{
"created": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"id": player_inserted.id,
"active": True,
"elo_score": 0.0,
"swiss_score": 0.0,
"elo_score": "0.0",
"swiss_score": "0.0",
"wins": 0,
"draws": 0,
"losses": 0,
@@ -60,10 +60,10 @@ async def test_delete_player(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
):
async with inserted_player(
DUMMY_PLAYER1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_PLAYER1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as player_inserted:
assert (
await send_tournament_request(
@@ -79,10 +79,10 @@ async def test_update_player(
) -> None:
body = {"name": "Some new name", "active": True}
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
):
async with inserted_player(
DUMMY_PLAYER1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_PLAYER1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as player_inserted:
response = await send_tournament_request(
HTTPMethod.PUT, f"players/{player_inserted.id}", auth_context, json=body

View File

@@ -31,28 +31,28 @@ async def test_reschedule_match(
) -> None:
async with (
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(update={"stage_item_id": stage_item_inserted.id})
DUMMY_ROUND1.model_copy(update={"stage_item_id": stage_item_inserted.id})
) as round_inserted,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team1_inserted,
inserted_team(
DUMMY_TEAM2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team2_inserted,
inserted_court(
DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court1_inserted,
inserted_court(
DUMMY_COURT2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_COURT2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as court2_inserted,
inserted_match(
DUMMY_MATCH1.copy(
DUMMY_MATCH1.model_copy(
update={
"round_id": round_inserted.id,
"team1_id": team1_inserted.id,
@@ -73,7 +73,7 @@ async def test_reschedule_match(
HTTPMethod.POST,
f"matches/{match_inserted.id}/reschedule",
auth_context,
json=body.dict(),
json=body.model_dump(),
)
== SUCCESS_RESPONSE
)

View File

@@ -20,12 +20,14 @@ async def test_create_round(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id, "type": StageType.SWISS})
DUMMY_STAGE_ITEM1.model_copy(
update={"stage_id": stage_inserted.id, "type": StageType.SWISS}
)
) as stage_item_inserted,
):
assert (
@@ -44,15 +46,15 @@ async def test_delete_round(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(update={"stage_item_id": stage_item_inserted.id})
DUMMY_ROUND1.model_copy(update={"stage_item_id": stage_item_inserted.id})
) as round_inserted,
):
assert (
@@ -69,15 +71,15 @@ async def test_update_round(
) -> None:
body = {"name": "Some new name", "is_draft": True, "is_active": False}
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(update={"stage_item_id": stage_item_inserted.id})
DUMMY_ROUND1.model_copy(update={"stage_item_id": stage_item_inserted.id})
) as round_inserted,
):
assert (

View File

@@ -32,21 +32,23 @@ async def test_schedule_all_matches(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with (
inserted_court(DUMMY_COURT1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_court(
DUMMY_COURT1.model_copy(update={"tournament_id": auth_context.tournament.id})
),
inserted_stage(
DUMMY_STAGE2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted_1,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_1,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_2,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_3,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_4,
):
tournament_id = assert_some(auth_context.tournament.id)

View File

@@ -14,7 +14,7 @@ from bracket.utils.http import HTTPMethod
from bracket.utils.types import JsonDict
from tests.integration_tests.models import AuthContext
SUCCESS_RESPONSE = SuccessResponse().dict()
SUCCESS_RESPONSE = SuccessResponse().model_dump()
def find_free_port() -> int:

View File

@@ -27,19 +27,19 @@ async def test_create_stage_item(
) -> None:
async with (
inserted_stage(
DUMMY_STAGE2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted_1,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_1,
inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted_2,
):
assert team_inserted_1.id and team_inserted_2.id
inputs = [
StageItemInputCreateBodyFinal(slot=1, team_id=team_inserted_1.id).dict(),
StageItemInputCreateBodyFinal(slot=2, team_id=team_inserted_2.id).dict(),
StageItemInputCreateBodyFinal(slot=1, team_id=team_inserted_1.id).model_dump(),
StageItemInputCreateBodyFinal(slot=2, team_id=team_inserted_2.id).model_dump(),
]
assert (
await send_tournament_request(
@@ -65,12 +65,12 @@ async def test_delete_stage_item(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted_1,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted_1.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted_1.id})
) as stage_item_inserted,
):
assert (
@@ -88,10 +88,10 @@ async def test_update_stage_item(
body = {"name": "Optimus"}
async with (
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
):
assert (

View File

@@ -32,15 +32,15 @@ async def test_stages_endpoint(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext, with_auth: bool
) -> None:
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(
DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})
DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})
) as stage_item_inserted,
inserted_round(
DUMMY_ROUND1.copy(update={"stage_item_id": stage_item_inserted.id})
DUMMY_ROUND1.model_copy(update={"stage_item_id": stage_item_inserted.id})
) as round_inserted,
):
if with_auth:
@@ -55,7 +55,7 @@ async def test_stages_endpoint(
{
"id": stage_inserted.id,
"tournament_id": auth_context.tournament.id,
"created": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"is_active": True,
"name": "Group Stage",
"stage_items": [
@@ -63,14 +63,14 @@ async def test_stages_endpoint(
"id": stage_item_inserted.id,
"stage_id": stage_inserted.id,
"name": "Group A",
"created": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"type": "ROUND_ROBIN",
"team_count": 4,
"rounds": [
{
"id": round_inserted.id,
"stage_item_id": stage_item_inserted.id,
"created": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"is_draft": False,
"is_active": False,
"name": "Round 1",
@@ -90,7 +90,7 @@ async def test_create_stage(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
):
assert (
await send_tournament_request(
@@ -109,9 +109,9 @@ async def test_delete_stage(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE2.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE2.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
):
assert (
@@ -128,11 +128,11 @@ async def test_update_stage(
) -> None:
body = {"name": "Optimus"}
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted,
inserted_stage_item(DUMMY_STAGE_ITEM1.copy(update={"stage_id": stage_inserted.id})),
inserted_stage_item(DUMMY_STAGE_ITEM1.model_copy(update={"stage_id": stage_inserted.id})),
):
assert (
await send_tournament_request(
@@ -152,9 +152,13 @@ async def test_activate_stage(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with (
inserted_team(DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(DUMMY_STAGE1.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(DUMMY_STAGE2.copy(update={"tournament_id": auth_context.tournament.id})),
inserted_team(DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})),
inserted_stage(
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
),
inserted_stage(
DUMMY_STAGE2.model_copy(update={"tournament_id": auth_context.tournament.id})
),
):
assert (
await send_tournament_request(

View File

@@ -13,19 +13,19 @@ async def test_teams_endpoint(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted:
assert await send_tournament_request(HTTPMethod.GET, "teams", auth_context, {}) == {
"data": [
{
"active": True,
"created": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"id": team_inserted.id,
"name": "Team 1",
"players": [],
"tournament_id": team_inserted.tournament_id,
"elo_score": 1200.0,
"swiss_score": 0.0,
"elo_score": "1200.0",
"swiss_score": "0.0",
"wins": 0,
"draws": 0,
"losses": 0,
@@ -58,7 +58,7 @@ async def test_delete_team(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted:
assert (
await send_tournament_request(
@@ -74,7 +74,7 @@ async def test_update_team(
) -> None:
body = {"name": "Some new name", "active": True, "player_ids": []}
async with inserted_team(
DUMMY_TEAM1.copy(update={"tournament_id": auth_context.tournament.id})
DUMMY_TEAM1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as team_inserted:
response = await send_tournament_request(
HTTPMethod.PUT, f"teams/{team_inserted.id}", auth_context, None, body

View File

@@ -21,9 +21,10 @@ async def test_tournaments_endpoint(
{
"id": auth_context.tournament.id,
"club_id": auth_context.club.id,
"created": DUMMY_MOCK_TIME.isoformat(),
"start_time": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"start_time": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"name": "Some Cool Tournament",
"logo_path": None,
"dashboard_public": True,
"dashboard_endpoint": "cool-tournament",
"players_can_be_in_multiple_teams": True,
@@ -44,8 +45,9 @@ async def test_tournament_endpoint(
"data": {
"id": auth_context.tournament.id,
"club_id": auth_context.club.id,
"created": DUMMY_MOCK_TIME.isoformat(),
"start_time": DUMMY_MOCK_TIME.isoformat(),
"created": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"start_time": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"logo_path": None,
"name": "Some Cool Tournament",
"dashboard_public": True,
"dashboard_endpoint": "cool-tournament",
@@ -62,7 +64,7 @@ async def test_create_tournament(
) -> None:
body = {
"name": "Some new name",
"start_time": DUMMY_MOCK_TIME.isoformat(),
"start_time": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"club_id": auth_context.club.id,
"dashboard_public": False,
"players_can_be_in_multiple_teams": True,
@@ -82,7 +84,7 @@ async def test_update_tournament(
) -> None:
body = {
"name": "Some new name",
"start_time": DUMMY_MOCK_TIME.isoformat(),
"start_time": DUMMY_MOCK_TIME.isoformat().replace("+00:00", "Z"),
"dashboard_public": False,
"players_can_be_in_multiple_teams": True,
"auto_assign_courts": True,
@@ -108,11 +110,13 @@ async def test_delete_tournament(
startup_and_shutdown_uvicorn_server: None, auth_context: AuthContext
) -> None:
async with inserted_tournament(
DUMMY_TOURNAMENT.copy(update={"club_id": auth_context.club.id})
DUMMY_TOURNAMENT.model_copy(update={"club_id": auth_context.club.id})
) as tournament_inserted:
assert (
await send_tournament_request(
HTTPMethod.DELETE, "", auth_context.copy(update={"tournament": tournament_inserted})
HTTPMethod.DELETE,
"",
auth_context.model_copy(update={"tournament": tournament_inserted}),
)
== SUCCESS_RESPONSE
)

View File

@@ -18,7 +18,7 @@ async def test_users_endpoint(
) == {
"data": {
"email": auth_context.user.email,
"created": "2200-01-01T00:00:00+00:00",
"created": "2200-01-01T00:00:00Z",
"id": auth_context.user.id,
"name": "Donald Duck",
"account_type": UserAccountType.REGULAR.value,

View File

@@ -111,7 +111,7 @@ async def inserted_stage(stage: Stage) -> AsyncIterator[Stage]:
@asynccontextmanager
async def inserted_stage_item(stage_item: StageItemToInsert) -> AsyncIterator[StageItem]:
async with inserted_generic(stage_item, stage_items, StageItem) as row_inserted:
yield StageItem(**row_inserted.dict())
yield StageItem(**row_inserted.model_dump())
@asynccontextmanager
@@ -140,7 +140,7 @@ async def inserted_auth_context() -> AsyncIterator[AuthContext]:
inserted_user(mock_user) as user_inserted,
inserted_club(DUMMY_CLUB) as club_inserted,
inserted_tournament(
DUMMY_TOURNAMENT.copy(update={"club_id": club_inserted.id})
DUMMY_TOURNAMENT.model_copy(update={"club_id": club_inserted.id})
) as tournament_inserted,
inserted_user_x_club(
UserXClub(

View File

@@ -50,7 +50,7 @@ def test_elo_calculation() -> None:
tournament_id=1,
active=True,
created=DUMMY_MOCK_TIME,
players=[DUMMY_PLAYER1.copy(update={"id": 1})],
players=[DUMMY_PLAYER1.model_copy(update={"id": 1})],
elo_score=DUMMY_PLAYER1.elo_score,
swiss_score=DUMMY_PLAYER1.swiss_score,
wins=DUMMY_PLAYER1.wins,
@@ -63,7 +63,7 @@ def test_elo_calculation() -> None:
tournament_id=1,
active=True,
created=DUMMY_MOCK_TIME,
players=[DUMMY_PLAYER2.copy(update={"id": 2})],
players=[DUMMY_PLAYER2.model_copy(update={"id": 2})],
elo_score=DUMMY_PLAYER2.elo_score,
swiss_score=DUMMY_PLAYER2.swiss_score,
wins=DUMMY_PLAYER2.wins,
@@ -74,9 +74,10 @@ def test_elo_calculation() -> None:
],
)
stage_item = StageItemWithRounds(
**DUMMY_STAGE_ITEM1.copy(update={"rounds": [round_]}).dict(),
**DUMMY_STAGE_ITEM1.model_dump(exclude={"id"}),
id=-1,
inputs=[],
rounds=[round_],
)
player_stats, team_stats = determine_ranking_for_stage_items([stage_item])
assert player_stats == {

View File

@@ -27,7 +27,7 @@ def test_no_draft_round() -> None:
def get_team(team: Team, team_id: int) -> FullTeamWithPlayers:
return FullTeamWithPlayers(
id=team_id,
**team.dict(),
**team.model_dump(exclude={"id"}),
players=[],
)
@@ -36,7 +36,7 @@ def get_match(
match: Match, team1: FullTeamWithPlayers, team2: FullTeamWithPlayers
) -> MatchWithDetailsDefinitive:
return MatchWithDetailsDefinitive(
**match.copy(update={"team1_id": team1.id, "team2_id": team2.id}).dict(),
**match.model_copy(update={"team1_id": team1.id, "team2_id": team2.id}).model_dump(),
team1=team1,
team2=team2,
court=None,
@@ -44,10 +44,10 @@ def get_match(
def test_constraints() -> None:
team1 = get_team(DUMMY_TEAM1.copy(update={"elo_score": 1125}), team_id=-1)
team2 = get_team(DUMMY_TEAM2.copy(update={"elo_score": 1175}), team_id=-2)
team3 = get_team(DUMMY_TEAM3.copy(update={"elo_score": 1200}), team_id=-3)
team4 = get_team(DUMMY_TEAM4.copy(update={"elo_score": 1250}), team_id=-4)
team1 = get_team(DUMMY_TEAM1.model_copy(update={"elo_score": Decimal("1125.0")}), team_id=-1)
team2 = get_team(DUMMY_TEAM2.model_copy(update={"elo_score": Decimal("1175.0")}), team_id=-2)
team3 = get_team(DUMMY_TEAM3.model_copy(update={"elo_score": Decimal("1200.0")}), team_id=-3)
team4 = get_team(DUMMY_TEAM4.model_copy(update={"elo_score": Decimal("1250.0")}), team_id=-4)
rounds = [
RoundWithMatches(