Skip to content
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: 14 additions & 4 deletions backend/src/api/routes/jams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getEntries, getEntryById } from '../../services/entries.js';
import { selectSessionPairs } from '../../services/pairing.js';
import {
insertComparison,
getComparisonCountForJam,
getComparisonCountForJudge,
getComparisonsForJam,
} from '../../services/database.js';
Expand Down Expand Up @@ -96,10 +97,19 @@ router.post(
await insertComparison(slug, judgeId, vote.entryAId, vote.entryBId, vote.score);
}

const count = await getComparisonCountForJudge(slug, judgeId);
const sessions = Math.floor(count / JUDGING_SESSION_SIZE);

res.json({ recorded: true, count: votes.length, sessions });
const [judgeCount, totalVotes] = await Promise.all([
getComparisonCountForJudge(slug, judgeId),
getComparisonCountForJam(slug),
]);
const sessions = Math.floor(judgeCount / JUDGING_SESSION_SIZE);

res.json({
recorded: true,
count: votes.length,
sessions,
entryCount: entries.length,
voteCount: totalVotes,
});
} catch (err) {
console.error('Error recording session:', err);
res.status(500).json({ error: 'Failed to record session' });
Expand Down
15 changes: 15 additions & 0 deletions backend/src/services/database/judgingQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ export async function getComparisonCountForJudge(
return parseCount(results[0]?.count);
}

/**
* Get total comparison count for a jam.
*/
export async function getComparisonCountForJam(jamSlug: string): Promise<number> {
const sql = getSql();

const results = await sql<{ count: string }[]>`
SELECT COUNT(*) as count
FROM jam_comparisons
WHERE jam_slug = ${jamSlug}
`;

return parseCount(results[0]?.count);
}

/**
* Get unique judge count for a jam.
*/
Expand Down
14 changes: 11 additions & 3 deletions client/src/hooks/useJudging.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback, useReducer } from 'react';
import { JUDGING_SESSION_SIZE } from '../constants/judging';
import { CONFIDENCE_N } from '../utils/jam';

export interface EntryMetrics {
classification: 'Whole Game' | 'Feature';
Expand Down Expand Up @@ -93,6 +94,8 @@ interface SessionState {
fetchedAt: number;
status: Status;
sessions: number;
entryCount: number;
voteCount: number;
error: string | null;
}

Expand All @@ -103,6 +106,8 @@ const initialState: SessionState = {
fetchedAt: 0,
status: 'loading',
sessions: 0,
entryCount: 0,
voteCount: 0,
error: null,
};

Expand All @@ -112,7 +117,7 @@ type SessionAction =
| { type: 'GO_BACK' }
| { type: 'RESET' }
| { type: 'SUBMIT_START' }
| { type: 'SUBMIT_SUCCESS'; sessions: number }
| { type: 'SUBMIT_SUCCESS'; sessions: number; entryCount: number; voteCount: number }
| { type: 'SUBMIT_ERROR'; error: string }
| { type: 'SET_ERROR'; error: string };

Expand Down Expand Up @@ -161,7 +166,7 @@ function sessionReducer(state: SessionState, action: SessionAction): SessionStat
return { ...state, status: 'submitting' };

case 'SUBMIT_SUCCESS':
return { ...state, status: 'submitted', sessions: action.sessions };
return { ...state, status: 'submitted', sessions: action.sessions, entryCount: action.entryCount, voteCount: action.voteCount };

case 'SUBMIT_ERROR':
return { ...state, status: 'review', error: action.error };
Expand Down Expand Up @@ -287,7 +292,7 @@ export function useJudging(jamSlug: string) {

const data = await res.json();
clearSession(jamSlug);
dispatch({ type: 'SUBMIT_SUCCESS', sessions: data.sessions ?? 0 });
dispatch({ type: 'SUBMIT_SUCCESS', sessions: data.sessions ?? 0, entryCount: data.entryCount ?? 0, voteCount: data.voteCount ?? 0 });
} catch (e) {
dispatch({ type: 'SUBMIT_ERROR', error: e instanceof Error ? e.message : 'Failed to submit session' });
}
Expand All @@ -309,13 +314,16 @@ export function useJudging(jamSlug: string) {
total: state.pairs.length || JUDGING_SESSION_SIZE,
};

const resultsUnlocked = state.entryCount > 0 && state.voteCount >= state.entryCount * CONFIDENCE_N;

return {
user,
pair,
progress,
loading: state.status === 'loading',
status: state.status,
sessions: state.sessions,
resultsUnlocked,
error: state.error,
votes: state.votes,
pairs: state.pairs,
Expand Down
7 changes: 6 additions & 1 deletion client/src/pages/Judge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default function Judge() {
loading,
status,
sessions,
resultsUnlocked,
error,
canGoBack,
submitScore,
Expand Down Expand Up @@ -92,7 +93,11 @@ export default function Judge() {
<button className="judge-continue-btn" onClick={startNewSession}>
Submit More Votes
</button>
<Link to={`/judge/${slug}/results`} className="judge-back-btn">See Results</Link>
{resultsUnlocked ? (
<Link to={`/judge/${slug}/results`} className="judge-back-btn">See Results</Link>
) : (
<Link to="/judge" className="judge-back-btn">Back to Jams</Link>
)}
</div>
</div>
);
Expand Down
Loading