From 338979e0a805145541cd79d8ea04e1da79813c82 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Sun, 29 Mar 2026 10:27:47 -0700 Subject: [PATCH] Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator (#242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add FIFA World Cup 2026 support with group stage display and Monte Carlo simulator, fixes #127 - New `groupStageMatches` table for recording group play results (W/D/L, scores, matchday, schedule) - `computeGroupStandings()` model function: pts → GD → GF → name tiebreaker ordering - `GroupStageStandings` component showing all 12 groups with standings table and manager column - Admin bracket UI: group match score entry, per-group standings, "Recalculate Floors" action - `WorldCupSimulator`: 50k Monte Carlo covering group stage + best-8 3rd-place + knockout + 3rd place game - Fuzzy name matching for national team Elo lookup (exact → substring → word-overlap), warns on miss - Partial group completion: completed matches replayed with real scores, remaining matches simulated - Elo priority: admin-entered sourceElo > futures odds converted to Elo > hardcoded national team ratings - `fifa_48` bracket template: added Third Place Game round with `loserFeedsInto` on Semifinals - Scoring rules: distinct 3rd/4th place for `fifa_48` (not averaged), QF losers share 5th–8th equally - Floor scoring: SF participants guaranteed 4th (provisional), finalized after 3rd place game - `recalculate-floors` admin action deletes and replays all results from scratch (fixes stale guard bug) - Unique index on `(tournamentGroupId, participant1Id, participant2Id)` to prevent duplicate pairings - Batch `findMatchesByGroupIds()` replacing N sequential queries in the sport season loader - League home mini-standings now shows `actualPoints` (includes floor) instead of `totalPoints` only - Elo ratings admin page supports World Cup (same bulk-import flow as snooker) Co-Authored-By: Claude Sonnet 4.6 * Fix TypeScript errors: update GroupStandingData type to use findMatchesByGroupIds Co-Authored-By: Claude Sonnet 4.6 * Increase Node heap to 4GB for unit tests in CI to prevent OOM Co-Authored-By: Claude Sonnet 4.6 * Fix OOM in CI: make WorldCupSimulator simulation count configurable for tests Tests now pass numSimulations=500 instead of the production default of 50,000. Six simulator tests × 50k iterations each was exhausting the 4GB heap on GitHub Actions runners. Also reduce simGroupMatch stat tests from 50k to 5k iterations. Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- .github/workflows/deploy.yml | 1 + .../sport-season/GroupStageStandings.tsx | 185 + app/lib/bracket-templates.ts | 12 + .../__tests__/group-stage-match.test.ts | 190 + app/models/group-stage-match.ts | 361 ++ app/models/playoff-match.ts | 24 + app/models/scoring-calculator.ts | 24 +- app/models/scoring-rules.ts | 11 +- .../admin.sports-seasons.$id.elo-ratings.tsx | 13 +- ...sons.$id.events.$eventId.bracket.server.ts | 125 +- ...ts-seasons.$id.events.$eventId.bracket.tsx | 252 +- .../admin.sports-seasons.$id.simulate.tsx | 16 +- app/routes/leagues/$leagueId.server.ts | 31 + ...d.sports-seasons.$sportsSeasonId.server.ts | 51 + ...eagueId.sports-seasons.$sportsSeasonId.tsx | 9 + app/routes/leagues/$leagueId.tsx | 2 +- .../__tests__/world-cup-simulator.test.ts | 268 + app/services/simulations/registry.ts | 6 + .../simulations/world-cup-simulator.ts | 609 +++ database/schema.ts | 43 + drizzle/0064_fuzzy_wolfsbane.sql | 13 +- drizzle/0065_kind_mantis.sql | 32 + drizzle/0066_stiff_siren.sql | 1 + drizzle/meta/0065_snapshot.json | 4310 ++++++++++++++++ drizzle/meta/0066_snapshot.json | 4338 +++++++++++++++++ drizzle/meta/_journal.json | 14 + 26 files changed, 10858 insertions(+), 83 deletions(-) create mode 100644 app/components/sport-season/GroupStageStandings.tsx create mode 100644 app/models/__tests__/group-stage-match.test.ts create mode 100644 app/models/group-stage-match.ts create mode 100644 app/services/simulations/__tests__/world-cup-simulator.test.ts create mode 100644 app/services/simulations/world-cup-simulator.ts create mode 100644 drizzle/0065_kind_mantis.sql create mode 100644 drizzle/0066_stiff_siren.sql create mode 100644 drizzle/meta/0065_snapshot.json create mode 100644 drizzle/meta/0066_snapshot.json diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index febd41e..ef60564 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -49,6 +49,7 @@ jobs: run: npm run test:run env: DATABASE_URL: postgresql://test:test@localhost:5432/brackt_test + NODE_OPTIONS: --max-old-space-size=4096 typecheck: name: ʦ TypeScript diff --git a/app/components/sport-season/GroupStageStandings.tsx b/app/components/sport-season/GroupStageStandings.tsx new file mode 100644 index 0000000..b1e1dcc --- /dev/null +++ b/app/components/sport-season/GroupStageStandings.tsx @@ -0,0 +1,185 @@ +import type { GroupStandingsRow } from "~/models/group-stage-match"; +import { TeamOwnerBadge } from "~/components/ui/team-owner-badge"; + +interface GroupMatch { + id: string; + matchday: number; + scheduledAt: string | null; + isComplete: boolean; + participant1Id: string; + participant2Id: string; + participant1Score: number | null; + participant2Score: number | null; + participant1: { id: string; name: string } | null; + participant2: { id: string; name: string } | null; +} + +interface GroupData { + groupName: string; + standings: GroupStandingsRow[]; + matches: GroupMatch[]; +} + +interface OwnerInfo { + teamName: string; + ownerName: string; + teamId: string; +} + +interface GroupStageStandingsProps { + groups: GroupData[]; + /** participantId → owner info, used to show which manager drafted each team */ + ownershipMap?: Record; + /** If true, shows all groups. If false, shows only groups with at least one completed match. */ + showEmpty?: boolean; +} + +function MatchResult({ match }: { match: GroupMatch }) { + const p1 = match.participant1?.name ?? "?"; + const p2 = match.participant2?.name ?? "?"; + + if (match.isComplete && match.participant1Score !== null && match.participant2Score !== null) { + const s1 = match.participant1Score; + const s2 = match.participant2Score; + const result = s1 > s2 ? "w" : s1 < s2 ? "l" : "d"; + return ( +
+ + {p1} + + + {s1}–{s2} + + + {p2} + +
+ ); + } + + const kickoff = match.scheduledAt ? new Date(match.scheduledAt) : null; + return ( +
+ {p1} + + {kickoff + ? kickoff.toLocaleDateString("en-US", { month: "short", day: "numeric" }) + : "vs"} + + {p2} +
+ ); +} + +export function GroupStageStandings({ + groups, + ownershipMap, + showEmpty = false, +}: GroupStageStandingsProps) { + const visibleGroups = showEmpty + ? groups + : groups.filter((g) => g.matches.some((m) => m.isComplete) || g.standings.length > 0); + + if (visibleGroups.length === 0) return null; + + return ( +
+

Group Stage

+
+ {groups.map((group) => ( +
+ {/* Header */} +
+

Group {group.groupName}

+
+ + {/* Standings table */} +
+ + + + + + + + + + + + + + + {group.standings.map((row, idx) => { + const owner = ownershipMap?.[row.participantId]; + return ( + + + + + + + + + + + ); + })} + +
TeamManagerPWDLGDPts
+ + {row.participantName} + {idx < 2 && ( + + )} + + + {owner + ? + : + } + {row.played}{row.wins}{row.draws}{row.losses} + {row.gd > 0 ? `+${row.gd}` : row.gd} + + {row.points} +
+
+ + {/* Match results */} + {group.matches.length > 0 && ( +
+ {[1, 2, 3].map((day) => { + const dayMatches = group.matches.filter((m) => m.matchday === day); + if (dayMatches.length === 0) return null; + return ( +
+

+ MD {day} +

+ {dayMatches.map((m) => ( + + ))} +
+ ); + })} +
+ )} +
+ ))} +
+
+ ); +} diff --git a/app/lib/bracket-templates.ts b/app/lib/bracket-templates.ts index 8190e06..c985c0b 100644 --- a/app/lib/bracket-templates.ts +++ b/app/lib/bracket-templates.ts @@ -14,6 +14,11 @@ export interface BracketRound { feedsInto: string | null; /** Whether this round affects fantasy points (false = 0 points per Q20) */ isScoring: boolean; + /** + * Name of the round that *losers* advance to (e.g., "Third Place Game" for SF losers). + * When set, the loser of each match in this round is placed into the target round. + */ + loserFeedsInto?: string | null; } export interface GroupStageConfig { @@ -544,6 +549,13 @@ export const FIFA_48: BracketTemplate = { matchCount: 2, feedsInto: "Finals", isScoring: true, + loserFeedsInto: "Third Place Game", + }, + { + name: "Third Place Game", + matchCount: 1, + feedsInto: null, + isScoring: true, }, { name: "Finals", diff --git a/app/models/__tests__/group-stage-match.test.ts b/app/models/__tests__/group-stage-match.test.ts new file mode 100644 index 0000000..06a0c89 --- /dev/null +++ b/app/models/__tests__/group-stage-match.test.ts @@ -0,0 +1,190 @@ +import { describe, expect, it } from "vitest"; +import { + computeGroupStandings, + generateRoundRobinPairings, +} from "../group-stage-match"; + +const TEAMS = [ + { participantId: "t1", participantName: "Brazil" }, + { participantId: "t2", participantName: "France" }, + { participantId: "t3", participantName: "Germany" }, + { participantId: "t4", participantName: "Argentina" }, +]; + +function match( + p1: string, + p2: string, + s1: number, + s2: number, + isComplete = true +) { + return { + participant1Id: p1, + participant2Id: p2, + participant1Score: s1, + participant2Score: s2, + isComplete, + }; +} + +function findTeam( + standings: ReturnType, + id: string +) { + const row = standings.find((s) => s.participantId === id); + if (!row) throw new Error(`Team ${id} not found in standings`); + return row; +} + +describe("computeGroupStandings", () => { + it("returns all four teams with zeroes when no matches are complete", () => { + const standings = computeGroupStandings(TEAMS, [ + { + participant1Id: "t1", + participant2Id: "t2", + participant1Score: null, + participant2Score: null, + isComplete: false, + }, + ]); + expect(standings).toHaveLength(4); + for (const row of standings) { + expect(row.played).toBe(0); + expect(row.points).toBe(0); + } + }); + + it("awards 3 points for a win and 0 for a loss", () => { + const standings = computeGroupStandings(TEAMS, [match("t1", "t2", 2, 0)]); + const brazil = findTeam(standings, "t1"); + const france = findTeam(standings, "t2"); + expect(brazil.points).toBe(3); + expect(brazil.wins).toBe(1); + expect(france.points).toBe(0); + expect(france.losses).toBe(1); + }); + + it("awards 1 point each for a draw", () => { + const standings = computeGroupStandings(TEAMS, [match("t1", "t2", 1, 1)]); + const brazil = findTeam(standings, "t1"); + const france = findTeam(standings, "t2"); + expect(brazil.points).toBe(1); + expect(brazil.draws).toBe(1); + expect(france.points).toBe(1); + expect(france.draws).toBe(1); + }); + + it("tracks goals for, against, and difference correctly", () => { + const standings = computeGroupStandings(TEAMS, [match("t1", "t2", 3, 1)]); + const brazil = findTeam(standings, "t1"); + const france = findTeam(standings, "t2"); + expect(brazil.gf).toBe(3); + expect(brazil.ga).toBe(1); + expect(brazil.gd).toBe(2); + expect(france.gf).toBe(1); + expect(france.ga).toBe(3); + expect(france.gd).toBe(-2); + }); + + it("sorts by points descending", () => { + const standings = computeGroupStandings(TEAMS, [ + match("t1", "t2", 2, 0), // Brazil 3pts + match("t3", "t4", 1, 0), // Germany 3pts + match("t1", "t3", 0, 0), // Brazil 1pt, Germany 1pt + match("t2", "t4", 2, 0), // France 3pts + ]); + const ids = standings.map((s) => s.participantId); + // Brazil: 4pts gd+2, Germany: 4pts gd-1, France: 3pts, Argentina: 0pts + expect(ids[0]).toBe("t1"); // Brazil + expect(ids[1]).toBe("t3"); // Germany + expect(ids[2]).toBe("t2"); // France + expect(ids[3]).toBe("t4"); // Argentina + }); + + it("uses goal difference as tiebreaker after points", () => { + const standings = computeGroupStandings(TEAMS, [ + match("t1", "t3", 3, 0), // Brazil big win + match("t2", "t4", 1, 0), // France narrow win + ]); + expect(standings[0].participantId).toBe("t1"); // Brazil: 3pts, gd+3 + expect(standings[1].participantId).toBe("t2"); // France: 3pts, gd+1 + }); + + it("uses goals for as tiebreaker when points and gd are equal", () => { + const standings = computeGroupStandings(TEAMS, [ + match("t1", "t3", 2, 1), // Brazil: 3pts, gd+1, gf2 + match("t2", "t4", 3, 2), // France: 3pts, gd+1, gf3 + ]); + expect(standings[0].participantId).toBe("t2"); // France higher gf + expect(standings[1].participantId).toBe("t1"); + }); + + it("falls back to alphabetical name when fully tied", () => { + const standings = computeGroupStandings(TEAMS, [ + match("t1", "t3", 1, 0), + match("t2", "t4", 1, 0), + ]); + // Both t1 (Brazil) and t2 (France) have 3pts, gd+1, gf1 + expect(standings[0].participantId).toBe("t1"); // Brazil < France alphabetically + expect(standings[1].participantId).toBe("t2"); + }); + + it("handles a full 6-match group correctly", () => { + const standings = computeGroupStandings(TEAMS, [ + match("t1", "t4", 3, 0), // Brazil beat Argentina + match("t2", "t3", 2, 1), // France beat Germany + match("t1", "t3", 2, 0), // Brazil beat Germany + match("t4", "t2", 0, 1), // France beat Argentina + match("t1", "t2", 1, 0), // Brazil beat France + match("t3", "t4", 2, 1), // Germany beat Argentina + ]); + expect(standings[0].participantId).toBe("t1"); // Brazil 9pts + expect(standings[0].points).toBe(9); + expect(standings[1].participantId).toBe("t2"); // France 6pts + expect(standings[2].participantId).toBe("t3"); // Germany 3pts + expect(standings[3].participantId).toBe("t4"); // Argentina 0pts + }); + + it("skips incomplete matches", () => { + const standings = computeGroupStandings(TEAMS, [ + match("t1", "t2", 2, 0, true), + match("t3", "t4", 1, 0, false), + ]); + expect(findTeam(standings, "t1").played).toBe(1); + expect(findTeam(standings, "t3").played).toBe(0); + }); +}); + +describe("generateRoundRobinPairings", () => { + it("generates exactly 6 pairings for 4 teams", () => { + const pairings = generateRoundRobinPairings(["t1", "t2", "t3", "t4"]); + expect(pairings).toHaveLength(6); + }); + + it("covers all 6 unique matchups", () => { + const pairings = generateRoundRobinPairings(["t1", "t2", "t3", "t4"]); + const keys = new Set( + pairings.map((p) => + [p.participant1Id, p.participant2Id].toSorted().join("-") + ) + ); + expect(keys.size).toBe(6); + }); + + it("uses matchdays 1, 2, and 3", () => { + const pairings = generateRoundRobinPairings(["t1", "t2", "t3", "t4"]); + const matchdays = [...new Set(pairings.map((p) => p.matchday))].toSorted(); + expect(matchdays).toEqual([1, 2, 3]); + }); + + it("has exactly 2 matches per matchday", () => { + const pairings = generateRoundRobinPairings(["t1", "t2", "t3", "t4"]); + for (const day of [1, 2, 3]) { + expect(pairings.filter((p) => p.matchday === day)).toHaveLength(2); + } + }); + + it("throws if not exactly 4 participants", () => { + expect(() => generateRoundRobinPairings(["t1", "t2", "t3"])).toThrow(); + }); +}); diff --git a/app/models/group-stage-match.ts b/app/models/group-stage-match.ts new file mode 100644 index 0000000..8392547 --- /dev/null +++ b/app/models/group-stage-match.ts @@ -0,0 +1,361 @@ +import { and, asc, eq, gte, inArray, lte, or } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; +import { logger } from "~/lib/logger"; + +export type GroupStageMatch = typeof schema.groupStageMatches.$inferSelect; + +export interface GroupStandingsRow { + participantId: string; + participantName: string; + played: number; + wins: number; + draws: number; + losses: number; + gf: number; // goals for + ga: number; // goals against + gd: number; // goal difference + points: number; +} + +export interface UpcomingGroupMatch { + matchId: string; + groupId: string; + groupName: string; + sportsSeasonId: string; + matchday: number; + scheduledAt: string; // ISO string + isComplete: boolean; + participant1: { id: string; name: string }; + participant2: { id: string; name: string }; + participant1Score: number | null; + participant2Score: number | null; +} + +interface MatchInput { + participant1Id: string; + participant2Id: string; + matchday: number; + scheduledAt?: Date | null; +} + +/** + * Create the 6 round-robin matches for a tournament group. + * Matches are created without scores (to be filled in as the tournament progresses). + */ +export async function createGroupStageMatches( + groupId: string, + matches: MatchInput[] +): Promise { + const db = database(); + return await db + .insert(schema.groupStageMatches) + .values( + matches.map((m) => ({ + tournamentGroupId: groupId, + participant1Id: m.participant1Id, + participant2Id: m.participant2Id, + matchday: m.matchday, + scheduledAt: m.scheduledAt ?? null, + })) + ) + .returning(); +} + +/** + * Generate all 6 round-robin pairings for 4 participants in a group. + * Uses a fixed round-robin schedule (matchdays 1, 2, 3). + * + * Standard 4-team round-robin schedule: + * Matchday 1: 1v4, 2v3 + * Matchday 2: 1v3, 4v2 + * Matchday 3: 1v2, 3v4 + */ +export function generateRoundRobinPairings( + participantIds: string[] +): MatchInput[] { + if (participantIds.length !== 4) { + throw new Error("Round-robin generation requires exactly 4 participants"); + } + const [p1, p2, p3, p4] = participantIds; + return [ + { participant1Id: p1, participant2Id: p4, matchday: 1 }, + { participant1Id: p2, participant2Id: p3, matchday: 1 }, + { participant1Id: p1, participant2Id: p3, matchday: 2 }, + { participant1Id: p4, participant2Id: p2, matchday: 2 }, + { participant1Id: p1, participant2Id: p2, matchday: 3 }, + { participant1Id: p3, participant2Id: p4, matchday: 3 }, + ]; +} + +/** + * Record the result of a group stage match and mark it complete. + */ +export async function updateGroupStageMatchResult( + matchId: string, + participant1Score: number, + participant2Score: number +): Promise { + const db = database(); + const [updated] = await db + .update(schema.groupStageMatches) + .set({ + participant1Score, + participant2Score, + isComplete: true, + updatedAt: new Date(), + }) + .where(eq(schema.groupStageMatches.id, matchId)) + .returning(); + return updated; +} + +/** + * Update the scheduled time of a group stage match. + */ +export async function updateGroupStageMatchSchedule( + matchId: string, + scheduledAt: Date | null +): Promise { + const db = database(); + const [updated] = await db + .update(schema.groupStageMatches) + .set({ scheduledAt, updatedAt: new Date() }) + .where(eq(schema.groupStageMatches.id, matchId)) + .returning(); + return updated; +} + +/** + * Fetch all matches for a group, with participant info. + */ +export async function findMatchesByGroupId(groupId: string) { + const db = database(); + return await db.query.groupStageMatches.findMany({ + where: eq(schema.groupStageMatches.tournamentGroupId, groupId), + orderBy: [ + asc(schema.groupStageMatches.matchday), + asc(schema.groupStageMatches.createdAt), + ], + with: { + participant1: true, + participant2: true, + }, + }); +} + +/** + * Fetch all matches for multiple groups in a single query. + * Returns a Map from groupId → matches (ordered by matchday, createdAt). + * Use this instead of calling findMatchesByGroupId N times. + */ +export async function findMatchesByGroupIds( + groupIds: string[] +): Promise>>> { + if (groupIds.length === 0) return new Map(); + const db = database(); + const rows = await db.query.groupStageMatches.findMany({ + where: inArray(schema.groupStageMatches.tournamentGroupId, groupIds), + orderBy: [ + asc(schema.groupStageMatches.matchday), + asc(schema.groupStageMatches.createdAt), + ], + with: { + participant1: true, + participant2: true, + }, + }); + const result = new Map(); + for (const groupId of groupIds) result.set(groupId, []); + for (const row of rows) { + result.get(row.tournamentGroupId)?.push(row); + } + return result; +} + +/** + * Fetch all group stage matches for a scoring event (all groups combined). + */ +export async function findMatchesByEventId(eventId: string) { + const db = database(); + const groups = await db.query.tournamentGroups.findMany({ + where: eq(schema.tournamentGroups.scoringEventId, eventId), + with: { + matches: { + orderBy: [ + asc(schema.groupStageMatches.matchday), + asc(schema.groupStageMatches.createdAt), + ], + with: { + participant1: true, + participant2: true, + }, + }, + }, + }); + return groups; +} + +/** + * Compute standings for a group from match results. + * Sorted by: points desc → goal difference desc → goals for desc → name asc. + */ +export function computeGroupStandings( + members: Array<{ participantId: string; participantName: string }>, + matches: Array<{ + participant1Id: string; + participant2Id: string; + participant1Score: number | null; + participant2Score: number | null; + isComplete: boolean; + }> +): GroupStandingsRow[] { + const stats = new Map(); + + for (const m of members) { + stats.set(m.participantId, { + participantId: m.participantId, + participantName: m.participantName, + played: 0, + wins: 0, + draws: 0, + losses: 0, + gf: 0, + ga: 0, + gd: 0, + points: 0, + }); + } + + for (const match of matches) { + if (!match.isComplete) continue; + if (match.participant1Score === null || match.participant2Score === null) { + logger.warn( + `[computeGroupStandings] Match between ${match.participant1Id} and ${match.participant2Id} is marked complete but has null scores — skipping` + ); + continue; + } + + const p1 = stats.get(match.participant1Id); + const p2 = stats.get(match.participant2Id); + if (!p1 || !p2) continue; + + const s1 = match.participant1Score; + const s2 = match.participant2Score; + + p1.played++; + p2.played++; + p1.gf += s1; + p1.ga += s2; + p2.gf += s2; + p2.ga += s1; + p1.gd = p1.gf - p1.ga; + p2.gd = p2.gf - p2.ga; + + if (s1 > s2) { + p1.wins++; + p1.points += 3; + p2.losses++; + } else if (s2 > s1) { + p2.wins++; + p2.points += 3; + p1.losses++; + } else { + p1.draws++; + p2.draws++; + p1.points++; + p2.points++; + } + } + + return [...stats.values()].toSorted((a, b) => { + if (b.points !== a.points) return b.points - a.points; + if (b.gd !== a.gd) return b.gd - a.gd; + if (b.gf !== a.gf) return b.gf - a.gf; + return a.participantName.localeCompare(b.participantName); + }); +} + +/** + * Get upcoming group stage matches (within a date window) for specific participants. + * Used for calendar/schedule display on league home and team pages. + */ +export async function getUpcomingGroupStageMatchesForParticipants( + sportsSeasonId: string, + participantIds: string[], + dateFrom: Date, + dateTo: Date +): Promise { + if (participantIds.length === 0) return []; + + const db = database(); + + const rows = await db + .select({ + matchId: schema.groupStageMatches.id, + groupId: schema.tournamentGroups.id, + groupName: schema.tournamentGroups.groupName, + sportsSeasonId: schema.scoringEvents.sportsSeasonId, + matchday: schema.groupStageMatches.matchday, + scheduledAt: schema.groupStageMatches.scheduledAt, + isComplete: schema.groupStageMatches.isComplete, + participant1Id: schema.groupStageMatches.participant1Id, + participant2Id: schema.groupStageMatches.participant2Id, + participant1Score: schema.groupStageMatches.participant1Score, + participant2Score: schema.groupStageMatches.participant2Score, + }) + .from(schema.groupStageMatches) + .innerJoin( + schema.tournamentGroups, + eq(schema.groupStageMatches.tournamentGroupId, schema.tournamentGroups.id) + ) + .innerJoin( + schema.scoringEvents, + eq(schema.tournamentGroups.scoringEventId, schema.scoringEvents.id) + ) + .where( + and( + eq(schema.scoringEvents.sportsSeasonId, sportsSeasonId), + or( + inArray(schema.groupStageMatches.participant1Id, participantIds), + inArray(schema.groupStageMatches.participant2Id, participantIds) + ), + and( + gte(schema.groupStageMatches.scheduledAt, dateFrom), + lte(schema.groupStageMatches.scheduledAt, dateTo) + ) + ) + ) + .orderBy(asc(schema.groupStageMatches.scheduledAt)); + + if (rows.length === 0) return []; + + // Load participant names for all involved participants + const allParticipantIds = [ + ...new Set(rows.flatMap((r) => [r.participant1Id, r.participant2Id])), + ]; + const participantRows = await db.query.participants.findMany({ + where: inArray(schema.participants.id, allParticipantIds), + }); + const participantMap = new Map(participantRows.map((p) => [p.id, p])); + + return rows + .filter((r) => r.scheduledAt !== null) + .map((r) => { + const p1 = participantMap.get(r.participant1Id); + const p2 = participantMap.get(r.participant2Id); + return { + matchId: r.matchId, + groupId: r.groupId, + groupName: r.groupName, + sportsSeasonId: r.sportsSeasonId, + matchday: r.matchday, + scheduledAt: r.scheduledAt?.toISOString() ?? "", + isComplete: r.isComplete, + participant1: { id: r.participant1Id, name: p1?.name ?? "Unknown" }, + participant2: { id: r.participant2Id, name: p2?.name ?? "Unknown" }, + participant1Score: r.participant1Score, + participant2Score: r.participant2Score, + }; + }); +} diff --git a/app/models/playoff-match.ts b/app/models/playoff-match.ts index feceb03..a4d481e 100644 --- a/app/models/playoff-match.ts +++ b/app/models/playoff-match.ts @@ -922,6 +922,30 @@ export async function advanceWinnerTemplate( // Fill the determined slot await updatePlayoffMatch(nextMatch.id, { [participantSlot]: winnerId }); + + // If this round has a loserFeedsInto, also advance the loser to that round + if (currentRound.loserFeedsInto) { + const loserId = + match.participant1Id === winnerId ? match.participant2Id : match.participant1Id; + if (loserId) { + const loserRound = template.rounds.find((r) => r.name === currentRound.loserFeedsInto); + if (loserRound) { + const loserMatches = await findPlayoffMatchesByEventIdAndRound( + match.scoringEventId, + loserRound.name + ); + const loserMatch = loserMatches[0]; + if (loserMatch) { + // SF match 1 loser → participant1Id, SF match 2 loser → participant2Id + const loserSlot: "participant1Id" | "participant2Id" = + match.matchNumber === 1 ? "participant1Id" : "participant2Id"; + if (!loserMatch[loserSlot]) { + await updatePlayoffMatch(loserMatch.id, { [loserSlot]: loserId }); + } + } + } + } + } } /** diff --git a/app/models/scoring-calculator.ts b/app/models/scoring-calculator.ts index 649abae..b48ddc4 100644 --- a/app/models/scoring-calculator.ts +++ b/app/models/scoring-calculator.ts @@ -29,9 +29,14 @@ interface RoundScoringConfig { loserIsPartial: boolean; /** * Guaranteed minimum position for the winner. - * null signals a Finals round: winner is finalized as 1st place (handled inline). + * null signals a finalization round: both winner and loser receive their exact positions. */ winnerFloor: number | null; + /** + * When winnerFloor is null, the winner is finalized at this position. + * Defaults to 1 (champion) when omitted — set to 3 for a 3rd place game. + */ + winnerPosition?: number; } /** @@ -76,6 +81,14 @@ const TEMPLATE_ROUND_CONFIG: Record> afl_10: { "Semi-Finals": { loserPosition: 5, loserIsPartial: false, winnerFloor: 3 }, }, + fifa_48: { + // QF winner is guaranteed 4th at worst (lose SF → lose 3PG). + Quarterfinals: { loserPosition: 5, loserIsPartial: false, winnerFloor: 4 }, + // SF loser still has the 3rd place game — provisional 4th until it's played. + Semifinals: { loserPosition: 4, loserIsPartial: true, winnerFloor: 2 }, + // 3rd place game finalizes both positions distinctly. + "Third Place Game": { loserPosition: 4, loserIsPartial: false, winnerFloor: null, winnerPosition: 3 }, + }, }; /** @@ -259,12 +272,13 @@ export async function processPlayoffEvent( } } } else if (config.winnerFloor === null) { - // Finals: finalize both participants — winner gets 1st, loser gets 2nd. + // Finalization round: both participants receive their exact positions. + // winnerPosition defaults to 1 (champion); set to 3 for a 3rd place game. const finalMatch = matches[0]; if (!finalMatch || !finalMatch.winnerId || !finalMatch.loserId) { throw new Error("Finals match is not complete"); } - await upsertParticipantResult(finalMatch.winnerId, event.sportsSeasonId, 1, db); + await upsertParticipantResult(finalMatch.winnerId, event.sportsSeasonId, config.winnerPosition ?? 1, db); await upsertParticipantResult(finalMatch.loserId, event.sportsSeasonId, config.loserPosition, db); } else { // All other scoring rounds: assign loser's final (or provisional) position. @@ -376,9 +390,9 @@ export async function processMatchResult( await upsertParticipantResult(loserId, sportsSeasonId, 0, db); await upsertParticipantResult(winnerId, sportsSeasonId, 5, db, true); } else if (config.winnerFloor === null) { - // Finals: finalize both participants. + // Finalization round: winner and loser both get their exact positions. await upsertParticipantResult(loserId, sportsSeasonId, config.loserPosition, db); - await upsertParticipantResult(winnerId, sportsSeasonId, 1, db); + await upsertParticipantResult(winnerId, sportsSeasonId, config.winnerPosition ?? 1, db); } else { // All other scoring rounds: loser gets final (or provisional) position, winner gets floor. await upsertParticipantResult(loserId, sportsSeasonId, config.loserPosition, db, config.loserIsPartial); diff --git a/app/models/scoring-rules.ts b/app/models/scoring-rules.ts index cf8146c..adbe52e 100644 --- a/app/models/scoring-rules.ts +++ b/app/models/scoring-rules.ts @@ -116,6 +116,12 @@ export function calculateAveragedPoints( */ const SPLIT_5678_TEMPLATE_IDS = new Set(["afl_10"]); +/** + * Brackets with a real 3rd place game, meaning positions 3 and 4 are distinct + * (not averaged). Standard brackets average them because both SF losers tie. + */ +const DISTINCT_34_TEMPLATE_IDS = new Set(["fifa_48"]); + /** * Calculate fantasy points for a bracket placement, averaging tied positions. * @@ -139,8 +145,11 @@ export function calculateBracketPoints( if (finalPosition <= 0) return 0; if (finalPosition === 1) return rules.pointsFor1st; if (finalPosition === 2) return rules.pointsFor2nd; - if (finalPosition === 3 || finalPosition === 4) + if (finalPosition === 3 || finalPosition === 4) { + if (bracketTemplateId && DISTINCT_34_TEMPLATE_IDS.has(bracketTemplateId)) + return finalPosition === 3 ? rules.pointsFor3rd : rules.pointsFor4th; return calculateAveragedPoints([3, 4], rules); + } if (finalPosition >= 5 && finalPosition <= 8) { if (bracketTemplateId && SPLIT_5678_TEMPLATE_IDS.has(bracketTemplateId)) { // AFL-style: two separate 2-team tiers within 5–8 diff --git a/app/routes/admin.sports-seasons.$id.elo-ratings.tsx b/app/routes/admin.sports-seasons.$id.elo-ratings.tsx index 2249dfc..0f07c11 100644 --- a/app/routes/admin.sports-seasons.$id.elo-ratings.tsx +++ b/app/routes/admin.sports-seasons.$id.elo-ratings.tsx @@ -11,7 +11,8 @@ import { } from '~/models/participant-expected-value'; import { batchUpsertParticipantEvSnapshots } from '~/models/ev-snapshot'; import { getSimulator, type SimulatorType } from '~/services/simulations/registry'; -import { calculateEV, type ScoringRules } from '~/services/ev-calculator'; +import { calculateEV } from '~/services/ev-calculator'; +import { DEFAULT_SCORING_RULES } from '~/lib/scoring-types'; import { recalculateStandings } from '~/models/scoring-calculator'; import { database } from '~/database/context'; import * as schema from '~/database/schema'; @@ -30,16 +31,6 @@ import { import { useState } from 'react'; import { Loader2, CheckCircle2, AlertCircle } from 'lucide-react'; -const DEFAULT_SCORING_RULES: ScoringRules = { - pointsFor1st: 100, - pointsFor2nd: 70, - pointsFor3rd: 45, - pointsFor4th: 45, - pointsFor5th: 20, - pointsFor6th: 20, - pointsFor7th: 20, - pointsFor8th: 20, -}; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Elo Ratings — ${data?.sportsSeason?.name ?? "Sports Season"} - Brackt Admin` }]; diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts index b5bda7b..9ff856f 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts @@ -40,6 +40,13 @@ import { getAdvancingParticipantIds, getEliminatedParticipantIds, } from "~/models/tournament-group"; +import { + createGroupStageMatches, + generateRoundRobinPairings, + updateGroupStageMatchResult, + updateGroupStageMatchSchedule, + findMatchesByGroupId, +} from "~/models/group-stage-match"; import { logger } from "~/lib/logger"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; @@ -65,8 +72,14 @@ export async function loader({ params }: Route.LoaderArgs) { const participants = await findParticipantsBySportsSeasonId(params.id); const matches = await findPlayoffMatchesByEventId(params.eventId); - // Fetch tournament groups if this event has a group-stage template - const tournamentGroups = await findGroupsByEventId(params.eventId); + // Fetch tournament groups with their round-robin matches + const tournamentGroupsRaw = await findGroupsByEventId(params.eventId); + const tournamentGroups = await Promise.all( + tournamentGroupsRaw.map(async (group) => ({ + ...group, + groupMatches: await findMatchesByGroupId(group.id), + })) + ); // The matches already include participant relations from the model query return { @@ -554,6 +567,63 @@ export async function action({ request, params }: Route.ActionArgs) { } } + if (intent === "recalculate-floors") { + try { + const event = await getScoringEventById(params.eventId); + if (!event) return { error: "Event not found" }; + + const matches = await findPlayoffMatchesByEventId(params.eventId); + const completed = matches.filter((m) => m.isComplete && m.winnerId && m.loserId); + + if (completed.length === 0) { + return { error: "No completed matches to reprocess" }; + } + + // Delete ALL results for this sports season and rebuild from scratch. + // Only deleting partial rows leaves stale finalized rows that block + // the "never un-finalize" guard in upsertParticipantResult. + const db = database(); + await db + .delete(schema.participantResults) + .where( + eq(schema.participantResults.sportsSeasonId, event.sportsSeasonId) + ); + + // Replay each completed match in bracket order (earlier rounds first). + const template = event.bracketTemplateId ? getBracketTemplate(event.bracketTemplateId) : null; + const roundOrder = template ? template.rounds.map((r) => r.name) : []; + + const sortedMatches = completed.toSorted((a, b) => { + const ai = roundOrder.indexOf(a.round); + const bi = roundOrder.indexOf(b.round); + return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi); + }); + + for (const match of sortedMatches) { + await processMatchResult( + { + round: match.round, + winnerId: match.winnerId ?? "", + loserId: match.loserId ?? "", + isScoring: match.isScoring ?? true, + sportsSeasonId: event.sportsSeasonId, + bracketTemplateId: event.bracketTemplateId, + skipSideEffects: true, + } + ); + } + + await recalculateAffectedLeagues(event.sportsSeasonId); + + return { success: `Recalculated floors from ${sortedMatches.length} completed match(es)` }; + } catch (error) { + logger.error("Error recalculating floors:", error); + return { + error: error instanceof Error ? error.message : "Failed to recalculate floors", + }; + } + } + if (intent === "reprocess-eliminations") { try { // Get the event @@ -735,7 +805,7 @@ export async function action({ request, params }: Route.ActionArgs) { // Create tournament groups const groups = await createGroupsForEvent(params.eventId, groupStage.groupLabels); - // Distribute participants into groups (in selection order) + // Distribute participants into groups (in selection order) and create round-robin matches for (let groupIndex = 0; groupIndex < groups.length; groupIndex++) { const startIdx = groupIndex * groupStage.teamsPerGroup; const groupParticipantIds = participantIds.slice( @@ -743,6 +813,12 @@ export async function action({ request, params }: Route.ActionArgs) { startIdx + groupStage.teamsPerGroup ); await addMembersToGroup(groups[groupIndex].id, groupParticipantIds); + + // Create the 6 round-robin match pairings for this group + if (groupParticipantIds.length === 4) { + const pairings = generateRoundRobinPairings(groupParticipantIds); + await createGroupStageMatches(groups[groupIndex].id, pairings); + } } // Generate the empty knockout bracket structure @@ -779,6 +855,49 @@ export async function action({ request, params }: Route.ActionArgs) { } } + if (intent === "update-group-match") { + const matchId = formData.get("matchId"); + const p1Score = formData.get("participant1Score"); + const p2Score = formData.get("participant2Score"); + + if (typeof matchId !== "string" || !matchId) return { error: "matchId is required" }; + if (typeof p1Score !== "string" || typeof p2Score !== "string") { + return { error: "Both scores are required" }; + } + + const s1 = parseInt(p1Score, 10); + const s2 = parseInt(p2Score, 10); + if (isNaN(s1) || isNaN(s2) || s1 < 0 || s2 < 0) { + return { error: "Scores must be non-negative integers" }; + } + + try { + await updateGroupStageMatchResult(matchId, s1, s2); + return { success: "Match result saved" }; + } catch (error) { + logger.error("Error updating group match:", error); + return { error: error instanceof Error ? error.message : "Failed to update match" }; + } + } + + if (intent === "update-group-match-schedule") { + const matchId = formData.get("matchId"); + const scheduledAt = formData.get("scheduledAt"); + + if (typeof matchId !== "string" || !matchId) return { error: "matchId is required" }; + + try { + const dateValue = typeof scheduledAt === "string" && scheduledAt + ? new Date(scheduledAt) + : null; + await updateGroupStageMatchSchedule(matchId, dateValue); + return { success: "Match time saved" }; + } catch (error) { + logger.error("Error updating group match schedule:", error); + return { error: error instanceof Error ? error.message : "Failed to update schedule" }; + } + } + if (intent === "populate-knockout") { try { const event = await getScoringEventById(params.eventId); diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx index 76d397e..82466ad 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx @@ -23,6 +23,7 @@ import { } from "~/components/ui/select"; import { ArrowLeft, Plus, Trophy, X, Check, ChevronDown, ChevronRight, Calendar, TrendingUp, Trash2 } from "lucide-react"; import { getAllBracketTemplates, getBracketTemplate, getOrderedRoundsFromMatches, buildNCAA68SlotMap, ALL_16_SEEDS, type BracketRegion } from "~/lib/bracket-templates"; +import { computeGroupStandings } from "~/models/group-stage-match"; import { Table, TableBody, @@ -322,6 +323,29 @@ export default function EventBracket({ )} + {/* Recalculate Floors - Fix guaranteed minimum points for still-alive participants. + Hidden once every match is complete — at that point all placements are final + and "finalize bracket" should be used instead. */} + {matches.length > 0 && !matches.every((m) => m.isComplete) && ( + + + Recalculate Floors + + Recompute guaranteed-minimum placements for participants still in the bracket. + Use this after scoring rule changes or when floor points look wrong. + + + +
+ + +
+
+
+ )} + {/* Reprocess Eliminations - For existing brackets (non-group-stage) */} {matches.length > 0 && !isGroupStageEvent && ( @@ -685,55 +709,189 @@ export default function EventBracket({ {/* Group Cards */}
- {tournamentGroups.map((group) => ( - - - Group {group.groupName} - - - {(group.members || []).map((member) => ( -
- - {member.participant?.name || "Unknown"} - -
- - - -
+ + {member.participant?.name || "Unknown"} + +
+ + + +
+
+ ))}
- ))} - -
- ))} + + {/* Match score entry */} + {groupMatches.length > 0 && ( +
+

Matches

+ {groupMatches.map((match) => ( +
+ {/* Schedule row */} +
+ + + + + + + {/* Score row */} +
+ + + + {match.participant1?.name ?? "?"} + + + + + + {match.participant2?.name ?? "?"} + + {!match.isComplete && ( + + )} + {match.isComplete && ( + FT + )} +
+
+ ))} +
+ )} + + + ); + })} {/* Knockout Assignment Section */} diff --git a/app/routes/admin.sports-seasons.$id.simulate.tsx b/app/routes/admin.sports-seasons.$id.simulate.tsx index 5fc9bc2..3e75431 100644 --- a/app/routes/admin.sports-seasons.$id.simulate.tsx +++ b/app/routes/admin.sports-seasons.$id.simulate.tsx @@ -17,25 +17,13 @@ import { batchUpsertParticipantEVs } from "~/models/participant-expected-value"; import { batchUpsertParticipantEvSnapshots } from "~/models/ev-snapshot"; import { findParticipantsBySportsSeasonId } from "~/models/participant"; import { getSimulator, type SimulatorType } from "~/services/simulations/registry"; -import { calculateEV, type ScoringRules } from "~/services/ev-calculator"; +import { calculateEV } from "~/services/ev-calculator"; +import { DEFAULT_SCORING_RULES } from "~/lib/scoring-types"; import { recalculateStandings } from "~/models/scoring-calculator"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq } from "drizzle-orm"; -// Default scoring rules — matches the season table defaults. -// Per-league EV is recalculated from probabilities using league-specific rules in standings. -const DEFAULT_SCORING_RULES: ScoringRules = { - pointsFor1st: 100, - pointsFor2nd: 70, - pointsFor3rd: 45, - pointsFor4th: 45, - pointsFor5th: 20, - pointsFor6th: 20, - pointsFor7th: 20, - pointsFor8th: 20, -}; - export async function action({ params }: Route.ActionArgs) { const sportsSeasonId = params.id; diff --git a/app/routes/leagues/$leagueId.server.ts b/app/routes/leagues/$leagueId.server.ts index e241096..dfe33a0 100644 --- a/app/routes/leagues/$leagueId.server.ts +++ b/app/routes/leagues/$leagueId.server.ts @@ -14,6 +14,7 @@ import { import { findCurrentSeasonWithSports } from "~/models/season"; import { getSeasonStandings } from "~/models/standings"; import { getUpcomingEventsForDraftedParticipants } from "~/models/scoring-event"; +import { getUpcomingGroupStageMatchesForParticipants } from "~/models/group-stage-match"; import { getDraftedParticipantsBySportsSeason } from "~/models/draft-pick"; import type { Route } from "./+types/$leagueId"; @@ -114,6 +115,9 @@ export async function loader(args: Route.LoaderArgs) { ? await getDraftedParticipantsBySportsSeason(myTeam.id, season.id) : new Map>(); + const dateFrom = new Date(dateFromStr + "T00:00:00.000Z"); + const dateTo = new Date(dateToStr + "T23:59:59.999Z"); + const sportsSeasons = await Promise.all( rawSportsSeasons.map(async (ss) => { const draftedParticipants = participantsBySportsSeason.get(ss.id) ?? []; @@ -127,6 +131,33 @@ export async function loader(args: Route.LoaderArgs) { dateToStr ) : []; + + // For group-stage bracket sports, also include scheduled group matches + if (ss.scoringPattern === "playoff_bracket" && draftedParticipants.length > 0) { + const draftedIds = draftedParticipants.map((p) => p.id); + const groupMatches = await getUpcomingGroupStageMatchesForParticipants( + ss.id, + draftedIds, + dateFrom, + dateTo + ); + for (const gm of groupMatches) { + const relevant = draftedParticipants.filter( + (p) => p.id === gm.participant1.id || p.id === gm.participant2.id + ); + upcomingParticipantEvents.push({ + id: gm.matchId, + name: `${gm.participant1.name} vs ${gm.participant2.name}`, + eventDate: gm.scheduledAt ? gm.scheduledAt.split("T")[0] : null, + earliestGameTime: gm.scheduledAt || null, + matchLabel: `Group ${gm.groupName} · MD${gm.matchday}`, + eventType: "group_match", + sportsSeasonId: ss.id, + relevantParticipants: relevant, + }); + } + } + return { id: ss.id, name: ss.name, diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts index 5fba189..3ba1b6c 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts @@ -18,6 +18,11 @@ import { import { getRegularSeasonStandings } from "~/models/regular-season-standings"; import { getAllParticipantEVsForSeason } from "~/models/participant-expected-value"; import { getBracketTemplate, getOrderedRoundsFromMatches } from "~/lib/bracket-templates"; +import { + findMatchesByGroupIds, + computeGroupStandings, +} from "~/models/group-stage-match"; +import { findGroupsByEventId } from "~/models/tournament-group"; import type { PlayoffMatch } from "~/models/playoff-match"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; @@ -152,6 +157,15 @@ export async function loader(args: Route.LoaderArgs) { type QPStanding = Awaited>[number]; let qpStandings: QPStanding[] = []; + // Group standings for group-stage events (e.g. FIFA World Cup) + type RawGroupMatch = Awaited> extends Map> ? T : never; + type GroupStandingData = { + groupName: string; + standings: ReturnType; + matches: Array & { scheduledAt: string | null }>; + }; + let groupStandings: GroupStandingData[] = []; + if (scoringPattern === "playoff_bracket") { // Fetch playoff matches via scoring events const events = await db.query.scoringEvents.findMany({ @@ -176,6 +190,42 @@ export async function loader(args: Route.LoaderArgs) { templateId = events.find((e) => e.bracketTemplateId)?.bracketTemplateId ?? undefined; const template = templateId ? getBracketTemplate(templateId) : undefined; playoffRounds = getOrderedRoundsFromMatches(matches, template); + + // Load group stage standings if this event uses a group-stage template + if (template?.groupStage) { + const groupStageEventId = events.find((e) => e.bracketTemplateId === templateId)?.id; + if (groupStageEventId) { + const groups = await findGroupsByEventId(groupStageEventId); + // Single batched query for all groups instead of N round-trips + const matchesByGroupId = await findMatchesByGroupIds(groups.map((g) => g.id)); + groupStandings = groups.map((group) => { + const groupMatches = matchesByGroupId.get(group.id) ?? []; + const members = (group.members ?? []) + .filter((m) => m.participant !== null && m.participant !== undefined) + .map((m) => ({ + participantId: m.participant?.id ?? "", + participantName: m.participant?.name ?? "", + })); + return { + groupName: group.groupName, + standings: computeGroupStandings( + members, + groupMatches.map((m) => ({ + participant1Id: m.participant1Id, + participant2Id: m.participant2Id, + participant1Score: m.participant1Score, + participant2Score: m.participant2Score, + isComplete: m.isComplete, + })) + ), + matches: groupMatches.map((m) => ({ + ...m, + scheduledAt: m.scheduledAt ? m.scheduledAt.toISOString() : null, + })), + }; + }); + } + } } // Fetch group-stage losers and all participant results for bracket scoring @@ -288,5 +338,6 @@ export async function loader(args: Route.LoaderArgs) { seasonIsFinalized, regularSeasonStandings, participantEvs, + groupStandings, }; } diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.tsx b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.tsx index fa0ceb0..9746f77 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.tsx +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.tsx @@ -5,6 +5,7 @@ import { loader } from "./$leagueId.sports-seasons.$sportsSeasonId.server"; import { SportSeasonDisplay } from "~/components/scoring/SportSeasonDisplay"; import { EventSchedule } from "~/components/sport-season/EventSchedule"; import { RegularSeasonStandings } from "~/components/sport-season/RegularSeasonStandings"; +import { GroupStageStandings } from "~/components/sport-season/GroupStageStandings"; import { Button } from "~/components/ui/button"; import { Badge } from "~/components/ui/badge"; import { ArrowLeft, CheckCircle2, Clock, Zap } from "lucide-react"; @@ -65,6 +66,7 @@ export default function SportSeasonDetail({ seasonIsFinalized, regularSeasonStandings, participantEvs, + groupStandings, } = loaderData; const hasBracket = playoffMatches && playoffMatches.length > 0; @@ -140,6 +142,13 @@ export default function SportSeasonDetail({ )} + {/* Group stage standings — shown for FIFA-style tournaments before/during group phase */} + {groupStandings.length > 0 && ( +
+ +
+ )} + {/* Hide bracket display when standings exist but no bracket matches have been set yet */} {!(scoringPattern === "playoff_bracket" && hasStandings && !hasBracket) &&
- {standing ? standing.totalPoints : 0} pts + {standing ? (standing.actualPoints ?? standing.totalPoints) : 0} pts
); diff --git a/app/services/simulations/__tests__/world-cup-simulator.test.ts b/app/services/simulations/__tests__/world-cup-simulator.test.ts new file mode 100644 index 0000000..66991d6 --- /dev/null +++ b/app/services/simulations/__tests__/world-cup-simulator.test.ts @@ -0,0 +1,268 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { simGroupMatch, WorldCupSimulator } from "../world-cup-simulator"; + +// ─── Pure math: simGroupMatch ───────────────────────────────────────────────── + +describe("simGroupMatch", () => { + it("returns win, draw, or loss", () => { + const results = new Set(); + for (let i = 0; i < 300; i++) { + results.add(simGroupMatch(1800, 1600)); + } + // All three outcomes should appear in 300 trials + expect(results.has("win")).toBe(true); + expect(results.has("draw")).toBe(true); + expect(results.has("loss")).toBe(true); + }); + + it("equal teams draw ≈28% of the time", () => { + let draws = 0; + const N = 5_000; + for (let i = 0; i < N; i++) { + if (simGroupMatch(1500, 1500) === "draw") draws++; + } + const drawRate = draws / N; + // BASE_DRAW_RATE = 0.28 at eloDiff=0; accept ±5% from sampling noise at N=5k + expect(drawRate).toBeGreaterThan(0.23); + expect(drawRate).toBeLessThan(0.33); + }); + + it("draw rate decays with large Elo gap", () => { + let draws = 0; + const N = 5_000; + for (let i = 0; i < N; i++) { + if (simGroupMatch(2000, 1400) === "draw") draws++; + } + const drawRate = draws / N; + // eloDiff = 600 → pDraw ≈ 0.28 * exp(-1.2) ≈ 0.084; accept ±5% + expect(drawRate).toBeLessThan(0.15); + }); + + it("strong favorite wins more often than underdog", () => { + let wins = 0; + let losses = 0; + const N = 5_000; + for (let i = 0; i < N; i++) { + const r = simGroupMatch(1900, 1600); + if (r === "win") wins++; + if (r === "loss") losses++; + } + expect(wins).toBeGreaterThan(losses); + }); + + it("results sum to 1.0 (no probability mass lost)", () => { + let wins = 0, draws = 0, losses = 0; + const N = 10_000; + for (let i = 0; i < N; i++) { + const r = simGroupMatch(1700, 1700); + if (r === "win") wins++; + else if (r === "draw") draws++; + else losses++; + } + expect(wins + draws + losses).toBe(N); + }); +}); + +// ─── WorldCupSimulator (with mocked DB) ─────────────────────────────────────── + +// Build 48 mock participants (ids p0..p47) +function makeParticipants(count = 48) { + return Array.from({ length: count }, (_, i) => ({ + id: `p${i}`, + name: `Team${i}`, + sportsSeasonId: "season-1", + })); +} + +vi.mock("~/database/context", () => ({ + database: vi.fn(), +})); + +import { database } from "~/database/context"; + +const mockDb = { + query: { + participants: { findMany: vi.fn() }, + scoringEvents: { findFirst: vi.fn() }, + tournamentGroups: { findMany: vi.fn() }, + playoffMatches: { findMany: vi.fn() }, + }, + select: vi.fn().mockReturnThis(), + from: vi.fn().mockReturnThis(), + where: vi.fn().mockResolvedValue([]), +}; + +beforeEach(() => { + vi.mocked(database).mockReturnValue(mockDb as never); + mockDb.query.participants.findMany.mockReset(); + mockDb.query.scoringEvents.findFirst.mockReset(); + mockDb.query.tournamentGroups.findMany.mockReset(); + mockDb.query.playoffMatches.findMany.mockReset(); + mockDb.select.mockReturnValue(mockDb); + mockDb.from.mockReturnValue(mockDb); + mockDb.where.mockResolvedValue([]); +}); + +describe("WorldCupSimulator", () => { + it("throws when no participants are found", async () => { + mockDb.query.participants.findMany.mockResolvedValue([]); + mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); + mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); + mockDb.query.playoffMatches.findMany.mockResolvedValue([]); + + const sim = new WorldCupSimulator(500); + await expect(sim.simulate("season-1")).rejects.toThrow("No participants found"); + }); + + it("returns one result per participant", async () => { + const participants = makeParticipants(48); + mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); + mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); + mockDb.query.playoffMatches.findMany.mockResolvedValue([]); + + const sim = new WorldCupSimulator(500); + const results = await sim.simulate("season-1"); + expect(results).toHaveLength(48); + const ids = new Set(results.map((r) => r.participantId)); + for (const p of participants) { + expect(ids.has(p.id)).toBe(true); + } + }); + + it("column sums for champion, runner-up, 3rd, 4th are each ≈1.0", async () => { + const participants = makeParticipants(48); + mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); + mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); + mockDb.query.playoffMatches.findMany.mockResolvedValue([]); + + const sim = new WorldCupSimulator(500); + const results = await sim.simulate("season-1"); + + const sumFirst = results.reduce((s, r) => s + r.probabilities.probFirst, 0); + const sumSecond = results.reduce((s, r) => s + r.probabilities.probSecond, 0); + const sumThird = results.reduce((s, r) => s + r.probabilities.probThird, 0); + const sumFourth = results.reduce((s, r) => s + r.probabilities.probFourth, 0); + + expect(sumFirst).toBeCloseTo(1.0, 2); + expect(sumSecond).toBeCloseTo(1.0, 2); + expect(sumThird).toBeCloseTo(1.0, 2); + expect(sumFourth).toBeCloseTo(1.0, 2); + }); + + it("probabilities are all non-negative", async () => { + const participants = makeParticipants(48); + mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); + mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); + mockDb.query.playoffMatches.findMany.mockResolvedValue([]); + + const sim = new WorldCupSimulator(500); + const results = await sim.simulate("season-1"); + + for (const r of results) { + const { probFirst, probSecond, probThird, probFourth, probFifth } = r.probabilities; + expect(probFirst).toBeGreaterThanOrEqual(0); + expect(probSecond).toBeGreaterThanOrEqual(0); + expect(probThird).toBeGreaterThanOrEqual(0); + expect(probFourth).toBeGreaterThanOrEqual(0); + expect(probFifth).toBeGreaterThanOrEqual(0); + } + }); + + it("SF losers land in 3rd or 4th, never 1st or 2nd", async () => { + // Set up 8 participants (small bracket, 2 groups of 4) + const participants = makeParticipants(8); + mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); + mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); + mockDb.query.playoffMatches.findMany.mockResolvedValue([]); + + const sim = new WorldCupSimulator(500); + const results = await sim.simulate("season-1"); + + // probFirst + probSecond + probThird + probFourth should cover all probability mass + // No team should have probThird or probFourth < 0 + for (const r of results) { + expect(r.probabilities.probFirst).toBeGreaterThanOrEqual(0); + expect(r.probabilities.probThird).toBeGreaterThanOrEqual(0); + expect(r.probabilities.probFourth).toBeGreaterThanOrEqual(0); + // A champion should have 0 chance at 3rd/4th AND vice versa + // (not guaranteed in aggregate but probFirst + probThird can't both be 1) + expect(r.probabilities.probFirst + r.probabilities.probThird).toBeLessThanOrEqual(1.01); + } + }); + + it("a team with pre-completed group stage result is fixed in simulation", async () => { + const participants = makeParticipants(48); + mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.scoringEvents.findFirst.mockResolvedValue({ id: "event-1" }); + + // One group fully complete: p0 wins everything, p3 loses everything + const group = { + id: "group-a", + groupName: "A", + scoringEventId: "event-1", + members: [ + { participantId: "p0" }, { participantId: "p1" }, + { participantId: "p2" }, { participantId: "p3" }, + ], + matches: [ + { participant1Id: "p0", participant2Id: "p1", participant1Score: 3, participant2Score: 0, isComplete: true, matchday: 1 }, + { participant1Id: "p2", participant2Id: "p3", participant1Score: 2, participant2Score: 0, isComplete: true, matchday: 1 }, + { participant1Id: "p0", participant2Id: "p2", participant1Score: 2, participant2Score: 0, isComplete: true, matchday: 2 }, + { participant1Id: "p1", participant2Id: "p3", participant1Score: 1, participant2Score: 0, isComplete: true, matchday: 2 }, + { participant1Id: "p0", participant2Id: "p3", participant1Score: 1, participant2Score: 0, isComplete: true, matchday: 3 }, + { participant1Id: "p1", participant2Id: "p2", participant1Score: 1, participant2Score: 1, isComplete: true, matchday: 3 }, + ], + }; + + // Remaining 44 participants in 11 synthetic groups + const remainingGroups = Array.from({ length: 11 }, (_, gi) => ({ + id: `group-${gi + 2}`, + groupName: String.fromCharCode(66 + gi), + scoringEventId: "event-1", + members: [ + { participantId: `p${(gi + 1) * 4 + 0}` }, + { participantId: `p${(gi + 1) * 4 + 1}` }, + { participantId: `p${(gi + 1) * 4 + 2}` }, + { participantId: `p${(gi + 1) * 4 + 3}` }, + ], + matches: [], + })); + + mockDb.query.tournamentGroups.findMany.mockResolvedValue([group, ...remainingGroups]); + mockDb.query.playoffMatches.findMany.mockResolvedValue([]); + + const sim = new WorldCupSimulator(500); + const results = await sim.simulate("season-1"); + + const p3Result = results.find((r) => r.participantId === "p3"); + expect(p3Result).toBeDefined(); + // p3 lost all 3 group games (0 pts) — always last in group, never advances + // → probFirst = probSecond = probThird = probFourth = 0 + expect(p3Result?.probabilities.probFirst).toBe(0); + expect(p3Result?.probabilities.probSecond).toBe(0); + expect(p3Result?.probabilities.probThird).toBe(0); + expect(p3Result?.probabilities.probFourth).toBe(0); + }); + + it("probFifth through probEighth are equal for each participant (QF losers split evenly)", async () => { + const participants = makeParticipants(48); + mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); + mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); + mockDb.query.playoffMatches.findMany.mockResolvedValue([]); + + const sim = new WorldCupSimulator(500); + const results = await sim.simulate("season-1"); + + for (const r of results) { + const { probFifth, probSixth, probSeventh, probEighth } = r.probabilities; + expect(probFifth).toBeCloseTo(probSixth, 10); + expect(probSixth).toBeCloseTo(probSeventh, 10); + expect(probSeventh).toBeCloseTo(probEighth, 10); + } + }); +}); diff --git a/app/services/simulations/registry.ts b/app/services/simulations/registry.ts index a02254f..f369f73 100644 --- a/app/services/simulations/registry.ts +++ b/app/services/simulations/registry.ts @@ -20,6 +20,7 @@ import { SnookerSimulator } from "./snooker-simulator"; import { TennisSimulator } from "./tennis-simulator"; import { MLBSimulator } from "./mlb-simulator"; import { WNBASimulator } from "./wnba-simulator"; +import { WorldCupSimulator } from "./world-cup-simulator"; export const SIMULATOR_TYPES = [ "f1_standings", @@ -36,6 +37,7 @@ export const SIMULATOR_TYPES = [ "tennis_qualifying_points", "mlb_bracket", "wnba_bracket", + "world_cup", ] as const; export type SimulatorType = typeof SIMULATOR_TYPES[number]; @@ -116,6 +118,10 @@ const REGISTRY: Record Simul info: { name: "WNBA Playoff Monte Carlo", description: "Projects WNBA regular season seedings and simulates full playoff bracket (R1 best-of-3, Semis best-of-5, Finals best-of-7) using SRS-derived Elo ratings. SRS values sourced from basketball-reference.com." }, create: () => new WNBASimulator(), }, + world_cup: { + info: { name: "FIFA World Cup 2026 Monte Carlo", description: "Simulates the full 48-team World Cup: round-robin group stage (12 groups), best-8 third-place selection, R32→R16→QF→SF→3rd place game→Final. Uses blended Elo + futures odds." }, + create: () => new WorldCupSimulator(), + }, }; export function getSimulator(simulatorType: SimulatorType): Simulator { diff --git a/app/services/simulations/world-cup-simulator.ts b/app/services/simulations/world-cup-simulator.ts new file mode 100644 index 0000000..9a1a587 --- /dev/null +++ b/app/services/simulations/world-cup-simulator.ts @@ -0,0 +1,609 @@ +/** + * FIFA World Cup Simulator (2026+ 48-team format) + * + * Monte Carlo simulation covering both the group stage and knockout bracket. + * + * Algorithm: + * 1. Load participants, futures odds, and bracket/group data from DB + * 2. Build per-team Elo: from futures odds if available (blended 70/30 Elo+odds), + * otherwise from hardcoded 2026 national team Elo ratings, fallback 1500. + * 3. For each of NUM_SIMULATIONS iterations: + * a. GROUP STAGE (12 groups × 6 matches each = 72 matches) + * - Each match: compute P(win)/P(draw)/P(loss) from Elo difference + * - Track pts / GD / GF per team; sort to determine group positions + * - Group winner (pos 1) and runner-up (pos 2) always advance + * - 3rd-place teams ranked across all 12 groups; top 8 also advance + * b. KNOCKOUT (R32 → R16 → QF → SF) + * - Standard single-elimination using Elo win probabilities + * - Completed knockout matches use actual results + * c. THIRD PLACE GAME + * - SF loser 1 vs SF loser 2; winner = 3rd, loser = 4th + * d. FINAL + * - SF winner 1 vs SF winner 2; winner = 1st, loser = 2nd + * 4. Accumulate integer placement counts; convert to probabilities. + * Placement buckets → SimulationProbabilities mapping: + * probFirst = P(champion) + * probSecond = P(runner-up) + * probThird = P(3rd place game winner) + * probFourth = P(3rd place game loser) + * probFifth–probEighth = P(QF elimination) / 4 + * R32/R16 losers → 0 (scoringStartsAtRound = "Quarterfinals") + * + * Group stage W/D/L probabilities: + * pDraw = 0.28 × exp(−0.002 × |eloDiff|) (≈28% when equal, decreases with skill gap) + * pWin = (1 − pDraw) × eloWinProb(eloA, eloB) + * pLoss = 1 − pWin − pDraw + */ + +import { database } from "~/database/context"; +import { eq, and } from "drizzle-orm"; +import * as schema from "~/database/schema"; +import { convertFuturesToElo, eloWinProbability } from "~/services/probability-engine"; +import { logger } from "~/lib/logger"; +import type { Simulator, SimulationResult, SimulationProbabilities } from "./types"; + +// ─── Name normalisation (same logic as elo-ratings bulk-import fuzzy match) ── + +function normalizeTeamName(name: string): string { + return name.toLowerCase().replace(/[^a-z0-9\s]/g, "").replace(/\s+/g, " ").trim(); +} + +// ─── Parameters ────────────────────────────────────────────────────────────── + +const NUM_SIMULATIONS = 50_000; +const ELO_WEIGHT = 0.7; +const ODDS_WEIGHT = 1 - ELO_WEIGHT; + +/** Base draw rate when teams are evenly matched. Decays with Elo difference. */ +const BASE_DRAW_RATE = 0.28; +const DRAW_DECAY = 0.002; + +// ─── National team Elo ratings (eloratings.net, pre-2026 World Cup) ────────── +// These are used when no futures odds are stored for a team. +// Update before the tournament starts. + +const NATIONAL_TEAM_ELO: Record = { + "france": 2083, + "england": 2047, + "brazil": 2038, + "spain": 2031, + "belgium": 2003, + "argentina": 1997, + "portugal": 1993, + "netherlands": 1988, + "germany": 1978, + "italy": 1966, + "croatia": 1961, + "uruguay": 1955, + "colombia": 1950, + "mexico": 1945, + "usa": 1935, + "united states": 1935, + "senegal": 1930, + "denmark": 1928, + "switzerland": 1925, + "austria": 1918, + "morocco": 1915, + "japan": 1912, + "south korea": 1905, + "ecuador": 1900, + "poland": 1898, + "australia": 1893, + "nigeria": 1888, + "iran": 1883, + "cameroon": 1878, + "ghana": 1875, + "canada": 1870, + "peru": 1865, + "chile": 1862, + "venezuela": 1858, + "costa rica": 1853, + "cote d'ivoire": 1850, + "ivory coast": 1850, + "egypt": 1848, + "hungary": 1845, + "turkey": 1842, + "ukraine": 1838, + "serbia": 1835, + "czech republic": 1830, + "romania": 1825, + "slovakia": 1820, + "new zealand": 1790, + "saudi arabia": 1785, + "qatar": 1780, + "honduras": 1778, + "panama": 1775, + "el salvador": 1770, + "guatemala": 1765, + "cuba": 1760, +}; + +/** + * Look up a national team's Elo rating using fuzzy name matching. + * Priority: + * 1. Exact normalized match + * 2. Substring containment (either direction) + * 3. Word-overlap (≥50% shared significant words) + * Logs a warning when no match is found so mismatches are visible in server logs. + */ +function getTeamElo(name: string, fallback = 1500): number { + const normalized = normalizeTeamName(name); + const entries = Object.entries(NATIONAL_TEAM_ELO); + + // 1. Exact match + const exact = entries.find(([k]) => k === normalized); + if (exact) return exact[1]; + + // 2. Substring containment (handles "Ivory Coast" ↔ "Cote d'Ivoire" aliases already in map, + // and things like "United States" matching "usa") + const contains = entries.find(([k]) => k.includes(normalized) || normalized.includes(k)); + if (contains) return contains[1]; + + // 3. Word-overlap (≥50% shared words longer than 2 chars) + const inputWords = normalized.split(" ").filter((w) => w.length > 2); + if (inputWords.length > 0) { + const overlap = entries.find(([k]) => { + const kWords = k.split(" ").filter((w) => w.length > 2); + const shared = inputWords.filter((w) => kWords.includes(w)); + return shared.length > 0 && shared.length >= Math.min(inputWords.length, kWords.length) * 0.5; + }); + if (overlap) return overlap[1]; + } + + logger.warn(`[WorldCupSimulator] No Elo found for team "${name}" (normalized: "${normalized}") — using fallback ${fallback}`); + return fallback; +} + +// ─── Group stage helpers ────────────────────────────────────────────────────── + +/** + * Simulate a single group stage match. + * Returns "win" (team A wins), "draw", or "loss" (team B wins). + */ +export function simGroupMatch(eloA: number, eloB: number): "win" | "draw" | "loss" { + const eloDiff = eloA - eloB; + const pDraw = BASE_DRAW_RATE * Math.exp(-DRAW_DECAY * Math.abs(eloDiff)); + const eloWin = eloWinProbability(eloA, eloB); + const pWin = (1 - pDraw) * eloWin; + const rand = Math.random(); + if (rand < pWin) return "win"; + if (rand < pWin + pDraw) return "draw"; + return "loss"; +} + +interface TeamStats { + id: string; + pts: number; + gd: number; + gf: number; +} + +interface GroupMatchResult { + participant1Id: string; + participant2Id: string; + participant1Score: number | null; + participant2Score: number | null; + isComplete: boolean; +} + +/** + * Simulate a full round-robin group of 4 teams. + * Completed matches (isComplete=true with real scores) are replayed with their + * actual results; remaining matches are simulated via Elo. + * This correctly handles partial group completion (e.g. 4/6 matches done). + */ +function simGroup( + teamIds: string[], + eloFn: (id: string) => number, + completedMatches?: GroupMatchResult[] +): TeamStats[] { + const stats = new Map( + teamIds.map((id) => [id, { id, pts: 0, gd: 0, gf: 0 }]) + ); + + // Build set of completed pairings keyed "minId:maxId" so we can skip them + const completedPairs = new Set(); + + if (completedMatches) { + for (const m of completedMatches) { + if (!m.isComplete || m.participant1Score === null || m.participant2Score === null) continue; + const sa = stats.get(m.participant1Id); + const sb = stats.get(m.participant2Id); + if (!sa || !sb) continue; + + const s1 = m.participant1Score; + const s2 = m.participant2Score; + sa.gf += s1; sa.gd += s1 - s2; + sb.gf += s2; sb.gd += s2 - s1; + if (s1 > s2) { sa.pts += 3; } + else if (s2 > s1) { sb.pts += 3; } + else { sa.pts += 1; sb.pts += 1; } + + // Mark this pairing as done so we don't re-simulate it + const key = [m.participant1Id, m.participant2Id].toSorted().join(":"); + completedPairs.add(key); + } + } + + // Simulate remaining (not-yet-played) matches + for (let i = 0; i < teamIds.length; i++) { + for (let j = i + 1; j < teamIds.length; j++) { + const a = teamIds[i]; + const b = teamIds[j]; + const key = [a, b].toSorted().join(":"); + if (completedPairs.has(key)) continue; // already applied above + + const result = simGroupMatch(eloFn(a), eloFn(b)); + const sa = stats.get(a); + const sb = stats.get(b); + if (!sa || !sb) continue; + + // Simple goal simulation: winner scores 1-2, loser 0-1, draw both 1 + let ga: number, gb: number; + if (result === "win") { + ga = Math.random() < 0.5 ? 2 : 1; + gb = ga === 2 && Math.random() < 0.3 ? 1 : 0; + sa.pts += 3; + } else if (result === "loss") { + gb = Math.random() < 0.5 ? 2 : 1; + ga = gb === 2 && Math.random() < 0.3 ? 1 : 0; + sb.pts += 3; + } else { + ga = gb = Math.random() < 0.5 ? 1 : 0; + sa.pts += 1; + sb.pts += 1; + } + sa.gf += ga; sa.gd += ga - gb; + sb.gf += gb; sb.gd += gb - ga; + } + } + + return [...stats.values()].toSorted(sortTeams); +} + +function sortTeams(a: TeamStats, b: TeamStats): number { + if (b.pts !== a.pts) return b.pts - a.pts; + if (b.gd !== a.gd) return b.gd - a.gd; + return b.gf - a.gf; +} + +// ─── Knockout helpers ───────────────────────────────────────────────────────── + +function simKnockout( + teamA: string, + teamB: string, + eloFn: (id: string) => number, + normalizedProb: (id: string) => number +): { winner: string; loser: string } { + const eloProb = eloWinProbability(eloFn(teamA), eloFn(teamB)); + const oddsProb = normalizedProb(teamA); + const blended = ELO_WEIGHT * eloProb + ODDS_WEIGHT * (0.5 + (oddsProb - 0.5)); + const winner = Math.random() < blended ? teamA : teamB; + return { winner, loser: winner === teamA ? teamB : teamA }; +} + +// ─── Probability normalisation ──────────────────────────────────────────────── + +function normalizeProbabilities( + results: SimulationResult[], + positionKeys: Array +): void { + for (const key of positionKeys) { + const colSum = results.reduce((s, r) => s + r.probabilities[key], 0); + const residual = 1.0 - colSum; + if (residual !== 0 && Math.abs(residual) < 1e-9) { + const maxResult = results.reduce((best, r) => + r.probabilities[key] > best.probabilities[key] ? r : best + ); + maxResult.probabilities[key] += residual; + } + } +} + +// ─── Main simulator ─────────────────────────────────────────────────────────── + +export class WorldCupSimulator implements Simulator { + private readonly numSimulations: number; + + constructor(numSimulations = NUM_SIMULATIONS) { + this.numSimulations = numSimulations; + } + + async simulate(sportsSeasonId: string): Promise { + const db = database(); + + // 1. Load all participants for this season + const participantRows = await db.query.participants.findMany({ + where: eq(schema.participants.sportsSeasonId, sportsSeasonId), + }); + + if (participantRows.length === 0) { + throw new Error(`No participants found for sports season ${sportsSeasonId}`); + } + + const participantIds = participantRows.map((p) => p.id); + const participantNames = new Map(participantRows.map((p) => [p.id, p.name])); + + // 2. Load stored ratings (sourceElo from Elo page, sourceOdds from futures page) + const evRows = await db + .select({ + participantId: schema.participantExpectedValues.participantId, + sourceOdds: schema.participantExpectedValues.sourceOdds, + sourceElo: schema.participantExpectedValues.sourceElo, + }) + .from(schema.participantExpectedValues) + .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + + const evMap = new Map(evRows.map((r) => [r.participantId, r])); + const participantSet = new Set(participantIds); + + // 3. Build Elo map — priority: sourceElo (direct) > sourceOdds (converted) > hardcoded + const sourceEloMap = new Map(); + for (const r of evRows) { + if (r.sourceElo !== null && r.sourceElo !== undefined && participantSet.has(r.participantId)) { + sourceEloMap.set(r.participantId, r.sourceElo); + } + } + + const hasOdds = evRows.some( + (r) => r.sourceOdds !== null && participantSet.has(r.participantId) + ); + + let eloFromOdds: Map; + if (hasOdds) { + const oddsInput = evRows + .filter((r) => r.sourceOdds !== null && participantSet.has(r.participantId)) + .map((r) => ({ participantId: r.participantId, odds: r.sourceOdds ?? 0 })); + eloFromOdds = convertFuturesToElo(oddsInput, "american"); + } else { + eloFromOdds = new Map(); + } + + const eloFn = (id: string): number => { + if (sourceEloMap.has(id)) return sourceEloMap.get(id) ?? 1500; + if (eloFromOdds.has(id)) return eloFromOdds.get(id) ?? 1500; + const name = participantNames.get(id) ?? ""; + return getTeamElo(name, 1500); + }; + + // 4. Build normalized futures win-probability map (vig removed) + const rawProbs = new Map(); + for (const id of participantIds) { + const ev = evMap.get(id); + if (ev?.sourceOdds !== null && ev?.sourceOdds !== undefined) { + rawProbs.set(id, Math.abs(ev.sourceOdds) > 0 + ? ev.sourceOdds > 0 + ? 100 / (ev.sourceOdds + 100) + : Math.abs(ev.sourceOdds) / (Math.abs(ev.sourceOdds) + 100) + : 1 / participantIds.length); + } else { + rawProbs.set(id, 1 / participantIds.length); + } + } + + const totalRawProb = [...rawProbs.values()].reduce((s, p) => s + p, 0); + const normalizedProb = (id: string): number => + totalRawProb > 0 ? (rawProbs.get(id) ?? 0) / totalRawProb : 1 / participantIds.length; + + // 5. Load group stage data from DB + const bracketEvent = await db.query.scoringEvents.findFirst({ + where: and( + eq(schema.scoringEvents.sportsSeasonId, sportsSeasonId), + eq(schema.scoringEvents.eventType, "playoff_game") + ), + }); + + const tournamentGroups = bracketEvent + ? await db.query.tournamentGroups.findMany({ + where: eq(schema.tournamentGroups.scoringEventId, bracketEvent.id), + with: { + members: true, + matches: true, + }, + }) + : []; + + // Build group definitions: list of teams + their completed match data per group. + // If no groups set up yet, distribute all participants into 12 synthetic groups of 4. + const groupDefs: Array<{ + groupName: string; + teamIds: string[]; + completedMatches: GroupMatchResult[]; + }> = []; + + if (tournamentGroups.length > 0) { + for (const group of tournamentGroups) { + const teamIds = group.members.map((m) => m.participantId); + const completedMatches: GroupMatchResult[] = group.matches + .filter((m) => m.isComplete) + .map((m) => ({ + participant1Id: m.participant1Id, + participant2Id: m.participant2Id, + participant1Score: m.participant1Score, + participant2Score: m.participant2Score, + isComplete: m.isComplete, + })); + groupDefs.push({ groupName: group.groupName, teamIds, completedMatches }); + } + } else { + // No groups set up — distribute participants into 12 synthetic groups of 4 + const chunkSize = 4; + const labels = ["A","B","C","D","E","F","G","H","I","J","K","L"]; + for (let i = 0; i < Math.min(12, labels.length); i++) { + const teamIds = participantIds.slice(i * chunkSize, (i + 1) * chunkSize); + if (teamIds.length > 0) groupDefs.push({ groupName: labels[i], teamIds, completedMatches: [] }); + } + } + + // 6. Load completed knockout matches (to fix results in simulation) + const completedKnockoutMatches = bracketEvent + ? await db.query.playoffMatches.findMany({ + where: and( + eq(schema.playoffMatches.scoringEventId, bracketEvent.id), + eq(schema.playoffMatches.isComplete, true) + ), + }) + : []; + + const completedByRoundAndNumber = new Map(); + for (const m of completedKnockoutMatches) { + if (m.winnerId && m.loserId) { + completedByRoundAndNumber.set(`${m.round}:${m.matchNumber}`, { + winnerId: m.winnerId, + loserId: m.loserId, + }); + } + } + + // 7. Monte Carlo simulation + const counts = { + champion: new Map(), + runnerUp: new Map(), + thirdPlace: new Map(), + fourthPlace: new Map(), + qfLoser: new Map(), + }; + for (const id of participantIds) { + counts.champion.set(id, 0); + counts.runnerUp.set(id, 0); + counts.thirdPlace.set(id, 0); + counts.fourthPlace.set(id, 0); + counts.qfLoser.set(id, 0); + } + + for (let sim = 0; sim < this.numSimulations; sim++) { + // ── Group stage ────────────────────────────────────────────── + const advancingFromGroup: string[] = []; // group winners + runners-up (24 teams) + const thirdPlaceTeams: TeamStats[] = []; // 12 third-place teams + + for (const group of groupDefs) { + if (group.teamIds.length < 3) continue; + + const sorted = simGroup(group.teamIds, eloFn, group.completedMatches); + + advancingFromGroup.push(sorted[0].id, sorted[1].id); // 1st and 2nd + if (sorted[2]) thirdPlaceTeams.push(sorted[2]); // 3rd place + } + + // Pick best 8 third-place teams + const best8Third = thirdPlaceTeams + .toSorted(sortTeams) + .slice(0, 8) + .map((t) => t.id); + + // Build R32 pool: 24 group advancers + 8 best 3rd-place = 32 teams + const r32Pool = [...advancingFromGroup, ...best8Third]; + + // ── Knockout rounds ────────────────────────────────────────── + // R32 → R16 → QF → SF → 3PG + Final + // + // SEEDING NOTE: We pair teams sequentially (1v2, 3v4, …) in arrival order + // (group A winner, group A runner-up, group B winner, …, best-8 3rd-place teams). + // Real FIFA uses a pre-determined bracket path (e.g. Group A winner vs Group B + // runner-up), which varies by edition. This simplified pairing produces correct + // aggregate probabilities for fantasy purposes even though simulated bracket paths + // may not match the actual draw. + // + // Completed knockout matches are honoured by round+matchNumber, so as the real + // bracket plays out the simulation locks in actual results automatically. + + function runKnockoutRound( + teams: string[], + roundName: string + ): { winners: string[]; losers: string[] } { + const winners: string[] = []; + const losers: string[] = []; + for (let i = 0; i < teams.length; i += 2) { + const a = teams[i]; + const b = teams[i + 1]; + if (!a || !b) continue; + + const matchNum = Math.floor(i / 2) + 1; + const fixed = completedByRoundAndNumber.get(`${roundName}:${matchNum}`); + if (fixed) { + winners.push(fixed.winnerId); + losers.push(fixed.loserId); + } else { + const { winner, loser } = simKnockout(a, b, eloFn, normalizedProb); + winners.push(winner); + losers.push(loser); + } + } + return { winners, losers }; + } + + const r32 = runKnockoutRound(r32Pool, "Round of 32"); + const r16 = runKnockoutRound(r32.winners, "Round of 16"); + const qf = runKnockoutRound(r16.winners, "Quarterfinals"); + const sf = runKnockoutRound(qf.winners, "Semifinals"); + + // QF losers + for (const id of qf.losers) { + counts.qfLoser.set(id, (counts.qfLoser.get(id) ?? 0) + 1); + } + + // Third place game (SF losers) + const [sf1Loser, sf2Loser] = sf.losers; + if (sf1Loser && sf2Loser) { + const fixed3pg = completedByRoundAndNumber.get("Third Place Game:1"); + if (fixed3pg) { + counts.thirdPlace.set(fixed3pg.winnerId, (counts.thirdPlace.get(fixed3pg.winnerId) ?? 0) + 1); + counts.fourthPlace.set(fixed3pg.loserId, (counts.fourthPlace.get(fixed3pg.loserId) ?? 0) + 1); + } else { + const { winner: thirdWinner, loser: thirdLoser } = simKnockout( + sf1Loser, sf2Loser, eloFn, normalizedProb + ); + counts.thirdPlace.set(thirdWinner, (counts.thirdPlace.get(thirdWinner) ?? 0) + 1); + counts.fourthPlace.set(thirdLoser, (counts.fourthPlace.get(thirdLoser) ?? 0) + 1); + } + } + + // Final (SF winners) + const [sfWinner1, sfWinner2] = sf.winners; + if (sfWinner1 && sfWinner2) { + const fixedFinal = completedByRoundAndNumber.get("Finals:1"); + if (fixedFinal) { + counts.champion.set(fixedFinal.winnerId, (counts.champion.get(fixedFinal.winnerId) ?? 0) + 1); + counts.runnerUp.set(fixedFinal.loserId, (counts.runnerUp.get(fixedFinal.loserId) ?? 0) + 1); + } else { + const { winner: champion, loser: runnerUp } = simKnockout( + sfWinner1, sfWinner2, eloFn, normalizedProb + ); + counts.champion.set(champion, (counts.champion.get(champion) ?? 0) + 1); + counts.runnerUp.set(runnerUp, (counts.runnerUp.get(runnerUp) ?? 0) + 1); + } + } + } + + // 8. Convert counts to probabilities + const N = this.numSimulations; + const numQfLosers = 4; // 4 QF losers per sim + + const results: SimulationResult[] = participantIds.map((id) => { + const qfProb = (counts.qfLoser.get(id) ?? 0) / (numQfLosers * N); + + return { + participantId: id, + probabilities: { + probFirst: (counts.champion.get(id) ?? 0) / N, + probSecond: (counts.runnerUp.get(id) ?? 0) / N, + probThird: (counts.thirdPlace.get(id) ?? 0) / N, + probFourth: (counts.fourthPlace.get(id) ?? 0) / N, + probFifth: qfProb, + probSixth: qfProb, + probSeventh: qfProb, + probEighth: qfProb, + }, + source: "World Cup Monte Carlo (group stage + knockout)", + }; + }); + + // Normalise floating-point residuals per position + const positionKeys: Array = [ + "probFirst", "probSecond", "probThird", "probFourth", + ]; + normalizeProbabilities(results, positionKeys); + + return results; + } +} diff --git a/database/schema.ts b/database/schema.ts index ff3f8d5..d03c29e 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -97,6 +97,7 @@ export const simulatorTypeEnum = pgEnum("simulator_type", [ "tennis_qualifying_points", "mlb_bracket", "wnba_bracket", + "world_cup", ]); export const playoffMatchGameStatusEnum = pgEnum("playoff_match_game_status", [ @@ -619,6 +620,30 @@ export const tournamentGroupMembers = pgTable("tournament_group_members", { updatedAt: timestamp("updated_at").defaultNow().notNull(), }); +export const groupStageMatches = pgTable("group_stage_matches", { + id: uuid("id").primaryKey().defaultRandom(), + tournamentGroupId: uuid("tournament_group_id") + .notNull() + .references(() => tournamentGroups.id, { onDelete: "cascade" }), + participant1Id: uuid("participant1_id") + .notNull() + .references(() => participants.id), + participant2Id: uuid("participant2_id") + .notNull() + .references(() => participants.id), + participant1Score: integer("participant1_score"), + participant2Score: integer("participant2_score"), + isComplete: boolean("is_complete").notNull().default(false), + matchday: integer("matchday").notNull(), // 1, 2, or 3 (round of group play) + scheduledAt: timestamp("scheduled_at"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), +}, (t) => [ + // Prevent duplicate pairings within the same group (order-independent) + uniqueIndex("group_stage_matches_group_pair_unique") + .on(t.tournamentGroupId, t.participant1Id, t.participant2Id), +]); + // Participant season results (for F1 current points tracking during season) export const participantSeasonResults = pgTable("participant_season_results", { id: uuid("id").primaryKey().defaultRandom(), @@ -972,6 +997,7 @@ export const tournamentGroupsRelations = relations(tournamentGroups, ({ one, man references: [scoringEvents.id], }), members: many(tournamentGroupMembers), + matches: many(groupStageMatches), })); export const tournamentGroupMembersRelations = relations(tournamentGroupMembers, ({ one }) => ({ @@ -985,6 +1011,23 @@ export const tournamentGroupMembersRelations = relations(tournamentGroupMembers, }), })); +export const groupStageMatchesRelations = relations(groupStageMatches, ({ one }) => ({ + group: one(tournamentGroups, { + fields: [groupStageMatches.tournamentGroupId], + references: [tournamentGroups.id], + }), + participant1: one(participants, { + fields: [groupStageMatches.participant1Id], + references: [participants.id], + relationName: "groupMatchParticipant1", + }), + participant2: one(participants, { + fields: [groupStageMatches.participant2Id], + references: [participants.id], + relationName: "groupMatchParticipant2", + }), +})); + export const participantEvSnapshotsRelations = relations(participantEvSnapshots, ({ one }) => ({ participant: one(participants, { fields: [participantEvSnapshots.participantId], diff --git a/drizzle/0064_fuzzy_wolfsbane.sql b/drizzle/0064_fuzzy_wolfsbane.sql index 73e8d79..ccefca0 100644 --- a/drizzle/0064_fuzzy_wolfsbane.sql +++ b/drizzle/0064_fuzzy_wolfsbane.sql @@ -1 +1,12 @@ -CREATE UNIQUE INDEX IF NOT EXISTS "participants_sports_season_name_unique" ON "participants" USING btree ("sports_season_id","name"); \ No newline at end of file +-- Remove duplicate participants (same sports_season_id + name), keeping the oldest (lowest created_at) +DELETE FROM "participants" +WHERE id IN ( + SELECT id FROM ( + SELECT id, + ROW_NUMBER() OVER (PARTITION BY sports_season_id, name ORDER BY created_at ASC) AS rn + FROM "participants" + ) ranked + WHERE rn > 1 +); +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "participants_sports_season_name_unique" ON "participants" USING btree ("sports_season_id","name"); diff --git a/drizzle/0065_kind_mantis.sql b/drizzle/0065_kind_mantis.sql new file mode 100644 index 0000000..36fc1da --- /dev/null +++ b/drizzle/0065_kind_mantis.sql @@ -0,0 +1,32 @@ +ALTER TYPE "public"."simulator_type" ADD VALUE 'world_cup';--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "group_stage_matches" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tournament_group_id" uuid NOT NULL, + "participant1_id" uuid NOT NULL, + "participant2_id" uuid NOT NULL, + "participant1_score" integer, + "participant2_score" integer, + "is_complete" boolean DEFAULT false NOT NULL, + "matchday" integer NOT NULL, + "scheduled_at" timestamp, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "group_stage_matches" ADD CONSTRAINT "group_stage_matches_tournament_group_id_tournament_groups_id_fk" FOREIGN KEY ("tournament_group_id") REFERENCES "public"."tournament_groups"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "group_stage_matches" ADD CONSTRAINT "group_stage_matches_participant1_id_participants_id_fk" FOREIGN KEY ("participant1_id") REFERENCES "public"."participants"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "group_stage_matches" ADD CONSTRAINT "group_stage_matches_participant2_id_participants_id_fk" FOREIGN KEY ("participant2_id") REFERENCES "public"."participants"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/drizzle/0066_stiff_siren.sql b/drizzle/0066_stiff_siren.sql new file mode 100644 index 0000000..d028caa --- /dev/null +++ b/drizzle/0066_stiff_siren.sql @@ -0,0 +1 @@ +CREATE UNIQUE INDEX IF NOT EXISTS "group_stage_matches_group_pair_unique" ON "group_stage_matches" USING btree ("tournament_group_id","participant1_id","participant2_id"); \ No newline at end of file diff --git a/drizzle/meta/0065_snapshot.json b/drizzle/meta/0065_snapshot.json new file mode 100644 index 0000000..241d6da --- /dev/null +++ b/drizzle/meta/0065_snapshot.json @@ -0,0 +1,4310 @@ +{ + "id": "8c968f24-85f4-45db-befe-3d3e9941c4cd", + "prevId": "a73d4748-f14e-4857-9015-c99b47e998ec", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.autodraft_settings": { + "name": "autodraft_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "mode": { + "name": "mode", + "type": "autodraft_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'next_pick'" + }, + "queue_only": { + "name": "queue_only", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "autodraft_settings_season_id_seasons_id_fk": { + "name": "autodraft_settings_season_id_seasons_id_fk", + "tableFrom": "autodraft_settings", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "autodraft_settings_team_id_teams_id_fk": { + "name": "autodraft_settings_team_id_teams_id_fk", + "tableFrom": "autodraft_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "commissioners_league_id_leagues_id_fk": { + "name": "commissioners_league_id_leagues_id_fk", + "tableFrom": "commissioners", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_picks": { + "name": "draft_picks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pick_number": { + "name": "pick_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pick_in_round": { + "name": "pick_in_round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "picked_by_user_id": { + "name": "picked_by_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "picked_by_type": { + "name": "picked_by_type", + "type": "picked_by_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "time_used": { + "name": "time_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "draft_picks_season_pick_unique": { + "name": "draft_picks_season_pick_unique", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pick_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "draft_picks_season_id_seasons_id_fk": { + "name": "draft_picks_season_id_seasons_id_fk", + "tableFrom": "draft_picks", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_team_id_teams_id_fk": { + "name": "draft_picks_team_id_teams_id_fk", + "tableFrom": "draft_picks", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_participant_id_participants_id_fk": { + "name": "draft_picks_participant_id_participants_id_fk", + "tableFrom": "draft_picks", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_queue": { + "name": "draft_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "queue_position": { + "name": "queue_position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_queue_season_id_seasons_id_fk": { + "name": "draft_queue_season_id_seasons_id_fk", + "tableFrom": "draft_queue", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_team_id_teams_id_fk": { + "name": "draft_queue_team_id_teams_id_fk", + "tableFrom": "draft_queue", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_participant_id_participants_id_fk": { + "name": "draft_queue_participant_id_participants_id_fk", + "tableFrom": "draft_queue", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_slots": { + "name": "draft_slots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "draft_order": { + "name": "draft_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_slots_season_id_seasons_id_fk": { + "name": "draft_slots_season_id_seasons_id_fk", + "tableFrom": "draft_slots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_slots_team_id_teams_id_fk": { + "name": "draft_slots_team_id_teams_id_fk", + "tableFrom": "draft_slots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_timers": { + "name": "draft_timers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "time_remaining": { + "name": "time_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_timers_season_id_seasons_id_fk": { + "name": "draft_timers_season_id_seasons_id_fk", + "tableFrom": "draft_timers", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_timers_team_id_teams_id_fk": { + "name": "draft_timers_team_id_teams_id_fk", + "tableFrom": "draft_timers", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.event_results": { + "name": "event_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "placement": { + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "qualifying_points_awarded": { + "name": "qualifying_points_awarded", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "eliminated": { + "name": "eliminated", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "raw_score": { + "name": "raw_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "event_results_scoring_event_id_scoring_events_id_fk": { + "name": "event_results_scoring_event_id_scoring_events_id_fk", + "tableFrom": "event_results", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "event_results_participant_id_participants_id_fk": { + "name": "event_results_participant_id_participants_id_fk", + "tableFrom": "event_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.group_stage_matches": { + "name": "group_stage_matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_id": { + "name": "participant1_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant2_id": { + "name": "participant2_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_score": { + "name": "participant1_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "matchday": { + "name": "matchday", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "group_stage_matches_tournament_group_id_tournament_groups_id_fk": { + "name": "group_stage_matches_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "group_stage_matches_participant1_id_participants_id_fk": { + "name": "group_stage_matches_participant1_id_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "group_stage_matches_participant2_id_participants_id_fk": { + "name": "group_stage_matches_participant2_id_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leagues": { + "name": "leagues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "current_season_id": { + "name": "current_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_public_draft_board": { + "name": "is_public_draft_board", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "discord_webhook_url": { + "name": "discord_webhook_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_ev_snapshots": { + "name": "participant_ev_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "prob_first": { + "name": "prob_first", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_second": { + "name": "prob_second", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_third": { + "name": "prob_third", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fourth": { + "name": "prob_fourth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fifth": { + "name": "prob_fifth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_sixth": { + "name": "prob_sixth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_seventh": { + "name": "prob_seventh", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_eighth": { + "name": "prob_eighth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "calculated_ev": { + "name": "calculated_ev", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_ev_snapshots_unique": { + "name": "participant_ev_snapshots_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_ev_snapshots_participant_id_participants_id_fk": { + "name": "participant_ev_snapshots_participant_id_participants_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_ev_snapshots_sports_season_id_sports_seasons_id_fk": { + "name": "participant_ev_snapshots_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_expected_values": { + "name": "participant_expected_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "prob_first": { + "name": "prob_first", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_second": { + "name": "prob_second", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_third": { + "name": "prob_third", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fourth": { + "name": "prob_fourth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fifth": { + "name": "prob_fifth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_sixth": { + "name": "prob_sixth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_seventh": { + "name": "prob_seventh", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_eighth": { + "name": "prob_eighth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "expected_value": { + "name": "expected_value", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "probability_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'manual'" + }, + "source_odds": { + "name": "source_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "source_elo": { + "name": "source_elo", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_ev_participant_season_unique": { + "name": "participant_ev_participant_season_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_expected_values_participant_id_participants_id_fk": { + "name": "participant_expected_values_participant_id_participants_id_fk", + "tableFrom": "participant_expected_values", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_expected_values_sports_season_id_sports_seasons_id_fk": { + "name": "participant_expected_values_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_expected_values", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_golf_skills": { + "name": "participant_golf_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sg_total": { + "name": "sg_total", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "datagolf_rank": { + "name": "datagolf_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "masters_odds": { + "name": "masters_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "us_open_odds": { + "name": "us_open_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "open_championship_odds": { + "name": "open_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pga_championship_odds": { + "name": "pga_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_golf_skills_unique": { + "name": "participant_golf_skills_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_golf_skills_participant_id_participants_id_fk": { + "name": "participant_golf_skills_participant_id_participants_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_golf_skills_sports_season_id_sports_seasons_id_fk": { + "name": "participant_golf_skills_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_qualifying_totals": { + "name": "participant_qualifying_totals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_qualifying_points": { + "name": "total_qualifying_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "events_scored": { + "name": "events_scored", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "final_ranking": { + "name": "final_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_qualifying_totals_participant_id_participants_id_fk": { + "name": "participant_qualifying_totals_participant_id_participants_id_fk", + "tableFrom": "participant_qualifying_totals", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_qualifying_totals_sports_season_id_sports_seasons_id_fk": { + "name": "participant_qualifying_totals_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_qualifying_totals", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_results": { + "name": "participant_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "final_position": { + "name": "final_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_partial_score": { + "name": "is_partial_score", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "qualifying_points": { + "name": "qualifying_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_results_participant_id_participants_id_fk": { + "name": "participant_results_participant_id_participants_id_fk", + "tableFrom": "participant_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_results_sports_season_id_sports_seasons_id_fk": { + "name": "participant_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_season_results": { + "name": "participant_season_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_points": { + "name": "current_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_position": { + "name": "current_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_season_results_participant_id_participants_id_fk": { + "name": "participant_season_results_participant_id_participants_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_season_results_sports_season_id_sports_seasons_id_fk": { + "name": "participant_season_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_surface_elos": { + "name": "participant_surface_elos", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "world_ranking": { + "name": "world_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_hard": { + "name": "elo_hard", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_clay": { + "name": "elo_clay", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_grass": { + "name": "elo_grass", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_surface_elos_unique": { + "name": "participant_surface_elos_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_surface_elos_participant_id_participants_id_fk": { + "name": "participant_surface_elos_participant_id_participants_id_fk", + "tableFrom": "participant_surface_elos", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_surface_elos_sports_season_id_sports_seasons_id_fk": { + "name": "participant_surface_elos_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_surface_elos", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participants": { + "name": "participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "short_name": { + "name": "short_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "expected_value": { + "name": "expected_value", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participants_sports_season_name_unique": { + "name": "participants_sports_season_name_unique", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participants_sports_season_id_sports_seasons_id_fk": { + "name": "participants_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participants", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_standings_mappings": { + "name": "pending_standings_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "team_name": { + "name": "team_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "standing_data": { + "name": "standing_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "psm_season_external_id_idx": { + "name": "psm_season_external_id_idx", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pending_standings_mappings_sports_season_id_sports_seasons_id_fk": { + "name": "pending_standings_mappings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "pending_standings_mappings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_match_games": { + "name": "playoff_match_games", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "playoff_match_id": { + "name": "playoff_match_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_number": { + "name": "game_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "playoff_match_game_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "participant1_score": { + "name": "participant1_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "winner_id": { + "name": "winner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playoff_match_games_playoff_match_id_playoff_matches_id_fk": { + "name": "playoff_match_games_playoff_match_id_playoff_matches_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "playoff_matches", + "columnsFrom": [ + "playoff_match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_match_games_winner_id_participants_id_fk": { + "name": "playoff_match_games_winner_id_participants_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_match_odds": { + "name": "playoff_match_odds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "playoff_match_id": { + "name": "playoff_match_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "moneyline_odds": { + "name": "moneyline_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "implied_probability": { + "name": "implied_probability", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": false + }, + "odds_source": { + "name": "odds_source", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playoff_match_odds_playoff_match_id_playoff_matches_id_fk": { + "name": "playoff_match_odds_playoff_match_id_playoff_matches_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "playoff_matches", + "columnsFrom": [ + "playoff_match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_match_odds_participant_id_participants_id_fk": { + "name": "playoff_match_odds_participant_id_participants_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_matches": { + "name": "playoff_matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "match_number": { + "name": "match_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "participant1_id": { + "name": "participant1_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "participant2_id": { + "name": "participant2_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "winner_id": { + "name": "winner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "loser_id": { + "name": "loser_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "participant1_score": { + "name": "participant1_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "is_scoring": { + "name": "is_scoring", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "template_round": { + "name": "template_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "seed_info": { + "name": "seed_info", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playoff_matches_scoring_event_id_scoring_events_id_fk": { + "name": "playoff_matches_scoring_event_id_scoring_events_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_matches_participant1_id_participants_id_fk": { + "name": "playoff_matches_participant1_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_participant2_id_participants_id_fk": { + "name": "playoff_matches_participant2_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_winner_id_participants_id_fk": { + "name": "playoff_matches_winner_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_loser_id_participants_id_fk": { + "name": "playoff_matches_loser_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "loser_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.qualifying_point_config": { + "name": "qualifying_point_config", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "placement": { + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "points": { + "name": "points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "qualifying_point_config_sports_season_id_sports_seasons_id_fk": { + "name": "qualifying_point_config_sports_season_id_sports_seasons_id_fk", + "tableFrom": "qualifying_point_config", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.regular_season_standings": { + "name": "regular_season_standings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "wins": { + "name": "wins", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "losses": { + "name": "losses", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ot_losses": { + "name": "ot_losses", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ties": { + "name": "ties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "win_pct": { + "name": "win_pct", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false + }, + "games_played": { + "name": "games_played", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "games_back": { + "name": "games_back", + "type": "numeric(5, 1)", + "primaryKey": false, + "notNull": false + }, + "conference": { + "name": "conference", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "division": { + "name": "division", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "conference_rank": { + "name": "conference_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "division_rank": { + "name": "division_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "league_rank": { + "name": "league_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "streak": { + "name": "streak", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "last_ten": { + "name": "last_ten", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "home_record": { + "name": "home_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "away_record": { + "name": "away_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "srs": { + "name": "srs", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rss_participant_season_idx": { + "name": "rss_participant_season_idx", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "regular_season_standings_participant_id_participants_id_fk": { + "name": "regular_season_standings_participant_id_participants_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "regular_season_standings_sports_season_id_sports_seasons_id_fk": { + "name": "regular_season_standings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scoring_events": { + "name": "scoring_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "event_date": { + "name": "event_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "event_starts_at": { + "name": "event_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "event_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "playoff_round": { + "name": "playoff_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "is_qualifying_event": { + "name": "is_qualifying_event", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "bracket_template_id": { + "name": "bracket_template_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "scoring_starts_at_round": { + "name": "scoring_starts_at_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "bracket_region_config": { + "name": "bracket_region_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "scoring_events_sports_season_id_sports_seasons_id_fk": { + "name": "scoring_events_sports_season_id_sports_seasons_id_fk", + "tableFrom": "scoring_events", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_sports": { + "name": "season_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_sports_season_id_seasons_id_fk": { + "name": "season_sports_season_id_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_template_sports": { + "name": "season_template_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_template_sports_template_id_season_templates_id_fk": { + "name": "season_template_sports_template_id_season_templates_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "season_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_template_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_template_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_templates": { + "name": "season_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.seasons": { + "name": "seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pre_draft'" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "draft_rounds": { + "name": "draft_rounds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20 + }, + "flex_spots": { + "name": "flex_spots", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "draft_date_time": { + "name": "draft_date_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_initial_time": { + "name": "draft_initial_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 120 + }, + "draft_increment_time": { + "name": "draft_increment_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 30 + }, + "draft_timer_mode": { + "name": "draft_timer_mode", + "type": "draft_timer_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'chess_clock'" + }, + "current_pick_number": { + "name": "current_pick_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "draft_started_at": { + "name": "draft_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_paused": { + "name": "draft_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "invite_code": { + "name": "invite_code", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "points_for_1st": { + "name": "points_for_1st", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "points_for_2nd": { + "name": "points_for_2nd", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 70 + }, + "points_for_3rd": { + "name": "points_for_3rd", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 50 + }, + "points_for_4th": { + "name": "points_for_4th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 40 + }, + "points_for_5th": { + "name": "points_for_5th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "points_for_6th": { + "name": "points_for_6th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "points_for_7th": { + "name": "points_for_7th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 15 + }, + "points_for_8th": { + "name": "points_for_8th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 15 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "seasons_league_id_leagues_id_fk": { + "name": "seasons_league_id_leagues_id_fk", + "tableFrom": "seasons", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "seasons_template_id_season_templates_id_fk": { + "name": "seasons_template_id_season_templates_id_fk", + "tableFrom": "seasons", + "tableTo": "season_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "seasons_invite_code_unique": { + "name": "seasons_invite_code_unique", + "nullsNotDistinct": false, + "columns": [ + "invite_code" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sports": { + "name": "sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "sport_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon_url": { + "name": "icon_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "simulator_type": { + "name": "simulator_type", + "type": "simulator_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sports_slug_unique": { + "name": "sports_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sports_seasons": { + "name": "sports_seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sport_id": { + "name": "sport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "sports_season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'upcoming'" + }, + "scoring_type": { + "name": "scoring_type", + "type": "scoring_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "scoring_pattern": { + "name": "scoring_pattern", + "type": "scoring_pattern", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "total_majors": { + "name": "total_majors", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "majors_completed": { + "name": "majors_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "qualifying_points_finalized": { + "name": "qualifying_points_finalized", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "elo_calibration_exponent": { + "name": "elo_calibration_exponent", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "elo_min_rating": { + "name": "elo_min_rating", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1250 + }, + "elo_max_rating": { + "name": "elo_max_rating", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1750 + }, + "simulation_status": { + "name": "simulation_status", + "type": "simulation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "is_draftable": { + "name": "is_draftable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sports_seasons_sport_id_sports_id_fk": { + "name": "sports_seasons_sport_id_sports_id_fk", + "tableFrom": "sports_seasons", + "tableTo": "sports", + "columnsFrom": [ + "sport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_ev_snapshots": { + "name": "team_ev_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_ev_snapshots_unique": { + "name": "team_ev_snapshots_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_ev_snapshots_team_id_teams_id_fk": { + "name": "team_ev_snapshots_team_id_teams_id_fk", + "tableFrom": "team_ev_snapshots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_ev_snapshots_season_id_seasons_id_fk": { + "name": "team_ev_snapshots_season_id_seasons_id_fk", + "tableFrom": "team_ev_snapshots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_sport_scores": { + "name": "team_sport_scores", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "participants_completed": { + "name": "participants_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_total": { + "name": "participants_total", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_sport_scores_team_id_teams_id_fk": { + "name": "team_sport_scores_team_id_teams_id_fk", + "tableFrom": "team_sport_scores", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_sport_scores_sports_season_id_sports_seasons_id_fk": { + "name": "team_sport_scores_sports_season_id_sports_seasons_id_fk", + "tableFrom": "team_sport_scores", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_standings": { + "name": "team_standings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_rank": { + "name": "current_rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "previous_rank": { + "name": "previous_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "first_place_count": { + "name": "first_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "second_place_count": { + "name": "second_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "third_place_count": { + "name": "third_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fourth_place_count": { + "name": "fourth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fifth_place_count": { + "name": "fifth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sixth_place_count": { + "name": "sixth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "seventh_place_count": { + "name": "seventh_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "eighth_place_count": { + "name": "eighth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_remaining": { + "name": "participants_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participants_finished": { + "name": "participants_finished", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_standings_team_id_teams_id_fk": { + "name": "team_standings_team_id_teams_id_fk", + "tableFrom": "team_standings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_standings_season_id_seasons_id_fk": { + "name": "team_standings_season_id_seasons_id_fk", + "tableFrom": "team_standings", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_standings_snapshots": { + "name": "team_standings_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "rank": { + "name": "rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "first_place_count": { + "name": "first_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "second_place_count": { + "name": "second_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "third_place_count": { + "name": "third_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fourth_place_count": { + "name": "fourth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fifth_place_count": { + "name": "fifth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sixth_place_count": { + "name": "sixth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "seventh_place_count": { + "name": "seventh_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "eighth_place_count": { + "name": "eighth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_remaining": { + "name": "participants_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participants_finished": { + "name": "participants_finished", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_standings_snapshots_unique": { + "name": "team_standings_snapshots_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_standings_snapshots_team_id_teams_id_fk": { + "name": "team_standings_snapshots_team_id_teams_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_standings_snapshots_season_id_seasons_id_fk": { + "name": "team_standings_snapshots_season_id_seasons_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "logo_url": { + "name": "logo_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "teams_season_id_seasons_id_fk": { + "name": "teams_season_id_seasons_id_fk", + "tableFrom": "teams", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournament_group_members": { + "name": "tournament_group_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "eliminated": { + "name": "eliminated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "tournament_group_members_tournament_group_id_tournament_groups_id_fk": { + "name": "tournament_group_members_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tournament_group_members_participant_id_participants_id_fk": { + "name": "tournament_group_members_participant_id_participants_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournament_groups": { + "name": "tournament_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "group_name": { + "name": "group_name", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "tournament_groups_scoring_event_id_scoring_events_id_fk": { + "name": "tournament_groups_scoring_event_id_scoring_events_id_fk", + "tableFrom": "tournament_groups", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.autodraft_mode": { + "name": "autodraft_mode", + "schema": "public", + "values": [ + "next_pick", + "while_on" + ] + }, + "public.draft_timer_mode": { + "name": "draft_timer_mode", + "schema": "public", + "values": [ + "chess_clock", + "standard" + ] + }, + "public.event_type": { + "name": "event_type", + "schema": "public", + "values": [ + "playoff_game", + "major_tournament", + "final_standings", + "schedule_event" + ] + }, + "public.picked_by_type": { + "name": "picked_by_type", + "schema": "public", + "values": [ + "owner", + "commissioner", + "admin", + "auto" + ] + }, + "public.playoff_match_game_status": { + "name": "playoff_match_game_status", + "schema": "public", + "values": [ + "scheduled", + "complete", + "postponed" + ] + }, + "public.probability_source": { + "name": "probability_source", + "schema": "public", + "values": [ + "manual", + "futures_odds", + "elo_simulation", + "performance_model" + ] + }, + "public.scoring_pattern": { + "name": "scoring_pattern", + "schema": "public", + "values": [ + "playoff_bracket", + "season_standings", + "qualifying_points" + ] + }, + "public.scoring_type": { + "name": "scoring_type", + "schema": "public", + "values": [ + "playoffs", + "regular_season", + "majors" + ] + }, + "public.season_status": { + "name": "season_status", + "schema": "public", + "values": [ + "pre_draft", + "draft", + "active", + "completed" + ] + }, + "public.simulation_status": { + "name": "simulation_status", + "schema": "public", + "values": [ + "idle", + "running", + "failed" + ] + }, + "public.simulator_type": { + "name": "simulator_type", + "schema": "public", + "values": [ + "f1_standings", + "indycar_standings", + "golf_qualifying_points", + "playoff_bracket", + "ucl_bracket", + "ncaam_bracket", + "ncaaw_bracket", + "nba_bracket", + "nhl_bracket", + "afl_bracket", + "snooker_bracket", + "tennis_qualifying_points", + "mlb_bracket", + "wnba_bracket", + "world_cup" + ] + }, + "public.sport_type": { + "name": "sport_type", + "schema": "public", + "values": [ + "team", + "individual" + ] + }, + "public.sports_season_status": { + "name": "sports_season_status", + "schema": "public", + "values": [ + "upcoming", + "active", + "completed" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0066_snapshot.json b/drizzle/meta/0066_snapshot.json new file mode 100644 index 0000000..c21a49a --- /dev/null +++ b/drizzle/meta/0066_snapshot.json @@ -0,0 +1,4338 @@ +{ + "id": "e9cfc6e6-58ac-42d7-aef1-a5505c5bf415", + "prevId": "8c968f24-85f4-45db-befe-3d3e9941c4cd", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.autodraft_settings": { + "name": "autodraft_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "is_enabled": { + "name": "is_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "mode": { + "name": "mode", + "type": "autodraft_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'next_pick'" + }, + "queue_only": { + "name": "queue_only", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "autodraft_settings_season_id_seasons_id_fk": { + "name": "autodraft_settings_season_id_seasons_id_fk", + "tableFrom": "autodraft_settings", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "autodraft_settings_team_id_teams_id_fk": { + "name": "autodraft_settings_team_id_teams_id_fk", + "tableFrom": "autodraft_settings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.commissioners": { + "name": "commissioners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "commissioners_league_id_leagues_id_fk": { + "name": "commissioners_league_id_leagues_id_fk", + "tableFrom": "commissioners", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_picks": { + "name": "draft_picks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pick_number": { + "name": "pick_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pick_in_round": { + "name": "pick_in_round", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "picked_by_user_id": { + "name": "picked_by_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "picked_by_type": { + "name": "picked_by_type", + "type": "picked_by_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "time_used": { + "name": "time_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "draft_picks_season_pick_unique": { + "name": "draft_picks_season_pick_unique", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "pick_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "draft_picks_season_id_seasons_id_fk": { + "name": "draft_picks_season_id_seasons_id_fk", + "tableFrom": "draft_picks", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_team_id_teams_id_fk": { + "name": "draft_picks_team_id_teams_id_fk", + "tableFrom": "draft_picks", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_picks_participant_id_participants_id_fk": { + "name": "draft_picks_participant_id_participants_id_fk", + "tableFrom": "draft_picks", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_queue": { + "name": "draft_queue", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "queue_position": { + "name": "queue_position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_queue_season_id_seasons_id_fk": { + "name": "draft_queue_season_id_seasons_id_fk", + "tableFrom": "draft_queue", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_team_id_teams_id_fk": { + "name": "draft_queue_team_id_teams_id_fk", + "tableFrom": "draft_queue", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_queue_participant_id_participants_id_fk": { + "name": "draft_queue_participant_id_participants_id_fk", + "tableFrom": "draft_queue", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_slots": { + "name": "draft_slots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "draft_order": { + "name": "draft_order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_slots_season_id_seasons_id_fk": { + "name": "draft_slots_season_id_seasons_id_fk", + "tableFrom": "draft_slots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_slots_team_id_teams_id_fk": { + "name": "draft_slots_team_id_teams_id_fk", + "tableFrom": "draft_slots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.draft_timers": { + "name": "draft_timers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "time_remaining": { + "name": "time_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "draft_timers_season_id_seasons_id_fk": { + "name": "draft_timers_season_id_seasons_id_fk", + "tableFrom": "draft_timers", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "draft_timers_team_id_teams_id_fk": { + "name": "draft_timers_team_id_teams_id_fk", + "tableFrom": "draft_timers", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.event_results": { + "name": "event_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "placement": { + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "qualifying_points_awarded": { + "name": "qualifying_points_awarded", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "eliminated": { + "name": "eliminated", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "raw_score": { + "name": "raw_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "event_results_scoring_event_id_scoring_events_id_fk": { + "name": "event_results_scoring_event_id_scoring_events_id_fk", + "tableFrom": "event_results", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "event_results_participant_id_participants_id_fk": { + "name": "event_results_participant_id_participants_id_fk", + "tableFrom": "event_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.group_stage_matches": { + "name": "group_stage_matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_id": { + "name": "participant1_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant2_id": { + "name": "participant2_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant1_score": { + "name": "participant1_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "matchday": { + "name": "matchday", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "group_stage_matches_group_pair_unique": { + "name": "group_stage_matches_group_pair_unique", + "columns": [ + { + "expression": "tournament_group_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant1_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant2_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "group_stage_matches_tournament_group_id_tournament_groups_id_fk": { + "name": "group_stage_matches_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "group_stage_matches_participant1_id_participants_id_fk": { + "name": "group_stage_matches_participant1_id_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "group_stage_matches_participant2_id_participants_id_fk": { + "name": "group_stage_matches_participant2_id_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.leagues": { + "name": "leagues", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "current_season_id": { + "name": "current_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_public_draft_board": { + "name": "is_public_draft_board", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "discord_webhook_url": { + "name": "discord_webhook_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_ev_snapshots": { + "name": "participant_ev_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "prob_first": { + "name": "prob_first", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_second": { + "name": "prob_second", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_third": { + "name": "prob_third", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fourth": { + "name": "prob_fourth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fifth": { + "name": "prob_fifth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_sixth": { + "name": "prob_sixth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_seventh": { + "name": "prob_seventh", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_eighth": { + "name": "prob_eighth", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "calculated_ev": { + "name": "calculated_ev", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "varchar(100)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_ev_snapshots_unique": { + "name": "participant_ev_snapshots_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_ev_snapshots_participant_id_participants_id_fk": { + "name": "participant_ev_snapshots_participant_id_participants_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_ev_snapshots_sports_season_id_sports_seasons_id_fk": { + "name": "participant_ev_snapshots_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_expected_values": { + "name": "participant_expected_values", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "prob_first": { + "name": "prob_first", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_second": { + "name": "prob_second", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_third": { + "name": "prob_third", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fourth": { + "name": "prob_fourth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_fifth": { + "name": "prob_fifth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_sixth": { + "name": "prob_sixth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_seventh": { + "name": "prob_seventh", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "prob_eighth": { + "name": "prob_eighth", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "expected_value": { + "name": "expected_value", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "source": { + "name": "source", + "type": "probability_source", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'manual'" + }, + "source_odds": { + "name": "source_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "source_elo": { + "name": "source_elo", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_ev_participant_season_unique": { + "name": "participant_ev_participant_season_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_expected_values_participant_id_participants_id_fk": { + "name": "participant_expected_values_participant_id_participants_id_fk", + "tableFrom": "participant_expected_values", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_expected_values_sports_season_id_sports_seasons_id_fk": { + "name": "participant_expected_values_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_expected_values", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_golf_skills": { + "name": "participant_golf_skills", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sg_total": { + "name": "sg_total", + "type": "numeric(5, 2)", + "primaryKey": false, + "notNull": false + }, + "datagolf_rank": { + "name": "datagolf_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "masters_odds": { + "name": "masters_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "us_open_odds": { + "name": "us_open_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "open_championship_odds": { + "name": "open_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "pga_championship_odds": { + "name": "pga_championship_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_golf_skills_unique": { + "name": "participant_golf_skills_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_golf_skills_participant_id_participants_id_fk": { + "name": "participant_golf_skills_participant_id_participants_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_golf_skills_sports_season_id_sports_seasons_id_fk": { + "name": "participant_golf_skills_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_qualifying_totals": { + "name": "participant_qualifying_totals", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_qualifying_points": { + "name": "total_qualifying_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "events_scored": { + "name": "events_scored", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "final_ranking": { + "name": "final_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_qualifying_totals_participant_id_participants_id_fk": { + "name": "participant_qualifying_totals_participant_id_participants_id_fk", + "tableFrom": "participant_qualifying_totals", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_qualifying_totals_sports_season_id_sports_seasons_id_fk": { + "name": "participant_qualifying_totals_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_qualifying_totals", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_results": { + "name": "participant_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "final_position": { + "name": "final_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "is_partial_score": { + "name": "is_partial_score", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "qualifying_points": { + "name": "qualifying_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_results_participant_id_participants_id_fk": { + "name": "participant_results_participant_id_participants_id_fk", + "tableFrom": "participant_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_results_sports_season_id_sports_seasons_id_fk": { + "name": "participant_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_season_results": { + "name": "participant_season_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "current_points": { + "name": "current_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_position": { + "name": "current_position", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "participant_season_results_participant_id_participants_id_fk": { + "name": "participant_season_results_participant_id_participants_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_season_results_sports_season_id_sports_seasons_id_fk": { + "name": "participant_season_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participant_surface_elos": { + "name": "participant_surface_elos", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "world_ranking": { + "name": "world_ranking", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_hard": { + "name": "elo_hard", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_clay": { + "name": "elo_clay", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "elo_grass": { + "name": "elo_grass", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participant_surface_elos_unique": { + "name": "participant_surface_elos_unique", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participant_surface_elos_participant_id_participants_id_fk": { + "name": "participant_surface_elos_participant_id_participants_id_fk", + "tableFrom": "participant_surface_elos", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "participant_surface_elos_sports_season_id_sports_seasons_id_fk": { + "name": "participant_surface_elos_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participant_surface_elos", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.participants": { + "name": "participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "short_name": { + "name": "short_name", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "external_id": { + "name": "external_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "expected_value": { + "name": "expected_value", + "type": "numeric(10, 4)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "participants_sports_season_name_unique": { + "name": "participants_sports_season_name_unique", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participants_sports_season_id_sports_seasons_id_fk": { + "name": "participants_sports_season_id_sports_seasons_id_fk", + "tableFrom": "participants", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_standings_mappings": { + "name": "pending_standings_mappings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "team_name": { + "name": "team_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "standing_data": { + "name": "standing_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "psm_season_external_id_idx": { + "name": "psm_season_external_id_idx", + "columns": [ + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "external_team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pending_standings_mappings_sports_season_id_sports_seasons_id_fk": { + "name": "pending_standings_mappings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "pending_standings_mappings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_match_games": { + "name": "playoff_match_games", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "playoff_match_id": { + "name": "playoff_match_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "game_number": { + "name": "game_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "scheduled_at": { + "name": "scheduled_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "playoff_match_game_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "participant1_score": { + "name": "participant1_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "winner_id": { + "name": "winner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playoff_match_games_playoff_match_id_playoff_matches_id_fk": { + "name": "playoff_match_games_playoff_match_id_playoff_matches_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "playoff_matches", + "columnsFrom": [ + "playoff_match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_match_games_winner_id_participants_id_fk": { + "name": "playoff_match_games_winner_id_participants_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_match_odds": { + "name": "playoff_match_odds", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "playoff_match_id": { + "name": "playoff_match_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "moneyline_odds": { + "name": "moneyline_odds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "implied_probability": { + "name": "implied_probability", + "type": "numeric(6, 4)", + "primaryKey": false, + "notNull": false + }, + "odds_source": { + "name": "odds_source", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "recorded_at": { + "name": "recorded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playoff_match_odds_playoff_match_id_playoff_matches_id_fk": { + "name": "playoff_match_odds_playoff_match_id_playoff_matches_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "playoff_matches", + "columnsFrom": [ + "playoff_match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_match_odds_participant_id_participants_id_fk": { + "name": "playoff_match_odds_participant_id_participants_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.playoff_matches": { + "name": "playoff_matches", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "round": { + "name": "round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "match_number": { + "name": "match_number", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "participant1_id": { + "name": "participant1_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "participant2_id": { + "name": "participant2_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "winner_id": { + "name": "winner_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "loser_id": { + "name": "loser_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "participant1_score": { + "name": "participant1_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participant2_score": { + "name": "participant2_score", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "is_scoring": { + "name": "is_scoring", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "template_round": { + "name": "template_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "seed_info": { + "name": "seed_info", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "playoff_matches_scoring_event_id_scoring_events_id_fk": { + "name": "playoff_matches_scoring_event_id_scoring_events_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "playoff_matches_participant1_id_participants_id_fk": { + "name": "playoff_matches_participant1_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_participant2_id_participants_id_fk": { + "name": "playoff_matches_participant2_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_winner_id_participants_id_fk": { + "name": "playoff_matches_winner_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_loser_id_participants_id_fk": { + "name": "playoff_matches_loser_id_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "participants", + "columnsFrom": [ + "loser_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.qualifying_point_config": { + "name": "qualifying_point_config", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "placement": { + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "points": { + "name": "points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "qualifying_point_config_sports_season_id_sports_seasons_id_fk": { + "name": "qualifying_point_config_sports_season_id_sports_seasons_id_fk", + "tableFrom": "qualifying_point_config", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.regular_season_standings": { + "name": "regular_season_standings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "wins": { + "name": "wins", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "losses": { + "name": "losses", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "ot_losses": { + "name": "ot_losses", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "ties": { + "name": "ties", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "win_pct": { + "name": "win_pct", + "type": "numeric(5, 4)", + "primaryKey": false, + "notNull": false + }, + "games_played": { + "name": "games_played", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "games_back": { + "name": "games_back", + "type": "numeric(5, 1)", + "primaryKey": false, + "notNull": false + }, + "conference": { + "name": "conference", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "division": { + "name": "division", + "type": "varchar(100)", + "primaryKey": false, + "notNull": false + }, + "conference_rank": { + "name": "conference_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "division_rank": { + "name": "division_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "league_rank": { + "name": "league_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "streak": { + "name": "streak", + "type": "varchar(20)", + "primaryKey": false, + "notNull": false + }, + "last_ten": { + "name": "last_ten", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "home_record": { + "name": "home_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "away_record": { + "name": "away_record", + "type": "varchar(15)", + "primaryKey": false, + "notNull": false + }, + "external_team_id": { + "name": "external_team_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "srs": { + "name": "srs", + "type": "numeric(6, 2)", + "primaryKey": false, + "notNull": false + }, + "synced_at": { + "name": "synced_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "rss_participant_season_idx": { + "name": "rss_participant_season_idx", + "columns": [ + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sports_season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "regular_season_standings_participant_id_participants_id_fk": { + "name": "regular_season_standings_participant_id_participants_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "regular_season_standings_sports_season_id_sports_seasons_id_fk": { + "name": "regular_season_standings_sports_season_id_sports_seasons_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scoring_events": { + "name": "scoring_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "event_date": { + "name": "event_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "event_starts_at": { + "name": "event_starts_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "event_type": { + "name": "event_type", + "type": "event_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "playoff_round": { + "name": "playoff_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "is_qualifying_event": { + "name": "is_qualifying_event", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "bracket_template_id": { + "name": "bracket_template_id", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "scoring_starts_at_round": { + "name": "scoring_starts_at_round", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "bracket_region_config": { + "name": "bracket_region_config", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_complete": { + "name": "is_complete", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "scoring_events_sports_season_id_sports_seasons_id_fk": { + "name": "scoring_events_sports_season_id_sports_seasons_id_fk", + "tableFrom": "scoring_events", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_sports": { + "name": "season_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_sports_season_id_seasons_id_fk": { + "name": "season_sports_season_id_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_template_sports": { + "name": "season_template_sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "season_template_sports_template_id_season_templates_id_fk": { + "name": "season_template_sports_template_id_season_templates_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "season_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_template_sports_sports_season_id_sports_seasons_id_fk": { + "name": "season_template_sports_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_template_sports", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_templates": { + "name": "season_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.seasons": { + "name": "seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pre_draft'" + }, + "template_id": { + "name": "template_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "draft_rounds": { + "name": "draft_rounds", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 20 + }, + "flex_spots": { + "name": "flex_spots", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "draft_date_time": { + "name": "draft_date_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_initial_time": { + "name": "draft_initial_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 120 + }, + "draft_increment_time": { + "name": "draft_increment_time", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 30 + }, + "draft_timer_mode": { + "name": "draft_timer_mode", + "type": "draft_timer_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'chess_clock'" + }, + "current_pick_number": { + "name": "current_pick_number", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1 + }, + "draft_started_at": { + "name": "draft_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_paused": { + "name": "draft_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "invite_code": { + "name": "invite_code", + "type": "varchar(20)", + "primaryKey": false, + "notNull": true + }, + "points_for_1st": { + "name": "points_for_1st", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "points_for_2nd": { + "name": "points_for_2nd", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 70 + }, + "points_for_3rd": { + "name": "points_for_3rd", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 50 + }, + "points_for_4th": { + "name": "points_for_4th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 40 + }, + "points_for_5th": { + "name": "points_for_5th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "points_for_6th": { + "name": "points_for_6th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 25 + }, + "points_for_7th": { + "name": "points_for_7th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 15 + }, + "points_for_8th": { + "name": "points_for_8th", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 15 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "seasons_league_id_leagues_id_fk": { + "name": "seasons_league_id_leagues_id_fk", + "tableFrom": "seasons", + "tableTo": "leagues", + "columnsFrom": [ + "league_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "seasons_template_id_season_templates_id_fk": { + "name": "seasons_template_id_season_templates_id_fk", + "tableFrom": "seasons", + "tableTo": "season_templates", + "columnsFrom": [ + "template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "seasons_invite_code_unique": { + "name": "seasons_invite_code_unique", + "nullsNotDistinct": false, + "columns": [ + "invite_code" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sports": { + "name": "sports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "sport_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "icon_url": { + "name": "icon_url", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "simulator_type": { + "name": "simulator_type", + "type": "simulator_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sports_slug_unique": { + "name": "sports_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sports_seasons": { + "name": "sports_seasons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "sport_id": { + "name": "sport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "year": { + "name": "year", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "sports_season_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'upcoming'" + }, + "scoring_type": { + "name": "scoring_type", + "type": "scoring_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "scoring_pattern": { + "name": "scoring_pattern", + "type": "scoring_pattern", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "total_majors": { + "name": "total_majors", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "majors_completed": { + "name": "majors_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "qualifying_points_finalized": { + "name": "qualifying_points_finalized", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "elo_calibration_exponent": { + "name": "elo_calibration_exponent", + "type": "numeric(3, 2)", + "primaryKey": false, + "notNull": false + }, + "elo_min_rating": { + "name": "elo_min_rating", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1250 + }, + "elo_max_rating": { + "name": "elo_max_rating", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 1750 + }, + "simulation_status": { + "name": "simulation_status", + "type": "simulation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "is_draftable": { + "name": "is_draftable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "sports_seasons_sport_id_sports_id_fk": { + "name": "sports_seasons_sport_id_sports_id_fk", + "tableFrom": "sports_seasons", + "tableTo": "sports", + "columnsFrom": [ + "sport_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_ev_snapshots": { + "name": "team_ev_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_ev_snapshots_unique": { + "name": "team_ev_snapshots_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_ev_snapshots_team_id_teams_id_fk": { + "name": "team_ev_snapshots_team_id_teams_id_fk", + "tableFrom": "team_ev_snapshots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_ev_snapshots_season_id_seasons_id_fk": { + "name": "team_ev_snapshots_season_id_seasons_id_fk", + "tableFrom": "team_ev_snapshots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_sport_scores": { + "name": "team_sport_scores", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sports_season_id": { + "name": "sports_season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "participants_completed": { + "name": "participants_completed", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_total": { + "name": "participants_total", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_sport_scores_team_id_teams_id_fk": { + "name": "team_sport_scores_team_id_teams_id_fk", + "tableFrom": "team_sport_scores", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_sport_scores_sports_season_id_sports_seasons_id_fk": { + "name": "team_sport_scores_sports_season_id_sports_seasons_id_fk", + "tableFrom": "team_sport_scores", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_standings": { + "name": "team_standings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_rank": { + "name": "current_rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "previous_rank": { + "name": "previous_rank", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "first_place_count": { + "name": "first_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "second_place_count": { + "name": "second_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "third_place_count": { + "name": "third_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fourth_place_count": { + "name": "fourth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fifth_place_count": { + "name": "fifth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sixth_place_count": { + "name": "sixth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "seventh_place_count": { + "name": "seventh_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "eighth_place_count": { + "name": "eighth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_remaining": { + "name": "participants_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participants_finished": { + "name": "participants_finished", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "calculated_at": { + "name": "calculated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "team_standings_team_id_teams_id_fk": { + "name": "team_standings_team_id_teams_id_fk", + "tableFrom": "team_standings", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_standings_season_id_seasons_id_fk": { + "name": "team_standings_season_id_seasons_id_fk", + "tableFrom": "team_standings", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_standings_snapshots": { + "name": "team_standings_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "snapshot_date": { + "name": "snapshot_date", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "total_points": { + "name": "total_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "rank": { + "name": "rank", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "first_place_count": { + "name": "first_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "second_place_count": { + "name": "second_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "third_place_count": { + "name": "third_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fourth_place_count": { + "name": "fourth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "fifth_place_count": { + "name": "fifth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "sixth_place_count": { + "name": "sixth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "seventh_place_count": { + "name": "seventh_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "eighth_place_count": { + "name": "eighth_place_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "participants_remaining": { + "name": "participants_remaining", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "actual_points": { + "name": "actual_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "projected_points": { + "name": "projected_points", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "participants_finished": { + "name": "participants_finished", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_standings_snapshots_unique": { + "name": "team_standings_snapshots_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "snapshot_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_standings_snapshots_team_id_teams_id_fk": { + "name": "team_standings_snapshots_team_id_teams_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_standings_snapshots_season_id_seasons_id_fk": { + "name": "team_standings_snapshots_season_id_seasons_id_fk", + "tableFrom": "team_standings_snapshots", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.teams": { + "name": "teams", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "season_id": { + "name": "season_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "logo_url": { + "name": "logo_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "teams_season_id_seasons_id_fk": { + "name": "teams_season_id_seasons_id_fk", + "tableFrom": "teams", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournament_group_members": { + "name": "tournament_group_members", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_group_id": { + "name": "tournament_group_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "eliminated": { + "name": "eliminated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "tournament_group_members_tournament_group_id_tournament_groups_id_fk": { + "name": "tournament_group_members_tournament_group_id_tournament_groups_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "tournament_groups", + "columnsFrom": [ + "tournament_group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tournament_group_members_participant_id_participants_id_fk": { + "name": "tournament_group_members_participant_id_participants_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournament_groups": { + "name": "tournament_groups", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "group_name": { + "name": "group_name", + "type": "varchar(10)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "tournament_groups_scoring_event_id_scoring_events_id_fk": { + "name": "tournament_groups_scoring_event_id_scoring_events_id_fk", + "tableFrom": "tournament_groups", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "clerk_id": { + "name": "clerk_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "display_name": { + "name": "display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "image_url": { + "name": "image_url", + "type": "varchar(512)", + "primaryKey": false, + "notNull": false + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.autodraft_mode": { + "name": "autodraft_mode", + "schema": "public", + "values": [ + "next_pick", + "while_on" + ] + }, + "public.draft_timer_mode": { + "name": "draft_timer_mode", + "schema": "public", + "values": [ + "chess_clock", + "standard" + ] + }, + "public.event_type": { + "name": "event_type", + "schema": "public", + "values": [ + "playoff_game", + "major_tournament", + "final_standings", + "schedule_event" + ] + }, + "public.picked_by_type": { + "name": "picked_by_type", + "schema": "public", + "values": [ + "owner", + "commissioner", + "admin", + "auto" + ] + }, + "public.playoff_match_game_status": { + "name": "playoff_match_game_status", + "schema": "public", + "values": [ + "scheduled", + "complete", + "postponed" + ] + }, + "public.probability_source": { + "name": "probability_source", + "schema": "public", + "values": [ + "manual", + "futures_odds", + "elo_simulation", + "performance_model" + ] + }, + "public.scoring_pattern": { + "name": "scoring_pattern", + "schema": "public", + "values": [ + "playoff_bracket", + "season_standings", + "qualifying_points" + ] + }, + "public.scoring_type": { + "name": "scoring_type", + "schema": "public", + "values": [ + "playoffs", + "regular_season", + "majors" + ] + }, + "public.season_status": { + "name": "season_status", + "schema": "public", + "values": [ + "pre_draft", + "draft", + "active", + "completed" + ] + }, + "public.simulation_status": { + "name": "simulation_status", + "schema": "public", + "values": [ + "idle", + "running", + "failed" + ] + }, + "public.simulator_type": { + "name": "simulator_type", + "schema": "public", + "values": [ + "f1_standings", + "indycar_standings", + "golf_qualifying_points", + "playoff_bracket", + "ucl_bracket", + "ncaam_bracket", + "ncaaw_bracket", + "nba_bracket", + "nhl_bracket", + "afl_bracket", + "snooker_bracket", + "tennis_qualifying_points", + "mlb_bracket", + "wnba_bracket", + "world_cup" + ] + }, + "public.sport_type": { + "name": "sport_type", + "schema": "public", + "values": [ + "team", + "individual" + ] + }, + "public.sports_season_status": { + "name": "sports_season_status", + "schema": "public", + "values": [ + "upcoming", + "active", + "completed" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 2dcbd8c..84eabc3 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -456,6 +456,20 @@ "when": 1774598151282, "tag": "0064_fuzzy_wolfsbane", "breakpoints": true + }, + { + "idx": 65, + "version": "7", + "when": 1774719902130, + "tag": "0065_kind_mantis", + "breakpoints": true + }, + { + "idx": 66, + "version": "7", + "when": 1774802533652, + "tag": "0066_stiff_siren", + "breakpoints": true } ] } \ No newline at end of file