Skip to content

Commit

Permalink
Extend dashboard pages (#289)
Browse files Browse the repository at this point in the history
  • Loading branch information
evroon authored Oct 11, 2023
1 parent 64ed11c commit 894f99c
Show file tree
Hide file tree
Showing 25 changed files with 592 additions and 72 deletions.
4 changes: 2 additions & 2 deletions backend/bracket/logic/scheduling/ladder_players_iter.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ def team_already_scheduled_before(player1: Player, player2: Player) -> bool:
) or team_already_scheduled_before(team2_players[0], team2_players[1]):
continue

team1 = TeamWithPlayers(players=team1_players)
team2 = TeamWithPlayers(players=team2_players)
team1 = TeamWithPlayers.from_players(team1_players)
team2 = TeamWithPlayers.from_players(team2_players)

suggested_match = check_team_combination_adheres_to_filter(team1, team2, filter_)
if suggested_match:
Expand Down
13 changes: 13 additions & 0 deletions backend/bracket/models/db/team.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from __future__ import annotations

# ruff: noqa: TCH001,TCH002
import json
from decimal import Decimal

Expand All @@ -20,6 +23,16 @@ class Team(BaseModelORM):
class TeamWithPlayers(BaseModel):
id: int | None = None
players: list[Player]
swiss_score: Decimal
elo_score: Decimal

@classmethod
def from_players(cls, players: list[Player]) -> TeamWithPlayers:
return TeamWithPlayers(
players=players,
elo_score=Decimal(sum(p.elo_score for p in players) / len(players)),
swiss_score=Decimal(sum(p.swiss_score for p in players) / len(players)),
)

