Skip to content

Commit

Permalink
Merge branch 'external-robot-design-judging' of https://github.com/FI…
Browse files Browse the repository at this point in the history
…RSTIsrael/lems into external-robot-design-judging
  • Loading branch information
johnmeshulam committed Apr 3, 2024
2 parents 77357a5 + 3eebc50 commit 71b8da9
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 29 deletions.
9 changes: 8 additions & 1 deletion apps/backend/src/routers/api/events/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Parser, FieldInfo } from '@json2csv/plainjs';
import * as db from '@lems/database';
import { JudgingCategory } from '@lems/types';
import { rubricsSchemas, RubricSchemaSection, RubricsSchema } from '@lems/season';
import { compareScoreArrays } from '@lems/utils/arrays';

const router = express.Router({ mergeParams: true });

Expand Down Expand Up @@ -111,7 +112,13 @@ router.get(
};
});

csvData.sort((a, b) => b.highestScore - a.highestScore);
const flattenScores = row => {
return Object.keys(row)
.filter(key => key.startsWith('round-'))
.map(key => row[key]);
};

csvData.sort((a, b) => compareScoreArrays(flattenScores(a), flattenScores(b)));

res.set('Content-Disposition', `attachment; filename=scores.csv`);
res.set('Content-Type', 'text/csv');
Expand Down
20 changes: 11 additions & 9 deletions apps/frontend/components/audience-display/scoreboard/scores.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import RemoveIcon from '@mui/icons-material/Remove';
import { WithId } from 'mongodb';
import { localizedMatchStage } from '../../../localization/field';
import { localizeTeam } from '../../../localization/teams';
import { compareScoreArrays } from '@lems/utils/arrays';

