feat: fill 0-QP rows for unplaced participants on qualifying event finalize

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Parsons 2026-04-12 14:28:07 -07:00
parent 3cdce8eed7
commit 335d1bf015
3 changed files with 53 additions and 3 deletions

View file

@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { filterNewResults } from "../admin.sports-seasons.$id.events.$eventId.helpers"; import { filterNewResults, findParticipantsWithoutResults } from "../admin.sports-seasons.$id.events.$eventId.helpers";
describe("filterNewResults", () => { describe("filterNewResults", () => {
it("returns all rows when no existing results", () => { it("returns all rows when no existing results", () => {
@ -27,3 +27,22 @@ describe("filterNewResults", () => {
expect(filterNewResults(incoming, existing)).toEqual([]); expect(filterNewResults(incoming, existing)).toEqual([]);
}); });
}); });
describe("findParticipantsWithoutResults", () => {
it("returns participantIds not in existingResultIds", () => {
const allIds = ["p1", "p2", "p3"];
const existingIds = new Set(["p1"]);
expect(findParticipantsWithoutResults(allIds, existingIds)).toEqual(["p2", "p3"]);
});
it("returns empty array when all participants have results", () => {
const allIds = ["p1", "p2"];
const existingIds = new Set(["p1", "p2"]);
expect(findParticipantsWithoutResults(allIds, existingIds)).toEqual([]);
});
it("returns all when none have results", () => {
const allIds = ["p1", "p2"];
expect(findParticipantsWithoutResults(allIds, new Set())).toEqual(["p1", "p2"]);
});
});

View file

@ -1,3 +1,15 @@
/**
* Return participantIds from allParticipantIds that have no entry
* in existingResultParticipantIds. Used to find participants who
* need a 0-QP row after a qualifying event is finalized.
*/
export function findParticipantsWithoutResults(
allParticipantIds: string[],
existingResultParticipantIds: Set<string>
): string[] {
return allParticipantIds.filter((id) => !existingResultParticipantIds.has(id));
}
/** /**
* Filter incoming batch results to only those whose participantId * Filter incoming batch results to only those whose participantId
* is not already in the set of existing result participantIds. * is not already in the set of existing result participantIds.

View file

@ -16,7 +16,7 @@ import {
type CreateEventResultData, type CreateEventResultData,
type UpdateEventResultData, type UpdateEventResultData,
} from "~/models/event-result"; } from "~/models/event-result";
import { filterNewResults } from "./admin.sports-seasons.$id.events.$eventId.helpers"; import { filterNewResults, findParticipantsWithoutResults } from "./admin.sports-seasons.$id.events.$eventId.helpers";
import { findParticipantResultsBySportsSeasonId } from "~/models/participant-result"; import { findParticipantResultsBySportsSeasonId } from "~/models/participant-result";
import { import {
upsertParticipantSeasonResult, upsertParticipantSeasonResult,
@ -126,9 +126,28 @@ export async function action({ request, params }: Route.ActionArgs) {
return { success: "Event completed and fantasy placements assigned to top 8!" }; return { success: "Event completed and fantasy placements assigned to top 8!" };
} }
// If this is a qualifying event, process qualifying points // If this is a qualifying event, process qualifying points then fill 0-QP rows
if (event.isQualifyingEvent) { if (event.isQualifyingEvent) {
await processQualifyingEvent(params.eventId); await processQualifyingEvent(params.eventId);
// Write 0-QP rows for all season participants who have no result for this event.
// This marks them as "competed, earned nothing" so simulations skip this event.
const allParticipants = await findParticipantsBySportsSeasonId(params.id);
const existingResults = await getEventResults(params.eventId);
const existingIds = new Set(existingResults.map((r) => r.participantId));
const missingIds = findParticipantsWithoutResults(
allParticipants.map((p) => p.id),
existingIds
);
for (const participantId of missingIds) {
await createEventResult({
scoringEventId: params.eventId,
participantId,
qualifyingPointsAwarded: 0,
});
}
return { success: "Event completed and qualifying points awarded!" }; return { success: "Event completed and qualifying points awarded!" };
} }