Creating new qualifying scoring_events or season_participants now auto-upserts the matching canonical rows. Needed so the check constraint doesn't reject qualifying events, and so new season participants flow through syncTournamentResults. - Extract tournament identity shared between backfill and event-creation into app/lib/tournament-identity.ts; scripts/backfill re-exports it. - createScoringEvent + bulkCreateScoringEvents now accept tournamentId. - Event creation routes auto-compute (name, year) and upsert tournament. - createParticipant + createManyParticipants auto-link to canonical participants by (sportId, name). Phase 3 Task 7 of canonical tournament layer migration. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
/**
|
|
* Extracts the canonical tournament identity — `(name, year)` — from a scoring
|
|
* event. Used both by the Phase 2 backfill and by the admin create-event flow
|
|
* that auto-provisions a canonical tournament for new qualifying events.
|
|
*
|
|
* Rules:
|
|
* - If the event name ends with a 4-digit year, strip it; that year wins.
|
|
* - Otherwise, fall back to the year portion of eventDate (YYYY-MM-DD).
|
|
* - If neither produces a year, throw.
|
|
*/
|
|
|
|
export interface ScoringEventIdentityInput {
|
|
name: string;
|
|
eventDate: string | Date | null | undefined;
|
|
eventType: string;
|
|
}
|
|
|
|
export interface TournamentIdentity {
|
|
name: string;
|
|
year: number;
|
|
}
|
|
|
|
const YEAR_SUFFIX = /\s+(\d{4})\s*$/;
|
|
|
|
export function extractTournamentIdentity(ev: ScoringEventIdentityInput): TournamentIdentity {
|
|
const trimmed = ev.name.trim();
|
|
const yearMatch = trimmed.match(YEAR_SUFFIX);
|
|
let cleanName = trimmed;
|
|
let year: number | null = null;
|
|
|
|
if (yearMatch) {
|
|
year = parseInt(yearMatch[1], 10);
|
|
cleanName = trimmed.replace(YEAR_SUFFIX, "").trim();
|
|
}
|
|
|
|
if (year === null && ev.eventDate != null) {
|
|
const iso = ev.eventDate instanceof Date
|
|
? ev.eventDate.toISOString()
|
|
: String(ev.eventDate);
|
|
year = parseInt(iso.slice(0, 4), 10);
|
|
}
|
|
|
|
if (year === null || Number.isNaN(year)) {
|
|
throw new Error(
|
|
`cannot determine year for event "${ev.name}" — no year in name and eventDate is null`,
|
|
);
|
|
}
|
|
|
|
return { name: cleanName, year };
|
|
}
|