Skip to content

Commit

Permalink
Allow changing inputs after creating stage item
Browse files Browse the repository at this point in the history
  • Loading branch information
evroon committed Oct 24, 2024
1 parent 773968f commit 60af847
Show file tree
Hide file tree
Showing 17 changed files with 1,054 additions and 1,005 deletions.
8 changes: 7 additions & 1 deletion backend/bracket/models/db/stage_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,18 @@ class StageItemCreateBody(BaseModelORM):
name: str | None = None
type: StageType
team_count: int = Field(ge=2, le=64)
inputs: list[StageItemInputCreateBody]
ranking_id: RankingId | None = None

def get_name_or_default_name(self) -> str:
return self.name if self.name is not None else self.type.value.replace("_", " ").title()


class StageItemWithInputsCreate(StageItemCreateBody):
inputs: list[StageItemInputCreateBody]

def get_name_or_default_name(self) -> str:
return self.name if self.name is not None else self.type.value.replace("_", " ").title()

@model_validator(mode="before")
def handle_inputs_length(cls, values: Any) -> Any:
if ("inputs" in values and "team_count" in values) and (
Expand Down
4 changes: 2 additions & 2 deletions backend/bracket/routes/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from bracket.models.db.user import UserPublic
from bracket.models.db.util import StageWithStageItems
from bracket.routes.auth import Token
from bracket.utils.id_types import StageItemId, StageItemInputId
from bracket.utils.id_types import StageId, StageItemId, StageItemInputId

DataT = TypeVar("DataT")

Expand Down Expand Up @@ -105,7 +105,7 @@ class RankingsResponse(DataResponse[list[Ranking]]):


class StageItemInputOptionsResponse(
DataResponse[list[StageItemInputOptionTentative | StageItemInputOptionFinal]]
DataResponse[dict[StageId, list[StageItemInputOptionTentative | StageItemInputOptionFinal]]]
):
pass

Expand Down
6 changes: 0 additions & 6 deletions backend/bracket/routes/stage_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@ async def create_stage_item(
stage_body: StageItemCreateBody,
user: UserPublic = Depends(user_authenticated_for_tournament),
) -> SuccessResponse:
if stage_body.team_count != len(stage_body.inputs):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Team count doesn't match number of inputs",
)

await check_foreign_keys_belong_to_tournament(stage_body, tournament_id)

