Handle edge case with empty stage item inputs (#997)

This commit is contained in:
Erik Vroon
2024-11-07 16:53:09 +01:00
committed by GitHub
parent 19372a8db4
commit 5719bbf3c1
6 changed files with 41 additions and 11 deletions

View File

@@ -1,12 +1,18 @@
from collections import defaultdict
from fastapi import HTTPException
from pydantic import BaseModel
from starlette import status
from bracket.logic.ranking.elo import (
determine_team_ranking_for_stage_item,
)
from bracket.logic.ranking.statistics import TeamStatistics
from bracket.models.db.stage_item_inputs import StageItemInputFinal, StageItemInputTentative
from bracket.models.db.stage_item_inputs import (
StageItemInputEmpty,
StageItemInputFinal,
StageItemInputTentative,
)
from bracket.models.db.team import Team
from bracket.models.db.util import StageWithStageItems
from bracket.sql.matches import clear_scores_for_matches_in_stage_item
@@ -65,7 +71,15 @@ async def get_team_update_for_input(
target_stage_item_input = await get_stage_item_input_by_id(
tournament_id, target_stage_item_input_id
)
assert isinstance(target_stage_item_input, StageItemInputFinal)
if isinstance(target_stage_item_input, StageItemInputEmpty):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Please first assign teams to all stage items in the current stage.",
)
assert isinstance(
target_stage_item_input, StageItemInputFinal
), f"Unexpected stage item type: {type(target_stage_item_input)}"
return StageItemInputUpdate(
stage_item_input=stage_item_input, team=target_stage_item_input.team
)

View File

@@ -126,12 +126,13 @@ async def activate_next_stage(
stages = await get_full_tournament_details(tournament_id)
deactivated_stage = next((stage for stage in stages if stage.is_active), None)
await sql_activate_next_stage(new_active_stage_id, tournament_id)
if stage_body.direction == "next":
await update_matches_in_activated_stage(tournament_id, new_active_stage_id)
else:
if deactivated_stage:
await update_matches_in_deactivated_stage(tournament_id, deactivated_stage)
await sql_activate_next_stage(new_active_stage_id, tournament_id)
return SuccessResponse()

View File

@@ -1,4 +1,4 @@
import { Alert, Button, Container, Grid, Loader, Modal, Title } from '@mantine/core';
import { Alert, Button, Container, Grid, Modal, Title } from '@mantine/core';
import { useForm } from '@mantine/form';
import { FaArrowRight } from '@react-icons/all-files/fa/FaArrowRight';
import { IconAlertCircle, IconSquareArrowRight } from '@tabler/icons-react';
@@ -11,6 +11,8 @@ import { StageItemInput, formatStageItemInput } from '../../interfaces/stage_ite
import { TeamInterface } from '../../interfaces/team';
import { getStageItemLookup } from '../../services/lookups';
import { activateNextStage } from '../../services/stage';
import RequestErrorAlert from '../utils/error_alert';
import { GenericSkeleton } from '../utils/skeletons';
type Update = { stage_item_input: StageItemInput; team: TeamInterface };
type StageItemUpdate = { updates: Update[]; stageItem: StageItemWithRounds };
@@ -47,7 +49,10 @@ function UpdatesToStageItemInputsTables({
swrRankingsPerStageItemResponse: SWRResponse;
}) {
if (swrRankingsPerStageItemResponse.isLoading) {
return <Loader />;
return <GenericSkeleton />;
}
if (swrRankingsPerStageItemResponse.error) {
return <RequestErrorAlert error={swrRankingsPerStageItemResponse.error} />;
}
const items = swrRankingsPerStageItemResponse.data.data;

View File

@@ -4,7 +4,13 @@ import React from 'react';
export function ErrorAlert({ title, message }: { title: string; message: string }) {
return (
<Alert icon={<IconAlertCircle size={16} />} title={title} color="red" radius="lg">
<Alert
icon={<IconAlertCircle size={32} />}
title={title}
color="red"
radius="lg"
variant="outline"
>
{message}
</Alert>
);

View File

@@ -2,11 +2,15 @@ import { Center, Grid, Skeleton } from '@mantine/core';
import React from 'react';
export function GenericSkeleton() {
return <Skeleton height={75} radius="lg" mb="xl" />;
}
export function GenericSkeletonThreeRows() {
return (
<>
<Skeleton height={75} radius="lg" mb="xl" />
<Skeleton height={75} radius="lg" mb="xl" />
<Skeleton height={75} radius="lg" mb="xl" />
<GenericSkeleton />
<GenericSkeleton />
<GenericSkeleton />
</>
);
}

View File

@@ -25,7 +25,7 @@ import { SWRResponse } from 'swr';
import NotFoundTitle from '../../404';
import { DropzoneButton } from '../../../components/utils/file_upload';
import { GenericSkeleton } from '../../../components/utils/skeletons';
import { GenericSkeletonThreeRows } from '../../../components/utils/skeletons';
import { capitalize, getBaseURL, getTournamentIdFromRouter } from '../../../components/utils/util';
import { Club } from '../../../interfaces/club';
import { Tournament } from '../../../interfaces/tournament';
@@ -266,7 +266,7 @@ export default function SettingsPage() {
let content = <NotFoundTitle />;
if (swrTournamentResponse.isLoading || swrClubsResponse.isLoading) {
content = <GenericSkeleton />;
content = <GenericSkeletonThreeRows />;
}
if (tournamentDataFull != null) {