interface ScoreboardScoresProps {
scoresheets: Array<WithId<Scoresheet>>;
Expand All @@ -36,19 +37,20 @@ const ScoreboardScores: React.FC<ScoreboardScoresProps> = ({ scoresheets, teams,

const maxScores = teams
.map(t => {
const scores = [
...scoresheets
.filter(
s => s.teamId === t._id && s.stage === eventState.currentStage && s.status === 'ready'
)
.map(s => s.data?.score || 0)
];
return {
team: t,
score: Math.max(
...scoresheets
.filter(
s => s.teamId === t._id && s.stage === eventState.currentStage && s.status === 'ready'
)
.map(s => s.data?.score || 0),
0
)
scores: scores,
score: Math.max(...scores, 0)
};
})
.sort((a, b) => b.score - a.score);
.sort((a, b) => compareScoreArrays(a.scores, b.scores));

const marquee = keyframes`
from {transform: translateY(0)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@ const HeadRefereeMatchScheduleRow: React.FC<HeadRefereeMatchScheduleRowProps> =
<TableRow
sx={{
backgroundColor: match._id === eventState.activeMatch ? '#e6f7e7' : undefined,
'&:last-child td, &:last-child th': { border: 0 }
'&:last-child td, &:last-child th': { border: 0 },
scrollMarginTop: 60
}}
id={`match-${match.number}`}
>
<TableCell align="center">{match.number}</TableCell>
<TableCell align="center">{dayjs(match.scheduledTime).format('HH:mm')}</TableCell>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ReactElement, useEffect, useRef, useState } from 'react';
import React, { ReactElement, useRef, useState } from 'react';
import { WithId } from 'mongodb';
import { Socket } from 'socket.io-client';
import { enqueueSnackbar } from 'notistack';
Expand Down
60 changes: 46 additions & 14 deletions apps/frontend/components/mc/awards-lineup.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useState, useEffect, useMemo, Fragment } from 'react';
import { WithId } from 'mongodb';
import { Box, Divider, Paper, Stack, Typography } from '@mui/material';
import { IconButton, Box, Paper, Stack, Typography } from '@mui/material';
import EastRoundedIcon from '@mui/icons-material/EastRounded';
import WestRoundedIcon from '@mui/icons-material/WestRounded';
import { Event, Team, Award } from '@lems/types';
import { apiFetch } from '../../lib/utils/fetch';
import { localizedAward } from '@lems/season';
import Markdown from 'react-markdown';
import { localizeTeam } from 'apps/frontend/localization/teams';
import { localizeTeam } from '../../localization/teams';

interface AwardsLineupProps {
event: WithId<Event>;
Expand All @@ -15,6 +17,24 @@ const AwardsLineup: React.FC<AwardsLineupProps> = ({ event }) => {
const [teams, setTeams] = useState<Array<WithId<Team>>>([]);
const [awards, setAwards] = useState<Array<WithId<Award>>>([]);

const getAwardIndexFromQuery = () => {
const searchParams = new URLSearchParams(window.location.search);
return searchParams.get('awardIndex') === null
? 0
: parseInt(searchParams.get('awardIndex') as string);
};
const updateAwardIndexInUrl = (index: number) => {
const searchParams = new URLSearchParams(window.location.search);
searchParams.set('awardIndex', index.toString());
const newRelativePathQuery = window.location.pathname + '?' + searchParams.toString();
history.pushState(null, '', newRelativePathQuery);
};
const [currentAward, setCurrentAwardState] = useState(getAwardIndexFromQuery());
const setCurrentAward = (index: number) => {
updateAwardIndexInUrl(index);
setCurrentAwardState(index);
};

useEffect(() => {
apiFetch(`/api/events/${event._id}/awards`).then(res =>
res.json().then(data => setAwards(data))
Expand All @@ -34,16 +54,16 @@ const AwardsLineup: React.FC<AwardsLineupProps> = ({ event }) => {
return (
<Fragment key={awardName}>
<Box>
<Typography fontSize="1.5rem" fontWeight={700}>
<Typography fontSize="1.75rem" fontWeight={700}>
פרס {localized.name}
</Typography>
<Typography fontSize="1.25rem">
<Typography fontSize="1.75rem">
<Markdown>{localized.description}</Markdown>
</Typography>

{sortedAwards.map(award => {
return (
<Typography fontSize="1.25rem" key={award.name + award.place} gutterBottom>
<Typography fontSize="1.75rem" key={award.name + award.place} gutterBottom>
{sortedAwards.length > 1 && 'במקום ה-' + award.place + ': '}
{award.winner
? typeof award.winner === 'string'
Expand All @@ -54,28 +74,26 @@ const AwardsLineup: React.FC<AwardsLineupProps> = ({ event }) => {
);
})}
</Box>
<Divider />
</Fragment>
);
});

if (advancingTeams.length > 0) {
const advancingTeamsText = (
<>
<Typography fontSize="1.5rem" fontWeight={700} sx={{ pt: 2 }}>
<Typography fontSize="1.75rem" fontWeight={700} sx={{ pt: 2 }}>
קבוצות המעפילות לתחרות האליפות
</Typography>
<Typography fontSize="1.25rem">
<Typography fontSize="1.45rem">
רגע לפני שנכריז מי הן הקבוצות הזוכות בפרס האליפות, ישנן {advancingTeams.length} קבוצות
נוספות בתחרות אשר יזכו להעפיל לתחרות האליפות. אנחנו שמחים להכריז שהקבוצות הבאות, ללא סדר
מסוים, זכאיות גם הן לעלות שלב:
</Typography>
{advancingTeams.map(team => (
<Typography key={team._id.toString()} fontSize="1.25rem" gutterBottom>
<Typography key={team._id.toString()} fontSize="1.75rem" gutterBottom>
{localizeTeam(team)}
</Typography>
))}
<Divider />
</>
);
// Place advancement script directly before champions award
Expand All @@ -87,12 +105,26 @@ const AwardsLineup: React.FC<AwardsLineupProps> = ({ event }) => {
}, [advancingTeams, awards]);

return (
<Stack component={Paper} width="100%" p={2} spacing={2}>
<Typography fontSize="2rem" fontWeight={700} align="center">
<>
<Typography fontSize="2.5rem" fontWeight={700} align="center" gutterBottom>
{event.name} | פרסים
</Typography>
{lineup}
</Stack>
<Paper sx={{ width: '100%', p: 2, mb: 4 }}>{lineup[currentAward]}</Paper>
<Stack direction="row" spacing={4} justifyContent="center" alignItems="center">
<IconButton onClick={() => setCurrentAward(Math.max(currentAward - 1, 0))} size="large">
<EastRoundedIcon fontSize="large" />
</IconButton>
<Typography fontSize="1.5rem">
{lineup.length} / {currentAward + 1}
</Typography>
<IconButton
onClick={() => setCurrentAward(Math.min(currentAward + 1, lineup.length - 1))}
size="large"
>
<WestRoundedIcon fontSize="large" />
</IconButton>
</Stack>
</>
);
};

Expand Down
16 changes: 15 additions & 1 deletion apps/frontend/pages/event/[eventId]/head-referee.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { GetServerSideProps, NextPage } from 'next';
import { useRouter } from 'next/router';
import { WithId } from 'mongodb';
Expand Down Expand Up @@ -52,6 +52,20 @@ const Page: NextPage<Props> = ({
const headRefereeGeneralSchedule =
(showGeneralSchedule && event.schedule?.filter(s => s.roles.includes('head-referee'))) || [];

useEffect(() => {
setTimeout(() => {
const currentMatch = matches.find(m => m._id == eventState.loadedMatch);
if (currentMatch) scrollToSelector(`match-${currentMatch.number}`);
}, 0);
}, []);

const scrollToSelector = (selector: string) => {
const element = document.getElementById(selector);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'nearest' });
}
};

const updateMatches = (newMatch: WithId<RobotGameMatch>) => {
setMatches(matches =>
matches.map(m => {
Expand Down
3 changes: 2 additions & 1 deletion apps/frontend/pages/event/[eventId]/reports/scoreboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { apiFetch, serverSideGetRequests } from '../../../../lib/utils/fetch';
import { localizedMatchStage } from '../../../../localization/field';
import { localizedRoles } from '../../../../localization/roles';
import { localizeTeam } from '../../../../localization/teams';
import { compareScoreArrays } from '@lems/utils/arrays';

interface Props {
user: WithId<SafeUser>;
Expand Down Expand Up @@ -118,7 +119,7 @@ const Page: NextPage<Props> = ({
max: Math.max(...row.scores.map(score => score || 0))
}));

teamsWithMaxScores.sort((a, b) => b.max - a.max);
teamsWithMaxScores.sort((a,b) => compareScoreArrays(a.scores,b.scores));

const dataGridRows = teamsWithMaxScores.map((row, index) => ({
rank: index + 1,
Expand Down
6 changes: 5 additions & 1 deletion libs/season/src/lib/localization/judging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export const localizedAward: {
description:
'פרס ביצועי הרובוט מוענק לקבוצה אשר השיגה את הניקוד הגבוה ביותר במשחקי הרובוט. לקבוצות ניתנת הזדמנות להתחרות בלפחות שלושה מקצים של 2.5 דקות והניקוד הגבוה ביותר קובע.'
},
volunteerOfTheYear: { name: 'מתנדב/ת השנה', description: '' },
volunteerOfTheYear: {
name: 'מתנדב/ת השנה',
description:
'פרס מתנדב/ת השנה מוענק למתנדב/ת שהשקיעו את מירב מאמצים ותרמו מעצמם משמעותית לתוכנית *FIRST* LEGO League.'
},
champions: {
name: 'האליפות',
description:
Expand Down
16 changes: 16 additions & 0 deletions libs/utils/arrays/src/lib/utils-arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,19 @@ export const reorder = (arr: Array<any>, startIndex: number, endIndex: number) =
result.splice(endIndex, 0, removed);
return result;
};

export const compareScoreArrays = (
scoresA: (number | undefined)[],
scoresB: (number | undefined)[]
) => {
const sortedA = [...scoresA].sort((a, b) => (b || 0) - (a || 0));
const sortedB = [...scoresB].sort((a, b) => (b || 0) - (a || 0));
const maxLen = Math.max(scoresA.length, scoresB.length);

let difference = 0;
// iterate over the scores until we find a difference
for (let i = 0; i < maxLen && difference == 0; i++) {
difference = (sortedB[i] || 0) - (sortedA[i] || 0);
}
return difference;
};

0 comments on commit 71b8da9

Please sign in to comment.