Skip to content

Commit

Permalink
WCA Live: Podiums and Competitors Views (#10819)
Browse files Browse the repository at this point in the history
* add new routes

* add new controller methods

* add new components

* add live_urls to routes.js.erb

* fix merge whoopsie

* Backend cleanup

* Data bindings fixes

* Rename results vars in ByPerson view

* Move route definition back to original place

* Update app/webpacker/components/Live/ByPerson/index.jsx

Co-authored-by: Kevin Matthews <[email protected]>

* Destructure more heavily

---------

Co-authored-by: Gregor Billing <[email protected]>
Co-authored-by: Gregor Billing <[email protected]>
Co-authored-by: Kevin Matthews <[email protected]>
  • Loading branch information
4 people authored Feb 13, 2025
1 parent 327985c commit 7c579f1
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 5 deletions.
22 changes: 22 additions & 0 deletions app/controllers/live_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,26 @@ def schedule_admin

@rounds = Round.joins(:competition_event).where(competition_event: { competition_id: @competition_id })
end

def podiums
@competition = Competition.find(params[:competition_id])
@competitors = @competition.registrations.includes(:user).accepted
@final_rounds = @competition.rounds.select(&:final_round?)
end

def competitors
@competition = Competition.find(params[:competition_id])
@competitors = @competition.registrations.includes(:user).accepted
end

def by_person
registration_id = params.require(:registration_id)
registration = Registration.find(registration_id)

@competition_id = params[:competition_id]
@competition = Competition.find(@competition_id)

@user = registration.user
@results = registration.live_results.includes(:live_attempts)
end
end
8 changes: 8 additions & 0 deletions app/models/round.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ def round_type_id
end
end

def event_id
event.id
end

def formats_used
cutoff_format = Format.c_find!(cutoff.number_of_attempts.to_s) if cutoff
[cutoff_format, format].compact
Expand Down Expand Up @@ -137,6 +141,10 @@ def advancement_condition_to_s(short: false)
advancement_condition ? advancement_condition.to_s(self, short: short) : ""
end

def live_podium
live_results.where(ranking: 1..3)
end

def previous_round
return nil if number == 1
Round.joins(:competition_event).find_by(competition_event: competition_event, number: number - 1)
Expand Down
5 changes: 5 additions & 0 deletions app/views/live/by_person.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<% provide(:title, 'WCA Live Integration Test Page') %>

<%= render layout: 'competitions/nav' do %>
<%= react_component('Live/ByPerson', { allResults: @results, user: @user, competitionId: @competition_id, }) %>
<% end %>
5 changes: 5 additions & 0 deletions app/views/live/competitors.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<% provide(:title, 'WCA Live Integration Test Page') %>

<%= render layout: 'competitions/nav' do %>
<%= react_component('Live/Competitors', { competitors: @competitors.as_json({ methods: [:user]}), competitionId: @competition.id, }) %>
<% end %>
5 changes: 5 additions & 0 deletions app/views/live/podiums.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<% provide(:title, 'WCA Live Integration Test Page') %>

<%= render layout: 'competitions/nav' do %>
<%= react_component('Live/Podiums', { podiums: @final_rounds.as_json({ methods: [:event_id, :live_podium]}), competitors: @competitors.as_json({ methods: [:user]}), competitionId: @competition.id, }) %>
<% end %>
85 changes: 85 additions & 0 deletions app/webpacker/components/Live/ByPerson/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from 'react';
import {
Header, Segment, Table,
} from 'semantic-ui-react';
import _ from 'lodash';
import { events } from '../../../lib/wca-data.js.erb';
import WCAQueryClientProvider from '../../../lib/providers/WCAQueryClientProvider';
import { formatAttemptResult } from '../../../lib/wca-live/attempts';
import { liveUrls } from '../../../lib/requests/routes.js.erb';
import { rankingCellStyle } from '../components/ResultsTable';

export default function Wrapper({
results, user, competitionId,
}) {
return (
<WCAQueryClientProvider>
<PersonResults results={results} user={user} competitionId={competitionId} />
</WCAQueryClientProvider>
);
}

function PersonResults({
results, user, competitionId,
}) {
const resultsByEvent = _.groupBy(results, 'event_id');

return (
<Segment>
<Header>
{user.name}
</Header>
{_.map(resultsByEvent, (eventResults, key) => (
<>
<Header as="h3">{events.byId[key].name}</Header>
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Round</Table.HeaderCell>
<Table.HeaderCell>Rank</Table.HeaderCell>
{_.times(events.byId[key].recommendedFormat().expectedSolveCount).map((num) => (
<Table.HeaderCell key={num}>
{num + 1}
</Table.HeaderCell>
))}
<Table.HeaderCell>Average</Table.HeaderCell>
<Table.HeaderCell>Best</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{eventResults.map((result) => {
const {
round,
attempts,
ranking,
average,
best,
} = result;

return (
<Table.Row>
<Table.Cell width={1}>
<a href={liveUrls.roundResults(competitionId, round.id)}>
Round
{' '}
{round.number}
</a>
</Table.Cell>
<Table.Cell width={1} style={rankingCellStyle(result)}>{ranking}</Table.Cell>
{attempts.map((a) => (
<Table.Cell>
{formatAttemptResult(a.result, events.byId[key].id)}
</Table.Cell>
))}
<Table.Cell>{formatAttemptResult(average, events.byId[key].id)}</Table.Cell>
<Table.Cell>{formatAttemptResult(best, events.byId[key].id)}</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
</>
))}
</Segment>
);
}
46 changes: 46 additions & 0 deletions app/webpacker/components/Live/Competitors/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import {
Input, Segment, Table,
} from 'semantic-ui-react';
import WCAQueryClientProvider from '../../../lib/providers/WCAQueryClientProvider';
import { liveUrls } from '../../../lib/requests/routes.js.erb';
import useInputState from '../../../lib/hooks/useInputState';
import CountryFlag from '../../wca/CountryFlag';

export default function Wrapper({
competitionId, competitors,
}) {
return (
<WCAQueryClientProvider>
<Competitors competitionId={competitionId} competitors={competitors} />
</WCAQueryClientProvider>
);
}

function Competitors({
competitionId, competitors,
}) {
const [searchInput, setSearchInput] = useInputState('');

return (
<Segment>
<Input placeholder="Search Competitor" value={searchInput} onChange={setSearchInput} icon="magnifying_glass" />
<Table basic="very" selectable>
<Table.Header>
<Table.HeaderCell width={1} />
<Table.HeaderCell />
</Table.Header>
<Table.Body>
{competitors.filter((c) => c.user.name.includes(searchInput)).map((c) => (
<Table.Row>
<Table.Cell><CountryFlag iso2={c.user.country_iso2} /></Table.Cell>
<Table.Cell>
<a href={liveUrls.personResults(competitionId, c.id)}>{c.user.name}</a>
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
</Segment>
);
}
43 changes: 43 additions & 0 deletions app/webpacker/components/Live/Podiums/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import {
Container,
Header,
} from 'semantic-ui-react';
import { events } from '../../../lib/wca-data.js.erb';
import WCAQueryClientProvider from '../../../lib/providers/WCAQueryClientProvider';
import ResultsTable from '../components/ResultsTable';

export default function Wrapper({
podiums, competitionId, competitors,
}) {
return (
<WCAQueryClientProvider>
<Podiums podiums={podiums} competitionId={competitionId} competitors={competitors} />
</WCAQueryClientProvider>
);
}

function Podiums({
podiums, competitionId, competitors,
}) {
return (
<Container fluid>
<Header>
Podiums
</Header>
{podiums.map((finalRound) => (
<>
<Header as="h3">{events.byId[finalRound.event_id].name}</Header>
{finalRound.live_podium.length > 0 ? (
<ResultsTable
results={finalRound.live_podium}
competitionId={competitionId}
competitors={competitors}
event={events.byId[finalRound.event_id]}
/>
) : 'Podiums to be determined'}
</>
))}
</Container>
);
}
7 changes: 2 additions & 5 deletions app/webpacker/components/Live/components/ResultsTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,11 @@ const customOrderBy = (competitor, resultsByRegistrationId, sortBy) => {
};

export const rankingCellStyle = (result) => {
if (!result) {
return {};
}
if (result.advancing) {
if (result?.advancing) {
return { backgroundColor: `rgb(${advancingColor})` };
}

if (result.advancing_questionable) {
if (result?.advancing_questionable) {
return { backgroundColor: `rgba(${advancingColor}, 0.5)` };
}

Expand Down
1 change: 1 addition & 0 deletions app/webpacker/lib/requests/routes.js.erb
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ export const actionUrls = {
}

export const liveUrls = {
personResults: (competitionId, registrationId) => `<%= CGI.unescape(Rails.application.routes.url_helpers.live_person_results_path(competition_id: "${competitionId}", registration_id: "${registrationId}")) %>`,
roundResultsAdmin: (competitionId, roundId) => `<%= CGI.unescape(Rails.application.routes.url_helpers.live_admin_round_results_path(competition_id: "${competitionId}", round_id: "${roundId}")) %>`,
checkRoundResultsAdmin: (competitionId, roundId) => `<%= CGI.unescape(Rails.application.routes.url_helpers.live_admin_check_round_results_path(competition_id: "${competitionId}", round_id: "${roundId}")) %>`,
api: {
Expand Down
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@
get 'competitions/edit/series-eligible-competitions-json' => 'competitions#series_eligible_competitions_json', as: :series_eligible_competitions_json

if WcaLive.enabled?
get 'competitions/:competition_id/live/competitors/:registration_id' => 'live#by_person', as: :live_person_results
get 'competitions/:competition_id/live/podiums' => 'live#podiums', as: :live_podiums
get 'competitions/:competition_id/live/competitors' => 'live#competitors', as: :live_competitors
get 'competitions/:competition_id/live/rounds/:round_id/admin' => 'live#admin', as: :live_admin_round_results
get 'competitions/:competition_id/live/rounds/:round_id/admin/check' => 'live#double_check', as: :live_admin_check_round_results
get 'competitions/:competition_id/live/admin' => 'live#schedule_admin', as: :live_schedule_admin
Expand Down

0 comments on commit 7c579f1

Please sign in to comment.