From 2848231235986bcff8ae1d208099934e3aa736d5 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Fri, 1 May 2026 20:13:18 -0700 Subject: [PATCH] Canonical tournament layer: schema + backfill (1/2) (#365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(schema): rename per-window tables to season_* prefix Renames participants, participant_expected_values, participant_qualifying_totals, participant_results, participant_surface_elos to season_* prefixed names. Renames event_results.participant_id to season_participant_id. Phase 1a of canonical tournament layer migration. Co-Authored-By: Claude Opus 4.7 * refactor: rename participant.ts model file to season-participant.ts Co-Authored-By: Claude Opus 4.7 * refactor(models): update model layer to use renamed schema exports Updated all model files to use the renamed schema exports from Task 1: - participants → seasonParticipants - participantExpectedValues → seasonParticipantExpectedValues - participantQualifyingTotals → seasonParticipantQualifyingTotals - participantResults → seasonParticipantResults - participantSurfaceElos → seasonParticipantSurfaceElos - eventResults.participantId → eventResults.seasonParticipantId - db.query relation accessors updated - Relation field .participant → .seasonParticipant where applicable - Import paths updated: ./participant → ./season-participant Files updated (14 model files + 3 test files): - draft-pick.ts - draft-utils.ts - event-result.ts - group-stage-match.ts - participant-result.ts - qualifying-points.ts - scoring-calculator.ts - scoring-event.ts - sports-season.ts - surface-elo.ts - team-score-events.ts - cs2-major-stage.ts - golf-skills.ts - participant-expected-value.ts - __tests__/sports-season.clone.test.ts - __tests__/auto-pick.test.ts - __tests__/executeAutoPick.timer.test.ts Typecheck errors decreased: 779 → 499 (280 fewer) All model file errors related to renamed schemas resolved. Co-Authored-By: Claude Opus 4.7 * refactor(routes): update route layer to use renamed schema exports - Update model import from ~/models/participant to ~/models/season-participant - Rename schema.participants to schema.seasonParticipants - Rename schema.participantResults to schema.seasonParticipantResults - Rename db.query.participants to db.query.seasonParticipants - Update 9 route files and 1 test file Affected files: - admin.sports-seasons.$id.events.$eventId.bracket.server.ts - admin.sports-seasons.$id.participants.tsx - api/draft.force-manual-pick.ts - api/draft.make-pick.ts - api/draft.replace-pick.ts - api/seasons.$seasonId.draft.ts - leagues/$leagueId.draft-board.$seasonId.tsx - leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts - admin/__tests__/sports-seasons-participants.test.ts Error count reduced from 499 to 453 (46 errors fixed). Co-Authored-By: Claude Opus 4.7 * refactor(routes): update route files for schema rename Update route imports from ~/models/participant to ~/models/season-participant and fix references to .participant/.participantId on event results to use .seasonParticipant/.seasonParticipantId after schema rename. Co-Authored-By: Claude Opus 4.7 * refactor(services): update simulators and services for renamed schema Update all simulators, services, and server files to use renamed schema tables: - participants → seasonParticipants - participantExpectedValues → seasonParticipantExpectedValues - participantResults → seasonParticipantResults - eventResults.participantId → eventResults.seasonParticipantId Files updated: - 20 sport simulators (NBA, NHL, NFL, MLB, etc.) - probability-updater.ts - standings-sync/index.ts - sports-data-sync.server.ts - server/socket.ts Typecheck errors reduced from 365 to 0. Co-Authored-By: Claude Opus 4.7 * migration: rename per-window tables to season_* prefix * fix(tests): update mock query keys after participants table rename Change mock db.query.participants to db.query.seasonParticipants in test files to match the schema rename from commit 66145a9. This fixes "Cannot read properties of undefined (reading 'findFirst'/'findMany')" errors that occurred when production code queries db.query.seasonParticipants but test mocks only defined the old participants key. Files updated: - app/services/simulations/__tests__/world-cup-simulator.test.ts - app/routes/api/__tests__/draft.force-manual-pick.test.ts - app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts - app/routes/api/__tests__/draft.make-pick.timer-mode.test.ts - server/__tests__/timer-autodraft.test.ts - app/models/__tests__/team-score-events.test.ts Co-Authored-By: Claude Opus 4.7 * fix(tests): update remaining mock paths and keys after schema rename * fix(tests): final two mock stragglers after schema rename - draft-pick.test.ts: assertion on db.query.participantQualifyingTotals - process-match-result.test.ts: mock key participants → seasonParticipants Co-Authored-By: Claude Opus 4.7 * chore: add post-phase1a baseline capture (temp, for diff verification) * chore: capture pre-migration baselines * chore: remove post-phase1a capture helper after verification * schema: add canonical tournament & participant tables Adds tournaments, participants (canonical), tournament_results, and participant_surface_elos (canonical). Adds nullable tournament_id to scoring_events and nullable participant_id to season_participants. Phase 1b of canonical tournament layer migration. Co-Authored-By: Claude Opus 4.7 * feat(models): add canonical tournament, participant, result, surface-elo models Adds CRUD modules for the canonical tables created in commit 775b905. Each module mirrors existing app/models conventions (database() from ~/database/context, schema from ~/database/schema, mock-based tests). Key implementation notes: - participant.ts exports use "Canonical" prefix (CanonicalParticipant, createCanonicalParticipant, etc.) to avoid collision with existing season-participant.ts exports - All four models include comprehensive unit tests following the audit-log.test.ts pattern - Tests use mocked db responses (no real database access) - Upsert functions use onConflictDoUpdate for appropriate unique constraints Part of Phase 1b of canonical tournament layer migration. Co-Authored-By: Claude Opus 4.7 * migration: create canonical tables, add nullable FKs * scripts: add extractTournamentIdentity helper for backfill Pure function that derives canonical (name, year) identity from a scoring_events row, stripping trailing 4-digit years from the name or falling back to eventDate. Used by the Phase 2 backfill to group per-window events into canonical tournaments. * scripts: add backfill orchestrator for canonical layer Populates canonical tournaments, participants, tournament_results, and participant_surface_elos from per-window data for qualifying-points sports. Skips already-linked rows, is idempotent, and supports dry-run mode. Critical invariants enforced by the implementation: - qualifying_points_awarded is never copied to tournament_results - season_participant_qualifying_totals is never touched - conflicting surface-Elo values between windows raise a loud error (recorded in report.errors) rather than overwriting * scripts: add backfill CLI with dry-run default Wires backfill-canonical-layer.ts to a CLI entry point exposed as `npm run backfill:canonical`. Defaults to --dry-run; requires --apply to actually write. Supports --sport= to limit to a single sport. Exits 2 if the backfill reports errors (e.g., surface-Elo conflicts). * fix(backfill-cli): wrap runBackfill in DatabaseContext.run The orchestrator uses database() from ~/database/context, which requires AsyncLocalStorage to be populated. Wrap the CLI invocation with DatabaseContext.run(db, ...) using server/db's cached connection pool. Co-Authored-By: Claude Opus 4.7 * fix(backfill-cli): exit 0 on success so pg pool doesn't block The cached postgres connection pool keeps the Node event loop open after main() returns. Explicit process.exit(0) on success mirrors the pattern in scripts/capture-baseline.ts. Co-Authored-By: Claude Opus 4.7 --------- Co-authored-by: Chris Parsons Co-authored-by: Claude Opus 4.7 --- app/models/__tests__/auto-pick.test.ts | 20 +- .../__tests__/canonical-surface-elo.test.ts | 100 + app/models/__tests__/draft-pick.test.ts | 4 +- .../__tests__/executeAutoPick.timer.test.ts | 6 +- .../participant-expected-value.test.ts | 4 +- app/models/__tests__/participant.test.ts | 152 + .../__tests__/process-match-result.test.ts | 6 +- .../__tests__/sports-season.clone.test.ts | 10 +- .../__tests__/team-score-events.test.ts | 4 +- .../__tests__/tournament-result.test.ts | 114 + app/models/__tests__/tournament.test.ts | 174 + app/models/canonical-surface-elo.ts | 43 + app/models/cs2-major-stage.ts | 12 +- app/models/draft-pick.ts | 42 +- app/models/draft-utils.ts | 36 +- app/models/event-result.ts | 16 +- app/models/golf-skills.ts | 12 +- app/models/group-stage-match.ts | 4 +- app/models/index.ts | 6 +- app/models/participant-expected-value.ts | 112 +- app/models/participant-result.ts | 36 +- app/models/participant.ts | 210 +- app/models/qualifying-points.ts | 32 +- app/models/scoring-calculator.ts | 38 +- app/models/scoring-event.ts | 8 +- app/models/season-participant.ts | 200 + app/models/sports-season.ts | 12 +- app/models/surface-elo.ts | 50 +- app/models/team-score-events.ts | 4 +- app/models/tournament-result.ts | 61 + app/models/tournament.ts | 94 + app/routes/admin.data-sync.tsx | 2 +- .../admin.sports-seasons.$id.elo-ratings.tsx | 2 +- ...sons.$id.events.$eventId.bracket.server.ts | 6 +- ...-seasons.$id.events.$eventId.cs2-setup.tsx | 2 +- ...orts-seasons.$id.events.$eventId.server.ts | 6 +- ...min.sports-seasons.$id.events.$eventId.tsx | 8 +- ...orts-seasons.$id.expected-values.server.ts | 2 +- .../admin.sports-seasons.$id.futures-odds.tsx | 2 +- .../admin.sports-seasons.$id.golf-skills.tsx | 2 +- .../admin.sports-seasons.$id.participants.tsx | 2 +- ...n.sports-seasons.$id.regular-standings.tsx | 2 +- .../admin.sports-seasons.$id.simulate.tsx | 2 +- .../admin.sports-seasons.$id.standings.tsx | 2 +- .../admin.sports-seasons.$id.surface-elo.tsx | 2 +- app/routes/admin.sports-seasons.$id.tsx | 2 +- .../sports-seasons-participants.test.ts | 4 +- .../__tests__/draft.force-manual-pick.test.ts | 10 +- ...draft.force-manual-pick.timer-mode.test.ts | 6 +- .../draft.make-pick.timer-mode.test.ts | 6 +- app/routes/api/draft.force-manual-pick.ts | 6 +- app/routes/api/draft.make-pick.ts | 6 +- app/routes/api/draft.replace-pick.ts | 10 +- app/routes/api/seasons.$seasonId.draft.ts | 6 +- .../$leagueId.draft-board.$seasonId.tsx | 20 +- .../leagues/$leagueId.draft.$seasonId.tsx | 2 +- ...d.sports-seasons.$sportsSeasonId.server.ts | 10 +- app/services/probability-updater.ts | 4 +- .../__tests__/world-cup-simulator.test.ts | 18 +- app/services/simulations/afl-simulator.ts | 14 +- .../simulations/auto-racing-simulator.ts | 4 +- app/services/simulations/bracket-simulator.ts | 12 +- .../simulations/cs-major-simulator.ts | 18 +- app/services/simulations/darts-simulator.ts | 16 +- app/services/simulations/golf-simulator.ts | 8 +- app/services/simulations/llws-simulator.ts | 14 +- app/services/simulations/mlb-simulator.ts | 16 +- app/services/simulations/nba-simulator.ts | 28 +- .../simulations/ncaa-football-simulator.ts | 16 +- app/services/simulations/ncaam-simulator.ts | 6 +- app/services/simulations/ncaaw-simulator.ts | 6 +- app/services/simulations/nfl-simulator.ts | 14 +- app/services/simulations/nhl-simulator.ts | 32 +- app/services/simulations/snooker-simulator.ts | 14 +- app/services/simulations/tennis-simulator.ts | 8 +- app/services/simulations/ucl-simulator.ts | 8 +- app/services/simulations/wnba-simulator.ts | 16 +- .../simulations/world-cup-simulator.ts | 14 +- app/services/standings-sync/index.ts | 2 +- app/utils/sports-data-sync.server.ts | 52 +- database/schema.ts | 335 +- drizzle/0087_small_susan_delgado.sql | 221 + drizzle/0088_cheerful_norrin_radd.sql | 93 + drizzle/meta/0087_snapshot.json | 5221 +++++++++++++++ drizzle/meta/0088_snapshot.json | 5688 +++++++++++++++++ drizzle/meta/_journal.json | 14 + package.json | 1 + .../backfill-canonical-layer.test.ts | 450 ++ scripts/backfill-canonical-layer.ts | 358 ++ scripts/backfill-cli.ts | 82 + .../__tests__/match-tournament.test.ts | 44 + scripts/backfill/match-tournament.ts | 57 + server/__tests__/timer-autodraft.test.ts | 6 +- server/socket.ts | 8 +- 94 files changed, 13936 insertions(+), 734 deletions(-) create mode 100644 app/models/__tests__/canonical-surface-elo.test.ts create mode 100644 app/models/__tests__/participant.test.ts create mode 100644 app/models/__tests__/tournament-result.test.ts create mode 100644 app/models/__tests__/tournament.test.ts create mode 100644 app/models/canonical-surface-elo.ts create mode 100644 app/models/season-participant.ts create mode 100644 app/models/tournament-result.ts create mode 100644 app/models/tournament.ts create mode 100644 drizzle/0087_small_susan_delgado.sql create mode 100644 drizzle/0088_cheerful_norrin_radd.sql create mode 100644 drizzle/meta/0087_snapshot.json create mode 100644 drizzle/meta/0088_snapshot.json create mode 100644 scripts/__tests__/backfill-canonical-layer.test.ts create mode 100644 scripts/backfill-canonical-layer.ts create mode 100644 scripts/backfill-cli.ts create mode 100644 scripts/backfill/__tests__/match-tournament.test.ts create mode 100644 scripts/backfill/match-tournament.ts diff --git a/app/models/__tests__/auto-pick.test.ts b/app/models/__tests__/auto-pick.test.ts index f432448..cba5bc8 100644 --- a/app/models/__tests__/auto-pick.test.ts +++ b/app/models/__tests__/auto-pick.test.ts @@ -8,7 +8,7 @@ vi.mock("~/models/draft-pick", () => ({ isParticipantDrafted: vi.fn(), })); -vi.mock("~/models/participant", () => ({ +vi.mock("~/models/season-participant", () => ({ getParticipantsForSeasonWithSports: vi.fn(), })); @@ -34,7 +34,7 @@ import { getTeamDraftPicksWithSports, isParticipantDrafted, } from "~/models/draft-pick"; -import { getParticipantsForSeasonWithSports } from "~/models/participant"; +import { getParticipantsForSeasonWithSports } from "~/models/season-participant"; import { getSeasonSportsSimple } from "~/models/season-sport"; import { getTeamQueue, getAllQueuesForSeason } from "~/models/draft-queue"; import { calculateDraftEligibility } from "~/lib/draft-eligibility"; @@ -49,7 +49,7 @@ const ALL_TEAM_IDS = [TEAM_ID, "team-2"]; function makeMockDb(overrides: Record = {}) { const mockDb: Record = { query: { - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([]), }, }, @@ -71,7 +71,7 @@ function makeMockDb(overrides: Record = {}) { // 3. select().from().where().orderBy() → top participant → [{id, ...}] function makeMockDbWithEvParticipant(participantId: string) { return { - query: { participants: { findMany: vi.fn().mockResolvedValue([]) } }, + query: { seasonParticipants: { findMany: vi.fn().mockResolvedValue([]) } }, select: vi.fn().mockReturnThis(), from: vi.fn().mockReturnThis(), innerJoin: vi.fn().mockReturnThis(), @@ -148,7 +148,7 @@ describe("autoPickForTeam – queueOnly constraint (AC2 & AC3)", () => { const mockDb = makeMockDb({ query: { - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([ { id: "p-1", @@ -188,7 +188,7 @@ describe("autoPickForTeam – queueOnly constraint (AC2 & AC3)", () => { const mockWhere = vi.fn().mockResolvedValue(undefined); const mockDb = makeMockDb({ query: { - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([ { id: "p-1", @@ -233,7 +233,7 @@ describe("autoPickForTeam – queueOnly constraint (AC2 & AC3)", () => { const mockWhere = vi.fn().mockResolvedValue(undefined); const mockDb = makeMockDb({ query: { - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([ { id: "p-snooker", @@ -280,7 +280,7 @@ describe("autoPickForTeam – queueOnly constraint (AC2 & AC3)", () => { // 4. select().from(participants).where().orderBy() — participant query (chain) const mockDb = { query: { - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([ { id: "p-snooker", @@ -330,7 +330,7 @@ describe("autoPickForTeam – queueOnly constraint (AC2 & AC3)", () => { const mockDb = makeMockDb({ query: { - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([ { id: "p-1", @@ -373,7 +373,7 @@ describe("autoPickForTeam – queueOnly constraint (AC2 & AC3)", () => { const mockDb = makeMockDb({ query: { - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([ { id: "p-1", diff --git a/app/models/__tests__/canonical-surface-elo.test.ts b/app/models/__tests__/canonical-surface-elo.test.ts new file mode 100644 index 0000000..db21c21 --- /dev/null +++ b/app/models/__tests__/canonical-surface-elo.test.ts @@ -0,0 +1,100 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("~/database/context", () => ({ + database: vi.fn(), +})); + +import { + upsertCanonicalSurfaceElo, + getCanonicalSurfaceElo, +} from "../canonical-surface-elo"; +import { database } from "~/database/context"; + +const PARTICIPANT_ID = "participant-1"; + +const SAMPLE_ELO = { + id: "elo-1", + participantId: PARTICIPANT_ID, + worldRanking: 1, + eloHard: 2400, + eloClay: 2350, + eloGrass: 2300, + updatedAt: new Date("2026-01-28T00:00:00Z"), +}; + +function makeUpsertDb(returnValue: object) { + return { + insert: vi.fn().mockReturnValue({ + values: vi.fn().mockReturnValue({ + onConflictDoUpdate: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([returnValue]), + }), + }), + }), + }; +} + +function makeSelectDb(rows: object[]) { + return { + select: vi.fn().mockReturnValue({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue(rows), + }), + }), + }), + }; +} + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("upsertCanonicalSurfaceElo", () => { + it("inserts or updates surface elo data", async () => { + vi.mocked(database).mockReturnValue(makeUpsertDb(SAMPLE_ELO) as never); + + const result = await upsertCanonicalSurfaceElo({ + participantId: PARTICIPANT_ID, + worldRanking: 1, + eloHard: 2400, + eloClay: 2350, + eloGrass: 2300, + }); + + expect(result).toEqual(SAMPLE_ELO); + }); + + it("uses onConflictDoUpdate on participantId", async () => { + const mockDb = makeUpsertDb(SAMPLE_ELO); + vi.mocked(database).mockReturnValue(mockDb as never); + + await upsertCanonicalSurfaceElo({ + participantId: PARTICIPANT_ID, + worldRanking: 1, + }); + + const onConflictCall = (mockDb.insert as ReturnType) + .mock.results[0].value.values.mock.results[0].value.onConflictDoUpdate; + + expect(onConflictCall).toHaveBeenCalled(); + }); +}); + +describe("getCanonicalSurfaceElo", () => { + it("returns elo data when found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_ELO]) as never); + + const result = await getCanonicalSurfaceElo(PARTICIPANT_ID); + + expect(result).toEqual(SAMPLE_ELO); + }); + + it("returns null when not found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([]) as never); + + const result = await getCanonicalSurfaceElo("nonexistent"); + + expect(result).toBeNull(); + }); +}); diff --git a/app/models/__tests__/draft-pick.test.ts b/app/models/__tests__/draft-pick.test.ts index d6475a3..38930d7 100644 --- a/app/models/__tests__/draft-pick.test.ts +++ b/app/models/__tests__/draft-pick.test.ts @@ -45,7 +45,7 @@ function makeDb(opts: MakeDbOpts = {}) { scoringEvents: { findMany: vi.fn().mockResolvedValue(scoringEvents), }, - participantQualifyingTotals: { + seasonParticipantQualifyingTotals: { findMany: vi.fn().mockResolvedValue(qualifyingTotals), }, }, @@ -220,6 +220,6 @@ describe("getDraftedParticipantsWithPoints", () => { await getDraftedParticipantsWithPoints("team-1", "season-1", db); - expect(db.query.participantQualifyingTotals.findMany).not.toHaveBeenCalled(); + expect(db.query.seasonParticipantQualifyingTotals.findMany).not.toHaveBeenCalled(); }); }); diff --git a/app/models/__tests__/executeAutoPick.timer.test.ts b/app/models/__tests__/executeAutoPick.timer.test.ts index d2867fb..593d8bf 100644 --- a/app/models/__tests__/executeAutoPick.timer.test.ts +++ b/app/models/__tests__/executeAutoPick.timer.test.ts @@ -21,7 +21,7 @@ vi.mock("~/models/draft-pick", () => ({ isParticipantDrafted: vi.fn(), })); -vi.mock("~/models/participant", () => ({ +vi.mock("~/models/season-participant", () => ({ getParticipantsForSeasonWithSports: vi.fn(), })); @@ -42,7 +42,7 @@ vi.mock("~/lib/draft-eligibility", () => ({ vi.mock("~/database/context"); import { getDraftPicksWithSports, getTeamDraftPicksWithSports, isParticipantDrafted } from "~/models/draft-pick"; -import { getParticipantsForSeasonWithSports } from "~/models/participant"; +import { getParticipantsForSeasonWithSports } from "~/models/season-participant"; import { getSeasonSportsSimple } from "~/models/season-sport"; import { getTeamQueue, getAllQueuesForSeason } from "~/models/draft-queue"; import { calculateDraftEligibility } from "~/lib/draft-eligibility"; @@ -127,7 +127,7 @@ function makeMockDb(seasonOverrides: Record = {}) { draftPicks: { findFirst: vi.fn().mockResolvedValue(null) }, seasons: { findFirst: vi.fn().mockResolvedValue(makeSeason(seasonOverrides)) }, draftSlots: { findMany: vi.fn().mockResolvedValue(mockDraftSlots) }, - participants: { + seasonParticipants: { // findMany: used by autoPickForTeam queue path findMany: vi.fn().mockResolvedValue([mockParticipantForQueue]), // findFirst: used by executeAutoPick to fetch full participant details diff --git a/app/models/__tests__/participant-expected-value.test.ts b/app/models/__tests__/participant-expected-value.test.ts index 6f605a0..46b5569 100644 --- a/app/models/__tests__/participant-expected-value.test.ts +++ b/app/models/__tests__/participant-expected-value.test.ts @@ -25,8 +25,8 @@ vi.mock("~/database/context", () => ({ })); vi.mock("~/database/schema", () => ({ - participants: { id: "id" }, - participantExpectedValues: { + seasonParticipants: { id: "id" }, + seasonParticipantExpectedValues: { participantId: "participantId", sportsSeasonId: "sportsSeasonId", }, diff --git a/app/models/__tests__/participant.test.ts b/app/models/__tests__/participant.test.ts new file mode 100644 index 0000000..3d0ee21 --- /dev/null +++ b/app/models/__tests__/participant.test.ts @@ -0,0 +1,152 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("~/database/context", () => ({ + database: vi.fn(), +})); + +import { + createCanonicalParticipant, + getCanonicalParticipantById, + findCanonicalParticipantsBySport, + findCanonicalParticipantBySportName, + upsertCanonicalParticipantBySportName, +} from "../participant"; +import { database } from "~/database/context"; + +const SPORT_ID = "sport-1"; +const PARTICIPANT_ID = "participant-1"; + +const SAMPLE_PARTICIPANT = { + id: PARTICIPANT_ID, + sportId: SPORT_ID, + name: "Novak Djokovic", + externalKey: "djokovic-n", + metadata: { atp_id: "12345" }, + createdAt: new Date("2026-01-01T00:00:00Z"), + updatedAt: new Date("2026-01-01T00:00:00Z"), +}; + +function makeInsertDb(returnValue: object) { + return { + insert: vi.fn().mockReturnValue({ + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([returnValue]), + }), + }), + }; +} + +function makeSelectDb(rows: object[]) { + return { + select: vi.fn().mockReturnValue({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue(rows), + orderBy: vi.fn().mockResolvedValue(rows), + }), + }), + }), + }; +} + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("createCanonicalParticipant", () => { + it("inserts a participant and returns it", async () => { + vi.mocked(database).mockReturnValue(makeInsertDb(SAMPLE_PARTICIPANT) as never); + + const result = await createCanonicalParticipant({ + sportId: SPORT_ID, + name: "Novak Djokovic", + }); + + expect(result).toEqual(SAMPLE_PARTICIPANT); + }); +}); + +describe("getCanonicalParticipantById", () => { + it("returns the participant when found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_PARTICIPANT]) as never); + + const result = await getCanonicalParticipantById(PARTICIPANT_ID); + + expect(result).toEqual(SAMPLE_PARTICIPANT); + }); + + it("returns null when not found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([]) as never); + + const result = await getCanonicalParticipantById("nonexistent"); + + expect(result).toBeNull(); + }); +}); + +describe("findCanonicalParticipantsBySport", () => { + it("returns participants ordered by name", async () => { + const participants = [SAMPLE_PARTICIPANT]; + vi.mocked(database).mockReturnValue(makeSelectDb(participants) as never); + + const result = await findCanonicalParticipantsBySport(SPORT_ID); + + expect(result).toEqual(participants); + }); +}); + +describe("findCanonicalParticipantBySportName", () => { + it("returns the participant when found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_PARTICIPANT]) as never); + + const result = await findCanonicalParticipantBySportName(SPORT_ID, "Novak Djokovic"); + + expect(result).toEqual(SAMPLE_PARTICIPANT); + }); + + it("returns null when not found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([]) as never); + + const result = await findCanonicalParticipantBySportName(SPORT_ID, "Roger Federer"); + + expect(result).toBeNull(); + }); +}); + +describe("upsertCanonicalParticipantBySportName", () => { + it("returns existing participant if found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_PARTICIPANT]) as never); + + const result = await upsertCanonicalParticipantBySportName(SPORT_ID, "Novak Djokovic"); + + expect(result).toEqual(SAMPLE_PARTICIPANT); + }); + + it("creates new participant if not found", async () => { + let callCount = 0; + vi.mocked(database).mockImplementation(() => { + callCount++; + return (callCount === 1 ? makeSelectDb([]) : makeInsertDb(SAMPLE_PARTICIPANT)) as never; + }); + + const result = await upsertCanonicalParticipantBySportName(SPORT_ID, "Novak Djokovic"); + + expect(result).toEqual(SAMPLE_PARTICIPANT); + }); + + it("passes extra fields when creating", async () => { + let callCount = 0; + vi.mocked(database).mockImplementation(() => { + callCount++; + return (callCount === 1 ? makeSelectDb([]) : makeInsertDb(SAMPLE_PARTICIPANT)) as never; + }); + + const result = await upsertCanonicalParticipantBySportName( + SPORT_ID, + "Novak Djokovic", + { externalKey: "djokovic-n" } + ); + + expect(result).toEqual(SAMPLE_PARTICIPANT); + }); +}); diff --git a/app/models/__tests__/process-match-result.test.ts b/app/models/__tests__/process-match-result.test.ts index 1112225..fdb5d6d 100644 --- a/app/models/__tests__/process-match-result.test.ts +++ b/app/models/__tests__/process-match-result.test.ts @@ -42,7 +42,7 @@ function makeDb(existingResult?: { id: string; isPartialScore: boolean }) { insert: vi.fn().mockReturnValue({ values: insertValues }), update: vi.fn().mockReturnValue({ set: updateSet }), query: { - participantResults: { findFirst }, + seasonParticipantResults: { findFirst }, seasonSports: { findMany: vi.fn().mockResolvedValue([]), // empty → standings loop exits immediately }, @@ -430,10 +430,10 @@ describe("processPlayoffEvent - NBA Play-In Round 1 loserAdvances fix", () => { playoffMatches: { findMany: vi.fn().mockResolvedValue(matches), }, - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue([]), }, - participantResults: { + seasonParticipantResults: { findFirst: vi.fn().mockResolvedValue(undefined), }, seasons: { diff --git a/app/models/__tests__/sports-season.clone.test.ts b/app/models/__tests__/sports-season.clone.test.ts index b209c18..c0acef8 100644 --- a/app/models/__tests__/sports-season.clone.test.ts +++ b/app/models/__tests__/sports-season.clone.test.ts @@ -6,9 +6,9 @@ vi.mock("~/database/context", () => ({ vi.mock("~/database/schema", () => ({ sportsSeasons: { id: "ss.id", sportId: "ss.sport_id" }, - participants: { sportsSeasonId: "p.sports_season_id" }, + seasonParticipants: { sportsSeasonId: "p.sports_season_id" }, scoringEvents: { sportsSeasonId: "se.sports_season_id" }, - participantExpectedValues: { sportsSeasonId: "pev.sports_season_id" }, + seasonParticipantExpectedValues: { sportsSeasonId: "pev.sports_season_id" }, })); vi.mock("drizzle-orm", () => ({ @@ -91,16 +91,16 @@ function makeMockDb({ const db: any = { query: { sportsSeasons: { findFirst: vi.fn().mockResolvedValue(ss) }, - participants: { findMany: vi.fn().mockResolvedValue(participants) }, + seasonParticipants: { findMany: vi.fn().mockResolvedValue(participants) }, scoringEvents: { findMany: vi.fn().mockResolvedValue(scoringEvents) }, - participantExpectedValues: { findMany: vi.fn().mockResolvedValue(sourceEvRows) }, + seasonParticipantExpectedValues: { findMany: vi.fn().mockResolvedValue(sourceEvRows) }, }, insert: vi.fn().mockImplementation((table: object) => { let key: string; let returnRows: object[]; if (table === schema.sportsSeasons) { key = "sportsSeasons"; returnRows = [insertedSeason]; - } else if (table === schema.participants) { + } else if (table === schema.seasonParticipants) { key = "participants"; returnRows = newParticipantRows; } else if (table === schema.scoringEvents) { key = "scoringEvents"; returnRows = []; diff --git a/app/models/__tests__/team-score-events.test.ts b/app/models/__tests__/team-score-events.test.ts index 9caaf99..7f543e6 100644 --- a/app/models/__tests__/team-score-events.test.ts +++ b/app/models/__tests__/team-score-events.test.ts @@ -64,7 +64,7 @@ function makeDb(opts: MakeDbOpts = {}) { teamScoreEvents: { findMany: vi.fn().mockResolvedValue(scoreEventRows), }, - participants: { + seasonParticipants: { findMany: vi.fn().mockResolvedValue(participantRows), }, }, @@ -268,7 +268,7 @@ describe("getRecentTeamScoreEvents", () => { it("does not query participants when rows array is empty", async () => { const { db } = makeDb({ scoreEventRows: [] }); await getRecentTeamScoreEvents("season-1", 10, db); - expect(db.query.participants.findMany).not.toHaveBeenCalled(); + expect(db.query.seasonParticipants.findMany).not.toHaveBeenCalled(); }); it("returns mapped entries with resolved participant names", async () => { diff --git a/app/models/__tests__/tournament-result.test.ts b/app/models/__tests__/tournament-result.test.ts new file mode 100644 index 0000000..0a6f440 --- /dev/null +++ b/app/models/__tests__/tournament-result.test.ts @@ -0,0 +1,114 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("~/database/context", () => ({ + database: vi.fn(), +})); + +import { + upsertTournamentResult, + getTournamentResults, + getTournamentResultByParticipant, +} from "../tournament-result"; +import { database } from "~/database/context"; + +const TOURNAMENT_ID = "tournament-1"; +const PARTICIPANT_ID = "participant-1"; + +const SAMPLE_RESULT = { + id: "result-1", + tournamentId: TOURNAMENT_ID, + participantId: PARTICIPANT_ID, + placement: 1, + rawScore: "7500.00", + createdAt: new Date("2026-01-28T00:00:00Z"), + updatedAt: new Date("2026-01-28T00:00:00Z"), +}; + +function makeUpsertDb(returnValue: object) { + return { + insert: vi.fn().mockReturnValue({ + values: vi.fn().mockReturnValue({ + onConflictDoUpdate: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([returnValue]), + }), + }), + }), + }; +} + +function makeSelectDb(rows: object[]) { + return { + select: vi.fn().mockReturnValue({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue(rows), + orderBy: vi.fn().mockResolvedValue(rows), + }), + }), + }), + }; +} + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("upsertTournamentResult", () => { + it("inserts or updates a result", async () => { + vi.mocked(database).mockReturnValue(makeUpsertDb(SAMPLE_RESULT) as never); + + const result = await upsertTournamentResult({ + tournamentId: TOURNAMENT_ID, + participantId: PARTICIPANT_ID, + placement: 1, + rawScore: "7500.00", + }); + + expect(result).toEqual(SAMPLE_RESULT); + }); + + it("uses onConflictDoUpdate on tournamentId and participantId", async () => { + const mockDb = makeUpsertDb(SAMPLE_RESULT); + vi.mocked(database).mockReturnValue(mockDb as never); + + await upsertTournamentResult({ + tournamentId: TOURNAMENT_ID, + participantId: PARTICIPANT_ID, + placement: 1, + }); + + const onConflictCall = (mockDb.insert as ReturnType) + .mock.results[0].value.values.mock.results[0].value.onConflictDoUpdate; + + expect(onConflictCall).toHaveBeenCalled(); + }); +}); + +describe("getTournamentResults", () => { + it("returns results ordered by placement", async () => { + const results = [SAMPLE_RESULT]; + vi.mocked(database).mockReturnValue(makeSelectDb(results) as never); + + const result = await getTournamentResults(TOURNAMENT_ID); + + expect(result).toEqual(results); + }); +}); + +describe("getTournamentResultByParticipant", () => { + it("returns the result when found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_RESULT]) as never); + + const result = await getTournamentResultByParticipant(TOURNAMENT_ID, PARTICIPANT_ID); + + expect(result).toEqual(SAMPLE_RESULT); + }); + + it("returns null when not found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([]) as never); + + const result = await getTournamentResultByParticipant(TOURNAMENT_ID, "nonexistent"); + + expect(result).toBeNull(); + }); +}); diff --git a/app/models/__tests__/tournament.test.ts b/app/models/__tests__/tournament.test.ts new file mode 100644 index 0000000..e7f0406 --- /dev/null +++ b/app/models/__tests__/tournament.test.ts @@ -0,0 +1,174 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("~/database/context", () => ({ + database: vi.fn(), +})); + +import { + createTournament, + getTournamentById, + findTournamentsBySport, + findTournamentBySportNameYear, + upsertTournament, + updateTournamentStatus, +} from "../tournament"; +import { database } from "~/database/context"; + +const SPORT_ID = "sport-1"; +const TOURNAMENT_ID = "tournament-1"; + +const SAMPLE_TOURNAMENT = { + id: TOURNAMENT_ID, + sportId: SPORT_ID, + name: "Australian Open", + year: 2026, + startsAt: new Date("2026-01-15T00:00:00Z"), + endsAt: new Date("2026-01-28T00:00:00Z"), + surface: "hard", + location: "Melbourne", + status: "scheduled" as const, + externalKey: "ao-2026", + createdAt: new Date("2026-01-01T00:00:00Z"), + updatedAt: new Date("2026-01-01T00:00:00Z"), +}; + +function makeInsertDb(returnValue: object) { + return { + insert: vi.fn().mockReturnValue({ + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([returnValue]), + }), + }), + }; +} + +function makeSelectDb(rows: object[]) { + return { + select: vi.fn().mockReturnValue({ + from: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + limit: vi.fn().mockResolvedValue(rows), + orderBy: vi.fn().mockResolvedValue(rows), + }), + }), + }), + }; +} + +function makeUpdateDb(returnValue: object) { + return { + update: vi.fn().mockReturnValue({ + set: vi.fn().mockReturnValue({ + where: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([returnValue]), + }), + }), + }), + }; +} + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe("createTournament", () => { + it("inserts a tournament and returns it", async () => { + vi.mocked(database).mockReturnValue(makeInsertDb(SAMPLE_TOURNAMENT) as never); + + const result = await createTournament({ + sportId: SPORT_ID, + name: "Australian Open", + year: 2026, + }); + + expect(result).toEqual(SAMPLE_TOURNAMENT); + }); +}); + +describe("getTournamentById", () => { + it("returns the tournament when found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_TOURNAMENT]) as never); + + const result = await getTournamentById(TOURNAMENT_ID); + + expect(result).toEqual(SAMPLE_TOURNAMENT); + }); + + it("returns null when not found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([]) as never); + + const result = await getTournamentById("nonexistent"); + + expect(result).toBeNull(); + }); +}); + +describe("findTournamentsBySport", () => { + it("returns tournaments ordered by year and date", async () => { + const tournaments = [SAMPLE_TOURNAMENT]; + vi.mocked(database).mockReturnValue(makeSelectDb(tournaments) as never); + + const result = await findTournamentsBySport(SPORT_ID); + + expect(result).toEqual(tournaments); + }); +}); + +describe("findTournamentBySportNameYear", () => { + it("returns the tournament when found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_TOURNAMENT]) as never); + + const result = await findTournamentBySportNameYear(SPORT_ID, "Australian Open", 2026); + + expect(result).toEqual(SAMPLE_TOURNAMENT); + }); + + it("returns null when not found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([]) as never); + + const result = await findTournamentBySportNameYear(SPORT_ID, "Wimbledon", 2026); + + expect(result).toBeNull(); + }); +}); + +describe("upsertTournament", () => { + it("returns existing tournament if found", async () => { + vi.mocked(database).mockReturnValue(makeSelectDb([SAMPLE_TOURNAMENT]) as never); + + const result = await upsertTournament({ + sportId: SPORT_ID, + name: "Australian Open", + year: 2026, + }); + + expect(result).toEqual(SAMPLE_TOURNAMENT); + }); + + it("creates new tournament if not found", async () => { + let callCount = 0; + vi.mocked(database).mockImplementation(() => { + callCount++; + return (callCount === 1 ? makeSelectDb([]) : makeInsertDb(SAMPLE_TOURNAMENT)) as never; + }); + + const result = await upsertTournament({ + sportId: SPORT_ID, + name: "Australian Open", + year: 2026, + }); + + expect(result).toEqual(SAMPLE_TOURNAMENT); + }); +}); + +describe("updateTournamentStatus", () => { + it("updates status and returns the tournament", async () => { + const updated = { ...SAMPLE_TOURNAMENT, status: "in_progress" as const }; + vi.mocked(database).mockReturnValue(makeUpdateDb(updated) as never); + + const result = await updateTournamentStatus(TOURNAMENT_ID, "in_progress"); + + expect(result.status).toBe("in_progress"); + }); +}); diff --git a/app/models/canonical-surface-elo.ts b/app/models/canonical-surface-elo.ts new file mode 100644 index 0000000..4267e3e --- /dev/null +++ b/app/models/canonical-surface-elo.ts @@ -0,0 +1,43 @@ +import { eq } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; + +export type CanonicalSurfaceElo = typeof schema.participantSurfaceElos.$inferSelect; +export type NewCanonicalSurfaceElo = typeof schema.participantSurfaceElos.$inferInsert; + +export async function upsertCanonicalSurfaceElo( + data: NewCanonicalSurfaceElo +): Promise { + if (!data.participantId) { + throw new Error("participantId is required"); + } + + const db = database(); + const [result] = await db + .insert(schema.participantSurfaceElos) + .values(data) + .onConflictDoUpdate({ + target: [schema.participantSurfaceElos.participantId], + set: { + worldRanking: data.worldRanking, + eloHard: data.eloHard, + eloClay: data.eloClay, + eloGrass: data.eloGrass, + updatedAt: new Date(), + }, + }) + .returning(); + return result; +} + +export async function getCanonicalSurfaceElo( + participantId: string +): Promise { + const db = database(); + const results = await db + .select() + .from(schema.participantSurfaceElos) + .where(eq(schema.participantSurfaceElos.participantId, participantId)) + .limit(1); + return results[0] ?? null; +} diff --git a/app/models/cs2-major-stage.ts b/app/models/cs2-major-stage.ts index 6a30bc5..bbaede8 100644 --- a/app/models/cs2-major-stage.ts +++ b/app/models/cs2-major-stage.ts @@ -13,7 +13,7 @@ */ import { database } from "~/database/context"; -import { cs2MajorStageResults, participants } from "~/database/schema"; +import { cs2MajorStageResults, seasonParticipants } from "~/database/schema"; import { eq, and, sql } from "drizzle-orm"; export interface Cs2StageResult { @@ -56,12 +56,12 @@ export async function getCs2StageResultsForEvent( finalPlacement: cs2MajorStageResults.finalPlacement, createdAt: cs2MajorStageResults.createdAt, updatedAt: cs2MajorStageResults.updatedAt, - participantName: participants.name, + participantName: seasonParticipants.name, }) .from(cs2MajorStageResults) - .innerJoin(participants, eq(cs2MajorStageResults.participantId, participants.id)) + .innerJoin(seasonParticipants, eq(cs2MajorStageResults.participantId, seasonParticipants.id)) .where(eq(cs2MajorStageResults.scoringEventId, scoringEventId)) - .orderBy(cs2MajorStageResults.stageEntry, participants.name); + .orderBy(cs2MajorStageResults.stageEntry, seasonParticipants.name); return rows; } @@ -135,7 +135,7 @@ export async function clearCs2StageAssignments( /** * Mark teams as eliminated from a specific stage. - * Records stageEliminated and stageEliminatedWins for the specified participants. + * Records stageEliminated and stageEliminatedWins for the specified seasonParticipants. * Teams NOT in eliminatedEntries that are in this stage are implicitly considered * to have advanced (stageEliminated remains null). */ @@ -166,7 +166,7 @@ export async function markCs2StageEliminations( } /** - * Set final placements for all participants in a CS2 Major event. + * Set final placements for all seasonParticipants in a CS2 Major event. * Called after the Champions Stage is complete. * placements: Map from participantId to final placement (1–32) */ diff --git a/app/models/draft-pick.ts b/app/models/draft-pick.ts index 1c7b17b..2ad46a3 100644 --- a/app/models/draft-pick.ts +++ b/app/models/draft-pick.ts @@ -78,14 +78,14 @@ export async function getDraftedParticipantsBySportsSeason( const results = await db .select({ - sportsSeasonId: schema.participants.sportsSeasonId, - participantId: schema.participants.id, - participantName: schema.participants.name, + sportsSeasonId: schema.seasonParticipants.sportsSeasonId, + participantId: schema.seasonParticipants.id, + participantName: schema.seasonParticipants.name, }) .from(schema.draftPicks) .innerJoin( - schema.participants, - eq(schema.draftPicks.participantId, schema.participants.id) + schema.seasonParticipants, + eq(schema.draftPicks.participantId, schema.seasonParticipants.id) ) .where( and( @@ -182,10 +182,10 @@ export async function getDraftedParticipantsWithPoints( // Batch-fetch QP totals for qualifying_points participants const qpMap = new Map(); // participantId → totalQP if (qpParticipantIds.size > 0 && qpSeasonIds.size > 0) { - const totals = await db.query.participantQualifyingTotals.findMany({ + const totals = await db.query.seasonParticipantQualifyingTotals.findMany({ where: and( - inArray(schema.participantQualifyingTotals.participantId, [...qpParticipantIds]), - inArray(schema.participantQualifyingTotals.sportsSeasonId, [...qpSeasonIds]) + inArray(schema.seasonParticipantQualifyingTotals.participantId, [...qpParticipantIds]), + inArray(schema.seasonParticipantQualifyingTotals.sportsSeasonId, [...qpSeasonIds]) ), columns: { participantId: true, totalQualifyingPoints: true }, }); @@ -245,19 +245,19 @@ export async function getDraftPicksWithSports(seasonId: string, providedDb?: Ret id: schema.draftPicks.id, teamId: schema.draftPicks.teamId, pickNumber: schema.draftPicks.pickNumber, - participantId: schema.participants.id, - participantName: schema.participants.name, + participantId: schema.seasonParticipants.id, + participantName: schema.seasonParticipants.name, sportId: schema.sports.id, sportName: schema.sports.name, }) .from(schema.draftPicks) .innerJoin( - schema.participants, - eq(schema.draftPicks.participantId, schema.participants.id) + schema.seasonParticipants, + eq(schema.draftPicks.participantId, schema.seasonParticipants.id) ) .innerJoin( schema.sportsSeasons, - eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) + eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id) ) .innerJoin( schema.sports, @@ -289,19 +289,19 @@ export async function getTeamDraftPicksWithSports(teamId: string, seasonId: stri id: schema.draftPicks.id, teamId: schema.draftPicks.teamId, pickNumber: schema.draftPicks.pickNumber, - participantId: schema.participants.id, - participantName: schema.participants.name, + participantId: schema.seasonParticipants.id, + participantName: schema.seasonParticipants.name, sportId: schema.sports.id, sportName: schema.sports.name, }) .from(schema.draftPicks) .innerJoin( - schema.participants, - eq(schema.draftPicks.participantId, schema.participants.id) + schema.seasonParticipants, + eq(schema.draftPicks.participantId, schema.seasonParticipants.id) ) .innerJoin( schema.sportsSeasons, - eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) + eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id) ) .innerJoin( schema.sports, @@ -338,13 +338,13 @@ export async function getDraftPicksForSeason(seasonId: string) { pickInRound: schema.draftPicks.pickInRound, timeUsed: schema.draftPicks.timeUsed, team: schema.teams, - participant: schema.participants, + participant: schema.seasonParticipants, sport: schema.sports, }) .from(schema.draftPicks) .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id)) - .innerJoin(schema.participants, eq(schema.draftPicks.participantId, schema.participants.id)) - .innerJoin(schema.sportsSeasons, eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id)) + .innerJoin(schema.seasonParticipants, eq(schema.draftPicks.participantId, schema.seasonParticipants.id)) + .innerJoin(schema.sportsSeasons, eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id)) .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) .where(eq(schema.draftPicks.seasonId, seasonId)) .orderBy(asc(schema.draftPicks.pickNumber)); diff --git a/app/models/draft-utils.ts b/app/models/draft-utils.ts index cfa80c9..47a844e 100644 --- a/app/models/draft-utils.ts +++ b/app/models/draft-utils.ts @@ -5,7 +5,7 @@ import { logger } from "~/lib/logger"; import type { InferSelectModel } from "drizzle-orm"; import { getTeamQueue, getAllQueuesForSeason } from "./draft-queue"; import { isParticipantDrafted, getDraftPicksWithSports, getTeamDraftPicksWithSports } from "./draft-pick"; -import { getParticipantsForSeasonWithSports } from "./participant"; +import { getParticipantsForSeasonWithSports } from "./season-participant"; import { getSeasonSportsSimple } from "./season-sport"; import { calculateDraftEligibility } from "~/lib/draft-eligibility"; import { getSocketIO, scheduleDraftRoomClosure } from "../../server/socket"; @@ -118,8 +118,8 @@ export async function autoPickForTeam( // Get participant details for queue items to check eligibility const queueParticipantIds = queue.map((item) => item.participantId); - const queueParticipants = await db.query.participants.findMany({ - where: inArray(schema.participants.id, queueParticipantIds), + const queueParticipants = await db.query.seasonParticipants.findMany({ + where: inArray(schema.seasonParticipants.id, queueParticipantIds), with: { sportsSeason: { with: { @@ -264,17 +264,17 @@ export async function getTopAvailableParticipant( for (const sportsSeasonId of sportsSeasonIds) { let participantQuery = db .select() - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); if (draftedIds.length > 0) { participantQuery = db .select() - .from(schema.participants) + .from(schema.seasonParticipants) .where( and( - eq(schema.participants.sportsSeasonId, sportsSeasonId), - notInArray(schema.participants.id, draftedIds) + eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), + notInArray(schema.seasonParticipants.id, draftedIds) ) ); } @@ -299,21 +299,21 @@ export async function getTopAvailableParticipant( // Single sport season let query = db .select() - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonIds[0])) - .orderBy(desc(schema.participants.vorpValue), schema.participants.name); + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonIds[0])) + .orderBy(desc(schema.seasonParticipants.vorpValue), schema.seasonParticipants.name); if (draftedIds.length > 0) { query = db .select() - .from(schema.participants) + .from(schema.seasonParticipants) .where( and( - eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]), - notInArray(schema.participants.id, draftedIds) + eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonIds[0]), + notInArray(schema.seasonParticipants.id, draftedIds) ) ) - .orderBy(desc(schema.participants.vorpValue), schema.participants.name); + .orderBy(desc(schema.seasonParticipants.vorpValue), schema.seasonParticipants.name); } const [topParticipant] = await query; @@ -471,7 +471,7 @@ export async function executeAutoPick(params: { success: boolean; error?: string; pick?: InferSelectModel; - participant?: InferSelectModel & { + participant?: InferSelectModel & { sportsSeason: InferSelectModel & { sport: InferSelectModel; }; @@ -581,8 +581,8 @@ export async function executeAutoPick(params: { } // Get participant details - const participantToPick = await db.query.participants.findFirst({ - where: eq(schema.participants.id, participantId), + const participantToPick = await db.query.seasonParticipants.findFirst({ + where: eq(schema.seasonParticipants.id, participantId), with: { sportsSeason: { with: { diff --git a/app/models/event-result.ts b/app/models/event-result.ts index 7d453ef..8021329 100644 --- a/app/models/event-result.ts +++ b/app/models/event-result.ts @@ -31,7 +31,7 @@ export async function createEventResult( .insert(schema.eventResults) .values({ scoringEventId: data.scoringEventId, - participantId: data.participantId, + seasonParticipantId: data.participantId, placement: data.placement, qualifyingPointsAwarded: data.qualifyingPointsAwarded?.toString(), eliminated: data.eliminated, @@ -57,7 +57,7 @@ export async function createEventResultsBulk( .values( results.map((r) => ({ scoringEventId: r.scoringEventId, - participantId: r.participantId, + seasonParticipantId: r.participantId, placement: r.placement, qualifyingPointsAwarded: r.qualifyingPointsAwarded?.toString(), eliminated: r.eliminated, @@ -81,7 +81,7 @@ export async function getEventResultById( return await db.query.eventResults.findFirst({ where: eq(schema.eventResults.id, resultId), with: { - participant: { + seasonParticipant: { with: { sportsSeason: { with: { @@ -108,7 +108,7 @@ export async function getEventResults( where: eq(schema.eventResults.scoringEventId, eventId), orderBy: schema.eventResults.placement, with: { - participant: { + seasonParticipant: { with: { sportsSeason: { with: { @@ -131,7 +131,7 @@ export async function getParticipantEventResults( const db = providedDb || database(); return await db.query.eventResults.findMany({ - where: eq(schema.eventResults.participantId, participantId), + where: eq(schema.eventResults.seasonParticipantId, participantId), with: { scoringEvent: true, }, @@ -203,7 +203,7 @@ export async function hasParticipantResult( const result = await db.query.eventResults.findFirst({ where: and( eq(schema.eventResults.scoringEventId, eventId), - eq(schema.eventResults.participantId, participantId) + eq(schema.eventResults.seasonParticipantId, participantId) ), }); @@ -226,10 +226,10 @@ export async function getEventResultsForParticipants( return await db.query.eventResults.findMany({ where: and( eq(schema.eventResults.scoringEventId, eventId), - inArray(schema.eventResults.participantId, participantIds) + inArray(schema.eventResults.seasonParticipantId, participantIds) ), with: { - participant: { + seasonParticipant: { with: { sportsSeason: { with: { diff --git a/app/models/golf-skills.ts b/app/models/golf-skills.ts index 7f08591..6b9bb0b 100644 --- a/app/models/golf-skills.ts +++ b/app/models/golf-skills.ts @@ -7,7 +7,7 @@ */ import { database } from "~/database/context"; -import { participantGolfSkills, participants } from "~/database/schema"; +import { participantGolfSkills, seasonParticipants } from "~/database/schema"; import { eq, sql } from "drizzle-orm"; export interface GolfSkillsRecord { @@ -40,7 +40,7 @@ export interface GolfSkillsInput { /** * Get all golf skill records for a sports season, joined with participant names. - * Returns one record per participant (participants with no record are excluded). + * Returns one record per participant (seasonParticipants with no record are excluded). */ export async function getGolfSkillsForSeason( sportsSeasonId: string @@ -58,12 +58,12 @@ export async function getGolfSkillsForSeason( openChampionshipOdds: participantGolfSkills.openChampionshipOdds, pgaChampionshipOdds: participantGolfSkills.pgaChampionshipOdds, updatedAt: participantGolfSkills.updatedAt, - participantName: participants.name, + participantName: seasonParticipants.name, }) .from(participantGolfSkills) - .innerJoin(participants, eq(participantGolfSkills.participantId, participants.id)) + .innerJoin(seasonParticipants, eq(participantGolfSkills.participantId, seasonParticipants.id)) .where(eq(participantGolfSkills.sportsSeasonId, sportsSeasonId)) - .orderBy(participants.name); + .orderBy(seasonParticipants.name); return rows.map((r) => ({ ...r, @@ -94,7 +94,7 @@ export async function getGolfSkillsMap( } /** - * Upsert golf skill ratings for a batch of participants. + * Upsert golf skill ratings for a batch of seasonParticipants. * Uses INSERT … ON CONFLICT DO UPDATE so all columns are overwritten atomically. */ export async function batchUpsertGolfSkills( diff --git a/app/models/group-stage-match.ts b/app/models/group-stage-match.ts index 8392547..62e7e16 100644 --- a/app/models/group-stage-match.ts +++ b/app/models/group-stage-match.ts @@ -334,8 +334,8 @@ export async function getUpcomingGroupStageMatchesForParticipants( 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 participantRows = await db.query.seasonParticipants.findMany({ + where: inArray(schema.seasonParticipants.id, allParticipantIds), }); const participantMap = new Map(participantRows.map((p) => [p.id, p])); diff --git a/app/models/index.ts b/app/models/index.ts index e32e39b..045f646 100644 --- a/app/models/index.ts +++ b/app/models/index.ts @@ -6,7 +6,7 @@ export * from "./team"; export * from "./commissioner"; export * from "./sport"; export * from "./sports-season"; -export * from "./participant"; +export * from "./season-participant"; export * from "./season-template"; export * from "./season-template-sport"; export * from "./season-sport"; @@ -18,3 +18,7 @@ export * from "./draft-timer"; export * from "./draft-utils"; export * from "./watchlist"; export * from "./audit-log"; +export * from "./tournament"; +export * from "./participant"; +export * from "./tournament-result"; +export * from "./canonical-surface-elo"; diff --git a/app/models/participant-expected-value.ts b/app/models/participant-expected-value.ts index c33f39f..bd6e670 100644 --- a/app/models/participant-expected-value.ts +++ b/app/models/participant-expected-value.ts @@ -1,12 +1,12 @@ /** * Model for Participant Expected Values * - * Manages probability distributions and calculated EVs for participants + * Manages probability distributions and calculated EVs for seasonParticipants * in sports seasons. */ import { database } from "~/database/context"; -import { participantExpectedValues, participants } from "~/database/schema"; +import { seasonParticipantExpectedValues, seasonParticipants } from "~/database/schema"; import { eq, and, count, sql } from "drizzle-orm"; import type { ProbabilityDistribution, ScoringRules } from "~/services/ev-calculator"; import { calculateEV, normalizeProbabilities, calculateReplacementLevel, calculateVORP } from "~/services/ev-calculator"; @@ -53,9 +53,9 @@ export interface UpdateProbabilityInput { * Recalculate and persist VORP for every participant in a sports season. * * VORP = participant EV − replacement level EV - * Replacement level = average EV of participants ranked 12th–14th in this season. + * Replacement level = average EV of seasonParticipants ranked 12th–14th in this season. * - * Call this after any operation that changes EVs for participants in the season. + * Call this after any operation that changes EVs for seasonParticipants in the season. */ export async function syncVorpForSeason(sportsSeasonId: string): Promise { const db = database(); @@ -72,12 +72,12 @@ export async function syncVorpForSeason(sportsSeasonId: string): Promise { const now = new Date(); - // Build a single CASE expression to update all participants in one query + // Build a single CASE expression to update all seasonParticipants in one query // instead of N individual UPDATE statements. const vorpCaseExpr = sql`CASE ${sql.join( sorted.map((ev) => { const vorp = calculateVORP(parseFloat(ev.expectedValue), replacementLevel); - return sql`WHEN ${participants.id} = ${ev.participantId} THEN ${vorp.toFixed(4)}::numeric`; + return sql`WHEN ${seasonParticipants.id} = ${ev.participantId} THEN ${vorp.toFixed(4)}::numeric`; }), sql` ` )} END`; @@ -85,9 +85,9 @@ export async function syncVorpForSeason(sportsSeasonId: string): Promise { const ids = sorted.map((ev) => ev.participantId); await db - .update(participants) + .update(seasonParticipants) .set({ vorpValue: vorpCaseExpr, updatedAt: now }) - .where(sql`${participants.id} = ANY(${sql`ARRAY[${sql.join(ids.map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`); + .where(sql`${seasonParticipants.id} = ANY(${sql`ARRAY[${sql.join(ids.map((id) => sql`${id}`), sql`, `)}]::uuid[]`})`); } /** @@ -118,11 +118,11 @@ export async function upsertParticipantEV( // Check if record exists const existing = await db .select() - .from(participantExpectedValues) + .from(seasonParticipantExpectedValues) .where( and( - eq(participantExpectedValues.participantId, participantId), - eq(participantExpectedValues.sportsSeasonId, sportsSeasonId) + eq(seasonParticipantExpectedValues.participantId, participantId), + eq(seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId) ) ) .limit(1); @@ -134,7 +134,7 @@ export async function upsertParticipantEV( if (existing.length > 0) { // Update existing const updated = await db - .update(participantExpectedValues) + .update(seasonParticipantExpectedValues) .set({ probFirst: probabilities.probFirst.toString(), probSecond: probabilities.probSecond.toString(), @@ -150,14 +150,14 @@ export async function upsertParticipantEV( calculatedAt: now, updatedAt: now, }) - .where(eq(participantExpectedValues.id, existing[0].id)) + .where(eq(seasonParticipantExpectedValues.id, existing[0].id)) .returning(); result = updated[0]; } else { // Create new const created = await db - .insert(participantExpectedValues) + .insert(seasonParticipantExpectedValues) .values({ participantId, sportsSeasonId, @@ -180,13 +180,13 @@ export async function upsertParticipantEV( result = created[0]; } - // Sync calculated EV to participants table for draft room ranking + // Sync calculated EV to seasonParticipants table for draft room ranking await db - .update(participants) + .update(seasonParticipants) .set({ expectedValue: expectedValue.toString(), updatedAt: now }) - .where(eq(participants.id, participantId)); + .where(eq(seasonParticipants.id, participantId)); - // Recalculate VORP for all participants in this season (replacement level may have shifted) + // Recalculate VORP for all seasonParticipants in this season (replacement level may have shifted) await syncVorpForSeason(sportsSeasonId); return result; @@ -209,7 +209,7 @@ export async function upsertParticipantEVWithNormalization( export async function countAllParticipantEVs(): Promise { const db = database(); - const result = await db.select({ value: count() }).from(participantExpectedValues); + const result = await db.select({ value: count() }).from(seasonParticipantExpectedValues); return result[0].value; } @@ -223,11 +223,11 @@ export async function getParticipantEV( const db = database(); const result = await db .select() - .from(participantExpectedValues) + .from(seasonParticipantExpectedValues) .where( and( - eq(participantExpectedValues.participantId, participantId), - eq(participantExpectedValues.sportsSeasonId, sportsSeasonId) + eq(seasonParticipantExpectedValues.participantId, participantId), + eq(seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId) ) ) .limit(1); @@ -244,8 +244,8 @@ export async function getAllParticipantEVsForSeason( const db = database(); return db .select() - .from(participantExpectedValues) - .where(eq(participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(seasonParticipantExpectedValues) + .where(eq(seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); } /** @@ -257,21 +257,21 @@ export async function deleteParticipantEV( ): Promise { const db = database(); await db - .delete(participantExpectedValues) + .delete(seasonParticipantExpectedValues) .where( and( - eq(participantExpectedValues.participantId, participantId), - eq(participantExpectedValues.sportsSeasonId, sportsSeasonId) + eq(seasonParticipantExpectedValues.participantId, participantId), + eq(seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId) ) ); // Reset this participant's EV and VORP to 0 (no longer ranked) await db - .update(participants) + .update(seasonParticipants) .set({ expectedValue: "0", vorpValue: "0", updatedAt: new Date() }) - .where(eq(participants.id, participantId)); + .where(eq(seasonParticipants.id, participantId)); - // Recalculate VORP for remaining participants — replacement level may have shifted + // Recalculate VORP for remaining seasonParticipants — replacement level may have shifted await syncVorpForSeason(sportsSeasonId); } @@ -301,12 +301,12 @@ export async function batchUpsertParticipantEVs( const expectedValue = calculateEV(probabilities, scoringRules); const existing = await tx - .select({ id: participantExpectedValues.id }) - .from(participantExpectedValues) + .select({ id: seasonParticipantExpectedValues.id }) + .from(seasonParticipantExpectedValues) .where( and( - eq(participantExpectedValues.participantId, participantId), - eq(participantExpectedValues.sportsSeasonId, sportsSeasonId) + eq(seasonParticipantExpectedValues.participantId, participantId), + eq(seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId) ) ) .limit(1); @@ -330,24 +330,24 @@ export async function batchUpsertParticipantEVs( if (existing.length > 0) { const [updated] = await tx - .update(participantExpectedValues) + .update(seasonParticipantExpectedValues) // Only overwrite sourceOdds if explicitly provided; preserve existing value otherwise .set(sourceOdds !== undefined ? { ...baseValues, sourceOdds } : baseValues) - .where(eq(participantExpectedValues.id, existing[0].id)) + .where(eq(seasonParticipantExpectedValues.id, existing[0].id)) .returning(); result = updated; } else { const [created] = await tx - .insert(participantExpectedValues) + .insert(seasonParticipantExpectedValues) .values({ participantId, sportsSeasonId, ...baseValues, sourceOdds: sourceOdds ?? null }) .returning(); result = created; } await tx - .update(participants) + .update(seasonParticipants) .set({ expectedValue: expectedValue.toString(), updatedAt: now }) - .where(eq(participants.id, participantId)); + .where(eq(seasonParticipants.id, participantId)); results.push(result); } @@ -361,7 +361,7 @@ export async function batchUpsertParticipantEVs( } /** - * Save American odds for a batch of participants without touching probabilities or EV. + * Save American odds for a batch of seasonParticipants without touching probabilities or EV. * Used by the futures-odds admin page to persist odds before running the full simulation. */ export async function batchSaveSourceOdds( @@ -374,24 +374,24 @@ export async function batchSaveSourceOdds( for (const { participantId, sportsSeasonId, sourceOdds } of inputs) { const existing = await tx - .select({ id: participantExpectedValues.id }) - .from(participantExpectedValues) + .select({ id: seasonParticipantExpectedValues.id }) + .from(seasonParticipantExpectedValues) .where( and( - eq(participantExpectedValues.participantId, participantId), - eq(participantExpectedValues.sportsSeasonId, sportsSeasonId) + eq(seasonParticipantExpectedValues.participantId, participantId), + eq(seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId) ) ) .limit(1); if (existing.length > 0) { await tx - .update(participantExpectedValues) + .update(seasonParticipantExpectedValues) .set({ sourceOdds, updatedAt: now }) - .where(eq(participantExpectedValues.id, existing[0].id)); + .where(eq(seasonParticipantExpectedValues.id, existing[0].id)); } else { // Insert a stub record — probabilities/EV will be filled in by the simulator - await tx.insert(participantExpectedValues).values({ + await tx.insert(seasonParticipantExpectedValues).values({ participantId, sportsSeasonId, probFirst: "0", @@ -414,7 +414,7 @@ export async function batchSaveSourceOdds( } /** - * Persist raw Elo ratings (and optional world rankings) for a batch of participants. + * Persist raw Elo ratings (and optional world rankings) for a batch of seasonParticipants. * Used by Elo-based simulators like snooker_bracket and darts_bracket. * Creates stub records if none exist; does not touch probabilities or EV. * Uses INSERT ... ON CONFLICT DO UPDATE to avoid N+1 queries. @@ -427,7 +427,7 @@ export async function batchSaveSourceElos( const now = new Date(); await db - .insert(participantExpectedValues) + .insert(seasonParticipantExpectedValues) .values( inputs.map(({ participantId, sportsSeasonId, sourceElo, worldRanking }) => ({ participantId, @@ -449,7 +449,7 @@ export async function batchSaveSourceElos( })) ) .onConflictDoUpdate({ - target: [participantExpectedValues.participantId, participantExpectedValues.sportsSeasonId], + target: [seasonParticipantExpectedValues.participantId, seasonParticipantExpectedValues.sportsSeasonId], set: { sourceElo: sql`excluded.source_elo`, worldRanking: sql`excluded.world_ranking`, @@ -499,26 +499,26 @@ export async function recalculateEV( const db = database(); const now = new Date(); const updated = await db - .update(participantExpectedValues) + .update(seasonParticipantExpectedValues) .set({ expectedValue: newEV.toString(), calculatedAt: now, updatedAt: now, }) - .where(eq(participantExpectedValues.id, existing.id)) + .where(eq(seasonParticipantExpectedValues.id, existing.id)) .returning(); - // Sync recalculated EV to participants table + // Sync recalculated EV to seasonParticipants table await db - .update(participants) + .update(seasonParticipants) .set({ expectedValue: newEV.toString(), updatedAt: now }) - .where(eq(participants.id, participantId)); + .where(eq(seasonParticipants.id, participantId)); return updated[0]; } /** - * Recalculate EVs for all participants in a sports season + * Recalculate EVs for all seasonParticipants in a sports season * Used when scoring rules change */ export async function recalculateAllEVsForSeason( diff --git a/app/models/participant-result.ts b/app/models/participant-result.ts index 72691fe..8c76e04 100644 --- a/app/models/participant-result.ts +++ b/app/models/participant-result.ts @@ -2,18 +2,18 @@ import { eq, and } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; -export type ParticipantResult = typeof schema.participantResults.$inferSelect; +export type ParticipantResult = typeof schema.seasonParticipantResults.$inferSelect; export type ParticipantResultWithParticipant = ParticipantResult & { participant: { id: string; name: string } | null; }; -export type NewParticipantResult = typeof schema.participantResults.$inferInsert; +export type NewParticipantResult = typeof schema.seasonParticipantResults.$inferInsert; export async function createParticipantResult( data: NewParticipantResult ): Promise { const db = database(); const [result] = await db - .insert(schema.participantResults) + .insert(schema.seasonParticipantResults) .values(data) .returning(); return result; @@ -24,7 +24,7 @@ export async function createManyParticipantResults( ): Promise { const db = database(); return await db - .insert(schema.participantResults) + .insert(schema.seasonParticipantResults) .values(data) .returning(); } @@ -33,8 +33,8 @@ export async function findParticipantResultById( id: string ): Promise { const db = database(); - return await db.query.participantResults.findFirst({ - where: eq(schema.participantResults.id, id), + return await db.query.seasonParticipantResults.findFirst({ + where: eq(schema.seasonParticipantResults.id, id), with: { participant: true, sportsSeason: { @@ -50,8 +50,8 @@ export async function findParticipantResultByParticipantId( participantId: string ): Promise { const db = database(); - return await db.query.participantResults.findFirst({ - where: eq(schema.participantResults.participantId, participantId), + return await db.query.seasonParticipantResults.findFirst({ + where: eq(schema.seasonParticipantResults.participantId, participantId), with: { participant: true, sportsSeason: { @@ -67,8 +67,8 @@ export async function findParticipantResultsBySportsSeasonId( sportsSeasonId: string ): Promise { const db = database(); - return await db.query.participantResults.findMany({ - where: eq(schema.participantResults.sportsSeasonId, sportsSeasonId), + return await db.query.seasonParticipantResults.findMany({ + where: eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId), orderBy: (results, { asc }) => [asc(results.finalPosition)], with: { participant: true, @@ -82,16 +82,16 @@ export async function updateParticipantResult( ): Promise { const db = database(); const [result] = await db - .update(schema.participantResults) + .update(schema.seasonParticipantResults) .set({ ...data, updatedAt: new Date() }) - .where(eq(schema.participantResults.id, id)) + .where(eq(schema.seasonParticipantResults.id, id)) .returning(); return result; } export async function deleteParticipantResult(id: string): Promise { const db = database(); - await db.delete(schema.participantResults).where(eq(schema.participantResults.id, id)); + await db.delete(schema.seasonParticipantResults).where(eq(schema.seasonParticipantResults.id, id)); } export async function deleteParticipantResultsBySportsSeasonId( @@ -99,8 +99,8 @@ export async function deleteParticipantResultsBySportsSeasonId( ): Promise { const db = database(); await db - .delete(schema.participantResults) - .where(eq(schema.participantResults.sportsSeasonId, sportsSeasonId)); + .delete(schema.seasonParticipantResults) + .where(eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId)); } /** @@ -117,10 +117,10 @@ export async function setParticipantResult( const db = database(); // Check if result already exists - const existing = await db.query.participantResults.findFirst({ + const existing = await db.query.seasonParticipantResults.findFirst({ where: and( - eq(schema.participantResults.participantId, participantId), - eq(schema.participantResults.sportsSeasonId, sportsSeasonId) + eq(schema.seasonParticipantResults.participantId, participantId), + eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId) ), }); diff --git a/app/models/participant.ts b/app/models/participant.ts index 686db3e..e9ba816 100644 --- a/app/models/participant.ts +++ b/app/models/participant.ts @@ -1,11 +1,13 @@ -import { eq, inArray, count, and, sql, asc, desc } from "drizzle-orm"; +import { eq, and, asc } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; -export type Participant = typeof schema.participants.$inferSelect; -export type NewParticipant = typeof schema.participants.$inferInsert; +export type CanonicalParticipant = typeof schema.participants.$inferSelect; +export type NewCanonicalParticipant = typeof schema.participants.$inferInsert; -export async function createParticipant(data: NewParticipant): Promise { +export async function createCanonicalParticipant( + data: NewCanonicalParticipant +): Promise { const db = database(); const [participant] = await db .insert(schema.participants) @@ -14,187 +16,61 @@ export async function createParticipant(data: NewParticipant): Promise { +export async function getCanonicalParticipantById( + id: string +): Promise { + const db = database(); + const results = await db + .select() + .from(schema.participants) + .where(eq(schema.participants.id, id)) + .limit(1); + return results[0] ?? null; +} + +export async function findCanonicalParticipantsBySport( + sportId: string +): Promise { const db = database(); return await db - .insert(schema.participants) - .values(data) - .returning(); + .select() + .from(schema.participants) + .where(eq(schema.participants.sportId, sportId)) + .orderBy(asc(schema.participants.name)); } -export async function findParticipantById(id: string): Promise { - const db = database(); - return await db.query.participants.findFirst({ - where: eq(schema.participants.id, id), - with: { - sportsSeason: { - with: { - sport: true, - }, - }, - }, - }); -} - -export async function findParticipantByName( - sportsSeasonId: string, +export async function findCanonicalParticipantBySportName( + sportId: string, name: string -): Promise { +): Promise { const db = database(); const results = await db .select() .from(schema.participants) .where( and( - eq(schema.participants.sportsSeasonId, sportsSeasonId), - sql`lower(${schema.participants.name}) = lower(${name})` + eq(schema.participants.sportId, sportId), + eq(schema.participants.name, name) ) ) .limit(1); - return results[0]; + return results[0] ?? null; } -export async function findParticipantsBySportsSeasonId(sportsSeasonId: string): Promise { - const db = database(); - return await db.query.participants.findMany({ - where: eq(schema.participants.sportsSeasonId, sportsSeasonId), - orderBy: (participants, { asc: orderAsc }) => [orderAsc(participants.name)], - }); -} +export async function upsertCanonicalParticipantBySportName( + sportId: string, + name: string, + extra?: Partial +): Promise { + const existing = await findCanonicalParticipantBySportName(sportId, name); -export async function findParticipantsByExternalId( - externalId: string -): Promise { - const db = database(); - return await db.query.participants.findMany({ - where: eq(schema.participants.externalId, externalId), - with: { - sportsSeason: { - with: { - sport: true, - }, - }, - }, - }); -} - -export async function updateParticipant( - id: string, - data: Partial -): Promise { - const db = database(); - const [participant] = await db - .update(schema.participants) - .set({ ...data, updatedAt: new Date() }) - .where(eq(schema.participants.id, id)) - .returning(); - return participant; -} - -export async function countAllParticipants(): Promise { - const db = database(); - const result = await db.select({ value: count() }).from(schema.participants); - return result[0].value; -} - -export async function deleteParticipant(id: string): Promise { - const db = database(); - await db.delete(schema.participants).where(eq(schema.participants.id, id)); -} - -export async function copyParticipantsFromSeason( - sourceSportsSeasonId: string, - targetSportsSeasonId: string -): Promise { - // Get all participants from source season - const sourceParticipants = await findParticipantsBySportsSeasonId(sourceSportsSeasonId); - - // Insert them into the target season - if (sourceParticipants.length === 0) { - return []; + if (existing) { + return existing; } - return await createManyParticipants( - sourceParticipants.map((p) => ({ - sportsSeasonId: targetSportsSeasonId, - name: p.name, - shortName: p.shortName, - externalId: p.externalId, - expectedValue: p.expectedValue, - })) - ); -} - -/** - * Get all participants for a season with sport information - * Used for draft eligibility calculations - */ -export async function getParticipantsForSeasonWithSports(seasonId: string, providedDb?: ReturnType) { - const db = providedDb || database(); - - // First get all sports seasons for this season - const seasonSports = await db.query.seasonSports.findMany({ - where: eq(schema.seasonSports.seasonId, seasonId), + return await createCanonicalParticipant({ + sportId, + name, + ...extra, }); - - if (seasonSports.length === 0) { - return []; - } - - const sportsSeasonIds = seasonSports.map((ss) => ss.sportsSeasonId); - - // Get all participants for these sports seasons with sport info - const participants = await db - .select({ - id: schema.participants.id, - name: schema.participants.name, - sport: { - id: schema.sports.id, - name: schema.sports.name, - }, - }) - .from(schema.participants) - .innerJoin( - schema.sportsSeasons, - eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) - ) - .innerJoin( - schema.sports, - eq(schema.sportsSeasons.sportId, schema.sports.id) - ) - .where( - sportsSeasonIds.length === 1 - ? eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]) - : inArray(schema.participants.sportsSeasonId, sportsSeasonIds) - ); - - return participants; -} - -export async function getDraftParticipants(seasonId: string) { - const db = database(); - - const seasonSports = await db.query.seasonSports.findMany({ - where: eq(schema.seasonSports.seasonId, seasonId), - }); - - const sportsSeasonIds = seasonSports.map((ss) => ss.sportsSeasonId); - if (sportsSeasonIds.length === 0) return []; - - return await db - .select({ - id: schema.participants.id, - name: schema.participants.name, - vorpValue: schema.participants.vorpValue, - sport: schema.sports, - }) - .from(schema.participants) - .innerJoin(schema.sportsSeasons, eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id)) - .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) - .where( - sportsSeasonIds.length === 1 - ? eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]) - : inArray(schema.participants.sportsSeasonId, sportsSeasonIds) - ) - .orderBy(desc(schema.participants.vorpValue), asc(schema.participants.name)); } diff --git a/app/models/qualifying-points.ts b/app/models/qualifying-points.ts index 8935781..f2f4dbc 100644 --- a/app/models/qualifying-points.ts +++ b/app/models/qualifying-points.ts @@ -144,16 +144,16 @@ export async function getOrCreateParticipantQPTotal( ) { const db = providedDb || database(); - let total = await db.query.participantQualifyingTotals.findFirst({ + let total = await db.query.seasonParticipantQualifyingTotals.findFirst({ where: and( - eq(schema.participantQualifyingTotals.participantId, participantId), - eq(schema.participantQualifyingTotals.sportsSeasonId, sportsSeasonId) + eq(schema.seasonParticipantQualifyingTotals.participantId, participantId), + eq(schema.seasonParticipantQualifyingTotals.sportsSeasonId, sportsSeasonId) ), }); if (!total) { const [created] = await db - .insert(schema.participantQualifyingTotals) + .insert(schema.seasonParticipantQualifyingTotals) .values({ participantId, sportsSeasonId, @@ -184,12 +184,12 @@ export async function addQualifyingPoints( // Update with new points (do NOT increment eventsScored here) const [updated] = await db - .update(schema.participantQualifyingTotals) + .update(schema.seasonParticipantQualifyingTotals) .set({ totalQualifyingPoints: (parseFloat(total.totalQualifyingPoints) + pointsToAdd).toString(), updatedAt: new Date(), }) - .where(eq(schema.participantQualifyingTotals.id, total.id)) + .where(eq(schema.seasonParticipantQualifyingTotals.id, total.id)) .returning(); return updated; @@ -208,7 +208,7 @@ export async function recalculateParticipantQP( // Get all event results for this participant in this sports season const eventResults = await db.query.eventResults.findMany({ - where: eq(schema.eventResults.participantId, participantId), + where: eq(schema.eventResults.seasonParticipantId, participantId), with: { scoringEvent: true, }, @@ -238,13 +238,13 @@ export async function recalculateParticipantQP( // Update with recalculated values await db - .update(schema.participantQualifyingTotals) + .update(schema.seasonParticipantQualifyingTotals) .set({ totalQualifyingPoints: totalQP.toString(), eventsScored, updatedAt: new Date(), }) - .where(eq(schema.participantQualifyingTotals.id, total.id)); + .where(eq(schema.seasonParticipantQualifyingTotals.id, total.id)); return { totalQP, eventsScored }; } @@ -271,9 +271,9 @@ export async function getQPStandings( ) { const db = providedDb || database(); - return await db.query.participantQualifyingTotals.findMany({ - where: eq(schema.participantQualifyingTotals.sportsSeasonId, sportsSeasonId), - orderBy: [desc(sql`CAST(${schema.participantQualifyingTotals.totalQualifyingPoints} AS DECIMAL)`)], + return await db.query.seasonParticipantQualifyingTotals.findMany({ + where: eq(schema.seasonParticipantQualifyingTotals.sportsSeasonId, sportsSeasonId), + orderBy: [desc(sql`CAST(${schema.seasonParticipantQualifyingTotals.totalQualifyingPoints} AS DECIMAL)`)], with: { participant: true, }, @@ -325,9 +325,9 @@ export async function updateFinalRankings( // Update all rankings for (const update of updates) { await db - .update(schema.participantQualifyingTotals) + .update(schema.seasonParticipantQualifyingTotals) .set({ finalRanking: update.finalRanking, updatedAt: new Date() }) - .where(eq(schema.participantQualifyingTotals.id, update.id)); + .where(eq(schema.seasonParticipantQualifyingTotals.id, update.id)); } return updates; @@ -343,6 +343,6 @@ export async function resetQualifyingPoints( const db = providedDb || database(); await db - .delete(schema.participantQualifyingTotals) - .where(eq(schema.participantQualifyingTotals.sportsSeasonId, sportsSeasonId)); + .delete(schema.seasonParticipantQualifyingTotals) + .where(eq(schema.seasonParticipantQualifyingTotals.sportsSeasonId, sportsSeasonId)); } diff --git a/app/models/scoring-calculator.ts b/app/models/scoring-calculator.ts index 21825bb..249fc69 100644 --- a/app/models/scoring-calculator.ts +++ b/app/models/scoring-calculator.ts @@ -201,8 +201,8 @@ export async function processPlayoffEvent( // PHASE 5.3: Handle teams that didn't make the playoffs // Get all participants in the sports season - const allParticipants = await db.query.participants.findMany({ - where: eq(schema.participants.sportsSeasonId, event.sportsSeasonId), + const allParticipants = await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, event.sportsSeasonId), }); // Get all participant IDs that are in ANY playoff match (winners or losers) @@ -222,10 +222,10 @@ export async function processPlayoffEvent( for (const participant of allParticipants) { if (!participantsInBracket.has(participant.id)) { // Check if they already have a result (don't overwrite) - const existingResult = await db.query.participantResults.findFirst({ + const existingResult = await db.query.seasonParticipantResults.findFirst({ where: and( - eq(schema.participantResults.participantId, participant.id), - eq(schema.participantResults.sportsSeasonId, event.sportsSeasonId) + eq(schema.seasonParticipantResults.participantId, participant.id), + eq(schema.seasonParticipantResults.sportsSeasonId, event.sportsSeasonId) ), }); @@ -506,10 +506,10 @@ async function upsertParticipantResult( db: ReturnType, isPartialScore = false ): Promise { - const existing = await db.query.participantResults.findFirst({ + const existing = await db.query.seasonParticipantResults.findFirst({ where: and( - eq(schema.participantResults.participantId, participantId), - eq(schema.participantResults.sportsSeasonId, sportsSeasonId) + eq(schema.seasonParticipantResults.participantId, participantId), + eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId) ), }); @@ -520,16 +520,16 @@ async function upsertParticipantResult( if (!existing.isPartialScore && isPartialScore) return null; await db - .update(schema.participantResults) + .update(schema.seasonParticipantResults) .set({ finalPosition, isPartialScore, updatedAt: new Date(), }) - .where(eq(schema.participantResults.id, existing.id)); + .where(eq(schema.seasonParticipantResults.id, existing.id)); return existing.finalPosition ?? 0; } else { - await db.insert(schema.participantResults).values({ + await db.insert(schema.seasonParticipantResults).values({ participantId, sportsSeasonId, finalPosition, @@ -651,7 +651,7 @@ export async function processQualifyingEvent( // Recalculate totals for all participants from scratch // This is the source of truth - sums all their event_results - const participantIds = new Set(results.map(r => r.participantId)); + const participantIds = new Set(results.map(r => r.seasonParticipantId)); for (const participantId of participantIds) { await recalculateParticipantQP(participantId, event.sportsSeasonId, db); } @@ -752,10 +752,10 @@ export async function finalizeQualifyingPoints( if (currentPlacement <= standings.length) { for (let i = currentPlacement - 1; i < standings.length; i++) { const standing = standings[i]; - const hasResult = await db.query.participantResults.findFirst({ + const hasResult = await db.query.seasonParticipantResults.findFirst({ where: and( - eq(schema.participantResults.participantId, standing.participantId), - eq(schema.participantResults.sportsSeasonId, sportsSeasonId) + eq(schema.seasonParticipantResults.participantId, standing.participantId), + eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId) ), }); @@ -1388,11 +1388,11 @@ export async function recalculateAffectedLeagues( .filter((id): id is string => id !== null); const finalizedLoserIds = new Set(); if (loserParticipantIds.length > 0) { - const loserResults = await db.query.participantResults.findMany({ + const loserResults = await db.query.seasonParticipantResults.findMany({ where: and( - inArray(schema.participantResults.participantId, loserParticipantIds), - eq(schema.participantResults.sportsSeasonId, sportsSeasonId), - eq(schema.participantResults.isPartialScore, false) + inArray(schema.seasonParticipantResults.participantId, loserParticipantIds), + eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId), + eq(schema.seasonParticipantResults.isPartialScore, false) ), }); for (const r of loserResults) finalizedLoserIds.add(r.participantId); diff --git a/app/models/scoring-event.ts b/app/models/scoring-event.ts index a11076e..e5dfb15 100644 --- a/app/models/scoring-event.ts +++ b/app/models/scoring-event.ts @@ -104,7 +104,7 @@ export async function getScoringEventsForSportsSeason( with: { eventResults: { with: { - participant: true, + seasonParticipant: true, }, }, }, @@ -224,7 +224,7 @@ export async function deleteScoringEvent( const results = await db.query.eventResults.findMany({ where: eq(schema.eventResults.scoringEventId, eventId), }); - affectedParticipantIds = results.map((r) => r.participantId); + affectedParticipantIds = results.map((r) => r.seasonParticipantId); wasQPProcessed = results.some( (r) => r.qualifyingPointsAwarded && parseFloat(r.qualifyingPointsAwarded) > 0 ); @@ -233,8 +233,8 @@ export async function deleteScoringEvent( await db.transaction(async (tx) => { if (event.eventType === "playoff_game") { await tx - .delete(schema.participantResults) - .where(eq(schema.participantResults.sportsSeasonId, event.sportsSeasonId)); + .delete(schema.seasonParticipantResults) + .where(eq(schema.seasonParticipantResults.sportsSeasonId, event.sportsSeasonId)); } // Cascades: playoffMatches, eventResults, tournamentGroups/Members await tx.delete(schema.scoringEvents).where(eq(schema.scoringEvents.id, eventId)); diff --git a/app/models/season-participant.ts b/app/models/season-participant.ts new file mode 100644 index 0000000..5d4850c --- /dev/null +++ b/app/models/season-participant.ts @@ -0,0 +1,200 @@ +import { eq, inArray, count, and, sql, asc, desc } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; + +export type Participant = typeof schema.seasonParticipants.$inferSelect; +export type NewParticipant = typeof schema.seasonParticipants.$inferInsert; + +export async function createParticipant(data: NewParticipant): Promise { + const db = database(); + const [participant] = await db + .insert(schema.seasonParticipants) + .values(data) + .returning(); + return participant; +} + +export async function createManyParticipants(data: NewParticipant[]): Promise { + const db = database(); + return await db + .insert(schema.seasonParticipants) + .values(data) + .returning(); +} + +export async function findParticipantById(id: string): Promise { + const db = database(); + return await db.query.seasonParticipants.findFirst({ + where: eq(schema.seasonParticipants.id, id), + with: { + sportsSeason: { + with: { + sport: true, + }, + }, + }, + }); +} + +export async function findParticipantByName( + sportsSeasonId: string, + name: string +): Promise { + const db = database(); + const results = await db + .select() + .from(schema.seasonParticipants) + .where( + and( + eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), + sql`lower(${schema.seasonParticipants.name}) = lower(${name})` + ) + ) + .limit(1); + return results[0]; +} + +export async function findParticipantsBySportsSeasonId(sportsSeasonId: string): Promise { + const db = database(); + return await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), + orderBy: (participants, { asc: orderAsc }) => [orderAsc(participants.name)], + }); +} + +export async function findParticipantsByExternalId( + externalId: string +): Promise { + const db = database(); + return await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.externalId, externalId), + with: { + sportsSeason: { + with: { + sport: true, + }, + }, + }, + }); +} + +export async function updateParticipant( + id: string, + data: Partial +): Promise { + const db = database(); + const [participant] = await db + .update(schema.seasonParticipants) + .set({ ...data, updatedAt: new Date() }) + .where(eq(schema.seasonParticipants.id, id)) + .returning(); + return participant; +} + +export async function countAllParticipants(): Promise { + const db = database(); + const result = await db.select({ value: count() }).from(schema.seasonParticipants); + return result[0].value; +} + +export async function deleteParticipant(id: string): Promise { + const db = database(); + await db.delete(schema.seasonParticipants).where(eq(schema.seasonParticipants.id, id)); +} + +export async function copyParticipantsFromSeason( + sourceSportsSeasonId: string, + targetSportsSeasonId: string +): Promise { + // Get all participants from source season + const sourceParticipants = await findParticipantsBySportsSeasonId(sourceSportsSeasonId); + + // Insert them into the target season + if (sourceParticipants.length === 0) { + return []; + } + + return await createManyParticipants( + sourceParticipants.map((p) => ({ + sportsSeasonId: targetSportsSeasonId, + name: p.name, + shortName: p.shortName, + externalId: p.externalId, + expectedValue: p.expectedValue, + })) + ); +} + +/** + * Get all participants for a season with sport information + * Used for draft eligibility calculations + */ +export async function getParticipantsForSeasonWithSports(seasonId: string, providedDb?: ReturnType) { + const db = providedDb || database(); + + // First get all sports seasons for this season + const seasonSports = await db.query.seasonSports.findMany({ + where: eq(schema.seasonSports.seasonId, seasonId), + }); + + if (seasonSports.length === 0) { + return []; + } + + const sportsSeasonIds = seasonSports.map((ss) => ss.sportsSeasonId); + + // Get all participants for these sports seasons with sport info + const participants = await db + .select({ + id: schema.seasonParticipants.id, + name: schema.seasonParticipants.name, + sport: { + id: schema.sports.id, + name: schema.sports.name, + }, + }) + .from(schema.seasonParticipants) + .innerJoin( + schema.sportsSeasons, + eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id) + ) + .innerJoin( + schema.sports, + eq(schema.sportsSeasons.sportId, schema.sports.id) + ) + .where( + sportsSeasonIds.length === 1 + ? eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonIds[0]) + : inArray(schema.seasonParticipants.sportsSeasonId, sportsSeasonIds) + ); + + return participants; +} + +export async function getDraftParticipants(seasonId: string) { + const db = database(); + + const seasonSports = await db.query.seasonSports.findMany({ + where: eq(schema.seasonSports.seasonId, seasonId), + }); + + const sportsSeasonIds = seasonSports.map((ss) => ss.sportsSeasonId); + if (sportsSeasonIds.length === 0) return []; + + return await db + .select({ + id: schema.seasonParticipants.id, + name: schema.seasonParticipants.name, + vorpValue: schema.seasonParticipants.vorpValue, + sport: schema.sports, + }) + .from(schema.seasonParticipants) + .innerJoin(schema.sportsSeasons, eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id)) + .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) + .where( + sportsSeasonIds.length === 1 + ? eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonIds[0]) + : inArray(schema.seasonParticipants.sportsSeasonId, sportsSeasonIds) + ) + .orderBy(desc(schema.seasonParticipants.vorpValue), asc(schema.seasonParticipants.name)); +} diff --git a/app/models/sports-season.ts b/app/models/sports-season.ts index ccb6470..4957b9d 100644 --- a/app/models/sports-season.ts +++ b/app/models/sports-season.ts @@ -211,8 +211,8 @@ export async function cloneSportsSeason( }; // Read all source data before writing anything - const sourceParticipants = await db.query.participants.findMany({ - where: eq(schema.participants.sportsSeasonId, sourceId), + const sourceParticipants = await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, sourceId), }); const sourceEvents = await db.query.scoringEvents.findMany({ @@ -225,8 +225,8 @@ export async function cloneSportsSeason( : null; // Read source futures odds and Elo ratings - const sourceEvRows = await db.query.participantExpectedValues.findMany({ - where: eq(schema.participantExpectedValues.sportsSeasonId, sourceId), + const sourceEvRows = await db.query.seasonParticipantExpectedValues.findMany({ + where: eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sourceId), }); // Build lookup: (externalId ?? name) → source EV, only for rows that have @@ -251,7 +251,7 @@ export async function cloneSportsSeason( const newParticipants = sourceParticipants.length > 0 ? await tx - .insert(schema.participants) + .insert(schema.seasonParticipants) .values( sourceParticipants.map((p: typeof sourceParticipants[number]) => ({ sportsSeasonId: newSeason.id, @@ -288,7 +288,7 @@ export async function cloneSportsSeason( .filter((r): r is NonNullable => r !== null); if (evRows.length > 0) { - await tx.insert(schema.participantExpectedValues).values(evRows); + await tx.insert(schema.seasonParticipantExpectedValues).values(evRows); } } diff --git a/app/models/surface-elo.ts b/app/models/surface-elo.ts index eed451c..edb4a27 100644 --- a/app/models/surface-elo.ts +++ b/app/models/surface-elo.ts @@ -7,7 +7,7 @@ */ import { database } from "~/database/context"; -import { participantSurfaceElos, participants } from "~/database/schema"; +import { seasonParticipantSurfaceElos, seasonParticipants } from "~/database/schema"; import { eq, sql } from "drizzle-orm"; export type CourtSurface = "hard" | "clay" | "grass"; @@ -38,7 +38,7 @@ export interface SurfaceEloInput { /** * Get all surface Elo records for a sports season, joined with participant names. - * Returns one record per participant (participants with no Elo record are excluded). + * Returns one record per participant (seasonParticipants with no Elo record are excluded). */ export async function getSurfaceElosForSeason( sportsSeasonId: string @@ -46,26 +46,26 @@ export async function getSurfaceElosForSeason( const db = database(); const rows = await db .select({ - id: participantSurfaceElos.id, - participantId: participantSurfaceElos.participantId, - sportsSeasonId: participantSurfaceElos.sportsSeasonId, - worldRanking: participantSurfaceElos.worldRanking, - eloHard: participantSurfaceElos.eloHard, - eloClay: participantSurfaceElos.eloClay, - eloGrass: participantSurfaceElos.eloGrass, - updatedAt: participantSurfaceElos.updatedAt, - participantName: participants.name, + id: seasonParticipantSurfaceElos.id, + participantId: seasonParticipantSurfaceElos.participantId, + sportsSeasonId: seasonParticipantSurfaceElos.sportsSeasonId, + worldRanking: seasonParticipantSurfaceElos.worldRanking, + eloHard: seasonParticipantSurfaceElos.eloHard, + eloClay: seasonParticipantSurfaceElos.eloClay, + eloGrass: seasonParticipantSurfaceElos.eloGrass, + updatedAt: seasonParticipantSurfaceElos.updatedAt, + participantName: seasonParticipants.name, }) - .from(participantSurfaceElos) - .innerJoin(participants, eq(participantSurfaceElos.participantId, participants.id)) - .where(eq(participantSurfaceElos.sportsSeasonId, sportsSeasonId)) - .orderBy(participants.name); + .from(seasonParticipantSurfaceElos) + .innerJoin(seasonParticipants, eq(seasonParticipantSurfaceElos.participantId, seasonParticipants.id)) + .where(eq(seasonParticipantSurfaceElos.sportsSeasonId, sportsSeasonId)) + .orderBy(seasonParticipants.name); return rows; } /** - * Upsert surface Elo ratings for a batch of participants. + * Upsert surface Elo ratings for a batch of seasonParticipants. * Uses INSERT ... ON CONFLICT DO UPDATE so all three surface columns are * overwritten atomically — the admin always submits all three values. */ @@ -77,7 +77,7 @@ export async function batchUpsertSurfaceElos( const now = new Date(); await db - .insert(participantSurfaceElos) + .insert(seasonParticipantSurfaceElos) .values( inputs.map(({ participantId, sportsSeasonId, worldRanking, eloHard, eloClay, eloGrass }) => ({ participantId, @@ -90,7 +90,7 @@ export async function batchUpsertSurfaceElos( })) ) .onConflictDoUpdate({ - target: [participantSurfaceElos.participantId, participantSurfaceElos.sportsSeasonId], + target: [seasonParticipantSurfaceElos.participantId, seasonParticipantSurfaceElos.sportsSeasonId], set: { worldRanking: sql`excluded.world_ranking`, eloHard: sql`excluded.elo_hard`, @@ -111,14 +111,14 @@ export async function getSurfaceEloMap( const db = database(); const rows = await db .select({ - participantId: participantSurfaceElos.participantId, - worldRanking: participantSurfaceElos.worldRanking, - eloHard: participantSurfaceElos.eloHard, - eloClay: participantSurfaceElos.eloClay, - eloGrass: participantSurfaceElos.eloGrass, + participantId: seasonParticipantSurfaceElos.participantId, + worldRanking: seasonParticipantSurfaceElos.worldRanking, + eloHard: seasonParticipantSurfaceElos.eloHard, + eloClay: seasonParticipantSurfaceElos.eloClay, + eloGrass: seasonParticipantSurfaceElos.eloGrass, }) - .from(participantSurfaceElos) - .where(eq(participantSurfaceElos.sportsSeasonId, sportsSeasonId)); + .from(seasonParticipantSurfaceElos) + .where(eq(seasonParticipantSurfaceElos.sportsSeasonId, sportsSeasonId)); return new Map(rows.map((r) => [r.participantId, { worldRanking: r.worldRanking, diff --git a/app/models/team-score-events.ts b/app/models/team-score-events.ts index ad76b2a..ab28493 100644 --- a/app/models/team-score-events.ts +++ b/app/models/team-score-events.ts @@ -230,8 +230,8 @@ export async function getRecentTeamScoreEvents( const participantNameById = new Map(); if (allParticipantIds.length > 0) { - const participantRows = await db.query.participants.findMany({ - where: inArray(schema.participants.id, allParticipantIds), + const participantRows = await db.query.seasonParticipants.findMany({ + where: inArray(schema.seasonParticipants.id, allParticipantIds), columns: { id: true, name: true }, }); for (const p of participantRows) { diff --git a/app/models/tournament-result.ts b/app/models/tournament-result.ts new file mode 100644 index 0000000..103a1ce --- /dev/null +++ b/app/models/tournament-result.ts @@ -0,0 +1,61 @@ +import { eq, and, asc } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; + +export type TournamentResult = typeof schema.tournamentResults.$inferSelect; +export type NewTournamentResult = typeof schema.tournamentResults.$inferInsert; + +export async function upsertTournamentResult( + data: NewTournamentResult +): Promise { + if (!data.tournamentId || !data.participantId) { + throw new Error("tournamentId and participantId are required"); + } + + const db = database(); + const [result] = await db + .insert(schema.tournamentResults) + .values(data) + .onConflictDoUpdate({ + target: [ + schema.tournamentResults.tournamentId, + schema.tournamentResults.participantId, + ], + set: { + placement: data.placement, + rawScore: data.rawScore, + updatedAt: new Date(), + }, + }) + .returning(); + return result; +} + +export async function getTournamentResults( + tournamentId: string +): Promise { + const db = database(); + return await db + .select() + .from(schema.tournamentResults) + .where(eq(schema.tournamentResults.tournamentId, tournamentId)) + .orderBy(asc(schema.tournamentResults.placement)); +} + +export async function getTournamentResultByParticipant( + tournamentId: string, + participantId: string +): Promise { + const db = database(); + const results = await db + .select() + .from(schema.tournamentResults) + .where( + and( + eq(schema.tournamentResults.tournamentId, tournamentId), + eq(schema.tournamentResults.participantId, participantId) + ) + ) + .limit(1); + return results[0] ?? null; +} diff --git a/app/models/tournament.ts b/app/models/tournament.ts new file mode 100644 index 0000000..e26659c --- /dev/null +++ b/app/models/tournament.ts @@ -0,0 +1,94 @@ +import { eq, and, asc } from "drizzle-orm"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; + +export type Tournament = typeof schema.tournaments.$inferSelect; +export type NewTournament = typeof schema.tournaments.$inferInsert; +export type TournamentStatus = Tournament["status"]; + +export async function createTournament( + data: NewTournament +): Promise { + const db = database(); + const [tournament] = await db + .insert(schema.tournaments) + .values(data) + .returning(); + return tournament; +} + +export async function getTournamentById( + id: string +): Promise { + const db = database(); + const results = await db + .select() + .from(schema.tournaments) + .where(eq(schema.tournaments.id, id)) + .limit(1); + return results[0] ?? null; +} + +export async function findTournamentsBySport( + sportId: string +): Promise { + const db = database(); + return await db + .select() + .from(schema.tournaments) + .where(eq(schema.tournaments.sportId, sportId)) + .orderBy(asc(schema.tournaments.year), asc(schema.tournaments.startsAt)); +} + +export async function findTournamentBySportNameYear( + sportId: string, + name: string, + year: number +): Promise { + const db = database(); + const results = await db + .select() + .from(schema.tournaments) + .where( + and( + eq(schema.tournaments.sportId, sportId), + eq(schema.tournaments.name, name), + eq(schema.tournaments.year, year) + ) + ) + .limit(1); + return results[0] ?? null; +} + +export async function upsertTournament( + data: NewTournament +): Promise { + if (!data.sportId || !data.name || !data.year) { + throw new Error("sportId, name, and year are required for upsert"); + } + + const existing = await findTournamentBySportNameYear( + data.sportId, + data.name, + data.year + ); + + if (existing) { + return existing; + } + + return await createTournament(data); +} + +export async function updateTournamentStatus( + id: string, + status: TournamentStatus +): Promise { + const db = database(); + const [tournament] = await db + .update(schema.tournaments) + .set({ status, updatedAt: new Date() }) + .where(eq(schema.tournaments.id, id)) + .returning(); + return tournament; +} diff --git a/app/routes/admin.data-sync.tsx b/app/routes/admin.data-sync.tsx index fff5737..1d3bed9 100644 --- a/app/routes/admin.data-sync.tsx +++ b/app/routes/admin.data-sync.tsx @@ -6,7 +6,7 @@ import { logger } from "~/lib/logger"; import { findAllSports } from "~/models/sport"; import { findAllSportsSeasons } from "~/models/sports-season"; import { findAllSeasonTemplates } from "~/models/season-template"; -import { countAllParticipants } from "~/models/participant"; +import { countAllParticipants } from "~/models/season-participant"; import { countAllParticipantEVs } from "~/models/participant-expected-value"; import { importSportsDataFromJSON } from "~/utils/sports-data-sync.server"; import { Button } from "~/components/ui/button"; diff --git a/app/routes/admin.sports-seasons.$id.elo-ratings.tsx b/app/routes/admin.sports-seasons.$id.elo-ratings.tsx index 385e911..8536efe 100644 --- a/app/routes/admin.sports-seasons.$id.elo-ratings.tsx +++ b/app/routes/admin.sports-seasons.$id.elo-ratings.tsx @@ -3,7 +3,7 @@ import type { Route } from './+types/admin.sports-seasons.$id.elo-ratings'; import { logger } from '~/lib/logger'; import { findSportsSeasonById, updateSportsSeason } from '~/models/sports-season'; -import { findParticipantsBySportsSeasonId } from '~/models/participant'; +import { findParticipantsBySportsSeasonId } from '~/models/season-participant'; import { getAllParticipantEVsForSeason, batchSaveSourceElos, 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 65f7cd1..f401485 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 @@ -1,7 +1,7 @@ import type { Route } from "./+types/admin.sports-seasons.$id.events.$eventId.bracket"; import { findSportsSeasonById } from "~/models/sports-season"; -import { findParticipantsBySportsSeasonId } from "~/models/participant"; +import { findParticipantsBySportsSeasonId } from "~/models/season-participant"; import { getScoringEventById, updateScoringEvent } from "~/models/scoring-event"; import { findPlayoffMatchesByEventId, @@ -595,9 +595,9 @@ export async function action({ request, params }: Route.ActionArgs) { // the "never un-finalize" guard in upsertParticipantResult. const db = database(); await db - .delete(schema.participantResults) + .delete(schema.seasonParticipantResults) .where( - eq(schema.participantResults.sportsSeasonId, event.sportsSeasonId) + eq(schema.seasonParticipantResults.sportsSeasonId, event.sportsSeasonId) ); // Replay each completed match in bracket order (earlier rounds first). diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.cs2-setup.tsx b/app/routes/admin.sports-seasons.$id.events.$eventId.cs2-setup.tsx index 4051206..9dec8fd 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.cs2-setup.tsx +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.cs2-setup.tsx @@ -3,7 +3,7 @@ import type { Route } from './+types/admin.sports-seasons.$id.events.$eventId.cs import { findSportsSeasonById } from '~/models/sports-season'; import { getScoringEventById } from '~/models/scoring-event'; -import { findParticipantsBySportsSeasonId } from '~/models/participant'; +import { findParticipantsBySportsSeasonId } from '~/models/season-participant'; import { getCs2StageResultsForEvent, upsertCs2StageAssignments, diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts b/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts index e1b3515..c1a997b 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.server.ts @@ -1,7 +1,7 @@ import type { Route } from "./+types/admin.sports-seasons.$id.events.$eventId"; import { logger } from "~/lib/logger"; import { findSportsSeasonById } from "~/models/sports-season"; -import { findParticipantsBySportsSeasonId, createParticipant } from "~/models/participant"; +import { findParticipantsBySportsSeasonId, createParticipant } from "~/models/season-participant"; import { getScoringEventById, completeScoringEvent, @@ -134,7 +134,7 @@ export async function action({ request, params }: Route.ActionArgs) { // This marks them as "competed, earned nothing" so simulations skip this event. const allParticipants = await findParticipantsBySportsSeasonId(params.id); const existingResults = await getEventResults(params.eventId); - const existingIds = new Set(existingResults.map((r) => r.participantId)); + const existingIds = new Set(existingResults.map((r) => r.seasonParticipantId)); const missingIds = findParticipantsWithoutResults( allParticipants.map((p) => p.id), existingIds @@ -373,7 +373,7 @@ export async function action({ request, params }: Route.ActionArgs) { try { const existingResults = await getEventResults(params.eventId); - const existingIds = new Set(existingResults.map((r) => r.participantId)); + const existingIds = new Set(existingResults.map((r) => r.seasonParticipantId)); const newResults = filterNewResults(incoming, existingIds); if (newResults.length > 0) { diff --git a/app/routes/admin.sports-seasons.$id.events.$eventId.tsx b/app/routes/admin.sports-seasons.$id.events.$eventId.tsx index b963899..7eba5ac 100644 --- a/app/routes/admin.sports-seasons.$id.events.$eventId.tsx +++ b/app/routes/admin.sports-seasons.$id.events.$eventId.tsx @@ -99,7 +99,7 @@ export default function EventResults({ // Create a map of participants with results for easy lookup const participantResultsMap = new Map( - results.map((r: { participant: { id: string } }) => [r.participant.id, r]) + results.map((r: { seasonParticipant: { id: string } }) => [r.seasonParticipant.id, r]) ); // Get participants without results @@ -419,7 +419,7 @@ export default function EventResults({ participants={participants} sportsSeasonId={sportsSeason.id} existingResultParticipantIds={ - new Set(results.map((r: { participant: { id: string } }) => r.participant.id)) + new Set(results.map((r: { seasonParticipant: { id: string } }) => r.seasonParticipant.id)) } /> )} @@ -576,7 +576,7 @@ export default function EventResults({ - {result.participant.name} + {result.seasonParticipant.name} {event.isQualifyingEvent && event.isComplete && ( {result.qualifyingPointsAwarded ? ( @@ -620,7 +620,7 @@ export default function EventResults({ variant="ghost" className="h-8 w-8 p-0 text-destructive hover:text-destructive" onClick={(e) => { - if (!confirm(`Delete result for ${result.participant.name}?`)) { + if (!confirm(`Delete result for ${result.seasonParticipant.name}?`)) { e.preventDefault(); } }} diff --git a/app/routes/admin.sports-seasons.$id.expected-values.server.ts b/app/routes/admin.sports-seasons.$id.expected-values.server.ts index 9dcbe4b..70104e0 100644 --- a/app/routes/admin.sports-seasons.$id.expected-values.server.ts +++ b/app/routes/admin.sports-seasons.$id.expected-values.server.ts @@ -1,7 +1,7 @@ import type { Route } from "./+types/admin.sports-seasons.$id.expected-values"; import { logger } from "~/lib/logger"; import { findSportsSeasonById } from "~/models/sports-season"; -import { findParticipantsBySportsSeasonId } from "~/models/participant"; +import { findParticipantsBySportsSeasonId } from "~/models/season-participant"; import { batchUpsertParticipantEVs, getAllParticipantEVsForSeason diff --git a/app/routes/admin.sports-seasons.$id.futures-odds.tsx b/app/routes/admin.sports-seasons.$id.futures-odds.tsx index dd02a22..e0bce3d 100644 --- a/app/routes/admin.sports-seasons.$id.futures-odds.tsx +++ b/app/routes/admin.sports-seasons.$id.futures-odds.tsx @@ -3,7 +3,7 @@ import type { Route } from './+types/admin.sports-seasons.$id.futures-odds'; import { logger } from '~/lib/logger'; import { findSportsSeasonById, updateSportsSeason } from '~/models/sports-season'; -import { findParticipantsBySportsSeasonId } from '~/models/participant'; +import { findParticipantsBySportsSeasonId } from '~/models/season-participant'; import { getAllParticipantEVsForSeason, batchSaveSourceOdds, batchUpsertParticipantEVs } from '~/models/participant-expected-value'; import { batchUpsertParticipantEvSnapshots } from '~/models/ev-snapshot'; import { getSimulator, type SimulatorType } from '~/services/simulations/registry'; diff --git a/app/routes/admin.sports-seasons.$id.golf-skills.tsx b/app/routes/admin.sports-seasons.$id.golf-skills.tsx index 5a0acea..efb5f8d 100644 --- a/app/routes/admin.sports-seasons.$id.golf-skills.tsx +++ b/app/routes/admin.sports-seasons.$id.golf-skills.tsx @@ -3,7 +3,7 @@ import type { Route } from './+types/admin.sports-seasons.$id.golf-skills'; import { logger } from '~/lib/logger'; import { findSportsSeasonById, updateSportsSeason } from '~/models/sports-season'; -import { findParticipantsBySportsSeasonId, findParticipantByName, createParticipant } from '~/models/participant'; +import { findParticipantsBySportsSeasonId, findParticipantByName, createParticipant } from '~/models/season-participant'; import { batchUpsertParticipantEVs } from '~/models/participant-expected-value'; import { batchUpsertParticipantEvSnapshots } from '~/models/ev-snapshot'; import { getGolfSkillsForSeason, batchUpsertGolfSkills } from '~/models/golf-skills'; diff --git a/app/routes/admin.sports-seasons.$id.participants.tsx b/app/routes/admin.sports-seasons.$id.participants.tsx index b2d4cb1..64e5da3 100644 --- a/app/routes/admin.sports-seasons.$id.participants.tsx +++ b/app/routes/admin.sports-seasons.$id.participants.tsx @@ -10,7 +10,7 @@ import { createParticipant, deleteParticipant, updateParticipant, -} from "~/models/participant"; +} from "~/models/season-participant"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; diff --git a/app/routes/admin.sports-seasons.$id.regular-standings.tsx b/app/routes/admin.sports-seasons.$id.regular-standings.tsx index 7561f82..83e45ac 100644 --- a/app/routes/admin.sports-seasons.$id.regular-standings.tsx +++ b/app/routes/admin.sports-seasons.$id.regular-standings.tsx @@ -3,7 +3,7 @@ import type { Route } from "./+types/admin.sports-seasons.$id.regular-standings" import { logger } from "~/lib/logger"; import { findSportsSeasonById } from "~/models/sports-season"; -import { findParticipantsBySportsSeasonId } from "~/models/participant"; +import { findParticipantsBySportsSeasonId } from "~/models/season-participant"; import { getRegularSeasonStandings, upsertManualStanding } from "~/models/regular-season-standings"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; diff --git a/app/routes/admin.sports-seasons.$id.simulate.tsx b/app/routes/admin.sports-seasons.$id.simulate.tsx index 3e75431..41f763e 100644 --- a/app/routes/admin.sports-seasons.$id.simulate.tsx +++ b/app/routes/admin.sports-seasons.$id.simulate.tsx @@ -15,7 +15,7 @@ import type { Route } from "./+types/admin.sports-seasons.$id.simulate"; import { findSportsSeasonById, updateSportsSeason } from "~/models/sports-season"; import { batchUpsertParticipantEVs } from "~/models/participant-expected-value"; import { batchUpsertParticipantEvSnapshots } from "~/models/ev-snapshot"; -import { findParticipantsBySportsSeasonId } from "~/models/participant"; +import { findParticipantsBySportsSeasonId } from "~/models/season-participant"; import { getSimulator, type SimulatorType } from "~/services/simulations/registry"; import { calculateEV } from "~/services/ev-calculator"; import { DEFAULT_SCORING_RULES } from "~/lib/scoring-types"; diff --git a/app/routes/admin.sports-seasons.$id.standings.tsx b/app/routes/admin.sports-seasons.$id.standings.tsx index 7b1cf81..2034543 100644 --- a/app/routes/admin.sports-seasons.$id.standings.tsx +++ b/app/routes/admin.sports-seasons.$id.standings.tsx @@ -3,7 +3,7 @@ import type { Route } from "./+types/admin.sports-seasons.$id.standings"; import { logger } from "~/lib/logger"; import { findSportsSeasonById } from "~/models/sports-season"; -import { findParticipantsBySportsSeasonId } from "~/models/participant"; +import { findParticipantsBySportsSeasonId } from "~/models/season-participant"; import { getSeasonResults, upsertSeasonResultsBulk } from "~/models/participant-season-result"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; diff --git a/app/routes/admin.sports-seasons.$id.surface-elo.tsx b/app/routes/admin.sports-seasons.$id.surface-elo.tsx index 14c477e..95b548d 100644 --- a/app/routes/admin.sports-seasons.$id.surface-elo.tsx +++ b/app/routes/admin.sports-seasons.$id.surface-elo.tsx @@ -3,7 +3,7 @@ import type { Route } from './+types/admin.sports-seasons.$id.surface-elo'; import { logger } from '~/lib/logger'; import { findSportsSeasonById, updateSportsSeason } from '~/models/sports-season'; -import { findParticipantsBySportsSeasonId, findParticipantByName, createParticipant } from '~/models/participant'; +import { findParticipantsBySportsSeasonId, findParticipantByName, createParticipant } from '~/models/season-participant'; import { batchUpsertParticipantEVs, } from '~/models/participant-expected-value'; diff --git a/app/routes/admin.sports-seasons.$id.tsx b/app/routes/admin.sports-seasons.$id.tsx index 8107c85..7fc3699 100644 --- a/app/routes/admin.sports-seasons.$id.tsx +++ b/app/routes/admin.sports-seasons.$id.tsx @@ -16,7 +16,7 @@ import { getPendingStandingsMappings, deletePendingStandingsMapping, } from "~/models/pending-standings-mappings"; -import { findParticipantsBySportsSeasonId, updateParticipant } from "~/models/participant"; +import { findParticipantsBySportsSeasonId, updateParticipant } from "~/models/season-participant"; import { getLastSyncedAt, upsertRegularSeasonStandings } from "~/models/regular-season-standings"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; diff --git a/app/routes/admin/__tests__/sports-seasons-participants.test.ts b/app/routes/admin/__tests__/sports-seasons-participants.test.ts index ebcb843..adc7d97 100644 --- a/app/routes/admin/__tests__/sports-seasons-participants.test.ts +++ b/app/routes/admin/__tests__/sports-seasons-participants.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -vi.mock("~/models/participant", () => ({ +vi.mock("~/models/season-participant", () => ({ findParticipantsBySportsSeasonId: vi.fn(), findParticipantByName: vi.fn(), createParticipant: vi.fn(), @@ -19,7 +19,7 @@ vi.mock("~/lib/logger", () => ({ import { findParticipantByName, updateParticipant, -} from "~/models/participant"; +} from "~/models/season-participant"; import { action } from "~/routes/admin.sports-seasons.$id.participants"; const mockParams = { id: "season-1" }; diff --git a/app/routes/api/__tests__/draft.force-manual-pick.test.ts b/app/routes/api/__tests__/draft.force-manual-pick.test.ts index 61b467b..bb905dd 100644 --- a/app/routes/api/__tests__/draft.force-manual-pick.test.ts +++ b/app/routes/api/__tests__/draft.force-manual-pick.test.ts @@ -16,7 +16,7 @@ vi.mock("~/models/draft-pick", () => ({ getDraftPicksWithSports: vi.fn(), getTeamDraftPicksWithSports: vi.fn(), })); -vi.mock("~/models/participant", () => ({ +vi.mock("~/models/season-participant", () => ({ getParticipantsForSeasonWithSports: vi.fn(), })); vi.mock("~/models/season-sport", () => ({ @@ -138,7 +138,7 @@ describe("draft.force-manual-pick action", () => { vi.mocked(getDraftPicksWithSports).mockResolvedValue([]); vi.mocked(getTeamDraftPicksWithSports).mockResolvedValue([]); - const { getParticipantsForSeasonWithSports } = await import("~/models/participant"); + const { getParticipantsForSeasonWithSports } = await import("~/models/season-participant"); vi.mocked(getParticipantsForSeasonWithSports).mockResolvedValue([]); const { getSeasonSportsSimple } = await import("~/models/season-sport"); @@ -159,7 +159,7 @@ describe("draft.force-manual-pick action", () => { seasons: { findFirst: vi.fn() }, commissioners: { findFirst: vi.fn() }, draftPicks: { findFirst: vi.fn() }, - participants: { findFirst: vi.fn() }, + seasonParticipants: { findFirst: vi.fn() }, draftSlots: { findMany: vi.fn() }, draftTimers: { findFirst: vi.fn() }, }, @@ -178,7 +178,7 @@ describe("draft.force-manual-pick action", () => { userId: COMMISSIONER_ID, }); mockDb.query.draftPicks.findFirst.mockResolvedValue(null); - mockDb.query.participants.findFirst.mockResolvedValue(mockParticipant); + mockDb.query.seasonParticipants.findFirst.mockResolvedValue(mockParticipant); mockDb.query.draftSlots.findMany.mockResolvedValue(mockDraftSlots); mockDb.query.draftTimers.findFirst.mockResolvedValue({ id: "timer-1", @@ -277,7 +277,7 @@ describe("draft.force-manual-pick action", () => { }); it("returns 404 when the participant does not exist", async () => { - mockDb.query.participants.findFirst.mockResolvedValue(null); + mockDb.query.seasonParticipants.findFirst.mockResolvedValue(null); const response = await action({ request: defaultPickRequest(), params: {}, context: ctx }); diff --git a/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts b/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts index 969fd67..4b8c869 100644 --- a/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts +++ b/app/routes/api/__tests__/draft.force-manual-pick.timer-mode.test.ts @@ -22,7 +22,7 @@ vi.mock("~/models/draft-pick", () => ({ getDraftPicksWithSports: vi.fn(), getTeamDraftPicksWithSports: vi.fn(), })); -vi.mock("~/models/participant", () => ({ +vi.mock("~/models/season-participant", () => ({ getParticipantsForSeasonWithSports: vi.fn(), })); vi.mock("~/models/season-sport", () => ({ @@ -141,7 +141,7 @@ describe("draft.force-manual-pick action — timer mode behavior", () => { vi.mocked(getDraftPicksWithSports).mockResolvedValue([]); vi.mocked(getTeamDraftPicksWithSports).mockResolvedValue([]); - const { getParticipantsForSeasonWithSports } = await import("~/models/participant"); + const { getParticipantsForSeasonWithSports } = await import("~/models/season-participant"); vi.mocked(getParticipantsForSeasonWithSports).mockResolvedValue([]); const { getSeasonSportsSimple } = await import("~/models/season-sport"); @@ -163,7 +163,7 @@ describe("draft.force-manual-pick action — timer mode behavior", () => { findFirst: vi.fn().mockResolvedValue({ id: "c-1", userId: COMMISSIONER_ID }), }, draftPicks: { findFirst: vi.fn().mockResolvedValue(null) }, - participants: { findFirst: vi.fn().mockResolvedValue(mockParticipant) }, + seasonParticipants: { findFirst: vi.fn().mockResolvedValue(mockParticipant) }, draftSlots: { findMany: vi.fn().mockResolvedValue(mockDraftSlots) }, draftTimers: { findFirst: vi.fn().mockResolvedValue({ id: "timer-1", seasonId: SEASON_ID, teamId: TEAM_ID, timeRemaining: 75 }), diff --git a/app/routes/api/__tests__/draft.make-pick.timer-mode.test.ts b/app/routes/api/__tests__/draft.make-pick.timer-mode.test.ts index 63dc674..16a112f 100644 --- a/app/routes/api/__tests__/draft.make-pick.timer-mode.test.ts +++ b/app/routes/api/__tests__/draft.make-pick.timer-mode.test.ts @@ -22,7 +22,7 @@ vi.mock("~/models/draft-pick", () => ({ getDraftPicksWithSports: vi.fn(), getTeamDraftPicksWithSports: vi.fn(), })); -vi.mock("~/models/participant", () => ({ +vi.mock("~/models/season-participant", () => ({ getParticipantsForSeasonWithSports: vi.fn(), })); vi.mock("~/models/season-sport", () => ({ @@ -138,7 +138,7 @@ describe("draft.make-pick action — timer mode behavior", () => { vi.mocked(getDraftPicksWithSports).mockResolvedValue([]); vi.mocked(getTeamDraftPicksWithSports).mockResolvedValue([]); - const { getParticipantsForSeasonWithSports } = await import("~/models/participant"); + const { getParticipantsForSeasonWithSports } = await import("~/models/season-participant"); vi.mocked(getParticipantsForSeasonWithSports).mockResolvedValue([]); const { getSeasonSportsSimple } = await import("~/models/season-sport"); @@ -158,7 +158,7 @@ describe("draft.make-pick action — timer mode behavior", () => { seasons: { findFirst: vi.fn() }, commissioners: { findFirst: vi.fn().mockResolvedValue(null) }, draftPicks: { findFirst: vi.fn().mockResolvedValue(null) }, - participants: { findFirst: vi.fn().mockResolvedValue(mockParticipant) }, + seasonParticipants: { findFirst: vi.fn().mockResolvedValue(mockParticipant) }, draftSlots: { findMany: vi.fn().mockResolvedValue(mockDraftSlots) }, draftTimers: { findFirst: vi.fn().mockResolvedValue({ id: "timer-1", timeRemaining: 75 }) }, draftQueue: { findMany: vi.fn().mockResolvedValue([]) }, diff --git a/app/routes/api/draft.force-manual-pick.ts b/app/routes/api/draft.force-manual-pick.ts index bbb1d53..8ffe099 100644 --- a/app/routes/api/draft.force-manual-pick.ts +++ b/app/routes/api/draft.force-manual-pick.ts @@ -5,7 +5,7 @@ import { eq, and, sql } from "drizzle-orm"; import { isUserAdmin } from "~/models/user"; import { calculateDraftEligibility } from "~/lib/draft-eligibility"; import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/draft-pick"; -import { getParticipantsForSeasonWithSports } from "~/models/participant"; +import { getParticipantsForSeasonWithSports } from "~/models/season-participant"; import { getSeasonSportsSimple } from "~/models/season-sport"; import { calculatePickInfo, checkAndTriggerNextAutodraft } from "~/models/draft-utils"; import { logCommissionerAction } from "~/models/audit-log"; @@ -87,8 +87,8 @@ export async function action(args: ActionFunctionArgs) { } // Get participant details - const participant = await db.query.participants.findFirst({ - where: eq(schema.participants.id, participantId), + const participant = await db.query.seasonParticipants.findFirst({ + where: eq(schema.seasonParticipants.id, participantId), with: { sportsSeason: { with: { diff --git a/app/routes/api/draft.make-pick.ts b/app/routes/api/draft.make-pick.ts index d3fb4e7..65db0a0 100644 --- a/app/routes/api/draft.make-pick.ts +++ b/app/routes/api/draft.make-pick.ts @@ -5,7 +5,7 @@ import { eq, and, sql } from "drizzle-orm"; import { isUserAdmin } from "~/models/user"; import { calculateDraftEligibility } from "~/lib/draft-eligibility"; import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/draft-pick"; -import { getParticipantsForSeasonWithSports } from "~/models/participant"; +import { getParticipantsForSeasonWithSports } from "~/models/season-participant"; import { getSeasonSportsSimple } from "~/models/season-sport"; import { calculatePickInfo, checkAndTriggerNextAutodraft, pruneIneligibleQueueItems } from "~/models/draft-utils"; import { getSocketIO, scheduleDraftRoomClosure } from "../../../server/socket"; @@ -96,8 +96,8 @@ export async function action(args: ActionFunctionArgs) { } // Get participant details - const participant = await db.query.participants.findFirst({ - where: eq(schema.participants.id, participantId), + const participant = await db.query.seasonParticipants.findFirst({ + where: eq(schema.seasonParticipants.id, participantId), with: { sportsSeason: { with: { diff --git a/app/routes/api/draft.replace-pick.ts b/app/routes/api/draft.replace-pick.ts index 38b415f..b125360 100644 --- a/app/routes/api/draft.replace-pick.ts +++ b/app/routes/api/draft.replace-pick.ts @@ -5,7 +5,7 @@ import { eq, and } from "drizzle-orm"; import { isCommissioner } from "~/models/commissioner"; import { calculateDraftEligibility } from "~/lib/draft-eligibility"; import { getDraftPicksWithSports, getTeamDraftPicksWithSports } from "~/models/draft-pick"; -import { getParticipantsForSeasonWithSports } from "~/models/participant"; +import { getParticipantsForSeasonWithSports } from "~/models/season-participant"; import { getSeasonSportsSimple } from "~/models/season-sport"; import { logCommissionerAction } from "~/models/audit-log"; import { getSocketIO } from "../../../server/socket"; @@ -78,8 +78,8 @@ export async function action(args: ActionFunctionArgs) { } } - const participant = await db.query.participants.findFirst({ - where: eq(schema.participants.id, participantId), + const participant = await db.query.seasonParticipants.findFirst({ + where: eq(schema.seasonParticipants.id, participantId), with: { sportsSeason: { with: { sport: true } }, }, @@ -128,8 +128,8 @@ export async function action(args: ActionFunctionArgs) { } // Fetch old participant name for the audit log (before overwriting the pick) - const oldParticipant = await db.query.participants.findFirst({ - where: eq(schema.participants.id, oldParticipantId), + const oldParticipant = await db.query.seasonParticipants.findFirst({ + where: eq(schema.seasonParticipants.id, oldParticipantId), }); // Update the pick in-place diff --git a/app/routes/api/seasons.$seasonId.draft.ts b/app/routes/api/seasons.$seasonId.draft.ts index 0d86a8d..57e233a 100644 --- a/app/routes/api/seasons.$seasonId.draft.ts +++ b/app/routes/api/seasons.$seasonId.draft.ts @@ -51,13 +51,13 @@ export async function loader({ params }: LoaderFunctionArgs) { round: schema.draftPicks.round, teamName: schema.teams.name, teamOwnerId: schema.teams.ownerId, - participantName: schema.participants.name, + participantName: schema.seasonParticipants.name, sport: schema.sports.name, }) .from(schema.draftPicks) .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id)) - .innerJoin(schema.participants, eq(schema.draftPicks.participantId, schema.participants.id)) - .innerJoin(schema.sportsSeasons, eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id)) + .innerJoin(schema.seasonParticipants, eq(schema.draftPicks.participantId, schema.seasonParticipants.id)) + .innerJoin(schema.sportsSeasons, eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id)) .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) .where(eq(schema.draftPicks.seasonId, seasonId)) .orderBy(schema.draftPicks.pickNumber); diff --git a/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx b/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx index 094a1b0..2ddeba7 100644 --- a/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx @@ -76,19 +76,19 @@ export async function loader(args: Route.LoaderArgs) { round: schema.draftPicks.round, pickInRound: schema.draftPicks.pickInRound, team: schema.teams, - participant: schema.participants, + participant: schema.seasonParticipants, sport: schema.sports, scoringPattern: schema.sportsSeasons.scoringPattern, }) .from(schema.draftPicks) .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id)) .innerJoin( - schema.participants, - eq(schema.draftPicks.participantId, schema.participants.id) + schema.seasonParticipants, + eq(schema.draftPicks.participantId, schema.seasonParticipants.id) ) .innerJoin( schema.sportsSeasons, - eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) + eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id) ) .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) .where(eq(schema.draftPicks.seasonId, seasonId)) @@ -104,13 +104,13 @@ export async function loader(args: Route.LoaderArgs) { const results = await db .select({ - participantId: schema.participantResults.participantId, - sportsSeasonId: schema.participantResults.sportsSeasonId, - finalPosition: schema.participantResults.finalPosition, - isPartialScore: schema.participantResults.isPartialScore, + participantId: schema.seasonParticipantResults.participantId, + sportsSeasonId: schema.seasonParticipantResults.sportsSeasonId, + finalPosition: schema.seasonParticipantResults.finalPosition, + isPartialScore: schema.seasonParticipantResults.isPartialScore, }) - .from(schema.participantResults) - .where(inArray(schema.participantResults.participantId, participantIds)); + .from(schema.seasonParticipantResults) + .where(inArray(schema.seasonParticipantResults.participantId, participantIds)); const resultByParticipant = new Map(results.map((r) => [r.participantId, r])); diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index 0b79451..1c5bdeb 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -17,7 +17,7 @@ import { getTeamWatchlist } from "~/models/watchlist"; import { findSeasonWithTeamsAndLeague } from "~/models/season"; import { findDraftSlotsBySeasonId } from "~/models/draft-slot"; import { getDraftPicksForSeason } from "~/models/draft-pick"; -import { getDraftParticipants } from "~/models/participant"; +import { getDraftParticipants } from "~/models/season-participant"; import { getSeasonTimers } from "~/models/draft-timer"; import { getSeasonAutodraftSettings } from "~/models/autodraft-settings"; import { hasCommissionerRecord } from "~/models/commissioner"; diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts index 0ff93e7..4af7fbc 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts @@ -233,15 +233,15 @@ export async function loader(args: Route.LoaderArgs) { // Fetch group-stage losers and all participant results for bracket scoring const [eliminatedResults, allResults] = await Promise.all([ - db.query.participantResults.findMany({ + db.query.seasonParticipantResults.findMany({ where: and( - eq(schema.participantResults.sportsSeasonId, sportsSeasonId), - eq(schema.participantResults.finalPosition, 0) + eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId), + eq(schema.seasonParticipantResults.finalPosition, 0) ), with: { participant: true }, }), - db.query.participantResults.findMany({ - where: eq(schema.participantResults.sportsSeasonId, sportsSeasonId), + db.query.seasonParticipantResults.findMany({ + where: eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId), }), ]); diff --git a/app/services/probability-updater.ts b/app/services/probability-updater.ts index 1ec048d..b6fd7cc 100644 --- a/app/services/probability-updater.ts +++ b/app/services/probability-updater.ts @@ -252,8 +252,8 @@ export async function previewProbabilityUpdate( const results = await findParticipantResultsBySportsSeasonId(sportsSeasonId); // Get all existing EVs with participant names - const existingEVs = await db.query.participantExpectedValues.findMany({ - where: eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId), + const existingEVs = await db.query.seasonParticipantExpectedValues.findMany({ + where: eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId), with: { participant: true, }, diff --git a/app/services/simulations/__tests__/world-cup-simulator.test.ts b/app/services/simulations/__tests__/world-cup-simulator.test.ts index 6fc3602..8760dfd 100644 --- a/app/services/simulations/__tests__/world-cup-simulator.test.ts +++ b/app/services/simulations/__tests__/world-cup-simulator.test.ts @@ -86,7 +86,7 @@ import { database } from "~/database/context"; const mockDb = { query: { - participants: { findMany: vi.fn() }, + seasonParticipants: { findMany: vi.fn() }, scoringEvents: { findFirst: vi.fn() }, tournamentGroups: { findMany: vi.fn() }, playoffMatches: { findMany: vi.fn() }, @@ -98,7 +98,7 @@ const mockDb = { beforeEach(() => { vi.mocked(database).mockReturnValue(mockDb as never); - mockDb.query.participants.findMany.mockReset(); + mockDb.query.seasonParticipants.findMany.mockReset(); mockDb.query.scoringEvents.findFirst.mockReset(); mockDb.query.tournamentGroups.findMany.mockReset(); mockDb.query.playoffMatches.findMany.mockReset(); @@ -109,7 +109,7 @@ beforeEach(() => { describe("WorldCupSimulator", () => { it("throws when no participants are found", async () => { - mockDb.query.participants.findMany.mockResolvedValue([]); + mockDb.query.seasonParticipants.findMany.mockResolvedValue([]); mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); mockDb.query.playoffMatches.findMany.mockResolvedValue([]); @@ -120,7 +120,7 @@ describe("WorldCupSimulator", () => { it("returns one result per participant", async () => { const participants = makeParticipants(48); - mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.seasonParticipants.findMany.mockResolvedValue(participants); mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); mockDb.query.playoffMatches.findMany.mockResolvedValue([]); @@ -136,7 +136,7 @@ describe("WorldCupSimulator", () => { 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.seasonParticipants.findMany.mockResolvedValue(participants); mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); mockDb.query.playoffMatches.findMany.mockResolvedValue([]); @@ -157,7 +157,7 @@ describe("WorldCupSimulator", () => { it("probabilities are all non-negative", async () => { const participants = makeParticipants(48); - mockDb.query.participants.findMany.mockResolvedValue(participants); + mockDb.query.seasonParticipants.findMany.mockResolvedValue(participants); mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); mockDb.query.playoffMatches.findMany.mockResolvedValue([]); @@ -178,7 +178,7 @@ describe("WorldCupSimulator", () => { 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.seasonParticipants.findMany.mockResolvedValue(participants); mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); mockDb.query.playoffMatches.findMany.mockResolvedValue([]); @@ -200,7 +200,7 @@ describe("WorldCupSimulator", () => { 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.seasonParticipants.findMany.mockResolvedValue(participants); mockDb.query.scoringEvents.findFirst.mockResolvedValue({ id: "event-1" }); // One group fully complete: p0 wins everything, p3 loses everything @@ -254,7 +254,7 @@ describe("WorldCupSimulator", () => { 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.seasonParticipants.findMany.mockResolvedValue(participants); mockDb.query.scoringEvents.findFirst.mockResolvedValue(null); mockDb.query.tournamentGroups.findMany.mockResolvedValue([]); mockDb.query.playoffMatches.findMany.mockResolvedValue([]); diff --git a/app/services/simulations/afl-simulator.ts b/app/services/simulations/afl-simulator.ts index 5616844..9168c17 100644 --- a/app/services/simulations/afl-simulator.ts +++ b/app/services/simulations/afl-simulator.ts @@ -198,16 +198,16 @@ export class AFLSimulator implements Simulator { // 1. Load participants, DB Elo, and standings in parallel. const [participantRows, evRows, standings] = await Promise.all([ db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)), + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)), db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)), + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)), getRegularSeasonStandings(sportsSeasonId), ]); diff --git a/app/services/simulations/auto-racing-simulator.ts b/app/services/simulations/auto-racing-simulator.ts index ee73f14..64072ee 100644 --- a/app/services/simulations/auto-racing-simulator.ts +++ b/app/services/simulations/auto-racing-simulator.ts @@ -103,8 +103,8 @@ export class AutoRacingSimulator implements Simulator { const db = database(); // 1. Load all participants for this sports season - const participants = await db.query.participants.findMany({ - where: eq(schema.participants.sportsSeasonId, sportsSeasonId), + const participants = await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), }); if (participants.length === 0) { diff --git a/app/services/simulations/bracket-simulator.ts b/app/services/simulations/bracket-simulator.ts index 80cd30b..12a17c4 100644 --- a/app/services/simulations/bracket-simulator.ts +++ b/app/services/simulations/bracket-simulator.ts @@ -11,7 +11,7 @@ */ import { database } from "~/database/context"; -import { participantExpectedValues } from "~/database/schema"; +import { seasonParticipantExpectedValues } from "~/database/schema"; import { eq } from "drizzle-orm"; import { simulateBracket } from "~/services/bracket-simulator"; import { convertFuturesToElo } from "~/services/probability-engine"; @@ -24,12 +24,12 @@ export class BracketSimulator implements Simulator { // Load all participants with their current EVs (which hold probability distributions) const evRows = await db .select({ - participantId: participantExpectedValues.participantId, - probFirst: participantExpectedValues.probFirst, - sourceOdds: participantExpectedValues.sourceOdds, + participantId: seasonParticipantExpectedValues.participantId, + probFirst: seasonParticipantExpectedValues.probFirst, + sourceOdds: seasonParticipantExpectedValues.sourceOdds, }) - .from(participantExpectedValues) - .where(eq(participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(seasonParticipantExpectedValues) + .where(eq(seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); if (evRows.length === 0) { throw new Error( diff --git a/app/services/simulations/cs-major-simulator.ts b/app/services/simulations/cs-major-simulator.ts index 2213d61..030ddef 100644 --- a/app/services/simulations/cs-major-simulator.ts +++ b/app/services/simulations/cs-major-simulator.ts @@ -433,9 +433,9 @@ export class CSMajorSimulator implements Simulator { // 1. Load all participants for this sports season. const allParticipants = await db - .select({ id: schema.participants.id }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .select({ id: schema.seasonParticipants.id }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); const participantIds = allParticipants.map((p) => p.id); @@ -446,12 +446,12 @@ export class CSMajorSimulator implements Simulator { // 2. Load Elo ratings and world rankings from participantExpectedValues. const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, - worldRanking: schema.participantExpectedValues.worldRanking, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, + worldRanking: schema.seasonParticipantExpectedValues.worldRanking, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); const eloMap = new Map(); for (const row of evRows) { @@ -508,7 +508,7 @@ export class CSMajorSimulator implements Simulator { if (completedEventIds.length > 0) { const actualResults = await db .select({ - participantId: schema.eventResults.participantId, + participantId: schema.eventResults.seasonParticipantId, qualifyingPointsAwarded: schema.eventResults.qualifyingPointsAwarded, }) .from(schema.eventResults) diff --git a/app/services/simulations/darts-simulator.ts b/app/services/simulations/darts-simulator.ts index 207d4e0..673b37c 100644 --- a/app/services/simulations/darts-simulator.ts +++ b/app/services/simulations/darts-simulator.ts @@ -220,12 +220,12 @@ export class DartsSimulator implements Simulator { // 3. Load Elo ratings and world rankings. const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, - worldRanking: schema.participantExpectedValues.worldRanking, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, + worldRanking: schema.seasonParticipantExpectedValues.worldRanking, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); const eloMap = new Map(); const rankingMap = new Map(); @@ -452,9 +452,9 @@ export class DartsSimulator implements Simulator { db: ReturnType ): Promise { const allParticipants = await db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); if (allParticipants.length < 2) { throw new Error( diff --git a/app/services/simulations/golf-simulator.ts b/app/services/simulations/golf-simulator.ts index 3011de1..4687f3d 100644 --- a/app/services/simulations/golf-simulator.ts +++ b/app/services/simulations/golf-simulator.ts @@ -185,9 +185,9 @@ export class GolfSimulator implements Simulator { // Load participants, skills, QP config, and scoring events in parallel. const [allParticipants, skillsMap, qpConfigRows, events] = await Promise.all([ db - .select({ id: schema.participants.id }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)), + .select({ id: schema.seasonParticipants.id }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)), getGolfSkillsMap(sportsSeasonId), getQPConfig(sportsSeasonId), db.query.scoringEvents.findMany({ @@ -226,7 +226,7 @@ export class GolfSimulator implements Simulator { if (completedEventIds.length > 0) { const actualResults = await db .select({ - participantId: schema.eventResults.participantId, + participantId: schema.eventResults.seasonParticipantId, qualifyingPointsAwarded: schema.eventResults.qualifyingPointsAwarded, }) .from(schema.eventResults) diff --git a/app/services/simulations/llws-simulator.ts b/app/services/simulations/llws-simulator.ts index 1ac4b01..b6dfcb3 100644 --- a/app/services/simulations/llws-simulator.ts +++ b/app/services/simulations/llws-simulator.ts @@ -264,9 +264,9 @@ export class LLWSSimulator implements Simulator { // 1. Load all participants. const participants = await db - .select({ id: schema.participants.id, name: schema.participants.name, externalId: schema.participants.externalId }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name, externalId: schema.seasonParticipants.externalId }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); if (participants.length !== US_TEAM_COUNT + INTL_TEAM_COUNT) { throw new Error( @@ -278,11 +278,11 @@ export class LLWSSimulator implements Simulator { // 2. Load championship futures odds. const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceOdds: schema.participantExpectedValues.sourceOdds, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); const rawOddsMap = new Map(); for (const row of evRows) { diff --git a/app/services/simulations/mlb-simulator.ts b/app/services/simulations/mlb-simulator.ts index a4a45e7..f8f2234 100644 --- a/app/services/simulations/mlb-simulator.ts +++ b/app/services/simulations/mlb-simulator.ts @@ -409,9 +409,9 @@ export class MLBSimulator implements Simulator { // 1. Load all participants for this sports season. const participantRows = await db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); if (participantRows.length === 0) { throw new Error( @@ -452,12 +452,12 @@ export class MLBSimulator implements Simulator { const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceOdds: schema.participantExpectedValues.sourceOdds, - sourceElo: schema.participantExpectedValues.sourceElo, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); const participantIdSet = new Set(participantIds); const oddsRows = evRows.filter( diff --git a/app/services/simulations/nba-simulator.ts b/app/services/simulations/nba-simulator.ts index cb4dacd..59cbd72 100644 --- a/app/services/simulations/nba-simulator.ts +++ b/app/services/simulations/nba-simulator.ts @@ -312,16 +312,16 @@ export class NBASimulator implements Simulator { // Load participant names and sourceElo values in parallel. const [participantRows, evRows] = await Promise.all([ db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)), + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)), db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)), + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)), ]); const nameById = new Map(participantRows.map((r) => [r.id, r.name])); @@ -472,17 +472,17 @@ export class NBASimulator implements Simulator { const [participantRows, standings, evRows] = await Promise.all([ db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)), + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)), getRegularSeasonStandings(sportsSeasonId), db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)), + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)), ]); if (participantRows.length === 0) { diff --git a/app/services/simulations/ncaa-football-simulator.ts b/app/services/simulations/ncaa-football-simulator.ts index d41fe9f..4ba53db 100644 --- a/app/services/simulations/ncaa-football-simulator.ts +++ b/app/services/simulations/ncaa-football-simulator.ts @@ -214,9 +214,9 @@ export class NCAAFootballSimulator implements Simulator { // 1. Load all participants for this sports season. const participants = await db - .select({ id: schema.participants.id }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .select({ id: schema.seasonParticipants.id }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); if (participants.length < BRACKET_SIZE) { throw new Error( @@ -228,12 +228,12 @@ export class NCAAFootballSimulator implements Simulator { // 2. Load Elo/FPI ratings and optional futures odds in a single query. const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, - sourceOdds: schema.participantExpectedValues.sourceOdds, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); // Build Elo and raw odds maps in a single pass. const eloFromDb = new Map(); diff --git a/app/services/simulations/ncaam-simulator.ts b/app/services/simulations/ncaam-simulator.ts index 65a13ac..c721129 100644 --- a/app/services/simulations/ncaam-simulator.ts +++ b/app/services/simulations/ncaam-simulator.ts @@ -494,9 +494,9 @@ export class NCAAMSimulator implements Simulator { // 6. Load participant names from DB; build net rating lookup by ID. const participantRows = await db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(inArray(schema.participants.id, participantIds)); + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(inArray(schema.seasonParticipants.id, participantIds)); if (participantRows.length < participantIds.length) { const foundIds = new Set(participantRows.map((r) => r.id)); diff --git a/app/services/simulations/ncaaw-simulator.ts b/app/services/simulations/ncaaw-simulator.ts index 0d9e0a6..5e2c20e 100644 --- a/app/services/simulations/ncaaw-simulator.ts +++ b/app/services/simulations/ncaaw-simulator.ts @@ -457,9 +457,9 @@ export class NCAAWSimulator implements Simulator { // 6. Load participant names from DB; build Barthag lookup by ID. const participantRows = await db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(inArray(schema.participants.id, participantIds)); + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(inArray(schema.seasonParticipants.id, participantIds)); if (participantRows.length < participantIds.length) { const foundIds = new Set(participantRows.map((r) => r.id)); diff --git a/app/services/simulations/nfl-simulator.ts b/app/services/simulations/nfl-simulator.ts index 9b7e26d..e23d4af 100644 --- a/app/services/simulations/nfl-simulator.ts +++ b/app/services/simulations/nfl-simulator.ts @@ -296,8 +296,8 @@ export class NFLSimulator implements Simulator { const db = database(); // Load participants - const participants = await db.query.participants.findMany({ - where: eq(schema.participants.sportsSeasonId, sportsSeasonId), + const participants = await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), }); if (participants.length === 0) { @@ -307,12 +307,12 @@ export class NFLSimulator implements Simulator { // Load EV rows (sourceElo + sourceOdds) const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, - sourceOdds: schema.participantExpectedValues.sourceOdds, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); // Build Elo map: prefer sourceElo, fall back to converting sourceOdds const eloMap = new Map(); diff --git a/app/services/simulations/nhl-simulator.ts b/app/services/simulations/nhl-simulator.ts index 5891634..8e24c30 100644 --- a/app/services/simulations/nhl-simulator.ts +++ b/app/services/simulations/nhl-simulator.ts @@ -278,18 +278,18 @@ export class NHLSimulator implements Simulator { // 1. Load participants, standings, and sourceElo values in parallel. const [participantRows, standings, evRows] = await Promise.all([ db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)), + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)), getRegularSeasonStandings(sportsSeasonId), db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, - sourceOdds: schema.participantExpectedValues.sourceOdds, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)), + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)), ]); if (participantRows.length === 0) { @@ -620,17 +620,17 @@ export class NHLSimulator implements Simulator { // Load all participants and EV data in parallel. const [participantRows, evRows] = await Promise.all([ db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)), + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)), db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceOdds: schema.participantExpectedValues.sourceOdds, - sourceElo: schema.participantExpectedValues.sourceElo, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)), + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)), ]); const allParticipantIds = participantRows.map((r) => r.id); diff --git a/app/services/simulations/snooker-simulator.ts b/app/services/simulations/snooker-simulator.ts index a9e8a5c..2b4db9a 100644 --- a/app/services/simulations/snooker-simulator.ts +++ b/app/services/simulations/snooker-simulator.ts @@ -251,11 +251,11 @@ export class SnookerSimulator implements Simulator { // 3. Load Elo ratings from participantExpectedValues. const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceElo: schema.participantExpectedValues.sourceElo, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); // Build Elo map; fall back to 1500 for any participant with no stored rating. const eloMap = new Map(); @@ -453,9 +453,9 @@ export class SnookerSimulator implements Simulator { db: ReturnType ): Promise { const allParticipants = await db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); if (allParticipants.length < 17) { throw new Error( diff --git a/app/services/simulations/tennis-simulator.ts b/app/services/simulations/tennis-simulator.ts index 032cccb..e14591e 100644 --- a/app/services/simulations/tennis-simulator.ts +++ b/app/services/simulations/tennis-simulator.ts @@ -262,9 +262,9 @@ export class TennisSimulator implements Simulator { // 1. Load all participants for this sports season. const allParticipants = await db - .select({ id: schema.participants.id }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)); + .select({ id: schema.seasonParticipants.id }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)); if (allParticipants.length < 128) { throw new Error( @@ -306,7 +306,7 @@ export class TennisSimulator implements Simulator { if (completedEventIds.length > 0) { const actualResults = await db .select({ - participantId: schema.eventResults.participantId, + participantId: schema.eventResults.seasonParticipantId, qualifyingPointsAwarded: schema.eventResults.qualifyingPointsAwarded, }) .from(schema.eventResults) diff --git a/app/services/simulations/ucl-simulator.ts b/app/services/simulations/ucl-simulator.ts index 18e6008..698431c 100644 --- a/app/services/simulations/ucl-simulator.ts +++ b/app/services/simulations/ucl-simulator.ts @@ -147,11 +147,11 @@ export class UCLSimulator implements Simulator { // 5. Load futures odds from participantExpectedValues. const evRows = await db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceOdds: schema.participantExpectedValues.sourceOdds, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); const evMap = new Map(evRows.map((r) => [r.participantId, r])); diff --git a/app/services/simulations/wnba-simulator.ts b/app/services/simulations/wnba-simulator.ts index a820202..98b36a1 100644 --- a/app/services/simulations/wnba-simulator.ts +++ b/app/services/simulations/wnba-simulator.ts @@ -159,18 +159,18 @@ export class WNBASimulator implements Simulator { // 1. Load participants, standings, and futures odds in parallel. const [participantRows, standings, evRows] = await Promise.all([ db - .select({ id: schema.participants.id, name: schema.participants.name }) - .from(schema.participants) - .where(eq(schema.participants.sportsSeasonId, sportsSeasonId)), + .select({ id: schema.seasonParticipants.id, name: schema.seasonParticipants.name }) + .from(schema.seasonParticipants) + .where(eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId)), getRegularSeasonStandings(sportsSeasonId), db .select({ - participantId: schema.participantExpectedValues.participantId, - sourceOdds: schema.participantExpectedValues.sourceOdds, - sourceElo: schema.participantExpectedValues.sourceElo, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)), + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)), ]); if (participantRows.length === 0) { diff --git a/app/services/simulations/world-cup-simulator.ts b/app/services/simulations/world-cup-simulator.ts index 9a1a587..8e7b681 100644 --- a/app/services/simulations/world-cup-simulator.ts +++ b/app/services/simulations/world-cup-simulator.ts @@ -313,8 +313,8 @@ export class WorldCupSimulator implements Simulator { const db = database(); // 1. Load all participants for this season - const participantRows = await db.query.participants.findMany({ - where: eq(schema.participants.sportsSeasonId, sportsSeasonId), + const participantRows = await db.query.seasonParticipants.findMany({ + where: eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), }); if (participantRows.length === 0) { @@ -327,12 +327,12 @@ export class WorldCupSimulator implements Simulator { // 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, + participantId: schema.seasonParticipantExpectedValues.participantId, + sourceOdds: schema.seasonParticipantExpectedValues.sourceOdds, + sourceElo: schema.seasonParticipantExpectedValues.sourceElo, }) - .from(schema.participantExpectedValues) - .where(eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId)); + .from(schema.seasonParticipantExpectedValues) + .where(eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId)); const evMap = new Map(evRows.map((r) => [r.participantId, r])); const participantSet = new Set(participantIds); diff --git a/app/services/standings-sync/index.ts b/app/services/standings-sync/index.ts index 1c272a4..fecb74f 100644 --- a/app/services/standings-sync/index.ts +++ b/app/services/standings-sync/index.ts @@ -1,7 +1,7 @@ import { database } from "~/database/context"; import { eq } from "drizzle-orm"; import * as schema from "~/database/schema"; -import { findParticipantsBySportsSeasonId, updateParticipant } from "~/models/participant"; +import { findParticipantsBySportsSeasonId, updateParticipant } from "~/models/season-participant"; import { upsertRegularSeasonStandings } from "~/models/regular-season-standings"; import { upsertPendingStandingsMappings } from "~/models/pending-standings-mappings"; import { findMatchingTeamName } from "~/lib/normalize-team-name"; diff --git a/app/utils/sports-data-sync.server.ts b/app/utils/sports-data-sync.server.ts index 1848913..d043e31 100644 --- a/app/utils/sports-data-sync.server.ts +++ b/app/utils/sports-data-sync.server.ts @@ -115,16 +115,16 @@ export async function exportSportsDataToJSON(): Promise { with: { sport: true }, orderBy: (s, { desc, asc }) => [desc(s.year), asc(s.name)], }), - db.query.participants.findMany({ + db.query.seasonParticipants.findMany({ with: { sportsSeason: { with: { sport: true } } }, orderBy: (p, { asc }) => [asc(p.name)], }), - db.query.participantExpectedValues.findMany({ + db.query.seasonParticipantExpectedValues.findMany({ with: { participant: { with: { sportsSeason: { with: { sport: true } } } }, }, }), - db.query.participantResults.findMany({ + db.query.seasonParticipantResults.findMany({ with: { participant: { with: { sportsSeason: { with: { sport: true } } } }, }, @@ -238,7 +238,7 @@ export async function importSportsDataFromJSON( // automatically removed when participants is deleted. await tx.delete(schema.seasonTemplateSports); await tx.delete(schema.seasonTemplates); - await tx.delete(schema.participants); // cascades to EVs + results + await tx.delete(schema.seasonParticipants); // cascades to EVs + results await tx.delete(schema.seasonSports); await tx.delete(schema.sportsSeasons); await tx.delete(schema.sports); @@ -358,28 +358,28 @@ export async function importSportsDataFromJSON( continue; } - const existing = await tx.query.participants.findFirst({ + const existing = await tx.query.seasonParticipants.findFirst({ where: and( - eq(schema.participants.sportsSeasonId, sportsSeasonId), - eq(schema.participants.name, participant.name) + eq(schema.seasonParticipants.sportsSeasonId, sportsSeasonId), + eq(schema.seasonParticipants.name, participant.name) ), }); if (existing) { // Update all mutable fields (fixes: merge mode was silently skipping) await tx - .update(schema.participants) + .update(schema.seasonParticipants) .set({ shortName: participant.shortName, externalId: participant.externalId, expectedValue: String(participant.expectedValue ?? 0), }) - .where(eq(schema.participants.id, existing.id)); + .where(eq(schema.seasonParticipants.id, existing.id)); participantIdMap.set(`${sportsSeasonId}:${participant.name}`, existing.id); updated++; } else { const [created_] = await tx - .insert(schema.participants) + .insert(schema.seasonParticipants) .values({ sportsSeasonId, name: participant.name, @@ -413,10 +413,10 @@ export async function importSportsDataFromJSON( continue; } - const existingEV = await tx.query.participantExpectedValues.findFirst({ + const existingEV = await tx.query.seasonParticipantExpectedValues.findFirst({ where: and( - eq(schema.participantExpectedValues.participantId, participantId), - eq(schema.participantExpectedValues.sportsSeasonId, sportsSeasonId) + eq(schema.seasonParticipantExpectedValues.participantId, participantId), + eq(schema.seasonParticipantExpectedValues.sportsSeasonId, sportsSeasonId) ), }); @@ -446,25 +446,25 @@ export async function importSportsDataFromJSON( if (existingEV) { if (mode === "merge") { await tx - .update(schema.participantExpectedValues) + .update(schema.seasonParticipantExpectedValues) .set({ ...evValues, updatedAt: new Date() }) - .where(eq(schema.participantExpectedValues.id, existingEV.id)); + .where(eq(schema.seasonParticipantExpectedValues.id, existingEV.id)); await tx - .update(schema.participants) + .update(schema.seasonParticipants) .set({ expectedValue: ev.expectedValue }) - .where(eq(schema.participants.id, participantId)); + .where(eq(schema.seasonParticipants.id, participantId)); updated++; } } else { - await tx.insert(schema.participantExpectedValues).values({ + await tx.insert(schema.seasonParticipantExpectedValues).values({ participantId, sportsSeasonId, ...evValues, }); await tx - .update(schema.participants) + .update(schema.seasonParticipants) .set({ expectedValue: ev.expectedValue }) - .where(eq(schema.participants.id, participantId)); + .where(eq(schema.seasonParticipants.id, participantId)); created++; } } @@ -489,10 +489,10 @@ export async function importSportsDataFromJSON( continue; } - const existingResult = await tx.query.participantResults.findFirst({ + const existingResult = await tx.query.seasonParticipantResults.findFirst({ where: and( - eq(schema.participantResults.participantId, participantId), - eq(schema.participantResults.sportsSeasonId, sportsSeasonId) + eq(schema.seasonParticipantResults.participantId, participantId), + eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId) ), }); @@ -505,13 +505,13 @@ export async function importSportsDataFromJSON( if (existingResult) { if (mode === "merge") { await tx - .update(schema.participantResults) + .update(schema.seasonParticipantResults) .set({ ...resultValues, updatedAt: new Date() }) - .where(eq(schema.participantResults.id, existingResult.id)); + .where(eq(schema.seasonParticipantResults.id, existingResult.id)); updated++; } } else { - await tx.insert(schema.participantResults).values({ + await tx.insert(schema.seasonParticipantResults).values({ participantId, sportsSeasonId, ...resultValues, diff --git a/database/schema.ts b/database/schema.ts index 4fbb104..f763fca 100644 --- a/database/schema.ts +++ b/database/schema.ts @@ -150,6 +150,12 @@ export const simulatorTypeEnum = pgEnum("simulator_type", [ "llws_bracket", ]); +export const tournamentStatusEnum = pgEnum("tournament_status", [ + "scheduled", + "in_progress", + "completed", +]); + export const playoffMatchGameStatusEnum = pgEnum("playoff_match_game_status", [ "scheduled", "complete", @@ -249,7 +255,7 @@ export const draftPicks = pgTable("draft_picks", { .references(() => teams.id, { onDelete: "cascade" }), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), pickNumber: integer("pick_number").notNull(), round: integer("round").notNull(), pickInRound: integer("pick_in_round").notNull(), @@ -272,7 +278,7 @@ export const draftQueue = pgTable("draft_queue", { .references(() => teams.id, { onDelete: "cascade" }), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), queuePosition: integer("queue_position").notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), @@ -288,7 +294,7 @@ export const watchlist = pgTable("watchlist", { .references(() => teams.id, { onDelete: "cascade" }), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), createdAt: timestamp("created_at").defaultNow().notNull(), }, (t) => ({ uniqueWatch: uniqueIndex("watchlist_season_team_participant_unique").on(t.seasonId, t.teamId, t.participantId), @@ -373,11 +379,14 @@ export const sportsSeasons = pgTable("sports_seasons", { updatedAt: timestamp("updated_at").defaultNow().notNull(), }); -export const participants = pgTable("participants", { +export const seasonParticipants = pgTable("season_participants", { id: uuid("id").primaryKey().defaultRandom(), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), + participantId: uuid("participant_id").references(() => participants.id, { + onDelete: "restrict", + }), name: varchar("name", { length: 255 }).notNull(), shortName: varchar("short_name", { length: 100 }), externalId: varchar("external_id", { length: 255 }), @@ -389,6 +398,96 @@ export const participants = pgTable("participants", { uniqueIndex("participants_sports_season_name_unique").on(table.sportsSeasonId, table.name), ]); +export const participants = pgTable( + "participants", + { + id: uuid("id").primaryKey().defaultRandom(), + sportId: uuid("sport_id") + .notNull() + .references(() => sports.id, { onDelete: "cascade" }), + name: varchar("name", { length: 255 }).notNull(), + externalKey: varchar("external_key", { length: 255 }), + metadata: jsonb("metadata"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), + }, + (t) => ({ + uniqueSportName: uniqueIndex("participants_sport_name_unique").on( + t.sportId, + t.name, + ), + }), +); + +export const tournaments = pgTable( + "tournaments", + { + id: uuid("id").primaryKey().defaultRandom(), + sportId: uuid("sport_id") + .notNull() + .references(() => sports.id, { onDelete: "cascade" }), + name: varchar("name", { length: 255 }).notNull(), + year: integer("year").notNull(), + startsAt: timestamp("starts_at"), + endsAt: timestamp("ends_at"), + surface: varchar("surface", { length: 50 }), // tennis only: hard | clay | grass + location: varchar("location", { length: 255 }), + status: tournamentStatusEnum("status").notNull().default("scheduled"), + externalKey: varchar("external_key", { length: 255 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), + }, + (t) => ({ + uniqueSportNameYear: uniqueIndex("tournaments_sport_name_year_unique").on( + t.sportId, + t.name, + t.year, + ), + }), +); + +export const tournamentResults = pgTable( + "tournament_results", + { + id: uuid("id").primaryKey().defaultRandom(), + tournamentId: uuid("tournament_id") + .notNull() + .references(() => tournaments.id, { onDelete: "cascade" }), + participantId: uuid("participant_id") + .notNull() + .references(() => participants.id, { onDelete: "cascade" }), + placement: integer("placement"), + rawScore: decimal("raw_score", { precision: 10, scale: 2 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), + }, + (t) => ({ + uniqueTournamentParticipant: uniqueIndex( + "tournament_results_tournament_participant_unique", + ).on(t.tournamentId, t.participantId), + }), +); + +export const participantSurfaceElos = pgTable( + "participant_surface_elos", + { + id: uuid("id").primaryKey().defaultRandom(), + participantId: uuid("participant_id") + .notNull() + .references(() => participants.id, { onDelete: "cascade" }), + worldRanking: integer("world_ranking"), + eloHard: integer("elo_hard"), + eloClay: integer("elo_clay"), + eloGrass: integer("elo_grass"), + updatedAt: timestamp("updated_at").defaultNow().notNull(), + }, + (t) => ({ + uniqueParticipant: uniqueIndex("participant_surface_elos_participant_unique").on( + t.participantId, + ), + }), +); + export const seasonTemplateSports = pgTable("season_template_sports", { id: uuid("id").primaryKey().defaultRandom(), templateId: uuid("template_id") @@ -411,11 +510,11 @@ export const seasonSports = pgTable("season_sports", { createdAt: timestamp("created_at").defaultNow().notNull(), }); -export const participantResults = pgTable("participant_results", { +export const seasonParticipantResults = pgTable("season_participant_results", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -435,6 +534,9 @@ export const scoringEvents = pgTable("scoring_events", { sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), + tournamentId: uuid("tournament_id").references(() => tournaments.id, { + onDelete: "set null", + }), name: varchar("name", { length: 255 }).notNull(), // "2024 Masters", "Super Bowl LIX", etc. eventDate: date("event_date"), eventStartsAt: timestamp("event_starts_at", { withTimezone: true }), @@ -460,9 +562,9 @@ export const eventResults = pgTable("event_results", { scoringEventId: uuid("scoring_event_id") .notNull() .references(() => scoringEvents.id, { onDelete: "cascade" }), - participantId: uuid("participant_id") + seasonParticipantId: uuid("season_participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), // Placement in this specific event placement: integer("placement"), // For qualifying events: QP awarded in this event @@ -483,10 +585,10 @@ export const playoffMatches = pgTable("playoff_matches", { .references(() => scoringEvents.id, { onDelete: "cascade" }), round: varchar("round", { length: 50 }).notNull(), // "Quarterfinals", "Semifinals", "Finals" matchNumber: integer("match_number").notNull(), // 1, 2, 3, 4 (for QF); 1, 2 (for SF); 1 (for F) - participant1Id: uuid("participant1_id").references(() => participants.id, { onDelete: "set null" }), - participant2Id: uuid("participant2_id").references(() => participants.id, { onDelete: "set null" }), - winnerId: uuid("winner_id").references(() => participants.id, { onDelete: "set null" }), - loserId: uuid("loser_id").references(() => participants.id, { onDelete: "set null" }), + participant1Id: uuid("participant1_id").references(() => seasonParticipants.id, { onDelete: "set null" }), + participant2Id: uuid("participant2_id").references(() => seasonParticipants.id, { onDelete: "set null" }), + winnerId: uuid("winner_id").references(() => seasonParticipants.id, { onDelete: "set null" }), + loserId: uuid("loser_id").references(() => seasonParticipants.id, { onDelete: "set null" }), isComplete: boolean("is_complete").notNull().default(false), // Optional detailed data participant1Score: decimal("participant1_score", { precision: 10, scale: 2 }), @@ -510,7 +612,7 @@ export const playoffMatchGames = pgTable("playoff_match_games", { status: playoffMatchGameStatusEnum("status").notNull().default("scheduled"), participant1Score: decimal("participant1_score", { precision: 10, scale: 2 }), participant2Score: decimal("participant2_score", { precision: 10, scale: 2 }), - winnerId: uuid("winner_id").references(() => participants.id, { onDelete: "set null" }), + winnerId: uuid("winner_id").references(() => seasonParticipants.id, { onDelete: "set null" }), notes: text("notes"), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), @@ -524,7 +626,7 @@ export const playoffMatchOdds = pgTable("playoff_match_odds", { .references(() => playoffMatches.id, { onDelete: "cascade" }), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), moneylineOdds: integer("moneyline_odds"), // American format e.g. -110, +150 impliedProbability: decimal("implied_probability", { precision: 6, scale: 4 }), oddsSource: varchar("odds_source", { length: 100 }), @@ -534,11 +636,11 @@ export const playoffMatchOdds = pgTable("playoff_match_odds", { }); // Aggregated participant qualifying points (for qualifying_points sports) -export const participantQualifyingTotals = pgTable("participant_qualifying_totals", { +export const seasonParticipantQualifyingTotals = pgTable("season_participant_qualifying_totals", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -639,11 +741,11 @@ export const teamStandingsSnapshots = pgTable("team_standings_snapshots", { })); // Expected value tracking (sports-season-specific) -export const participantExpectedValues = pgTable("participant_expected_values", { +export const seasonParticipantExpectedValues = pgTable("season_participant_expected_values", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -688,7 +790,7 @@ export const tournamentGroupMembers = pgTable("tournament_group_members", { .references(() => tournamentGroups.id, { onDelete: "cascade" }), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), eliminated: boolean("eliminated").notNull().default(false), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().notNull(), @@ -701,10 +803,10 @@ export const groupStageMatches = pgTable("group_stage_matches", { .references(() => tournamentGroups.id, { onDelete: "cascade" }), participant1Id: uuid("participant1_id") .notNull() - .references(() => participants.id), + .references(() => seasonParticipants.id), participant2Id: uuid("participant2_id") .notNull() - .references(() => participants.id), + .references(() => seasonParticipants.id), participant1Score: integer("participant1_score"), participant2Score: integer("participant2_score"), isComplete: boolean("is_complete").notNull().default(false), @@ -723,7 +825,7 @@ export const participantSeasonResults = pgTable("participant_season_results", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -737,7 +839,7 @@ export const participantEvSnapshots = pgTable("participant_ev_snapshots", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -783,6 +885,8 @@ export const teamEvSnapshots = pgTable("team_ev_snapshots", { // Relations export const sportsRelations = relations(sports, ({ many }) => ({ sportsSeasons: many(sportsSeasons), + tournaments: many(tournaments), + participants: many(participants), })); export const sportsSeasonsRelations = relations(sportsSeasons, ({ one, many }) => ({ @@ -790,23 +894,70 @@ export const sportsSeasonsRelations = relations(sportsSeasons, ({ one, many }) = fields: [sportsSeasons.sportId], references: [sports.id], }), - participants: many(participants), + participants: many(seasonParticipants), seasonTemplateSports: many(seasonTemplateSports), seasonSports: many(seasonSports), - participantResults: many(participantResults), + participantResults: many(seasonParticipantResults), regularSeasonStandings: many(regularSeasonStandings), pendingStandingsMappings: many(pendingStandingsMappings), })); -export const participantsRelations = relations(participants, ({ one, many }) => ({ +export const seasonParticipantsRelations = relations(seasonParticipants, ({ one, many }) => ({ sportsSeason: one(sportsSeasons, { - fields: [participants.sportsSeasonId], + fields: [seasonParticipants.sportsSeasonId], references: [sportsSeasons.id], }), - results: many(participantResults), + canonicalParticipant: one(participants, { + fields: [seasonParticipants.participantId], + references: [participants.id], + }), + results: many(seasonParticipantResults), regularSeasonStandings: many(regularSeasonStandings), })); +export const participantsRelations = relations(participants, ({ one, many }) => ({ + sport: one(sports, { + fields: [participants.sportId], + references: [sports.id], + }), + seasonParticipants: many(seasonParticipants), + tournamentResults: many(tournamentResults), + surfaceElo: one(participantSurfaceElos, { + fields: [participants.id], + references: [participantSurfaceElos.participantId], + }), +})); + +export const tournamentsRelations = relations(tournaments, ({ one, many }) => ({ + sport: one(sports, { + fields: [tournaments.sportId], + references: [sports.id], + }), + scoringEvents: many(scoringEvents), + tournamentResults: many(tournamentResults), +})); + +export const tournamentResultsRelations = relations(tournamentResults, ({ one }) => ({ + tournament: one(tournaments, { + fields: [tournamentResults.tournamentId], + references: [tournaments.id], + }), + participant: one(participants, { + fields: [tournamentResults.participantId], + references: [participants.id], + }), +})); + +export const participantSurfaceElosRelations = relations( + participantSurfaceElos, + ({ one }) => ({ + participant: one(participants, { + fields: [participantSurfaceElos.participantId], + references: [participants.id], + }), + }), +); + export const seasonTemplatesRelations = relations(seasonTemplates, ({ many }) => ({ seasonTemplateSports: many(seasonTemplateSports), seasons: many(seasons), @@ -847,13 +998,13 @@ export const seasonSportsRelations = relations(seasonSports, ({ one }) => ({ }), })); -export const participantResultsRelations = relations(participantResults, ({ one }) => ({ - participant: one(participants, { - fields: [participantResults.participantId], - references: [participants.id], +export const seasonParticipantResultsRelations = relations(seasonParticipantResults, ({ one }) => ({ + participant: one(seasonParticipants, { + fields: [seasonParticipantResults.participantId], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { - fields: [participantResults.sportsSeasonId], + fields: [seasonParticipantResults.sportsSeasonId], references: [sportsSeasons.id], }), })); @@ -889,9 +1040,9 @@ export const draftPicksRelations = relations(draftPicks, ({ one }) => ({ fields: [draftPicks.teamId], references: [teams.id], }), - participant: one(participants, { + participant: one(seasonParticipants, { fields: [draftPicks.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), })); @@ -904,9 +1055,9 @@ export const draftQueueRelations = relations(draftQueue, ({ one }) => ({ fields: [draftQueue.teamId], references: [teams.id], }), - participant: one(participants, { + participant: one(seasonParticipants, { fields: [draftQueue.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), })); @@ -919,9 +1070,9 @@ export const watchlistRelations = relations(watchlist, ({ one }) => ({ fields: [watchlist.teamId], references: [teams.id], }), - participant: one(participants, { + participant: one(seasonParticipants, { fields: [watchlist.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), })); @@ -943,6 +1094,10 @@ export const scoringEventsRelations = relations(scoringEvents, ({ one, many }) = fields: [scoringEvents.sportsSeasonId], references: [sportsSeasons.id], }), + tournament: one(tournaments, { + fields: [scoringEvents.tournamentId], + references: [tournaments.id], + }), eventResults: many(eventResults), playoffMatches: many(playoffMatches), tournamentGroups: many(tournamentGroups), @@ -954,9 +1109,9 @@ export const eventResultsRelations = relations(eventResults, ({ one }) => ({ fields: [eventResults.scoringEventId], references: [scoringEvents.id], }), - participant: one(participants, { - fields: [eventResults.participantId], - references: [participants.id], + seasonParticipant: one(seasonParticipants, { + fields: [eventResults.seasonParticipantId], + references: [seasonParticipants.id], }), })); @@ -965,21 +1120,21 @@ export const playoffMatchesRelations = relations(playoffMatches, ({ one, many }) fields: [playoffMatches.scoringEventId], references: [scoringEvents.id], }), - participant1: one(participants, { + participant1: one(seasonParticipants, { fields: [playoffMatches.participant1Id], - references: [participants.id], + references: [seasonParticipants.id], }), - participant2: one(participants, { + participant2: one(seasonParticipants, { fields: [playoffMatches.participant2Id], - references: [participants.id], + references: [seasonParticipants.id], }), - winner: one(participants, { + winner: one(seasonParticipants, { fields: [playoffMatches.winnerId], - references: [participants.id], + references: [seasonParticipants.id], }), - loser: one(participants, { + loser: one(seasonParticipants, { fields: [playoffMatches.loserId], - references: [participants.id], + references: [seasonParticipants.id], }), games: many(playoffMatchGames), odds: many(playoffMatchOdds), @@ -990,9 +1145,9 @@ export const playoffMatchGamesRelations = relations(playoffMatchGames, ({ one }) fields: [playoffMatchGames.playoffMatchId], references: [playoffMatches.id], }), - winner: one(participants, { + winner: one(seasonParticipants, { fields: [playoffMatchGames.winnerId], - references: [participants.id], + references: [seasonParticipants.id], }), })); @@ -1001,19 +1156,19 @@ export const playoffMatchOddsRelations = relations(playoffMatchOdds, ({ one }) = fields: [playoffMatchOdds.playoffMatchId], references: [playoffMatches.id], }), - participant: one(participants, { + participant: one(seasonParticipants, { fields: [playoffMatchOdds.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), })); -export const participantQualifyingTotalsRelations = relations(participantQualifyingTotals, ({ one }) => ({ - participant: one(participants, { - fields: [participantQualifyingTotals.participantId], - references: [participants.id], +export const seasonParticipantQualifyingTotalsRelations = relations(seasonParticipantQualifyingTotals, ({ one }) => ({ + participant: one(seasonParticipants, { + fields: [seasonParticipantQualifyingTotals.participantId], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { - fields: [participantQualifyingTotals.sportsSeasonId], + fields: [seasonParticipantQualifyingTotals.sportsSeasonId], references: [sportsSeasons.id], }), })); @@ -1058,21 +1213,21 @@ export const teamStandingsSnapshotsRelations = relations(teamStandingsSnapshots, }), })); -export const participantExpectedValuesRelations = relations(participantExpectedValues, ({ one }) => ({ - participant: one(participants, { - fields: [participantExpectedValues.participantId], - references: [participants.id], +export const seasonParticipantExpectedValuesRelations = relations(seasonParticipantExpectedValues, ({ one }) => ({ + participant: one(seasonParticipants, { + fields: [seasonParticipantExpectedValues.participantId], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { - fields: [participantExpectedValues.sportsSeasonId], + fields: [seasonParticipantExpectedValues.sportsSeasonId], references: [sportsSeasons.id], }), })); export const participantSeasonResultsRelations = relations(participantSeasonResults, ({ one }) => ({ - participant: one(participants, { + participant: one(seasonParticipants, { fields: [participantSeasonResults.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { fields: [participantSeasonResults.sportsSeasonId], @@ -1095,9 +1250,9 @@ export const tournamentGroupMembersRelations = relations(tournamentGroupMembers, fields: [tournamentGroupMembers.tournamentGroupId], references: [tournamentGroups.id], }), - participant: one(participants, { + participant: one(seasonParticipants, { fields: [tournamentGroupMembers.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), })); @@ -1106,22 +1261,22 @@ export const groupStageMatchesRelations = relations(groupStageMatches, ({ one }) fields: [groupStageMatches.tournamentGroupId], references: [tournamentGroups.id], }), - participant1: one(participants, { + participant1: one(seasonParticipants, { fields: [groupStageMatches.participant1Id], - references: [participants.id], + references: [seasonParticipants.id], relationName: "groupMatchParticipant1", }), - participant2: one(participants, { + participant2: one(seasonParticipants, { fields: [groupStageMatches.participant2Id], - references: [participants.id], + references: [seasonParticipants.id], relationName: "groupMatchParticipant2", }), })); export const participantEvSnapshotsRelations = relations(participantEvSnapshots, ({ one }) => ({ - participant: one(participants, { + participant: one(seasonParticipants, { fields: [participantEvSnapshots.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { fields: [participantEvSnapshots.sportsSeasonId], @@ -1146,7 +1301,7 @@ export const regularSeasonStandings = pgTable("regular_season_standings", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -1177,9 +1332,9 @@ export const regularSeasonStandings = pgTable("regular_season_standings", { })); export const regularSeasonStandingsRelations = relations(regularSeasonStandings, ({ one }) => ({ - participant: one(participants, { + participant: one(seasonParticipants, { fields: [regularSeasonStandings.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { fields: [regularSeasonStandings.sportsSeasonId], @@ -1216,11 +1371,11 @@ export const pendingStandingsMappingsRelations = relations(pendingStandingsMappi // Stores surface-specific Elo ratings for tennis (and future surface-based sports). // One row per (participantId, sportsSeasonId); nullable per surface. -export const participantSurfaceElos = pgTable("participant_surface_elos", { +export const seasonParticipantSurfaceElos = pgTable("season_participant_surface_elos", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -1234,13 +1389,13 @@ export const participantSurfaceElos = pgTable("participant_surface_elos", { .on(t.participantId, t.sportsSeasonId), })); -export const participantSurfaceElosRelations = relations(participantSurfaceElos, ({ one }) => ({ - participant: one(participants, { - fields: [participantSurfaceElos.participantId], - references: [participants.id], +export const seasonParticipantSurfaceElosRelations = relations(seasonParticipantSurfaceElos, ({ one }) => ({ + participant: one(seasonParticipants, { + fields: [seasonParticipantSurfaceElos.participantId], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { - fields: [participantSurfaceElos.sportsSeasonId], + fields: [seasonParticipantSurfaceElos.sportsSeasonId], references: [sportsSeasons.id], }), })); @@ -1255,7 +1410,7 @@ export const participantGolfSkills = pgTable("participant_golf_skills", { id: uuid("id").primaryKey().defaultRandom(), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), sportsSeasonId: uuid("sports_season_id") .notNull() .references(() => sportsSeasons.id, { onDelete: "cascade" }), @@ -1275,9 +1430,9 @@ export const participantGolfSkills = pgTable("participant_golf_skills", { })); export const participantGolfSkillsRelations = relations(participantGolfSkills, ({ one }) => ({ - participant: one(participants, { + participant: one(seasonParticipants, { fields: [participantGolfSkills.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), sportsSeason: one(sportsSeasons, { fields: [participantGolfSkills.sportsSeasonId], @@ -1302,7 +1457,7 @@ export const cs2MajorStageResults = pgTable("cs2_major_stage_results", { .references(() => scoringEvents.id, { onDelete: "cascade" }), participantId: uuid("participant_id") .notNull() - .references(() => participants.id, { onDelete: "cascade" }), + .references(() => seasonParticipants.id, { onDelete: "cascade" }), stageEntry: integer("stage_entry").notNull(), stageEliminated: integer("stage_eliminated"), stageEliminatedWins: integer("stage_eliminated_wins"), @@ -1319,9 +1474,9 @@ export const cs2MajorStageResultsRelations = relations(cs2MajorStageResults, ({ fields: [cs2MajorStageResults.scoringEventId], references: [scoringEvents.id], }), - participant: one(participants, { + participant: one(seasonParticipants, { fields: [cs2MajorStageResults.participantId], - references: [participants.id], + references: [seasonParticipants.id], }), })); diff --git a/drizzle/0087_small_susan_delgado.sql b/drizzle/0087_small_susan_delgado.sql new file mode 100644 index 0000000..346fea1 --- /dev/null +++ b/drizzle/0087_small_susan_delgado.sql @@ -0,0 +1,221 @@ +ALTER TABLE "participant_expected_values" RENAME TO "season_participant_expected_values";--> statement-breakpoint +ALTER TABLE "participant_qualifying_totals" RENAME TO "season_participant_qualifying_totals";--> statement-breakpoint +ALTER TABLE "participant_results" RENAME TO "season_participant_results";--> statement-breakpoint +ALTER TABLE "participant_surface_elos" RENAME TO "season_participant_surface_elos";--> statement-breakpoint +ALTER TABLE "participants" RENAME TO "season_participants";--> statement-breakpoint +ALTER TABLE "event_results" RENAME COLUMN "participant_id" TO "season_participant_id";--> statement-breakpoint +ALTER TABLE "cs2_major_stage_results" DROP CONSTRAINT IF EXISTS "cs2_major_stage_results_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "draft_picks" DROP CONSTRAINT IF EXISTS "draft_picks_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "draft_queue" DROP CONSTRAINT IF EXISTS "draft_queue_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "event_results" DROP CONSTRAINT IF EXISTS "event_results_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "group_stage_matches" DROP CONSTRAINT IF EXISTS "group_stage_matches_participant1_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "group_stage_matches" DROP CONSTRAINT IF EXISTS "group_stage_matches_participant2_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "participant_ev_snapshots" DROP CONSTRAINT IF EXISTS "participant_ev_snapshots_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_expected_values" DROP CONSTRAINT IF EXISTS "participant_expected_values_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_expected_values" DROP CONSTRAINT IF EXISTS "participant_expected_values_sports_season_id_sports_seasons_id_fk"; +--> statement-breakpoint +ALTER TABLE "participant_golf_skills" DROP CONSTRAINT IF EXISTS "participant_golf_skills_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_qualifying_totals" DROP CONSTRAINT IF EXISTS "participant_qualifying_totals_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_qualifying_totals" DROP CONSTRAINT IF EXISTS "participant_qualifying_totals_sports_season_id_sports_seasons_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_results" DROP CONSTRAINT IF EXISTS "participant_results_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_results" DROP CONSTRAINT IF EXISTS "participant_results_sports_season_id_sports_seasons_id_fk"; +--> statement-breakpoint +ALTER TABLE "participant_season_results" DROP CONSTRAINT IF EXISTS "participant_season_results_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_surface_elos" DROP CONSTRAINT IF EXISTS "participant_surface_elos_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participant_surface_elos" DROP CONSTRAINT IF EXISTS "participant_surface_elos_sports_season_id_sports_seasons_id_fk"; +--> statement-breakpoint +ALTER TABLE "season_participants" DROP CONSTRAINT IF EXISTS "participants_sports_season_id_sports_seasons_id_fk"; +--> statement-breakpoint +ALTER TABLE "playoff_match_games" DROP CONSTRAINT IF EXISTS "playoff_match_games_winner_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "playoff_match_odds" DROP CONSTRAINT IF EXISTS "playoff_match_odds_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "playoff_matches" DROP CONSTRAINT IF EXISTS "playoff_matches_participant1_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "playoff_matches" DROP CONSTRAINT IF EXISTS "playoff_matches_participant2_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "playoff_matches" DROP CONSTRAINT IF EXISTS "playoff_matches_winner_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "playoff_matches" DROP CONSTRAINT IF EXISTS "playoff_matches_loser_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "regular_season_standings" DROP CONSTRAINT IF EXISTS "regular_season_standings_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "tournament_group_members" DROP CONSTRAINT IF EXISTS "tournament_group_members_participant_id_participants_id_fk"; +--> statement-breakpoint +ALTER TABLE "watchlist" DROP CONSTRAINT IF EXISTS "watchlist_participant_id_participants_id_fk"; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "cs2_major_stage_results" ADD CONSTRAINT "cs2_major_stage_results_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "draft_picks" ADD CONSTRAINT "draft_picks_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "draft_queue" ADD CONSTRAINT "draft_queue_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "event_results" ADD CONSTRAINT "event_results_season_participant_id_season_participants_id_fk" FOREIGN KEY ("season_participant_id") REFERENCES "public"."season_participants"("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_season_participants_id_fk" FOREIGN KEY ("participant1_id") REFERENCES "public"."season_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_season_participants_id_fk" FOREIGN KEY ("participant2_id") REFERENCES "public"."season_participants"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "participant_ev_snapshots" ADD CONSTRAINT "participant_ev_snapshots_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_expected_values" ADD CONSTRAINT "season_participant_expected_values_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_expected_values" ADD CONSTRAINT "season_participant_expected_values_sports_season_id_sports_seasons_id_fk" FOREIGN KEY ("sports_season_id") REFERENCES "public"."sports_seasons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "participant_golf_skills" ADD CONSTRAINT "participant_golf_skills_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_qualifying_totals" ADD CONSTRAINT "season_participant_qualifying_totals_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_qualifying_totals" ADD CONSTRAINT "season_participant_qualifying_totals_sports_season_id_sports_seasons_id_fk" FOREIGN KEY ("sports_season_id") REFERENCES "public"."sports_seasons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_results" ADD CONSTRAINT "season_participant_results_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_results" ADD CONSTRAINT "season_participant_results_sports_season_id_sports_seasons_id_fk" FOREIGN KEY ("sports_season_id") REFERENCES "public"."sports_seasons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "participant_season_results" ADD CONSTRAINT "participant_season_results_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_surface_elos" ADD CONSTRAINT "season_participant_surface_elos_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participant_surface_elos" ADD CONSTRAINT "season_participant_surface_elos_sports_season_id_sports_seasons_id_fk" FOREIGN KEY ("sports_season_id") REFERENCES "public"."sports_seasons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participants" ADD CONSTRAINT "season_participants_sports_season_id_sports_seasons_id_fk" FOREIGN KEY ("sports_season_id") REFERENCES "public"."sports_seasons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "playoff_match_games" ADD CONSTRAINT "playoff_match_games_winner_id_season_participants_id_fk" FOREIGN KEY ("winner_id") REFERENCES "public"."season_participants"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "playoff_match_odds" ADD CONSTRAINT "playoff_match_odds_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "playoff_matches" ADD CONSTRAINT "playoff_matches_participant1_id_season_participants_id_fk" FOREIGN KEY ("participant1_id") REFERENCES "public"."season_participants"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "playoff_matches" ADD CONSTRAINT "playoff_matches_participant2_id_season_participants_id_fk" FOREIGN KEY ("participant2_id") REFERENCES "public"."season_participants"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "playoff_matches" ADD CONSTRAINT "playoff_matches_winner_id_season_participants_id_fk" FOREIGN KEY ("winner_id") REFERENCES "public"."season_participants"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "playoff_matches" ADD CONSTRAINT "playoff_matches_loser_id_season_participants_id_fk" FOREIGN KEY ("loser_id") REFERENCES "public"."season_participants"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "regular_season_standings" ADD CONSTRAINT "regular_season_standings_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tournament_group_members" ADD CONSTRAINT "tournament_group_members_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "watchlist" ADD CONSTRAINT "watchlist_participant_id_season_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."season_participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/drizzle/0088_cheerful_norrin_radd.sql b/drizzle/0088_cheerful_norrin_radd.sql new file mode 100644 index 0000000..d035bf4 --- /dev/null +++ b/drizzle/0088_cheerful_norrin_radd.sql @@ -0,0 +1,93 @@ +CREATE TYPE "public"."tournament_status" AS ENUM('scheduled', 'in_progress', 'completed');--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "participant_surface_elos" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "participant_id" uuid NOT NULL, + "world_ranking" integer, + "elo_hard" integer, + "elo_clay" integer, + "elo_grass" integer, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "participants" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "sport_id" uuid NOT NULL, + "name" varchar(255) NOT NULL, + "external_key" varchar(255), + "metadata" jsonb, + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "tournament_results" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "tournament_id" uuid NOT NULL, + "participant_id" uuid NOT NULL, + "placement" integer, + "raw_score" numeric(10, 2), + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "tournaments" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "sport_id" uuid NOT NULL, + "name" varchar(255) NOT NULL, + "year" integer NOT NULL, + "starts_at" timestamp, + "ends_at" timestamp, + "surface" varchar(50), + "location" varchar(255), + "status" "tournament_status" DEFAULT 'scheduled' NOT NULL, + "external_key" varchar(255), + "created_at" timestamp DEFAULT now() NOT NULL, + "updated_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "scoring_events" ADD COLUMN "tournament_id" uuid;--> statement-breakpoint +ALTER TABLE "season_participants" ADD COLUMN "participant_id" uuid;--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "participant_surface_elos" ADD CONSTRAINT "participant_surface_elos_participant_id_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "participants" ADD CONSTRAINT "participants_sport_id_sports_id_fk" FOREIGN KEY ("sport_id") REFERENCES "public"."sports"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tournament_results" ADD CONSTRAINT "tournament_results_tournament_id_tournaments_id_fk" FOREIGN KEY ("tournament_id") REFERENCES "public"."tournaments"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tournament_results" ADD CONSTRAINT "tournament_results_participant_id_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."participants"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tournaments" ADD CONSTRAINT "tournaments_sport_id_sports_id_fk" FOREIGN KEY ("sport_id") REFERENCES "public"."sports"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "participant_surface_elos_participant_unique" ON "participant_surface_elos" USING btree ("participant_id");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "participants_sport_name_unique" ON "participants" USING btree ("sport_id","name");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "tournament_results_tournament_participant_unique" ON "tournament_results" USING btree ("tournament_id","participant_id");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "tournaments_sport_name_year_unique" ON "tournaments" USING btree ("sport_id","name","year");--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "scoring_events" ADD CONSTRAINT "scoring_events_tournament_id_tournaments_id_fk" FOREIGN KEY ("tournament_id") REFERENCES "public"."tournaments"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "season_participants" ADD CONSTRAINT "season_participants_participant_id_participants_id_fk" FOREIGN KEY ("participant_id") REFERENCES "public"."participants"("id") ON DELETE restrict ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/drizzle/meta/0087_snapshot.json b/drizzle/meta/0087_snapshot.json new file mode 100644 index 0000000..23b423c --- /dev/null +++ b/drizzle/meta/0087_snapshot.json @@ -0,0 +1,5221 @@ +{ + "id": "e93f3b1e-7804-4550-a740-a85b21ab0588", + "prevId": "da70c605-a87a-4653-85ed-427055b273c5", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "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": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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.commissioner_audit_log": { + "name": "commissioner_audit_log", + "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 + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "actor_display_name": { + "name": "actor_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "audit_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "affected_team_ids": { + "name": "affected_team_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_log_season_id_idx": { + "name": "audit_log_season_id_idx", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "commissioner_audit_log_season_id_seasons_id_fk": { + "name": "commissioner_audit_log_season_id_seasons_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "commissioner_audit_log_league_id_leagues_id_fk": { + "name": "commissioner_audit_log_league_id_leagues_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "leagues", + "columnsFrom": [ + "league_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.cs2_major_stage_results": { + "name": "cs2_major_stage_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 + }, + "stage_entry": { + "name": "stage_entry", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "stage_eliminated": { + "name": "stage_eliminated", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "stage_eliminated_wins": { + "name": "stage_eliminated_wins", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "final_placement": { + "name": "final_placement", + "type": "integer", + "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": { + "cs2_major_stage_results_unique": { + "name": "cs2_major_stage_results_unique", + "columns": [ + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk": { + "name": "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cs2_major_stage_results_participant_id_season_participants_id_fk": { + "name": "cs2_major_stage_results_participant_id_season_participants_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_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_season_participants_id_fk": { + "name": "draft_picks_participant_id_season_participants_id_fk", + "tableFrom": "draft_picks", + "tableTo": "season_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_season_participants_id_fk": { + "name": "draft_queue_participant_id_season_participants_id_fk", + "tableFrom": "draft_queue", + "tableTo": "season_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 + }, + "season_participant_id": { + "name": "season_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_season_participant_id_season_participants_id_fk": { + "name": "event_results_season_participant_id_season_participants_id_fk", + "tableFrom": "event_results", + "tableTo": "season_participants", + "columnsFrom": [ + "season_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_season_participants_id_fk": { + "name": "group_stage_matches_participant1_id_season_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "group_stage_matches_participant2_id_season_participants_id_fk": { + "name": "group_stage_matches_participant2_id_season_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "season_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_season_participants_id_fk": { + "name": "participant_ev_snapshots_participant_id_season_participants_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "season_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_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_season_participants_id_fk": { + "name": "participant_golf_skills_participant_id_season_participants_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "season_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_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_season_participants_id_fk": { + "name": "participant_season_results_participant_id_season_participants_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "season_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.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_season_participants_id_fk": { + "name": "playoff_match_games_winner_id_season_participants_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "season_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_season_participants_id_fk": { + "name": "playoff_match_odds_participant_id_season_participants_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "season_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_season_participants_id_fk": { + "name": "playoff_matches_participant1_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_participant2_id_season_participants_id_fk": { + "name": "playoff_matches_participant2_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_winner_id_season_participants_id_fk": { + "name": "playoff_matches_winner_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_loser_id_season_participants_id_fk": { + "name": "playoff_matches_loser_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_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_season_participants_id_fk": { + "name": "regular_season_standings_participant_id_season_participants_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "season_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_participant_expected_values": { + "name": "season_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 + }, + "world_ranking": { + "name": "world_ranking", + "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": { + "season_participant_expected_values_participant_id_season_participants_id_fk": { + "name": "season_participant_expected_values_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_expected_values", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_expected_values_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_expected_values_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_expected_values", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_qualifying_totals": { + "name": "season_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": { + "season_participant_qualifying_totals_participant_id_season_participants_id_fk": { + "name": "season_participant_qualifying_totals_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_qualifying_totals", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_qualifying_totals_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_qualifying_totals_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_qualifying_totals", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_results": { + "name": "season_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": { + "season_participant_results_participant_id_season_participants_id_fk": { + "name": "season_participant_results_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_results", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_results_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_surface_elos": { + "name": "season_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": { + "season_participant_surface_elos_participant_id_season_participants_id_fk": { + "name": "season_participant_surface_elos_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_surface_elos", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_surface_elos_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_surface_elos_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_surface_elos", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participants": { + "name": "season_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'" + }, + "vorp_value": { + "name": "vorp_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": { + "season_participants_sports_season_id_sports_seasons_id_fk": { + "name": "season_participants_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participants", + "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_completed_at": { + "name": "draft_completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_paused": { + "name": "draft_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "overnight_pause_mode": { + "name": "overnight_pause_mode", + "type": "overnight_pause_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "overnight_pause_start": { + "name": "overnight_pause_start", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "overnight_pause_end": { + "name": "overnight_pause_end", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "overnight_pause_timezone": { + "name": "overnight_pause_timezone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": 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.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "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()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "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'" + }, + "draft_on": { + "name": "draft_on", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "draft_off": { + "name": "draft_off", + "type": "date", + "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": { + "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_score_events": { + "name": "team_score_events", + "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 + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "scoring_event_name": { + "name": "scoring_event_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "sport_name": { + "name": "sport_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "match_id": { + "name": "match_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "participant_ids": { + "name": "participant_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "points_delta": { + "name": "points_delta", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_score_events_match_unique": { + "name": "team_score_events_match_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "match_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "match_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_score_events_event_unique": { + "name": "team_score_events_event_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "match_id IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_score_events_team_id_teams_id_fk": { + "name": "team_score_events_team_id_teams_id_fk", + "tableFrom": "team_score_events", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_score_events_season_id_seasons_id_fk": { + "name": "team_score_events_season_id_seasons_id_fk", + "tableFrom": "team_score_events", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_score_events_scoring_event_id_scoring_events_id_fk": { + "name": "team_score_events_scoring_event_id_scoring_events_id_fk", + "tableFrom": "team_score_events", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "team_score_events_match_id_playoff_matches_id_fk": { + "name": "team_score_events_match_id_playoff_matches_id_fk", + "tableFrom": "team_score_events", + "tableTo": "playoff_matches", + "columnsFrom": [ + "match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "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_season_participants_id_fk": { + "name": "tournament_group_members_participant_id_season_participants_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "season_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": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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 + }, + "timezone": { + "name": "timezone", + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verifications": { + "name": "verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.watchlist": { + "name": "watchlist", + "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 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "watchlist_season_team_participant_unique": { + "name": "watchlist_season_team_participant_unique", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "watchlist_season_id_seasons_id_fk": { + "name": "watchlist_season_id_seasons_id_fk", + "tableFrom": "watchlist", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "watchlist_team_id_teams_id_fk": { + "name": "watchlist_team_id_teams_id_fk", + "tableFrom": "watchlist", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "watchlist_participant_id_season_participants_id_fk": { + "name": "watchlist_participant_id_season_participants_id_fk", + "tableFrom": "watchlist", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.audit_action": { + "name": "audit_action", + "schema": "public", + "values": [ + "league_settings_changed", + "draft_settings_changed", + "scoring_rules_changed", + "sports_changed", + "draft_order_set", + "draft_order_randomized", + "draft_started", + "draft_paused", + "draft_resumed", + "draft_reset", + "draft_rollback", + "force_autopick", + "force_manual_pick", + "draft_pick_changed", + "time_bank_edited" + ] + }, + "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.overnight_pause_mode": { + "name": "overnight_pause_mode", + "schema": "public", + "values": [ + "none", + "league", + "per_user" + ] + }, + "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", + "nfl_bracket", + "afl_bracket", + "snooker_bracket", + "tennis_qualifying_points", + "mlb_bracket", + "wnba_bracket", + "world_cup", + "darts_bracket", + "cs2_major_qualifying_points", + "ncaa_football_bracket", + "llws_bracket" + ] + }, + "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/0088_snapshot.json b/drizzle/meta/0088_snapshot.json new file mode 100644 index 0000000..00d1949 --- /dev/null +++ b/drizzle/meta/0088_snapshot.json @@ -0,0 +1,5688 @@ +{ + "id": "feb9e343-a91c-4c94-a77c-6a1d876e42b5", + "prevId": "e93f3b1e-7804-4550-a740-a85b21ab0588", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.accounts": { + "name": "accounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "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": { + "accounts_user_id_users_id_fk": { + "name": "accounts_user_id_users_id_fk", + "tableFrom": "accounts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "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.commissioner_audit_log": { + "name": "commissioner_audit_log", + "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 + }, + "league_id": { + "name": "league_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "actor_display_name": { + "name": "actor_display_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "action": { + "name": "action", + "type": "audit_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "affected_team_ids": { + "name": "affected_team_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "details": { + "name": "details", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "audit_log_season_id_idx": { + "name": "audit_log_season_id_idx", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "commissioner_audit_log_season_id_seasons_id_fk": { + "name": "commissioner_audit_log_season_id_seasons_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "commissioner_audit_log_league_id_leagues_id_fk": { + "name": "commissioner_audit_log_league_id_leagues_id_fk", + "tableFrom": "commissioner_audit_log", + "tableTo": "leagues", + "columnsFrom": [ + "league_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.cs2_major_stage_results": { + "name": "cs2_major_stage_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 + }, + "stage_entry": { + "name": "stage_entry", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "stage_eliminated": { + "name": "stage_eliminated", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "stage_eliminated_wins": { + "name": "stage_eliminated_wins", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "final_placement": { + "name": "final_placement", + "type": "integer", + "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": { + "cs2_major_stage_results_unique": { + "name": "cs2_major_stage_results_unique", + "columns": [ + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk": { + "name": "cs2_major_stage_results_scoring_event_id_scoring_events_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cs2_major_stage_results_participant_id_season_participants_id_fk": { + "name": "cs2_major_stage_results_participant_id_season_participants_id_fk", + "tableFrom": "cs2_major_stage_results", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_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_season_participants_id_fk": { + "name": "draft_picks_participant_id_season_participants_id_fk", + "tableFrom": "draft_picks", + "tableTo": "season_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_season_participants_id_fk": { + "name": "draft_queue_participant_id_season_participants_id_fk", + "tableFrom": "draft_queue", + "tableTo": "season_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 + }, + "season_participant_id": { + "name": "season_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_season_participant_id_season_participants_id_fk": { + "name": "event_results_season_participant_id_season_participants_id_fk", + "tableFrom": "event_results", + "tableTo": "season_participants", + "columnsFrom": [ + "season_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_season_participants_id_fk": { + "name": "group_stage_matches_participant1_id_season_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "group_stage_matches_participant2_id_season_participants_id_fk": { + "name": "group_stage_matches_participant2_id_season_participants_id_fk", + "tableFrom": "group_stage_matches", + "tableTo": "season_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_season_participants_id_fk": { + "name": "participant_ev_snapshots_participant_id_season_participants_id_fk", + "tableFrom": "participant_ev_snapshots", + "tableTo": "season_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_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_season_participants_id_fk": { + "name": "participant_golf_skills_participant_id_season_participants_id_fk", + "tableFrom": "participant_golf_skills", + "tableTo": "season_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_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_season_participants_id_fk": { + "name": "participant_season_results_participant_id_season_participants_id_fk", + "tableFrom": "participant_season_results", + "tableTo": "season_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 + }, + "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_participant_unique": { + "name": "participant_surface_elos_participant_unique", + "columns": [ + { + "expression": "participant_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" + } + }, + "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()" + }, + "sport_id": { + "name": "sport_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "external_key": { + "name": "external_key", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "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": { + "participants_sport_name_unique": { + "name": "participants_sport_name_unique", + "columns": [ + { + "expression": "sport_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "participants_sport_id_sports_id_fk": { + "name": "participants_sport_id_sports_id_fk", + "tableFrom": "participants", + "tableTo": "sports", + "columnsFrom": [ + "sport_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_season_participants_id_fk": { + "name": "playoff_match_games_winner_id_season_participants_id_fk", + "tableFrom": "playoff_match_games", + "tableTo": "season_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_season_participants_id_fk": { + "name": "playoff_match_odds_participant_id_season_participants_id_fk", + "tableFrom": "playoff_match_odds", + "tableTo": "season_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_season_participants_id_fk": { + "name": "playoff_matches_participant1_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant1_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_participant2_id_season_participants_id_fk": { + "name": "playoff_matches_participant2_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "participant2_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_winner_id_season_participants_id_fk": { + "name": "playoff_matches_winner_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_participants", + "columnsFrom": [ + "winner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "playoff_matches_loser_id_season_participants_id_fk": { + "name": "playoff_matches_loser_id_season_participants_id_fk", + "tableFrom": "playoff_matches", + "tableTo": "season_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_season_participants_id_fk": { + "name": "regular_season_standings_participant_id_season_participants_id_fk", + "tableFrom": "regular_season_standings", + "tableTo": "season_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 + }, + "tournament_id": { + "name": "tournament_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "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" + }, + "scoring_events_tournament_id_tournaments_id_fk": { + "name": "scoring_events_tournament_id_tournaments_id_fk", + "tableFrom": "scoring_events", + "tableTo": "tournaments", + "columnsFrom": [ + "tournament_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_expected_values": { + "name": "season_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 + }, + "world_ranking": { + "name": "world_ranking", + "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": { + "season_participant_expected_values_participant_id_season_participants_id_fk": { + "name": "season_participant_expected_values_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_expected_values", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_expected_values_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_expected_values_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_expected_values", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_qualifying_totals": { + "name": "season_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": { + "season_participant_qualifying_totals_participant_id_season_participants_id_fk": { + "name": "season_participant_qualifying_totals_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_qualifying_totals", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_qualifying_totals_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_qualifying_totals_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_qualifying_totals", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_results": { + "name": "season_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": { + "season_participant_results_participant_id_season_participants_id_fk": { + "name": "season_participant_results_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_results", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_results_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_results_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_results", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participant_surface_elos": { + "name": "season_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": { + "season_participant_surface_elos_participant_id_season_participants_id_fk": { + "name": "season_participant_surface_elos_participant_id_season_participants_id_fk", + "tableFrom": "season_participant_surface_elos", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participant_surface_elos_sports_season_id_sports_seasons_id_fk": { + "name": "season_participant_surface_elos_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participant_surface_elos", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.season_participants": { + "name": "season_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 + }, + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "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'" + }, + "vorp_value": { + "name": "vorp_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": { + "season_participants_sports_season_id_sports_seasons_id_fk": { + "name": "season_participants_sports_season_id_sports_seasons_id_fk", + "tableFrom": "season_participants", + "tableTo": "sports_seasons", + "columnsFrom": [ + "sports_season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "season_participants_participant_id_participants_id_fk": { + "name": "season_participants_participant_id_participants_id_fk", + "tableFrom": "season_participants", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "restrict", + "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_completed_at": { + "name": "draft_completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft_paused": { + "name": "draft_paused", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "overnight_pause_mode": { + "name": "overnight_pause_mode", + "type": "overnight_pause_mode", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "overnight_pause_start": { + "name": "overnight_pause_start", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "overnight_pause_end": { + "name": "overnight_pause_end", + "type": "varchar(5)", + "primaryKey": false, + "notNull": false + }, + "overnight_pause_timezone": { + "name": "overnight_pause_timezone", + "type": "varchar(50)", + "primaryKey": false, + "notNull": 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.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "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()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "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'" + }, + "draft_on": { + "name": "draft_on", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "draft_off": { + "name": "draft_off", + "type": "date", + "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": { + "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_score_events": { + "name": "team_score_events", + "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 + }, + "scoring_event_id": { + "name": "scoring_event_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "scoring_event_name": { + "name": "scoring_event_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "sport_name": { + "name": "sport_name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "match_id": { + "name": "match_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "participant_ids": { + "name": "participant_ids", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "points_delta": { + "name": "points_delta", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "occurred_at": { + "name": "occurred_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "team_score_events_match_unique": { + "name": "team_score_events_match_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "match_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "match_id IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + }, + "team_score_events_event_unique": { + "name": "team_score_events_event_unique", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scoring_event_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "where": "match_id IS NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_score_events_team_id_teams_id_fk": { + "name": "team_score_events_team_id_teams_id_fk", + "tableFrom": "team_score_events", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_score_events_season_id_seasons_id_fk": { + "name": "team_score_events_season_id_seasons_id_fk", + "tableFrom": "team_score_events", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_score_events_scoring_event_id_scoring_events_id_fk": { + "name": "team_score_events_scoring_event_id_scoring_events_id_fk", + "tableFrom": "team_score_events", + "tableTo": "scoring_events", + "columnsFrom": [ + "scoring_event_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "team_score_events_match_id_playoff_matches_id_fk": { + "name": "team_score_events_match_id_playoff_matches_id_fk", + "tableFrom": "team_score_events", + "tableTo": "playoff_matches", + "columnsFrom": [ + "match_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "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_season_participants_id_fk": { + "name": "tournament_group_members_participant_id_season_participants_id_fk", + "tableFrom": "tournament_group_members", + "tableTo": "season_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.tournament_results": { + "name": "tournament_results", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "tournament_id": { + "name": "tournament_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 + }, + "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": { + "tournament_results_tournament_participant_unique": { + "name": "tournament_results_tournament_participant_unique", + "columns": [ + { + "expression": "tournament_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tournament_results_tournament_id_tournaments_id_fk": { + "name": "tournament_results_tournament_id_tournaments_id_fk", + "tableFrom": "tournament_results", + "tableTo": "tournaments", + "columnsFrom": [ + "tournament_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tournament_results_participant_id_participants_id_fk": { + "name": "tournament_results_participant_id_participants_id_fk", + "tableFrom": "tournament_results", + "tableTo": "participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tournaments": { + "name": "tournaments", + "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 + }, + "starts_at": { + "name": "starts_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "ends_at": { + "name": "ends_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "tournament_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'scheduled'" + }, + "external_key": { + "name": "external_key", + "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": { + "tournaments_sport_name_year_unique": { + "name": "tournaments_sport_name_year_unique", + "columns": [ + { + "expression": "sport_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "year", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tournaments_sport_id_sports_id_fk": { + "name": "tournaments_sport_id_sports_id_fk", + "tableFrom": "tournaments", + "tableTo": "sports", + "columnsFrom": [ + "sport_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": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "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 + }, + "timezone": { + "name": "timezone", + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_clerk_id_unique": { + "name": "users_clerk_id_unique", + "nullsNotDistinct": false, + "columns": [ + "clerk_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verifications": { + "name": "verifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "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": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.watchlist": { + "name": "watchlist", + "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 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "watchlist_season_team_participant_unique": { + "name": "watchlist_season_team_participant_unique", + "columns": [ + { + "expression": "season_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "participant_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "watchlist_season_id_seasons_id_fk": { + "name": "watchlist_season_id_seasons_id_fk", + "tableFrom": "watchlist", + "tableTo": "seasons", + "columnsFrom": [ + "season_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "watchlist_team_id_teams_id_fk": { + "name": "watchlist_team_id_teams_id_fk", + "tableFrom": "watchlist", + "tableTo": "teams", + "columnsFrom": [ + "team_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "watchlist_participant_id_season_participants_id_fk": { + "name": "watchlist_participant_id_season_participants_id_fk", + "tableFrom": "watchlist", + "tableTo": "season_participants", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.audit_action": { + "name": "audit_action", + "schema": "public", + "values": [ + "league_settings_changed", + "draft_settings_changed", + "scoring_rules_changed", + "sports_changed", + "draft_order_set", + "draft_order_randomized", + "draft_started", + "draft_paused", + "draft_resumed", + "draft_reset", + "draft_rollback", + "force_autopick", + "force_manual_pick", + "draft_pick_changed", + "time_bank_edited" + ] + }, + "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.overnight_pause_mode": { + "name": "overnight_pause_mode", + "schema": "public", + "values": [ + "none", + "league", + "per_user" + ] + }, + "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", + "nfl_bracket", + "afl_bracket", + "snooker_bracket", + "tennis_qualifying_points", + "mlb_bracket", + "wnba_bracket", + "world_cup", + "darts_bracket", + "cs2_major_qualifying_points", + "ncaa_football_bracket", + "llws_bracket" + ] + }, + "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" + ] + }, + "public.tournament_status": { + "name": "tournament_status", + "schema": "public", + "values": [ + "scheduled", + "in_progress", + "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 1c532f4..9587123 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -610,6 +610,20 @@ "when": 1777505810235, "tag": "0086_marvelous_infant_terrible", "breakpoints": true + }, + { + "idx": 87, + "version": "7", + "when": 1777661299612, + "tag": "0087_small_susan_delgado", + "breakpoints": true + }, + { + "idx": 88, + "version": "7", + "when": 1777666830394, + "tag": "0088_cheerful_norrin_radd", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index 821019d..ee9c521 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "db:generate": "dotenv -- drizzle-kit generate", "db:migrate": "dotenv -- drizzle-kit migrate", "db:sync-prod": "bash scripts/sync-prod-db.sh", + "backfill:canonical": "dotenv -- tsx scripts/backfill-cli.ts", "dev": "NODE_OPTIONS='--import ./instrument.server.mjs' dotenv -- tsx watch server.ts", "start": "NODE_ENV=production NODE_OPTIONS='--import ./instrument.server.mjs' node dist/server.js", "start:production": "NODE_ENV=production NODE_OPTIONS='--import ./instrument.server.mjs' node dist/server.js", diff --git a/scripts/__tests__/backfill-canonical-layer.test.ts b/scripts/__tests__/backfill-canonical-layer.test.ts new file mode 100644 index 0000000..fa93454 --- /dev/null +++ b/scripts/__tests__/backfill-canonical-layer.test.ts @@ -0,0 +1,450 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("~/database/context", () => ({ + database: vi.fn(), +})); + +import { runBackfill } from "../backfill-canonical-layer"; +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; + +/** + * Fixture rows used across multiple tests. + */ +const SPORT_ID = "sport-golf"; +const SEASON_ID = "season-golf-2026"; + +const GOLF_SEASON = { + id: SEASON_ID, + sportId: SPORT_ID, + scoringPattern: "qualifying_points", +} as const; + +function makeEvent(overrides: Partial> = {}) { + return { + id: "event-1", + sportsSeasonId: SEASON_ID, + tournamentId: null, + name: "Masters Tournament", + eventDate: "2026-04-09", + eventType: "tournament", + ...overrides, + }; +} + +function makeSeasonParticipant( + overrides: Partial> = {}, +) { + return { + id: "sp-1", + sportsSeasonId: SEASON_ID, + participantId: null, + name: "Scottie Scheffler", + ...overrides, + }; +} + +/** + * Builds a fake drizzle db that routes select/insert/update calls based + * on the target table. The caller supplies per-table select-result + * arrays (one per call to that table's select()). Inserts return the + * value they were given, extended with a stub id. Updates are recorded + * but otherwise no-op. + */ +interface TableState { + /** Queue of results to return for successive select() calls on this table. */ + selects?: unknown[][]; + /** Rows that insert().values().returning() should yield. */ + insertReturns?: unknown[]; + /** Incremented on each update() call. */ + updates?: { count: number }; +} + +interface FakeDbOptions { + tables: Map; + /** Records every insert call: tableRef → rows seen. */ + inserts?: Map; + /** Records every update call: tableRef → count. */ + updateCounts?: Map; +} + +function makeFakeDb(opts: FakeDbOptions) { + const { tables, inserts, updateCounts } = opts; + + // Each select() starts a new chain that ultimately resolves to the + // next queued selects[] entry for the passed table. The chain is a + // thenable via .limit/.where resolution. + const db = { + select: vi.fn().mockImplementation(() => { + let boundTable: unknown; + const chain: Record = { + from: vi.fn().mockImplementation((table: unknown) => { + boundTable = table; + return chain; + }), + where: vi.fn().mockImplementation(() => chain), + limit: vi.fn().mockImplementation(() => chain), + // Terminal: make chain thenable so `await chain` yields the queued result. + then: (resolve: (v: unknown) => unknown) => { + const state = tables.get(boundTable); + const queue = state?.selects ?? []; + const next = queue.shift() ?? []; + return Promise.resolve(next).then(resolve); + }, + }; + return chain; + }), + + insert: vi.fn().mockImplementation((table: unknown) => { + return { + values: vi.fn().mockImplementation((row: unknown) => { + if (inserts) { + const seen = inserts.get(table) ?? []; + seen.push(row); + inserts.set(table, seen); + } + const state = tables.get(table); + const returnRows = state?.insertReturns ?? [ + { ...(row as object), id: `generated-${Math.random()}` }, + ]; + const afterValues = { + returning: vi.fn().mockResolvedValue(returnRows), + // If the caller doesn't chain .returning(), make it awaitable anyway. + then: (resolve: (v: unknown) => unknown) => + Promise.resolve(returnRows).then(resolve), + }; + return afterValues; + }), + }; + }), + + update: vi.fn().mockImplementation((table: unknown) => { + if (updateCounts) { + updateCounts.set(table, (updateCounts.get(table) ?? 0) + 1); + } + return { + set: vi.fn().mockReturnValue({ + where: vi.fn().mockResolvedValue(undefined), + }), + }; + }), + }; + + return db; +} + +beforeEach(() => { + vi.clearAllMocks(); +}); + +// ─── 1. empty DB no-op ───────────────────────────────────────────────────── + +describe("runBackfill - empty DB", () => { + it("returns zero counts when no qualifying-points seasons exist", async () => { + const tables = new Map([ + [schema.sportsSeasons, { selects: [[]] }], + ]); + vi.mocked(database).mockReturnValue(makeFakeDb({ tables }) as never); + + const report = await runBackfill({ dryRun: false }); + + expect(report).toMatchObject({ + tournamentsCreated: 0, + tournamentsLinked: 0, + participantsCreated: 0, + participantsLinked: 0, + tournamentResultsCreated: 0, + surfaceElosCreated: 0, + errors: [], + }); + }); +}); + +// ─── 2. golf window with 4 events creates 4 tournaments ─────────────────── + +describe("runBackfill - golf tournaments", () => { + it("creates 4 canonical tournaments for a golf season with 4 events", async () => { + const events = [ + makeEvent({ id: "ev-1", name: "Masters Tournament", eventDate: "2026-04-09" }), + makeEvent({ id: "ev-2", name: "PGA Championship", eventDate: "2026-05-14" }), + makeEvent({ id: "ev-3", name: "U.S. Open", eventDate: "2026-06-18" }), + makeEvent({ id: "ev-4", name: "The Open Championship", eventDate: "2026-07-16" }), + ]; + + const inserts = new Map(); + + // Build per-event insertReturns so each new tournament gets its id. + const tables = new Map([ + [schema.sportsSeasons, { selects: [[GOLF_SEASON]] }], + [ + schema.scoringEvents, + { + // 1st select: unlinked events; 2nd: refetch for results loop. + selects: [events, events.map((e, i) => ({ ...e, tournamentId: `t-${i + 1}` }))], + }, + ], + [ + schema.tournaments, + { + // One select-miss per event, all return [] (no existing row). + selects: [[], [], [], []], + // One insert per event; return sequential ids. + insertReturns: [{ id: "t-1" }], + }, + ], + [schema.seasonParticipants, { selects: [[]] }], + [schema.eventResults, { selects: [[], [], [], []] }], + [schema.seasonParticipantSurfaceElos, { selects: [[]] }], + ]); + + // Because our insertReturns is consumed once per test, re-queue per event + // by overriding insert behaviour via the fake db options. + const tournamentIds = ["t-1", "t-2", "t-3", "t-4"]; + let insertIdx = 0; + const db: ReturnType = makeFakeDb({ tables, inserts }); + // Override insert for tournaments specifically to return sequential ids. + db.insert.mockImplementation((table: unknown) => ({ + values: vi.fn().mockImplementation((row: unknown) => { + const seen = inserts.get(table) ?? []; + seen.push(row); + inserts.set(table, seen); + let returnRows: unknown[]; + if (table === schema.tournaments) { + returnRows = [{ ...(row as object), id: tournamentIds[insertIdx++] }]; + } else { + returnRows = [{ ...(row as object), id: "x" }]; + } + return { + returning: vi.fn().mockResolvedValue(returnRows), + then: (resolve: (v: unknown) => unknown) => + Promise.resolve(returnRows).then(resolve), + }; + }), + })); + + vi.mocked(database).mockReturnValue(db as never); + + const report = await runBackfill({ dryRun: false }); + + expect(report.tournamentsCreated).toBe(4); + expect(report.tournamentsLinked).toBe(4); + expect(inserts.get(schema.tournaments)).toHaveLength(4); + expect(report.errors).toEqual([]); + }); +}); + +// ─── 3. re-running doesn't duplicate ────────────────────────────────────── + +describe("runBackfill - idempotence", () => { + it("does not create new tournaments when they already exist", async () => { + const events = [ + makeEvent({ id: "ev-1", name: "Masters Tournament", eventDate: "2026-04-09" }), + ]; + const existingTournament = { + id: "t-existing", + sportId: SPORT_ID, + name: "Masters Tournament", + year: 2026, + }; + + const inserts = new Map(); + const updateCounts = new Map(); + const tables = new Map([ + [schema.sportsSeasons, { selects: [[GOLF_SEASON]] }], + [ + schema.scoringEvents, + { + selects: [events, [{ ...events[0], tournamentId: "t-existing" }]], + }, + ], + [schema.tournaments, { selects: [[existingTournament]] }], + [schema.seasonParticipants, { selects: [[]] }], + [schema.eventResults, { selects: [[]] }], + [schema.seasonParticipantSurfaceElos, { selects: [[]] }], + ]); + + vi.mocked(database).mockReturnValue( + makeFakeDb({ tables, inserts, updateCounts }) as never, + ); + + const report = await runBackfill({ dryRun: false }); + + expect(report.tournamentsCreated).toBe(0); + expect(report.tournamentsLinked).toBe(1); + expect(inserts.get(schema.tournaments)).toBeUndefined(); + }); +}); + +// ─── 4. participant backfill ─────────────────────────────────────────────── + +describe("runBackfill - participants", () => { + it("creates canonical participants and links season_participants", async () => { + const sps = [ + makeSeasonParticipant({ id: "sp-1", name: "Scottie Scheffler" }), + makeSeasonParticipant({ id: "sp-2", name: "Rory McIlroy" }), + ]; + + const inserts = new Map(); + const updateCounts = new Map(); + const pIds = ["p-1", "p-2"]; + let pIdx = 0; + + const tables = new Map([ + [schema.sportsSeasons, { selects: [[GOLF_SEASON]] }], + [schema.scoringEvents, { selects: [[], []] }], + [schema.seasonParticipants, { selects: [sps] }], + [schema.participants, { selects: [[], []] }], + [schema.eventResults, { selects: [] }], + [schema.seasonParticipantSurfaceElos, { selects: [[]] }], + ]); + + const db = makeFakeDb({ tables, inserts, updateCounts }); + db.insert.mockImplementation((table: unknown) => ({ + values: vi.fn().mockImplementation((row: unknown) => { + const seen = inserts.get(table) ?? []; + seen.push(row); + inserts.set(table, seen); + let returnRows: unknown[]; + if (table === schema.participants) { + returnRows = [{ ...(row as object), id: pIds[pIdx++] }]; + } else { + returnRows = [{ ...(row as object), id: "x" }]; + } + return { + returning: vi.fn().mockResolvedValue(returnRows), + then: (resolve: (v: unknown) => unknown) => + Promise.resolve(returnRows).then(resolve), + }; + }), + })); + + vi.mocked(database).mockReturnValue(db as never); + + const report = await runBackfill({ dryRun: false }); + + expect(report.participantsCreated).toBe(2); + expect(report.participantsLinked).toBe(2); + expect(inserts.get(schema.participants)).toHaveLength(2); + expect(updateCounts.get(schema.seasonParticipants)).toBe(2); + }); +}); + +// ─── 5. Masters 2026 round-trip ─────────────────────────────────────────── + +describe("runBackfill - tournament_results from event_results", () => { + it("copies placement and rawScore but NOT qualifying_points_awarded", async () => { + const events = [ + makeEvent({ + id: "ev-masters", + tournamentId: "t-masters", + name: "Masters Tournament", + eventDate: "2026-04-09", + }), + ]; + const seasonParticipant = { + id: "sp-1", + sportsSeasonId: SEASON_ID, + participantId: "p-scottie", + name: "Scottie Scheffler", + }; + const eventResult = { + id: "er-1", + scoringEventId: "ev-masters", + seasonParticipantId: "sp-1", + placement: 1, + rawScore: "-12.00", + qualifyingPointsAwarded: "100.00", + }; + + const inserts = new Map(); + + const tables = new Map([ + [schema.sportsSeasons, { selects: [[GOLF_SEASON]] }], + [ + schema.scoringEvents, + { + // 1st: no unlinked events (tournamentId already set) + // 2nd: refetch — events linked to tournament + selects: [[], events], + }, + ], + [schema.tournaments, { selects: [] }], + [schema.seasonParticipants, { selects: [[], [seasonParticipant]] }], + [schema.participants, { selects: [] }], + [schema.eventResults, { selects: [[eventResult]] }], + [schema.tournamentResults, { selects: [[]] }], + [schema.seasonParticipantSurfaceElos, { selects: [[]] }], + ]); + + vi.mocked(database).mockReturnValue( + makeFakeDb({ tables, inserts }) as never, + ); + + const report = await runBackfill({ dryRun: false }); + + expect(report.tournamentResultsCreated).toBe(1); + const trInserts = inserts.get(schema.tournamentResults) ?? []; + expect(trInserts).toHaveLength(1); + const inserted = trInserts[0] as Record; + expect(inserted.tournamentId).toBe("t-masters"); + expect(inserted.participantId).toBe("p-scottie"); + expect(inserted.placement).toBe(1); + expect(inserted.rawScore).toBe("-12.00"); + // CRITICAL: qualifying_points_awarded must NOT be copied. + expect(inserted).not.toHaveProperty("qualifyingPointsAwarded"); + }); +}); + +// ─── 6. Surface elo conflict detection ──────────────────────────────────── + +describe("runBackfill - surface elo conflicts", () => { + it("records a conflict error when two windows disagree on eloHard", async () => { + const sp = { + id: "sp-1", + sportsSeasonId: SEASON_ID, + participantId: "p-tennis-1", + name: "Carlos Alcaraz", + }; + const windowElo = { + id: "se-1", + participantId: "sp-1", + sportsSeasonId: SEASON_ID, + eloHard: 2050, + eloClay: 2100, + eloGrass: 2000, + worldRanking: 2, + }; + const existingCanonicalElo = { + id: "cpe-1", + participantId: "p-tennis-1", + eloHard: 2000, // differs! + eloClay: 2100, + eloGrass: 2000, + worldRanking: 2, + }; + + const inserts = new Map(); + const tables = new Map([ + [schema.sportsSeasons, { selects: [[GOLF_SEASON]] }], + [schema.scoringEvents, { selects: [[], []] }], + [schema.seasonParticipants, { selects: [[], [sp]] }], + [schema.participants, { selects: [] }], + [schema.eventResults, { selects: [] }], + [schema.seasonParticipantSurfaceElos, { selects: [[windowElo]] }], + [schema.participantSurfaceElos, { selects: [[existingCanonicalElo]] }], + ]); + + vi.mocked(database).mockReturnValue( + makeFakeDb({ tables, inserts }) as never, + ); + + const report = await runBackfill({ dryRun: false }); + + expect(report.surfaceElosCreated).toBe(0); + expect(report.errors).toHaveLength(1); + expect(report.errors[0]).toMatch(/conflict for participant p-tennis-1/); + expect(report.errors[0]).toMatch(/eloHard/); + // Must not have inserted a conflicting row. + expect(inserts.get(schema.participantSurfaceElos)).toBeUndefined(); + }); +}); diff --git a/scripts/backfill-canonical-layer.ts b/scripts/backfill-canonical-layer.ts new file mode 100644 index 0000000..bfc3f46 --- /dev/null +++ b/scripts/backfill-canonical-layer.ts @@ -0,0 +1,358 @@ +/** + * Phase 2 one-off backfill: populate canonical tables + * (`tournaments`, `participants`, `tournament_results`, + * `participant_surface_elos`) from existing per-window data. + * + * See CLAUDE.md and the Phase 2 plan docs. Rules that this script + * MUST obey: + * - Never copy `qualifying_points_awarded` from `event_results` to + * `tournament_results`. QP stays per-window. + * - Never touch `season_participant_qualifying_totals`. + * - Abort loud (collect into `report.errors`) if two windows disagree + * on a canonical participant's surface-Elo values. + * + * `dryRun: true` means: run every read, compute every count, but never + * issue an `insert()` or `update()`. + */ + +import { eq, and, isNull } from "drizzle-orm"; + +import { database } from "~/database/context"; +import * as schema from "~/database/schema"; + +import { extractTournamentIdentity } from "./backfill/match-tournament"; + +export interface BackfillOptions { + dryRun: boolean; + sportId?: string; +} + +export interface BackfillReport { + tournamentsCreated: number; + /** Count of scoring_events whose tournament_id was (or would be) set. */ + tournamentsLinked: number; + participantsCreated: number; + /** Count of season_participants whose participant_id was (or would be) set. */ + participantsLinked: number; + tournamentResultsCreated: number; + surfaceElosCreated: number; + warnings: string[]; + errors: string[]; +} + +type Db = ReturnType; +type SportsSeasonRow = typeof schema.sportsSeasons.$inferSelect; +type ScoringEventRow = typeof schema.scoringEvents.$inferSelect; +type SeasonParticipantRow = typeof schema.seasonParticipants.$inferSelect; +type EventResultRow = typeof schema.eventResults.$inferSelect; +type SeasonParticipantSurfaceEloRow = + typeof schema.seasonParticipantSurfaceElos.$inferSelect; +type TournamentRow = typeof schema.tournaments.$inferSelect; +type ParticipantRow = typeof schema.participants.$inferSelect; +type TournamentResultRow = typeof schema.tournamentResults.$inferSelect; +type ParticipantSurfaceEloRow = + typeof schema.participantSurfaceElos.$inferSelect; + +export async function runBackfill( + opts: BackfillOptions, +): Promise { + const report: BackfillReport = { + tournamentsCreated: 0, + tournamentsLinked: 0, + participantsCreated: 0, + participantsLinked: 0, + tournamentResultsCreated: 0, + surfaceElosCreated: 0, + warnings: [], + errors: [], + }; + + const db = database(); + + // 1. Load qualifying-points seasons (optionally filtered by sport). + const whereClauses = [ + eq(schema.sportsSeasons.scoringPattern, "qualifying_points"), + ]; + if (opts.sportId) { + whereClauses.push(eq(schema.sportsSeasons.sportId, opts.sportId)); + } + + const seasons = (await db + .select() + .from(schema.sportsSeasons) + .where(and(...whereClauses))) as SportsSeasonRow[]; + + for (const season of seasons) { + await backfillSeason(db, season, opts, report); + } + + return report; +} + +async function backfillSeason( + db: Db, + season: SportsSeasonRow, + opts: BackfillOptions, + report: BackfillReport, +): Promise { + // ─── a. Tournament linking ──────────────────────────────────────────────── + const unlinkedEvents = (await db + .select() + .from(schema.scoringEvents) + .where( + and( + eq(schema.scoringEvents.sportsSeasonId, season.id), + isNull(schema.scoringEvents.tournamentId), + ), + )) as ScoringEventRow[]; + + for (const ev of unlinkedEvents) { + let identity; + try { + identity = extractTournamentIdentity({ + name: ev.name, + eventDate: ev.eventDate, + eventType: ev.eventType, + }); + } catch (e) { + report.warnings.push( + `skip scoring_event ${ev.id} (${ev.name}): ${(e as Error).message}`, + ); + continue; + } + + // Look up existing canonical tournament. + const [existing] = (await db + .select() + .from(schema.tournaments) + .where( + and( + eq(schema.tournaments.sportId, season.sportId), + eq(schema.tournaments.name, identity.name), + eq(schema.tournaments.year, identity.year), + ), + ) + .limit(1)) as TournamentRow[]; + + let tournamentId: string | undefined = existing?.id; + + if (!existing) { + const startsAt = ev.eventDate ? new Date(ev.eventDate) : null; + const status = + startsAt && startsAt.getTime() < Date.now() ? "completed" : "scheduled"; + + if (!opts.dryRun) { + const [inserted] = (await db + .insert(schema.tournaments) + .values({ + sportId: season.sportId, + name: identity.name, + year: identity.year, + startsAt, + status, + }) + .returning()) as TournamentRow[]; + tournamentId = inserted.id; + } + report.tournamentsCreated += 1; + } + + if (!opts.dryRun && tournamentId) { + await db + .update(schema.scoringEvents) + .set({ tournamentId, updatedAt: new Date() }) + .where(eq(schema.scoringEvents.id, ev.id)); + } + report.tournamentsLinked += 1; + } + + // ─── b. Participant linking ─────────────────────────────────────────────── + const unlinkedParticipants = (await db + .select() + .from(schema.seasonParticipants) + .where( + and( + eq(schema.seasonParticipants.sportsSeasonId, season.id), + isNull(schema.seasonParticipants.participantId), + ), + )) as SeasonParticipantRow[]; + + for (const sp of unlinkedParticipants) { + const [existing] = (await db + .select() + .from(schema.participants) + .where( + and( + eq(schema.participants.sportId, season.sportId), + eq(schema.participants.name, sp.name), + ), + ) + .limit(1)) as ParticipantRow[]; + + let participantId: string | undefined = existing?.id; + + if (!existing) { + if (!opts.dryRun) { + const [inserted] = (await db + .insert(schema.participants) + .values({ + sportId: season.sportId, + name: sp.name, + }) + .returning()) as ParticipantRow[]; + participantId = inserted.id; + } + report.participantsCreated += 1; + } + + if (!opts.dryRun && participantId) { + await db + .update(schema.seasonParticipants) + .set({ participantId, updatedAt: new Date() }) + .where(eq(schema.seasonParticipants.id, sp.id)); + } + report.participantsLinked += 1; + } + + // ─── c. Tournament results (copy completed event_results) ──────────────── + // Refetch events — they may now have tournamentId set (if !dryRun). + const allEvents = (await db + .select() + .from(schema.scoringEvents) + .where( + eq(schema.scoringEvents.sportsSeasonId, season.id), + )) as ScoringEventRow[]; + + for (const ev of allEvents) { + if (!ev.tournamentId) { + // In dry-run we may not have a tournamentId yet; skip result copy. + continue; + } + + const results = (await db + .select() + .from(schema.eventResults) + .where( + eq(schema.eventResults.scoringEventId, ev.id), + )) as EventResultRow[]; + + for (const r of results) { + // Only copy rows with real placement/rawScore data. + if (r.placement == null && r.rawScore == null) { + continue; + } + + // Look up the season_participants row to get canonical participantId. + const [sp] = (await db + .select() + .from(schema.seasonParticipants) + .where( + eq(schema.seasonParticipants.id, r.seasonParticipantId), + ) + .limit(1)) as SeasonParticipantRow[]; + + if (!sp || !sp.participantId) { + // Link step should have handled this; skip defensively. + continue; + } + + // Check if a tournament_results row already exists. + const [existingResult] = (await db + .select() + .from(schema.tournamentResults) + .where( + and( + eq(schema.tournamentResults.tournamentId, ev.tournamentId), + eq(schema.tournamentResults.participantId, sp.participantId), + ), + ) + .limit(1)) as TournamentResultRow[]; + + if (existingResult) { + continue; + } + + if (!opts.dryRun) { + // NOTE: intentionally do NOT copy qualifyingPointsAwarded. + await db.insert(schema.tournamentResults).values({ + tournamentId: ev.tournamentId, + participantId: sp.participantId, + placement: r.placement, + rawScore: r.rawScore, + }); + } + report.tournamentResultsCreated += 1; + } + } + + // ─── d. Surface Elo (per-window → canonical) ───────────────────────────── + const elos = (await db + .select() + .from(schema.seasonParticipantSurfaceElos) + .where( + eq(schema.seasonParticipantSurfaceElos.sportsSeasonId, season.id), + )) as SeasonParticipantSurfaceEloRow[]; + + for (const elo of elos) { + const [sp] = (await db + .select() + .from(schema.seasonParticipants) + .where( + eq(schema.seasonParticipants.id, elo.participantId), + ) + .limit(1)) as SeasonParticipantRow[]; + + if (!sp || !sp.participantId) { + continue; + } + + const canonicalParticipantId = sp.participantId; + + const [existingElo] = (await db + .select() + .from(schema.participantSurfaceElos) + .where( + eq( + schema.participantSurfaceElos.participantId, + canonicalParticipantId, + ), + ) + .limit(1)) as ParticipantSurfaceEloRow[]; + + if (existingElo) { + // Conflict detection: compare eloHard/eloClay/eloGrass/worldRanking. + const fields: Array = [ + "eloHard", + "eloClay", + "eloGrass", + "worldRanking", + ]; + const mismatches: string[] = []; + for (const f of fields) { + if (existingElo[f] !== elo[f as keyof SeasonParticipantSurfaceEloRow]) { + mismatches.push( + `${f}: existing=${String(existingElo[f])} vs incoming=${String(elo[f as keyof SeasonParticipantSurfaceEloRow])}`, + ); + } + } + if (mismatches.length > 0) { + report.errors.push( + `conflict for participant ${canonicalParticipantId} (season ${season.id}): ${mismatches.join(", ")}`, + ); + } + // Do not overwrite. + continue; + } + + if (!opts.dryRun) { + await db.insert(schema.participantSurfaceElos).values({ + participantId: canonicalParticipantId, + eloHard: elo.eloHard, + eloClay: elo.eloClay, + eloGrass: elo.eloGrass, + worldRanking: elo.worldRanking, + }); + } + report.surfaceElosCreated += 1; + } +} diff --git a/scripts/backfill-cli.ts b/scripts/backfill-cli.ts new file mode 100644 index 0000000..3782049 --- /dev/null +++ b/scripts/backfill-cli.ts @@ -0,0 +1,82 @@ +/** + * CLI entry point for the Phase 2 canonical backfill. + * + * Usage: + * npm run backfill:canonical -- [--dry-run | --apply] [--sport=] + * + * Defaults to --dry-run for safety. No writes will be issued unless + * --apply is passed explicitly. + */ + +import { DatabaseContext } from "~/database/context"; +import { db } from "../server/db"; +import { runBackfill } from "./backfill-canonical-layer"; +import type { BackfillOptions } from "./backfill-canonical-layer"; + +function printHelp(): void { + console.log( + [ + "Usage: backfill-cli [options]", + "", + "Options:", + " --dry-run Run without writing (default).", + " --apply Actually write to the database.", + " --sport= Only backfill for the given sport id.", + " --help Show this message.", + ].join("\n"), + ); +} + +function parseArgs(): BackfillOptions { + const args = process.argv.slice(2); + const opts: BackfillOptions = { dryRun: true }; // dry-run by default for safety + for (const a of args) { + if (a === "--apply") { + opts.dryRun = false; + } else if (a === "--dry-run") { + opts.dryRun = true; + } else if (a.startsWith("--sport=")) { + opts.sportId = a.slice("--sport=".length); + } else if (a === "--help" || a === "-h") { + printHelp(); + process.exit(0); + } else { + console.error(`unknown arg: ${a}`); + process.exit(1); + } + } + return opts; +} + +async function main() { + const opts = parseArgs(); + console.log( + `Running backfill (dryRun=${opts.dryRun}, sportId=${opts.sportId ?? "all"})`, + ); + const report = await DatabaseContext.run(db, () => runBackfill(opts)); + + console.log("---"); + console.log(`tournamentsCreated: ${report.tournamentsCreated}`); + console.log(`tournamentsLinked: ${report.tournamentsLinked}`); + console.log(`participantsCreated: ${report.participantsCreated}`); + console.log(`participantsLinked: ${report.participantsLinked}`); + console.log(`tournamentResultsCreated: ${report.tournamentResultsCreated}`); + console.log(`surfaceElosCreated: ${report.surfaceElosCreated}`); + + if (report.warnings.length) { + console.log("\nWARNINGS:"); + for (const w of report.warnings) console.log(` ${w}`); + } + if (report.errors.length) { + console.log("\nERRORS:"); + for (const e of report.errors) console.log(` ${e}`); + process.exit(2); + } +} + +main() + .then(() => process.exit(0)) + .catch((e) => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/backfill/__tests__/match-tournament.test.ts b/scripts/backfill/__tests__/match-tournament.test.ts new file mode 100644 index 0000000..484ee30 --- /dev/null +++ b/scripts/backfill/__tests__/match-tournament.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect } from "vitest"; +import { extractTournamentIdentity } from "../match-tournament"; + +describe("extractTournamentIdentity", () => { + it("uses eventDate year when the name has no trailing year", () => { + const identity = extractTournamentIdentity({ + name: "Masters Tournament", + eventDate: "2026-04-09", + eventType: "tournament", + }); + + expect(identity).toEqual({ name: "Masters Tournament", year: 2026 }); + }); + + it("derives the year from eventDate for a simple tournament name", () => { + const identity = extractTournamentIdentity({ + name: "Wimbledon", + eventDate: "2026-07-01", + eventType: "tournament", + }); + + expect(identity).toEqual({ name: "Wimbledon", year: 2026 }); + }); + + it("strips a trailing year from the name and uses it as the canonical year", () => { + const identity = extractTournamentIdentity({ + name: "Wimbledon 2026", + eventDate: "2026-07-01", + eventType: "tournament", + }); + + expect(identity).toEqual({ name: "Wimbledon", year: 2026 }); + }); + + it("throws when neither the name nor eventDate supply a year", () => { + expect(() => + extractTournamentIdentity({ + name: "Wimbledon", + eventDate: null, + eventType: "tournament", + }), + ).toThrow(/cannot determine year/); + }); +}); diff --git a/scripts/backfill/match-tournament.ts b/scripts/backfill/match-tournament.ts new file mode 100644 index 0000000..e7c7c92 --- /dev/null +++ b/scripts/backfill/match-tournament.ts @@ -0,0 +1,57 @@ +/** + * Pure function(s) for extracting canonical tournament identity + * (name + year) from a `scoring_events` row. + * + * Used by the Phase 2 backfill to group per-window events into + * canonical `tournaments` rows. + */ + +export interface ScoringEventInput { + name: string; + eventDate: string | null; + eventType: string; +} + +export interface TournamentIdentity { + /** Tournament name with any trailing 4-digit year stripped. */ + name: string; + year: number; +} + +const TRAILING_YEAR_RE = / (\d{4})$/; + +/** + * Extracts the canonical `(name, year)` identity for a tournament + * from a scoring event. + * + * Resolution order for year: + * 1. A trailing 4-digit year on the event name (e.g. "Wimbledon 2026"). + * The year is stripped from the returned name. + * 2. The first 4 characters of `eventDate` (format `YYYY-MM-DD`). + * + * Throws if neither source supplies a year. + */ +export function extractTournamentIdentity( + ev: ScoringEventInput, +): TournamentIdentity { + const trimmedName = ev.name.trim(); + const match = trimmedName.match(TRAILING_YEAR_RE); + + if (match) { + const yearFromName = Number(match[1]); + const nameWithoutYear = trimmedName.slice(0, match.index).trim(); + return { name: nameWithoutYear, year: yearFromName }; + } + + if (ev.eventDate) { + const yearStr = ev.eventDate.slice(0, 4); + const yearFromDate = Number(yearStr); + if (Number.isFinite(yearFromDate) && yearStr.length === 4) { + return { name: trimmedName, year: yearFromDate }; + } + } + + throw new Error( + `cannot determine year for scoring event "${ev.name}" (eventDate=${ev.eventDate ?? "null"})`, + ); +} diff --git a/server/__tests__/timer-autodraft.test.ts b/server/__tests__/timer-autodraft.test.ts index afd66c2..9e6459a 100644 --- a/server/__tests__/timer-autodraft.test.ts +++ b/server/__tests__/timer-autodraft.test.ts @@ -31,7 +31,7 @@ beforeEach(() => { autodraftSettings: { findFirst: vi.fn() }, draftPicks: { findMany: vi.fn(), findFirst: vi.fn() }, draftQueue: { findMany: vi.fn() }, - participants: { findMany: vi.fn() }, + seasonParticipants: { findMany: vi.fn() }, seasonTemplateSports: { findMany: vi.fn() }, }, update: vi.fn().mockReturnThis(), @@ -146,7 +146,7 @@ describe('Timer Autodraft Integration', () => { it('should fall back to highest EV when queue is empty and queueOnly is OFF', async () => { mockDb.query.draftQueue.findMany.mockResolvedValue([]); - mockDb.query.participants.findMany.mockResolvedValue([ + mockDb.query.seasonParticipants.findMany.mockResolvedValue([ { id: 'participant-high-ev', name: 'High EV Participant', expectedValue: 1000 }, ]); @@ -154,7 +154,7 @@ describe('Timer Autodraft Integration', () => { expect(queueItems.length).toBe(0); const queueOnly = false; - const availableParticipants = await mockDb.query.participants.findMany(); + const availableParticipants = await mockDb.query.seasonParticipants.findMany(); const selectedParticipant = queueOnly ? null : availableParticipants[0]; expect(selectedParticipant).not.toBeNull(); expect(selectedParticipant?.expectedValue).toBe(1000); diff --git a/server/socket.ts b/server/socket.ts index 066e221..a8b32e1 100644 --- a/server/socket.ts +++ b/server/socket.ts @@ -232,18 +232,18 @@ export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer { pickInRound: schema.draftPicks.pickInRound, timeUsed: schema.draftPicks.timeUsed, team: schema.teams, - participant: schema.participants, + participant: schema.seasonParticipants, sport: schema.sports, }) .from(schema.draftPicks) .innerJoin(schema.teams, eq(schema.draftPicks.teamId, schema.teams.id)) .innerJoin( - schema.participants, - eq(schema.draftPicks.participantId, schema.participants.id) + schema.seasonParticipants, + eq(schema.draftPicks.participantId, schema.seasonParticipants.id) ) .innerJoin( schema.sportsSeasons, - eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id) + eq(schema.seasonParticipants.sportsSeasonId, schema.sportsSeasons.id) ) .innerJoin( schema.sports,