Fix paths of swiss endpoints (#903)

This commit is contained in:
Erik Vroon
2024-09-10 19:21:24 +02:00
committed by GitHub
parent bb1dd7ed95
commit b3073c0fa6
9 changed files with 64 additions and 45 deletions

View File

@@ -3,26 +3,43 @@ from fastapi import HTTPException
from bracket.logic.scheduling.ladder_teams import get_possible_upcoming_matches_for_swiss
from bracket.models.db.match import MatchFilter, SuggestedMatch
from bracket.models.db.round import Round
from bracket.models.db.stage_item import StageType
from bracket.models.db.stage_item import StageItem, StageType
from bracket.models.db.util import RoundWithMatches, StageItemWithRounds
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.id_types import StageItemId, TournamentId
from bracket.utils.types import assert_some
async def get_upcoming_matches_for_swiss_round(
match_filter: MatchFilter, round_: Round, tournament_id: TournamentId
) -> list[SuggestedMatch]:
[stage] = await get_full_tournament_details(
tournament_id, stage_item_ids={round_.stage_item_id}
async def get_draft_round_in_stage_item(
tournament_id: TournamentId,
stage_item_id: StageItemId,
) -> tuple[RoundWithMatches, StageItemWithRounds]:
[stage] = await get_full_tournament_details(tournament_id, stage_item_ids={stage_item_id})
draft_round, stage_item = next(
(
(round_, stage_item)
for stage_item in stage.stage_items
for round_ in stage_item.rounds
if round_.is_draft
),
(None, None),
)
assert len(stage.stage_items) == 1
[stage_item] = stage.stage_items
if draft_round is None or stage_item is None:
raise HTTPException(400, "Expected stage item to be of type SWISS.")
return draft_round, stage_item
async def get_upcoming_matches_for_swiss_round(
match_filter: MatchFilter, stage_item: StageItem, round_: Round, tournament_id: TournamentId
) -> list[SuggestedMatch]:
if stage_item.type is not StageType.SWISS:
raise HTTPException(400, "Expected stage item to be of type SWISS.")
if not round_.is_draft:
raise HTTPException(400, "There is no draft round, so no matches can be scheduled.")
rounds = await get_rounds_for_stage_item(tournament_id, assert_some(stage_item.id))
teams = await get_teams_with_members(tournament_id, only_active_teams=True)

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends
from bracket.logic.planning.matches import (
get_scheduled_matches,
@@ -10,6 +10,7 @@ from bracket.logic.ranking.elo import (
recalculate_ranking_for_stage_item_id,
)
from bracket.logic.scheduling.upcoming_matches import (
get_draft_round_in_stage_item,
get_upcoming_matches_for_swiss_round,
)
from bracket.models.db.match import (
@@ -21,36 +22,34 @@ from bracket.models.db.match import (
MatchRescheduleBody,
SuggestedMatch,
)
from bracket.models.db.round import Round
from bracket.models.db.user import UserPublic
from bracket.models.db.util import RoundWithMatches
from bracket.routes.auth import user_authenticated_for_tournament
from bracket.routes.models import SingleMatchResponse, SuccessResponse, UpcomingMatchesResponse
from bracket.routes.util import match_dependency, round_dependency, round_with_matches_dependency
from bracket.routes.util import match_dependency
from bracket.sql.courts import get_all_courts_in_tournament
from bracket.sql.matches import sql_create_match, sql_delete_match, sql_update_match
from bracket.sql.rounds import get_round_by_id
from bracket.sql.stages import get_full_tournament_details
from bracket.sql.tournaments import sql_get_tournament
from bracket.sql.validation import check_foreign_keys_belong_to_tournament
from bracket.utils.id_types import MatchId, TournamentId
from bracket.utils.id_types import MatchId, StageItemId, TournamentId
from bracket.utils.types import assert_some
router = APIRouter()
@router.get(
"/tournaments/{tournament_id}/rounds/{round_id}/upcoming_matches",
"/tournaments/{tournament_id}/stage_items/{stage_item_id}/upcoming_matches",
response_model=UpcomingMatchesResponse,
)
async def get_matches_to_schedule(
tournament_id: TournamentId,
stage_item_id: StageItemId,
elo_diff_threshold: int = 200,
iterations: int = 200,
only_recommended: bool = False,
limit: int = 50,
_: UserPublic = Depends(user_authenticated_for_tournament),
round_: Round = Depends(round_dependency),
) -> UpcomingMatchesResponse:
match_filter = MatchFilter(
elo_diff_threshold=elo_diff_threshold,
@@ -59,11 +58,12 @@ async def get_matches_to_schedule(
iterations=iterations,
)
if not round_.is_draft:
raise HTTPException(400, "There is no draft round, so no matches can be scheduled.")
draft_round, stage_item = await get_draft_round_in_stage_item(tournament_id, stage_item_id)
return UpcomingMatchesResponse(
data=await get_upcoming_matches_for_swiss_round(match_filter, round_, tournament_id)
data=await get_upcoming_matches_for_swiss_round(
match_filter, stage_item, draft_round, tournament_id
)
)
@@ -123,20 +123,17 @@ async def reschedule_match(
@router.post(
"/tournaments/{tournament_id}/rounds/{round_id}/schedule_auto",
"/tournaments/{tournament_id}/stage_items/{stage_item_id}/schedule_auto",
response_model=SuccessResponse,
)
async def create_matches_automatically(
tournament_id: TournamentId,
stage_item_id: StageItemId,
elo_diff_threshold: int = 100,
iterations: int = 200,
only_recommended: bool = False,
_: UserPublic = Depends(user_authenticated_for_tournament),
round_: RoundWithMatches = Depends(round_with_matches_dependency),
) -> SuccessResponse:
if not round_.is_draft:
raise HTTPException(400, "There is no draft round, so no matches can be scheduled.")
match_filter = MatchFilter(
elo_diff_threshold=elo_diff_threshold,
only_recommended=only_recommended,
@@ -144,13 +141,14 @@ async def create_matches_automatically(
iterations=iterations,
)
draft_round, stage_item = await get_draft_round_in_stage_item(tournament_id, stage_item_id)
courts = await get_all_courts_in_tournament(tournament_id)
tournament = await sql_get_tournament(tournament_id)
limit = len(courts) - len(round_.matches)
limit = len(courts) - len(draft_round.matches)
for __ in range(limit):
all_matches_to_schedule = await get_upcoming_matches_for_swiss_round(
match_filter, round_, tournament_id
match_filter, stage_item, draft_round, tournament_id
)
if len(all_matches_to_schedule) < 1:
break
@@ -158,10 +156,10 @@ async def create_matches_automatically(
match = all_matches_to_schedule[0]
assert isinstance(match, SuggestedMatch)
assert round_.id and match.team1.id and match.team2.id
assert draft_round.id and match.team1.id and match.team2.id
await sql_create_match(
MatchCreateBody(
round_id=round_.id,
round_id=draft_round.id,
team1_id=match.team1.id,
team2_id=match.team2.id,
court_id=None,

View File

@@ -66,13 +66,13 @@ async def test_schedule_matches_auto(
],
),
)
round_1_id = await sql_create_round(
await sql_create_round(
RoundToInsert(stage_item_id=stage_item_1.id, name="", is_draft=True, is_active=False),
)
response = await send_tournament_request(
HTTPMethod.POST,
f"rounds/{round_1_id}/schedule_auto",
f"stage_items/{stage_item_1.id}/schedule_auto",
auth_context,
)
stages = await get_full_tournament_details(tournament_id)

View File

@@ -271,7 +271,7 @@ async def test_upcoming_matches_endpoint(
"stage_item_id": stage_item_inserted.id,
}
)
) as round_inserted,
),
inserted_team(
DUMMY_TEAM1.model_copy(
update={"tournament_id": auth_context.tournament.id, "elo_score": Decimal("1150.0")}
@@ -308,7 +308,10 @@ async def test_upcoming_matches_endpoint(
) as player_inserted_4,
):
json_response = await send_tournament_request(
HTTPMethod.GET, f"rounds/{round_inserted.id}/upcoming_matches", auth_context, {}
HTTPMethod.GET,
f"stage_items/{stage_item_inserted.id}/upcoming_matches",
auth_context,
{},
)
assert json_response == {
"data": [

View File

@@ -12,19 +12,16 @@ export function AutoCreateMatchesButton({
tournamentData,
swrStagesResponse,
swrUpcomingMatchesResponse,
roundId,
stageItemId,
schedulerSettings,
}: {
schedulerSettings: SchedulerSettings;
roundId: number;
stageItemId: number;
tournamentData: Tournament;
swrStagesResponse: SWRResponse;
swrUpcomingMatchesResponse: SWRResponse;
}) {
const { t } = useTranslation();
if (roundId == null) {
return null;
}
return (
<Button
size="md"
@@ -35,7 +32,7 @@ export function AutoCreateMatchesButton({
onClick={async () => {
await createMatchesAuto(
tournamentData.id,
roundId,
stageItemId,
schedulerSettings.eloThreshold,
schedulerSettings.onlyRecommended,
schedulerSettings.iterations

View File

@@ -6,6 +6,7 @@ import { BracketDisplaySettings } from '../../interfaces/brackets';
import { SchedulerSettings } from '../../interfaces/match';
import { RoundInterface } from '../../interfaces/round';
import { StageWithStageItems } from '../../interfaces/stage';
import { StageItemWithRounds } from '../../interfaces/stage_item';
import { Tournament } from '../../interfaces/tournament';
import { AutoCreateMatchesButton } from '../buttons/create_matches_auto';
import UpcomingMatchesTable from '../tables/upcoming_matches';
@@ -40,6 +41,7 @@ function SchedulingSystem({
export default function Scheduler({
activeStage,
stageItem,
tournamentData,
draftRound,
swrRoundsResponse,
@@ -48,6 +50,7 @@ export default function Scheduler({
displaySettings,
}: {
activeStage: StageWithStageItems;
stageItem: StageItemWithRounds;
draftRound: RoundInterface;
tournamentData: Tournament;
swrRoundsResponse: SWRResponse;
@@ -70,7 +73,7 @@ export default function Scheduler({
swrStagesResponse={swrRoundsResponse}
swrUpcomingMatchesResponse={swrUpcomingMatchesResponse}
tournamentData={tournamentData}
roundId={draftRound.id}
stageItemId={stageItem.id}
schedulerSettings={schedulerSettings}
/>
</Group>

View File

@@ -90,7 +90,7 @@ export default function TournamentPage() {
}
}
const swrUpcomingMatchesResponse = getUpcomingMatches(id, draftRound?.id, schedulerSettings);
const swrUpcomingMatchesResponse = getUpcomingMatches(id, stageItemId, schedulerSettings);
const scheduler =
draftRound != null &&
stageItem != null &&
@@ -100,6 +100,7 @@ export default function TournamentPage() {
<>
<Scheduler
activeStage={activeStage}
stageItem={stageItem}
draftRound={draftRound}
tournamentData={tournamentDataFull}
swrRoundsResponse={swrStagesResponse}

View File

@@ -197,13 +197,13 @@ export function getUser(): SWRResponse {
export function getUpcomingMatches(
tournament_id: number,
round_id: number,
stage_item_id: number,
schedulerSettings: SchedulerSettings
): SWRResponse {
return useSWR(
round_id == null
stage_item_id == null
? null
: `tournaments/${tournament_id}/rounds/${round_id}/upcoming_matches?elo_diff_threshold=${schedulerSettings.eloThreshold}&only_recommended=${schedulerSettings.onlyRecommended}&limit=${schedulerSettings.limit}&iterations=${schedulerSettings.iterations}`,
: `tournaments/${tournament_id}/stage_items/${stage_item_id}/upcoming_matches?elo_diff_threshold=${schedulerSettings.eloThreshold}&only_recommended=${schedulerSettings.onlyRecommended}&limit=${schedulerSettings.limit}&iterations=${schedulerSettings.iterations}`,
fetcher
);
}

View File

@@ -11,13 +11,13 @@ export async function createRound(tournament_id: number, stage_item_id: number)
export async function createMatchesAuto(
tournament_id: number,
round_id: number,
stage_item_id: number,
elo_diff_threshold: number,
only_recommended: string,
iterations: number
) {
return createAxios()
.post(`tournaments/${tournament_id}/rounds/${round_id}/schedule_auto`, {
.post(`tournaments/${tournament_id}/stage_items/${stage_item_id}/schedule_auto`, {
elo_diff_threshold,
only_recommended,
iterations,