Fix handling of available inputs (#993)

This commit is contained in:
Erik Vroon
2024-11-06 17:03:56 +01:00
committed by GitHub
parent 7d68f35a3b
commit cfb74c3959
6 changed files with 67 additions and 56 deletions

View File

@@ -18,7 +18,7 @@ from bracket.models.db.team import FullTeamWithPlayers
from bracket.models.db.util import StageWithStageItems
from bracket.sql.rounds import get_next_round_name, sql_create_round
from bracket.sql.stage_items import get_stage_item
from bracket.utils.id_types import StageId, TournamentId
from bracket.utils.id_types import StageId, StageItemId, TournamentId
from tests.integration_tests.mocks import MOCK_NOW
@@ -71,10 +71,9 @@ async def build_matches_for_stage_item(stage_item: StageItem, tournament_id: Tou
def determine_available_inputs(
stage_id: StageId,
teams: list[FullTeamWithPlayers],
stages: list[StageWithStageItems],
) -> list[StageItemInputOptionTentative | StageItemInputOptionFinal]:
) -> dict[StageId, list[StageItemInputOptionTentative | StageItemInputOptionFinal]]:
"""
Returns available inputs for the given stage.
@@ -83,35 +82,41 @@ def determine_available_inputs(
- Previous ROUND_ROBIN stage items
"""
results_team_ids = {team.id: False for team in teams}
results_tentative = []
results_tentative: dict[tuple[StageItemId, int], StageItemInputOptionTentative] = {}
results = {}
for stage in stages:
# First, set options that are used in this round to have `already_taken=True`
for stage_item in stage.stage_items:
item_team_id_inputs = [
input.team_id for input in stage_item.inputs if input.team_id is not None
]
for input_ in item_team_id_inputs:
if input_ in results_team_ids:
if stage_id != stage.id:
results_team_ids.pop(input_)
else:
results_team_ids[input_] = True
for input_ in stage_item.inputs:
if input_.team_id is not None and input_.team_id in results_team_ids:
results_team_ids[input_.team_id] = True
if stage_id == stage.id:
break
if (
input_.winner_from_stage_item_id is not None
and input_.winner_position is not None
and (key := (input_.winner_from_stage_item_id, input_.winner_position))
in results_tentative
):
results_tentative[key].already_taken = True
# Store results for this stage
results_final = [
StageItemInputOptionFinal(team_id=team_id, already_taken=taken)
for team_id, taken in results_team_ids.items()
]
results[stage.id] = results_final + list(results_tentative.values())
# Then, add inputs from non-elimination stage items that can be used in the next stage.
for stage_item in stage.stage_items:
for winner_position in range(1, 5):
results_tentative.append(
StageItemInputOptionTentative(
winner_from_stage_item_id=stage_item.id,
winner_position=winner_position,
already_taken=False,
if stage_item.type in {StageType.ROUND_ROBIN, StageType.SWISS}:
for winner_position in range(1, 5):
results_tentative[(stage_item.id, winner_position)] = (
StageItemInputOptionTentative(
winner_from_stage_item_id=stage_item.id,
winner_position=winner_position,
already_taken=False,
)
)
)
results_final = [
StageItemInputOptionFinal(team_id=team_id, already_taken=taken)
for team_id, taken in results_team_ids.items()
]
return results_final + results_tentative
return results

View File

@@ -145,10 +145,7 @@ async def get_available_inputs(
) -> StageItemInputOptionsResponse:
stages = await get_full_tournament_details(tournament_id)
teams = await get_teams_with_members(tournament_id)
available_inputs = {
stage.id: determine_available_inputs(stage.id, teams, stages) for stage in stages
}
return StageItemInputOptionsResponse(data=available_inputs)
return StageItemInputOptionsResponse(data=determine_available_inputs(teams, stages))
@router.get("/tournaments/{tournament_id}/next_stage_rankings")

View File

@@ -84,8 +84,12 @@ export function RoundsGridCols({
return <NoRoundsAlert readOnly={readOnly} />;
}
const items = stageItem.rounds
let result: React.JSX.Element[] | React.JSX.Element = stageItem.rounds
.sort((r1: any, r2: any) => (r1.name > r2.name ? 1 : -1))
.filter(
(round: RoundInterface) =>
round.matches.length > 0 || displaySettings.matchVisibility === 'all'
)
.map((round: RoundInterface) => (
<Round
key={round.id}
@@ -98,24 +102,34 @@ export function RoundsGridCols({
/>
));
let result: React.JSX.Element[] | React.JSX.Element = items;
if (result.length < 1) {
result = (
<Container mt="1rem">
<Stack align="center">
<NoContent title={t('no_round_description')} />
<AddRoundButton
t={t}
tournamentData={tournamentData}
stageItem={stageItem}
swrStagesResponse={swrStagesResponse}
swrUpcomingMatchesResponse={swrUpcomingMatchesResponse}
size="lg"
/>
</Stack>
</Container>
);
if (stageItem.rounds.length < 1) {
result = (
<Container mt="1rem">
<Stack align="center">
<NoContent title={t('no_round_description')} />
{stageItem.rounds.length < 1 && (
<AddRoundButton
t={t}
tournamentData={tournamentData}
stageItem={stageItem}
swrStagesResponse={swrStagesResponse}
swrUpcomingMatchesResponse={swrUpcomingMatchesResponse}
size="lg"
/>
)}
</Stack>
</Container>
);
} else {
result = (
<Container mt="1rem">
<Stack align="center">
<NoContent title={t('no_round_found_title')} />
</Stack>
</Container>
);
}
}
const hideAddRoundButton = tournamentData == null || readOnly;

View File

@@ -70,10 +70,6 @@ export default function Round({
/>
);
if (matches.length < 1 && displaySettings.matchVisibility !== 'all') {
return null;
}
const item = (
<div
style={{

View File

@@ -2,7 +2,7 @@ import { Alert, Container, Text, Title } from '@mantine/core';
import { IconAlertCircle } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next';
import React from 'react';
import { MdOutlineConstruction } from 'react-icons/md';
import { HiMiniWrenchScrewdriver } from 'react-icons/hi2';
import classes from './empty_table_info.module.css';
@@ -39,7 +39,7 @@ export function NoContent({
}) {
return (
<Container mt="md">
<div className={classes.label}>{icon || <MdOutlineConstruction />}</div>
<div className={classes.label}>{icon || <HiMiniWrenchScrewdriver />}</div>
<Title className={classes.title}>{title}</Title>
<Text size="lg" ta="center" className={classes.description}>
{description}

View File

@@ -8,7 +8,6 @@ export interface StageItemWithRounds {
name: string;
type_name: string;
team_count: number;
is_active: boolean;
rounds: RoundInterface[];
inputs: StageItemInput[];
stage_id: number;