Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle edge case with empty stage item inputs #997

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions backend/bracket/logic/scheduling/handle_stage_activation.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
)
Expand Down
3 changes: 2 additions & 1 deletion backend/bracket/routes/stages.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()


Expand Down
9 changes: 7 additions & 2 deletions frontend/src/components/modals/activate_next_stage_modal.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 };
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/components/utils/error_alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
);
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/components/utils/skeletons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 />
</>
);
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/tournaments/[id]/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -266,7 +266,7 @@ export default function SettingsPage() {
let content = <NotFoundTitle />;

if (swrTournamentResponse.isLoading || swrClubsResponse.isLoading) {
content = <GenericSkeleton />;
content = <GenericSkeletonThreeRows />;
}

if (tournamentDataFull != null) {
Expand Down