stages = await get_full_tournament_details(tournament_id)
Expand Down
10 changes: 5 additions & 5 deletions backend/bracket/routes/stages.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,18 +137,18 @@ async def activate_next_stage(


@router.get(
"/tournaments/{tournament_id}/stages/{stage_id}/available_inputs",
"/tournaments/{tournament_id}/available_inputs",
response_model=StageItemInputOptionsResponse,
)
async def get_available_inputs(
tournament_id: TournamentId,
stage_id: StageId,
_: UserPublic = Depends(user_authenticated_for_tournament),
stage: Stage = Depends(stage_dependency),
) -> StageItemInputOptionsResponse:
stages = await get_full_tournament_details(tournament_id)
teams = await get_teams_with_members(tournament_id)
available_inputs = determine_available_inputs(stage_id, teams, stages)
available_inputs = {
stage.id: determine_available_inputs(stage.id, teams, stages) for stage in stages
}
return StageItemInputOptionsResponse(data=available_inputs)


Expand All @@ -157,7 +157,7 @@ async def get_rankings(
tournament_id: TournamentId,
stage_id: StageId,
_: UserPublic = Depends(user_authenticated_for_tournament),
stage_without_details: Stage = Depends(stage_dependency),
__: Stage = Depends(stage_dependency),
) -> StageRankingResponse:
"""
Get the rankings for the stage items in this stage.
Expand Down
55 changes: 31 additions & 24 deletions backend/bracket/sql/stage_items.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,52 @@
from bracket.database import database
from bracket.models.db.stage_item import StageItem, StageItemCreateBody
from bracket.models.db.stage_item import StageItem, StageItemCreateBody, StageItemWithInputsCreate
from bracket.models.db.util import StageItemWithRounds
from bracket.sql.rankings import get_default_rankings_in_tournament
from bracket.sql.stage_item_inputs import sql_create_stage_item_input
from bracket.sql.stages import get_full_tournament_details
from bracket.utils.id_types import StageItemId, TournamentId


async def sql_create_stage_item(
tournament_id: TournamentId, stage_item: StageItemCreateBody
async def sql_create_stage_item_with_inputs(
tournament_id: TournamentId, stage_item: StageItemWithInputsCreate
) -> StageItem:
async with database.transaction():
query = """
INSERT INTO stage_items (type, stage_id, name, team_count, ranking_id)
VALUES (:stage_item_type, :stage_id, :name, :team_count, :ranking_id)
RETURNING *
"""
result = await database.fetch_one(
query=query,
values={
"stage_item_type": stage_item.type.value,
"stage_id": stage_item.stage_id,
"name": stage_item.get_name_or_default_name(),
"team_count": stage_item.team_count,
"ranking_id": stage_item.ranking_id
if stage_item.ranking_id
else (await get_default_rankings_in_tournament(tournament_id)).id,
},
stage_item_result = await sql_create_stage_item(
tournament_id, StageItemCreateBody(**stage_item.model_dump())
)

if result is None:
raise ValueError("Could not create stage")

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_)

return stage_item_result


async def sql_create_stage_item(
tournament_id: TournamentId, stage_item: StageItemCreateBody
) -> StageItem:
query = """
INSERT INTO stage_items (type, stage_id, name, team_count, ranking_id)
VALUES (:stage_item_type, :stage_id, :name, :team_count, :ranking_id)
RETURNING *
"""
result = await database.fetch_one(
query=query,
values={
"stage_item_type": stage_item.type.value,
"stage_id": stage_item.stage_id,
"name": stage_item.get_name_or_default_name(),
"team_count": stage_item.team_count,
"ranking_id": stage_item.ranking_id
if stage_item.ranking_id
else (await get_default_rankings_in_tournament(tournament_id)).id,
},
)
if result is None:
raise ValueError("Could not create stage")

return StageItem.model_validate(dict(result._mapping))


async def sql_delete_stage_item(stage_item_id: StageItemId) -> None:
query = """
DELETE FROM stage_items
Expand Down
19 changes: 11 additions & 8 deletions backend/bracket/utils/db_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
from bracket.models.db.ranking import RankingInsertable
from bracket.models.db.round import RoundInsertable
from bracket.models.db.stage import StageInsertable
from bracket.models.db.stage_item import StageItemCreateBody, StageItemInsertable
from bracket.models.db.stage_item import (
StageItemInsertable,
StageItemWithInputsCreate,
)
from bracket.models.db.stage_item_inputs import (
StageItemInputCreateBodyFinal,
StageItemInputCreateBodyTentative,
Expand All @@ -44,7 +47,7 @@
users_x_clubs,
)
from bracket.sql.matches import sql_update_match
from bracket.sql.stage_items import sql_create_stage_item
from bracket.sql.stage_items import sql_create_stage_item_with_inputs
from bracket.sql.stages import get_full_tournament_details
from bracket.sql.tournaments import sql_get_tournament
from bracket.sql.users import create_user, get_user
Expand Down Expand Up @@ -295,9 +298,9 @@ async def insert_dummy(
await insert_dummy(DUMMY_COURT1, CourtId, {"tournament_id": tournament_id_1})
await insert_dummy(DUMMY_COURT2, CourtId, {"tournament_id": tournament_id_1})

stage_item_1 = await sql_create_stage_item(
stage_item_1 = await sql_create_stage_item_with_inputs(
tournament_id_1,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_id_1,
name=DUMMY_STAGE_ITEM1.name,
team_count=DUMMY_STAGE_ITEM1.team_count,
Expand All @@ -322,9 +325,9 @@ async def insert_dummy(
],
),
)
stage_item_2 = await sql_create_stage_item(
stage_item_2 = await sql_create_stage_item_with_inputs(
tournament_id_1,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_id_1,
name=DUMMY_STAGE_ITEM2.name,
team_count=DUMMY_STAGE_ITEM2.team_count,
Expand All @@ -349,9 +352,9 @@ async def insert_dummy(
],
),
)
stage_item_3 = await sql_create_stage_item(
stage_item_3 = await sql_create_stage_item_with_inputs(
tournament_id_1,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_id_2,
name=DUMMY_STAGE_ITEM3.name,
team_count=DUMMY_STAGE_ITEM3.team_count,
Expand Down
12 changes: 6 additions & 6 deletions backend/tests/integration_tests/api/activate_next_stage_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from bracket.logic.scheduling.builder import build_matches_for_stage_item
from bracket.models.db.match import MatchBody, MatchWithDetailsDefinitive
from bracket.models.db.stage_item import StageItemCreateBody
from bracket.models.db.stage_item import StageItemWithInputsCreate
from bracket.models.db.stage_item_inputs import (
StageItemInputCreateBodyFinal,
StageItemInputCreateBodyTentative,
)
from bracket.models.db.util import StageWithStageItems
from bracket.sql.matches import sql_update_match
from bracket.sql.shared import sql_delete_stage_item_with_foreign_keys
from bracket.sql.stage_items import sql_create_stage_item
from bracket.sql.stage_items import sql_create_stage_item_with_inputs
from bracket.sql.stages import get_full_tournament_details
from bracket.utils.dummy_records import (
DUMMY_COURT1,
Expand Down Expand Up @@ -55,9 +55,9 @@ async def test_activate_next_stage(
) as team_inserted_4,
):
tournament_id = auth_context.tournament.id
stage_item_1 = await sql_create_stage_item(
stage_item_1 = await sql_create_stage_item_with_inputs(
tournament_id,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_inserted_1.id,
name=DUMMY_STAGE_ITEM1.name,
team_count=DUMMY_STAGE_ITEM1.team_count,
Expand All @@ -82,9 +82,9 @@ async def test_activate_next_stage(
],
),
)
stage_item_2 = await sql_create_stage_item(
stage_item_2 = await sql_create_stage_item_with_inputs(
tournament_id,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_inserted_2.id,
name=DUMMY_STAGE_ITEM3.name,
team_count=2,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from heliclockter import datetime_utc

from bracket.models.db.round import RoundInsertable
from bracket.models.db.stage_item import StageItemCreateBody, StageType
from bracket.models.db.stage_item import StageItemWithInputsCreate, StageType
from bracket.models.db.stage_item_inputs import (
StageItemInputCreateBodyFinal,
)
from bracket.sql.rounds import get_round_by_id, sql_create_round
from bracket.sql.shared import sql_delete_stage_item_with_foreign_keys
from bracket.sql.stage_items import sql_create_stage_item
from bracket.sql.stage_items import sql_create_stage_item_with_inputs
from bracket.sql.stages import get_full_tournament_details
from bracket.utils.dummy_records import (
DUMMY_COURT1,
Expand Down Expand Up @@ -48,9 +48,9 @@ async def test_schedule_matches_auto(
) as team_inserted_2,
):
tournament_id = auth_context.tournament.id
stage_item_1 = await sql_create_stage_item(
stage_item_1 = await sql_create_stage_item_with_inputs(
tournament_id,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_inserted_1.id,
name=DUMMY_STAGE_ITEM1.name,
team_count=2,
Expand Down Expand Up @@ -111,9 +111,9 @@ async def test_start_next_round(
) as team_inserted_2,
):
tournament_id = auth_context.tournament.id
stage_item_1 = await sql_create_stage_item(
stage_item_1 = await sql_create_stage_item_with_inputs(
tournament_id,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_inserted_1.id,
name=DUMMY_STAGE_ITEM1.name,
team_count=2,
Expand Down
15 changes: 2 additions & 13 deletions backend/tests/integration_tests/api/inputs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,12 @@ async def test_available_inputs(
inserted_stage(
DUMMY_STAGE1.model_copy(update={"tournament_id": auth_context.tournament.id})
) as stage_inserted_1,
# inserted_stage(
# DUMMY_STAGE2.model_copy(update={'tournament_id': auth_context.tournament.id})
# ) as stage_inserted_2,
inserted_stage_item(
DUMMY_STAGE_ITEM1.model_copy(
update={"stage_id": stage_inserted_1.id, "ranking_id": auth_context.ranking.id}
)
),
):
response = await send_tournament_request(
HTTPMethod.GET, f"stages/{stage_inserted_1.id}/available_inputs", auth_context
)
response = await send_tournament_request(HTTPMethod.GET, "available_inputs", auth_context)

assert response == {
"data": [
{"team_id": team_inserted.id},
# {'winner_from_stage_item_id': 1, 'winner_position': 1},
# {'winner_from_stage_item_id': 1, 'winner_position': 2},
]
}
assert response == {"data": {str(stage_inserted_1.id): [{"team_id": team_inserted.id}]}}
12 changes: 6 additions & 6 deletions backend/tests/integration_tests/api/scheduling_matches_test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from bracket.logic.scheduling.builder import build_matches_for_stage_item
from bracket.models.db.stage_item import StageItemCreateBody
from bracket.models.db.stage_item import StageItemWithInputsCreate
from bracket.models.db.stage_item_inputs import (
StageItemInputCreateBodyFinal,
StageItemInputCreateBodyTentative,
)
from bracket.sql.shared import sql_delete_stage_item_with_foreign_keys
from bracket.sql.stage_items import sql_create_stage_item
from bracket.sql.stage_items import sql_create_stage_item_with_inputs
from bracket.sql.stages import get_full_tournament_details
from bracket.utils.dummy_records import (
DUMMY_COURT1,
Expand Down Expand Up @@ -51,9 +51,9 @@ async def test_schedule_all_matches(
) as team_inserted_4,
):
tournament_id = auth_context.tournament.id
stage_item_1 = await sql_create_stage_item(
stage_item_1 = await sql_create_stage_item_with_inputs(
tournament_id,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_inserted_1.id,
name=DUMMY_STAGE_ITEM1.name,
team_count=DUMMY_STAGE_ITEM1.team_count,
Expand All @@ -78,9 +78,9 @@ async def test_schedule_all_matches(
],
),
)
stage_item_2 = await sql_create_stage_item(
stage_item_2 = await sql_create_stage_item_with_inputs(
tournament_id,
StageItemCreateBody(
StageItemWithInputsCreate(
stage_id=stage_inserted_1.id,
name=DUMMY_STAGE_ITEM3.name,
team_count=2,
Expand Down
2 changes: 1 addition & 1 deletion frontend/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
Loading

0 comments on commit 60af847

Please sign in to comment.