@property
def player_ids(self) -> list[int]:
Expand Down
10 changes: 9 additions & 1 deletion backend/bracket/sql/stages.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ async def get_stages_with_rounds_and_matches(
WITH teams_with_players AS (
SELECT DISTINCT ON (teams.id)
teams.*,
to_json(array_remove(array_agg(p), NULL)) as players
to_json(array_remove(array_agg(p), NULL)) as players,
(
SELECT COALESCE(avg(swiss_score), 0.0)
FROM players
) AS swiss_score,
(
SELECT COALESCE(avg(elo_score), 0.0)
FROM players
) AS elo_score
FROM teams
LEFT JOIN players_x_teams pt on pt.team_id = teams.id
LEFT JOIN players p on pt.player_id = p.id
Expand Down
6 changes: 5 additions & 1 deletion backend/bracket/sql/teams.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ async def get_teams_with_members(
active_team_filter = 'AND teams.active IS TRUE' if only_active_teams else ''
team_id_filter = 'AND teams.id = :team_id' if team_id is not None else ''
query = f'''
SELECT teams.*, to_json(array_agg(p.*)) AS players
SELECT
teams.*,
to_json(array_agg(p.*)) AS players,
COALESCE(avg(p.elo_score), 0.0) AS elo_score,
COALESCE(avg(p.swiss_score), 0.0) AS swiss_score
FROM teams
LEFT JOIN players_x_teams pt on pt.team_id = teams.id
LEFT JOIN players p on pt.player_id = p.id
Expand Down
32 changes: 32 additions & 0 deletions backend/tests/integration_tests/api/matches_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -270,6 +272,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down Expand Up @@ -305,6 +309,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -334,6 +340,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down Expand Up @@ -369,6 +377,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -398,6 +408,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down Expand Up @@ -433,6 +445,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -462,6 +476,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down Expand Up @@ -497,6 +513,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -526,6 +544,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down Expand Up @@ -561,6 +581,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -590,6 +612,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down Expand Up @@ -625,6 +649,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -654,6 +680,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down Expand Up @@ -689,6 +717,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'team2': {
'id': None,
Expand Down Expand Up @@ -718,6 +748,8 @@ async def test_upcoming_matches_endpoint(
'losses': 0,
},
],
'swiss_score': 0.0,
'elo_score': 1250.0,
},
'elo_diff': 0.0,
'swiss_diff': 0.0,
Expand Down
2 changes: 2 additions & 0 deletions backend/tests/integration_tests/api/teams_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ async def test_teams_endpoint(
'name': 'Team 1',
'players': [],
'tournament_id': team_inserted.tournament_id,
'elo_score': 0.0,
'swiss_score': 0.0,
}
],
}
Expand Down
4 changes: 4 additions & 0 deletions backend/tests/unit_tests/elo_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,17 @@ def test_elo_calculation() -> None:
active=True,
created=DUMMY_MOCK_TIME,
players=[DUMMY_PLAYER1.copy(update={'id': 1})],
elo_score=DUMMY_PLAYER1.elo_score,
swiss_score=DUMMY_PLAYER1.swiss_score,
),
team2=FullTeamWithPlayers(
name='Dummy team 2',
tournament_id=1,
active=True,
created=DUMMY_MOCK_TIME,
players=[DUMMY_PLAYER2.copy(update={'id': 2})],
elo_score=DUMMY_PLAYER2.elo_score,
swiss_score=DUMMY_PLAYER2.swiss_score,
),
)
],
Expand Down
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
"react-dom": "^18.2.0",
"react-icons": "^4.8.0",
"react-redux": "^8.0.5",
"swr": "^2.1.5"
"swr": "^2.1.5",
"react-qr-code": "^2.0.12"
},
"devDependencies": {
"@babel/core": "^7.22.17",
Expand Down
39 changes: 39 additions & 0 deletions frontend/src/components/brackets/courts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Grid, Title } from '@mantine/core';
import React from 'react';

import { RoundInterface } from '../../interfaces/round';
import { TournamentMinimal } from '../../interfaces/tournament';
import Match from './match';

function getRoundsGridCols(activeRound: RoundInterface, tournamentData: TournamentMinimal) {
return activeRound.matches
.sort((m1, m2) => ((m1.court ? m1.court.name : 'y') > (m2.court ? m2.court.name : 'z') ? 1 : 0))
.map((match) => (
<Grid.Col sm={6} lg={4} xl={4} key={match.id}>
<Match
key={match.id}
tournamentData={tournamentData}
swrRoundsResponse={null}
swrCourtsResponse={null}
swrUpcomingMatchesResponse={null}
match={match}
readOnly
/>
</Grid.Col>
));
}

export default function Courts({
tournamentData,
activeRound,
}: {
tournamentData: TournamentMinimal;
activeRound: RoundInterface;
}) {
return (
<div>
<Title>{activeRound.name}</Title>
<Grid>{getRoundsGridCols(activeRound, tournamentData)}</Grid>
</div>
);
}
39 changes: 39 additions & 0 deletions frontend/src/components/brackets/courts_large.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Grid, Title } from '@mantine/core';
import React from 'react';

import { RoundInterface } from '../../interfaces/round';
import { TournamentMinimal } from '../../interfaces/tournament';
import MatchLarge from './match_large';

function getRoundsGridCols(activeRound: RoundInterface, tournamentData: TournamentMinimal) {
return activeRound.matches
.sort((m1, m2) => ((m1.court ? m1.court.name : 'y') > (m2.court ? m2.court.name : 'z') ? 1 : 0))
.map((match) => (
<Grid.Col sm={6} lg={6} xl={6} key={match.id}>
<MatchLarge
key={match.id}
tournamentData={tournamentData}
swrRoundsResponse={null}
swrCourtsResponse={null}
swrUpcomingMatchesResponse={null}
match={match}
readOnly
/>
</Grid.Col>
));
}

export default function CourtsLarge({
tournamentData,
activeRound,
}: {
tournamentData: TournamentMinimal;
activeRound: RoundInterface;
}) {
return (
<div>
<Title>{activeRound.name}</Title>
<Grid>{getRoundsGridCols(activeRound, tournamentData)}</Grid>
</div>
);
}
9 changes: 6 additions & 3 deletions frontend/src/components/brackets/match.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Center, Grid, UnstyledButton, createStyles, useMantineTheme } from '@mantine/core';
import assert from 'assert';
import { Property } from 'csstype';
import React, { useState } from 'react';
import { SWRResponse } from 'swr';
Expand Down Expand Up @@ -33,7 +34,7 @@ const useStyles = createStyles((theme) => ({
},
}));

function MatchBadge({ match, theme }: { match: MatchInterface; theme: any }) {
export function MatchBadge({ match, theme }: { match: MatchInterface; theme: any }) {
const visibility: Visibility = match.court ? 'visible' : 'hidden';
const badgeColor = theme.colorScheme === 'dark' ? theme.colors.blue[7] : theme.colors.blue[2];
return (
Expand Down Expand Up @@ -62,8 +63,8 @@ export default function Match({
match,
readOnly,
}: {
swrRoundsResponse: SWRResponse;
swrCourtsResponse: SWRResponse;
swrRoundsResponse: SWRResponse | null;
swrCourtsResponse: SWRResponse | null;
swrUpcomingMatchesResponse: SWRResponse | null;
tournamentData: TournamentMinimal;
match: MatchInterface;
Expand Down Expand Up @@ -107,6 +108,8 @@ export default function Match({
if (readOnly) {
return <div className={classes.root}>{bracket}</div>;
}
assert(swrRoundsResponse != null);
assert(swrCourtsResponse != null);

return (
<>
Expand Down
Loading

1 comment on commit 894f99c

@vercel
Copy link

@vercel vercel bot commented on 894f99c Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.