@opprs/db-prisma 2.2.1-canary.ccb79aa → 2.2.1-canary.cd8b178
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +963 -156
- package/dist/index.d.cts +795 -105
- package/dist/index.d.ts +795 -105
- package/dist/index.js +870 -139
- package/package.json +2 -2
- package/prisma/migrations/20260104092800_add_location_model_and_tournament_fields/migration.sql +45 -0
- package/prisma/migrations/20260104210034_add_policy_acceptance_fields/migration.sql +19 -0
- package/prisma/migrations/20260104231435_split_entries_standings/migration.sql +137 -0
- package/prisma/migrations/20260105000000_add_oppr_ranking_models/migration.sql +108 -0
- package/prisma/schema.prisma +225 -37
- package/prisma/seed.ts +107 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opprs/db-prisma",
|
|
3
|
-
"version": "2.2.1-canary.
|
|
3
|
+
"version": "2.2.1-canary.cd8b178",
|
|
4
4
|
"description": "Database backend for OPPR (Open Pinball Player Ranking System) using Prisma and PostgreSQL",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"oppr",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"vitest": "^4.0.16"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"@opprs/core": "^2.2.1-canary.
|
|
59
|
+
"@opprs/core": "^2.2.1-canary.cd8b178"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
62
|
"node": ">=20.9.0"
|
package/prisma/migrations/20260104092800_add_location_model_and_tournament_fields/migration.sql
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE "Location" (
|
|
3
|
+
"id" TEXT NOT NULL,
|
|
4
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
5
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
6
|
+
"externalId" TEXT,
|
|
7
|
+
"name" TEXT NOT NULL,
|
|
8
|
+
"address" TEXT,
|
|
9
|
+
"city" TEXT,
|
|
10
|
+
"state" TEXT,
|
|
11
|
+
"country" TEXT,
|
|
12
|
+
|
|
13
|
+
CONSTRAINT "Location_pkey" PRIMARY KEY ("id")
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
-- CreateIndex
|
|
17
|
+
CREATE UNIQUE INDEX "Location_externalId_key" ON "Location"("externalId");
|
|
18
|
+
|
|
19
|
+
-- CreateIndex
|
|
20
|
+
CREATE INDEX "Location_externalId_idx" ON "Location"("externalId");
|
|
21
|
+
|
|
22
|
+
-- CreateIndex
|
|
23
|
+
CREATE INDEX "Location_name_idx" ON "Location"("name");
|
|
24
|
+
|
|
25
|
+
-- CreateIndex
|
|
26
|
+
CREATE INDEX "Location_city_idx" ON "Location"("city");
|
|
27
|
+
|
|
28
|
+
-- AlterTable: Add new columns to Tournament
|
|
29
|
+
ALTER TABLE "Tournament" ADD COLUMN "description" VARCHAR(2000);
|
|
30
|
+
ALTER TABLE "Tournament" ADD COLUMN "locationId" TEXT;
|
|
31
|
+
ALTER TABLE "Tournament" ADD COLUMN "organizerId" TEXT;
|
|
32
|
+
|
|
33
|
+
-- DropColumn: Remove old location string column (replaced by Location relation)
|
|
34
|
+
ALTER TABLE "Tournament" DROP COLUMN IF EXISTS "location";
|
|
35
|
+
|
|
36
|
+
-- CreateIndex for new Tournament columns
|
|
37
|
+
CREATE INDEX "Tournament_locationId_idx" ON "Tournament"("locationId");
|
|
38
|
+
|
|
39
|
+
CREATE INDEX "Tournament_organizerId_idx" ON "Tournament"("organizerId");
|
|
40
|
+
|
|
41
|
+
-- AddForeignKey: Tournament -> Location
|
|
42
|
+
ALTER TABLE "Tournament" ADD CONSTRAINT "Tournament_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "Location"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
43
|
+
|
|
44
|
+
-- AddForeignKey: Tournament -> Player (organizer)
|
|
45
|
+
ALTER TABLE "Tournament" ADD CONSTRAINT "Tournament_organizerId_fkey" FOREIGN KEY ("organizerId") REFERENCES "Player"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Warnings:
|
|
3
|
+
|
|
4
|
+
- You are about to drop the column `email` on the `Player` table. All the data in the column will be lost.
|
|
5
|
+
|
|
6
|
+
*/
|
|
7
|
+
-- DropIndex
|
|
8
|
+
DROP INDEX "Player_email_idx";
|
|
9
|
+
|
|
10
|
+
-- DropIndex
|
|
11
|
+
DROP INDEX "Player_email_key";
|
|
12
|
+
|
|
13
|
+
-- AlterTable
|
|
14
|
+
ALTER TABLE "Player" DROP COLUMN "email";
|
|
15
|
+
|
|
16
|
+
-- AlterTable
|
|
17
|
+
ALTER TABLE "User" ADD COLUMN "codeOfConductAcceptedAt" TIMESTAMP(3),
|
|
18
|
+
ADD COLUMN "privacyPolicyAcceptedAt" TIMESTAMP(3),
|
|
19
|
+
ADD COLUMN "tosAcceptedAt" TIMESTAMP(3);
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Warnings:
|
|
3
|
+
|
|
4
|
+
- You are about to drop the `TournamentResult` table. If the table is not empty, all the data it contains will be lost.
|
|
5
|
+
|
|
6
|
+
*/
|
|
7
|
+
-- CreateEnum
|
|
8
|
+
CREATE TYPE "MatchResult" AS ENUM ('WIN', 'LOSS', 'TIE');
|
|
9
|
+
|
|
10
|
+
-- DropForeignKey
|
|
11
|
+
ALTER TABLE "TournamentResult" DROP CONSTRAINT "TournamentResult_playerId_fkey";
|
|
12
|
+
|
|
13
|
+
-- DropForeignKey
|
|
14
|
+
ALTER TABLE "TournamentResult" DROP CONSTRAINT "TournamentResult_tournamentId_fkey";
|
|
15
|
+
|
|
16
|
+
-- DropTable
|
|
17
|
+
DROP TABLE "TournamentResult";
|
|
18
|
+
|
|
19
|
+
-- CreateTable
|
|
20
|
+
CREATE TABLE "Round" (
|
|
21
|
+
"id" TEXT NOT NULL,
|
|
22
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
23
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
24
|
+
"tournamentId" TEXT NOT NULL,
|
|
25
|
+
"number" INTEGER NOT NULL,
|
|
26
|
+
"name" TEXT,
|
|
27
|
+
"isFinals" BOOLEAN NOT NULL DEFAULT false,
|
|
28
|
+
|
|
29
|
+
CONSTRAINT "Round_pkey" PRIMARY KEY ("id")
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
-- CreateTable
|
|
33
|
+
CREATE TABLE "Match" (
|
|
34
|
+
"id" TEXT NOT NULL,
|
|
35
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
36
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
37
|
+
"tournamentId" TEXT NOT NULL,
|
|
38
|
+
"roundId" TEXT,
|
|
39
|
+
"number" INTEGER,
|
|
40
|
+
"machineName" TEXT,
|
|
41
|
+
|
|
42
|
+
CONSTRAINT "Match_pkey" PRIMARY KEY ("id")
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
-- CreateTable
|
|
46
|
+
CREATE TABLE "Entry" (
|
|
47
|
+
"id" TEXT NOT NULL,
|
|
48
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
49
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
50
|
+
"matchId" TEXT NOT NULL,
|
|
51
|
+
"playerId" TEXT NOT NULL,
|
|
52
|
+
"result" "MatchResult" NOT NULL,
|
|
53
|
+
"position" INTEGER,
|
|
54
|
+
|
|
55
|
+
CONSTRAINT "Entry_pkey" PRIMARY KEY ("id")
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
-- CreateTable
|
|
59
|
+
CREATE TABLE "Standing" (
|
|
60
|
+
"id" TEXT NOT NULL,
|
|
61
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
62
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
63
|
+
"tournamentId" TEXT NOT NULL,
|
|
64
|
+
"playerId" TEXT NOT NULL,
|
|
65
|
+
"position" INTEGER NOT NULL,
|
|
66
|
+
"isFinals" BOOLEAN NOT NULL DEFAULT false,
|
|
67
|
+
"optedOut" BOOLEAN NOT NULL DEFAULT false,
|
|
68
|
+
"linearPoints" DOUBLE PRECISION DEFAULT 0,
|
|
69
|
+
"dynamicPoints" DOUBLE PRECISION DEFAULT 0,
|
|
70
|
+
"totalPoints" DOUBLE PRECISION,
|
|
71
|
+
"ageInDays" INTEGER DEFAULT 0,
|
|
72
|
+
"decayMultiplier" DOUBLE PRECISION DEFAULT 1.0,
|
|
73
|
+
"decayedPoints" DOUBLE PRECISION,
|
|
74
|
+
"efficiency" DOUBLE PRECISION,
|
|
75
|
+
|
|
76
|
+
CONSTRAINT "Standing_pkey" PRIMARY KEY ("id")
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
-- CreateIndex
|
|
80
|
+
CREATE INDEX "Round_tournamentId_idx" ON "Round"("tournamentId");
|
|
81
|
+
|
|
82
|
+
-- CreateIndex
|
|
83
|
+
CREATE INDEX "Round_tournamentId_isFinals_idx" ON "Round"("tournamentId", "isFinals");
|
|
84
|
+
|
|
85
|
+
-- CreateIndex
|
|
86
|
+
CREATE UNIQUE INDEX "Round_tournamentId_number_isFinals_key" ON "Round"("tournamentId", "number", "isFinals");
|
|
87
|
+
|
|
88
|
+
-- CreateIndex
|
|
89
|
+
CREATE INDEX "Match_tournamentId_idx" ON "Match"("tournamentId");
|
|
90
|
+
|
|
91
|
+
-- CreateIndex
|
|
92
|
+
CREATE INDEX "Match_roundId_idx" ON "Match"("roundId");
|
|
93
|
+
|
|
94
|
+
-- CreateIndex
|
|
95
|
+
CREATE INDEX "Entry_matchId_idx" ON "Entry"("matchId");
|
|
96
|
+
|
|
97
|
+
-- CreateIndex
|
|
98
|
+
CREATE INDEX "Entry_playerId_idx" ON "Entry"("playerId");
|
|
99
|
+
|
|
100
|
+
-- CreateIndex
|
|
101
|
+
CREATE UNIQUE INDEX "Entry_matchId_playerId_key" ON "Entry"("matchId", "playerId");
|
|
102
|
+
|
|
103
|
+
-- CreateIndex
|
|
104
|
+
CREATE INDEX "Standing_playerId_idx" ON "Standing"("playerId");
|
|
105
|
+
|
|
106
|
+
-- CreateIndex
|
|
107
|
+
CREATE INDEX "Standing_tournamentId_idx" ON "Standing"("tournamentId");
|
|
108
|
+
|
|
109
|
+
-- CreateIndex
|
|
110
|
+
CREATE INDEX "Standing_tournamentId_isFinals_idx" ON "Standing"("tournamentId", "isFinals");
|
|
111
|
+
|
|
112
|
+
-- CreateIndex
|
|
113
|
+
CREATE INDEX "Standing_position_idx" ON "Standing"("position");
|
|
114
|
+
|
|
115
|
+
-- CreateIndex
|
|
116
|
+
CREATE UNIQUE INDEX "Standing_playerId_tournamentId_isFinals_key" ON "Standing"("playerId", "tournamentId", "isFinals");
|
|
117
|
+
|
|
118
|
+
-- AddForeignKey
|
|
119
|
+
ALTER TABLE "Round" ADD CONSTRAINT "Round_tournamentId_fkey" FOREIGN KEY ("tournamentId") REFERENCES "Tournament"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
120
|
+
|
|
121
|
+
-- AddForeignKey
|
|
122
|
+
ALTER TABLE "Match" ADD CONSTRAINT "Match_tournamentId_fkey" FOREIGN KEY ("tournamentId") REFERENCES "Tournament"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
123
|
+
|
|
124
|
+
-- AddForeignKey
|
|
125
|
+
ALTER TABLE "Match" ADD CONSTRAINT "Match_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
126
|
+
|
|
127
|
+
-- AddForeignKey
|
|
128
|
+
ALTER TABLE "Entry" ADD CONSTRAINT "Entry_matchId_fkey" FOREIGN KEY ("matchId") REFERENCES "Match"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
129
|
+
|
|
130
|
+
-- AddForeignKey
|
|
131
|
+
ALTER TABLE "Entry" ADD CONSTRAINT "Entry_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
132
|
+
|
|
133
|
+
-- AddForeignKey
|
|
134
|
+
ALTER TABLE "Standing" ADD CONSTRAINT "Standing_tournamentId_fkey" FOREIGN KEY ("tournamentId") REFERENCES "Tournament"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
135
|
+
|
|
136
|
+
-- AddForeignKey
|
|
137
|
+
ALTER TABLE "Standing" ADD CONSTRAINT "Standing_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "OpprRankingChangeType" AS ENUM ('INITIAL', 'TOURNAMENT_RESULT', 'RANKING_REFRESH', 'RD_DECAY', 'MANUAL_ADJUSTMENT');
|
|
3
|
+
|
|
4
|
+
-- CreateTable
|
|
5
|
+
CREATE TABLE "OpprPlayerRanking" (
|
|
6
|
+
"id" TEXT NOT NULL,
|
|
7
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
9
|
+
"playerId" TEXT NOT NULL,
|
|
10
|
+
"rating" DOUBLE PRECISION NOT NULL DEFAULT 1500,
|
|
11
|
+
"ratingDeviation" DOUBLE PRECISION NOT NULL DEFAULT 200,
|
|
12
|
+
"lastRatingUpdate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
13
|
+
"ranking" INTEGER,
|
|
14
|
+
"isRated" BOOLEAN NOT NULL DEFAULT false,
|
|
15
|
+
|
|
16
|
+
CONSTRAINT "OpprPlayerRanking_pkey" PRIMARY KEY ("id")
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
-- CreateTable
|
|
20
|
+
CREATE TABLE "OpprRankingHistory" (
|
|
21
|
+
"id" TEXT NOT NULL,
|
|
22
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
23
|
+
"opprPlayerRankingId" TEXT NOT NULL,
|
|
24
|
+
"rating" DOUBLE PRECISION NOT NULL,
|
|
25
|
+
"ratingDeviation" DOUBLE PRECISION NOT NULL,
|
|
26
|
+
"ranking" INTEGER,
|
|
27
|
+
"isRated" BOOLEAN NOT NULL,
|
|
28
|
+
"changeType" "OpprRankingChangeType" NOT NULL,
|
|
29
|
+
"tournamentId" TEXT,
|
|
30
|
+
"notes" VARCHAR(500),
|
|
31
|
+
|
|
32
|
+
CONSTRAINT "OpprRankingHistory_pkey" PRIMARY KEY ("id")
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
-- CreateIndex
|
|
36
|
+
CREATE UNIQUE INDEX "OpprPlayerRanking_playerId_key" ON "OpprPlayerRanking"("playerId");
|
|
37
|
+
|
|
38
|
+
-- CreateIndex
|
|
39
|
+
CREATE INDEX "OpprPlayerRanking_playerId_idx" ON "OpprPlayerRanking"("playerId");
|
|
40
|
+
|
|
41
|
+
-- CreateIndex
|
|
42
|
+
CREATE INDEX "OpprPlayerRanking_rating_idx" ON "OpprPlayerRanking"("rating");
|
|
43
|
+
|
|
44
|
+
-- CreateIndex
|
|
45
|
+
CREATE INDEX "OpprPlayerRanking_ranking_idx" ON "OpprPlayerRanking"("ranking");
|
|
46
|
+
|
|
47
|
+
-- CreateIndex
|
|
48
|
+
CREATE INDEX "OpprPlayerRanking_isRated_idx" ON "OpprPlayerRanking"("isRated");
|
|
49
|
+
|
|
50
|
+
-- CreateIndex
|
|
51
|
+
CREATE INDEX "OpprRankingHistory_opprPlayerRankingId_idx" ON "OpprRankingHistory"("opprPlayerRankingId");
|
|
52
|
+
|
|
53
|
+
-- CreateIndex
|
|
54
|
+
CREATE INDEX "OpprRankingHistory_createdAt_idx" ON "OpprRankingHistory"("createdAt");
|
|
55
|
+
|
|
56
|
+
-- CreateIndex
|
|
57
|
+
CREATE INDEX "OpprRankingHistory_tournamentId_idx" ON "OpprRankingHistory"("tournamentId");
|
|
58
|
+
|
|
59
|
+
-- CreateIndex
|
|
60
|
+
CREATE INDEX "OpprRankingHistory_opprPlayerRankingId_createdAt_idx" ON "OpprRankingHistory"("opprPlayerRankingId", "createdAt");
|
|
61
|
+
|
|
62
|
+
-- AddForeignKey
|
|
63
|
+
ALTER TABLE "OpprPlayerRanking" ADD CONSTRAINT "OpprPlayerRanking_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
64
|
+
|
|
65
|
+
-- AddForeignKey
|
|
66
|
+
ALTER TABLE "OpprRankingHistory" ADD CONSTRAINT "OpprRankingHistory_opprPlayerRankingId_fkey" FOREIGN KEY ("opprPlayerRankingId") REFERENCES "OpprPlayerRanking"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
67
|
+
|
|
68
|
+
-- AddForeignKey
|
|
69
|
+
ALTER TABLE "OpprRankingHistory" ADD CONSTRAINT "OpprRankingHistory_tournamentId_fkey" FOREIGN KEY ("tournamentId") REFERENCES "Tournament"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
70
|
+
|
|
71
|
+
-- Migrate existing Player rating data to OpprPlayerRanking
|
|
72
|
+
INSERT INTO "OpprPlayerRanking" ("id", "createdAt", "updatedAt", "playerId", "rating", "ratingDeviation", "lastRatingUpdate", "ranking", "isRated")
|
|
73
|
+
SELECT
|
|
74
|
+
gen_random_uuid()::text,
|
|
75
|
+
NOW(),
|
|
76
|
+
NOW(),
|
|
77
|
+
"id",
|
|
78
|
+
"rating",
|
|
79
|
+
"ratingDeviation",
|
|
80
|
+
"lastRatingUpdate",
|
|
81
|
+
"ranking",
|
|
82
|
+
"isRated"
|
|
83
|
+
FROM "Player";
|
|
84
|
+
|
|
85
|
+
-- Create initial history records for all migrated rankings
|
|
86
|
+
INSERT INTO "OpprRankingHistory" ("id", "createdAt", "opprPlayerRankingId", "rating", "ratingDeviation", "ranking", "isRated", "changeType", "notes")
|
|
87
|
+
SELECT
|
|
88
|
+
gen_random_uuid()::text,
|
|
89
|
+
NOW(),
|
|
90
|
+
opr."id",
|
|
91
|
+
opr."rating",
|
|
92
|
+
opr."ratingDeviation",
|
|
93
|
+
opr."ranking",
|
|
94
|
+
opr."isRated",
|
|
95
|
+
'INITIAL',
|
|
96
|
+
'Migrated from Player model'
|
|
97
|
+
FROM "OpprPlayerRanking" opr;
|
|
98
|
+
|
|
99
|
+
-- Drop old indexes from Player table
|
|
100
|
+
DROP INDEX IF EXISTS "Player_rating_idx";
|
|
101
|
+
DROP INDEX IF EXISTS "Player_ranking_idx";
|
|
102
|
+
|
|
103
|
+
-- Remove old columns from Player table
|
|
104
|
+
ALTER TABLE "Player" DROP COLUMN "rating";
|
|
105
|
+
ALTER TABLE "Player" DROP COLUMN "ratingDeviation";
|
|
106
|
+
ALTER TABLE "Player" DROP COLUMN "ranking";
|
|
107
|
+
ALTER TABLE "Player" DROP COLUMN "isRated";
|
|
108
|
+
ALTER TABLE "Player" DROP COLUMN "lastRatingUpdate";
|
package/prisma/schema.prisma
CHANGED
|
@@ -21,25 +21,19 @@ model Player {
|
|
|
21
21
|
playerNumber Int @unique // 5-digit unique identifier (10000-99999)
|
|
22
22
|
name String?
|
|
23
23
|
|
|
24
|
-
//
|
|
25
|
-
rating Float @default(1500) // Glicko rating
|
|
26
|
-
ratingDeviation Float @default(200) // Rating uncertainty (RD)
|
|
27
|
-
ranking Int? // World ranking position
|
|
28
|
-
isRated Boolean @default(false) // Has 5+ events
|
|
24
|
+
// General player statistics
|
|
29
25
|
eventCount Int @default(0) // Number of events participated
|
|
30
|
-
|
|
31
|
-
// Timestamps for rating calculations
|
|
32
|
-
lastRatingUpdate DateTime @default(now())
|
|
33
26
|
lastEventDate DateTime?
|
|
34
27
|
|
|
35
28
|
// Relations
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
standings Standing[]
|
|
30
|
+
entries Entry[]
|
|
31
|
+
user User?
|
|
32
|
+
organizedTournaments Tournament[] @relation("OrganizedTournaments")
|
|
33
|
+
opprRanking OpprPlayerRanking?
|
|
38
34
|
|
|
39
35
|
@@index([externalId])
|
|
40
36
|
@@index([playerNumber])
|
|
41
|
-
@@index([rating])
|
|
42
|
-
@@index([ranking])
|
|
43
37
|
}
|
|
44
38
|
|
|
45
39
|
// Tournament model - represents a pinball tournament event
|
|
@@ -51,9 +45,17 @@ model Tournament {
|
|
|
51
45
|
// Tournament identification
|
|
52
46
|
externalId String? @unique // External ID from OPPR or other systems
|
|
53
47
|
name String
|
|
54
|
-
|
|
48
|
+
description String? @db.VarChar(2000)
|
|
55
49
|
date DateTime
|
|
56
50
|
|
|
51
|
+
// Location relation
|
|
52
|
+
locationId String?
|
|
53
|
+
location Location? @relation(fields: [locationId], references: [id], onDelete: SetNull)
|
|
54
|
+
|
|
55
|
+
// Organizer relation
|
|
56
|
+
organizerId String?
|
|
57
|
+
organizer Player? @relation("OrganizedTournaments", fields: [organizerId], references: [id], onDelete: SetNull)
|
|
58
|
+
|
|
57
59
|
// Tournament configuration (stored as JSON)
|
|
58
60
|
// Contains TGPConfig structure from OPPR
|
|
59
61
|
tgpConfig Json?
|
|
@@ -72,48 +74,117 @@ model Tournament {
|
|
|
72
74
|
firstPlaceValue Float?
|
|
73
75
|
|
|
74
76
|
// Relations
|
|
75
|
-
|
|
77
|
+
rounds Round[]
|
|
78
|
+
matches Match[]
|
|
79
|
+
standings Standing[]
|
|
80
|
+
rankingHistoryRecords OpprRankingHistory[]
|
|
76
81
|
|
|
77
82
|
@@index([date])
|
|
78
83
|
@@index([eventBooster])
|
|
79
84
|
@@index([externalId])
|
|
85
|
+
@@index([locationId])
|
|
86
|
+
@@index([organizerId])
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
//
|
|
83
|
-
model
|
|
84
|
-
id
|
|
85
|
-
createdAt
|
|
86
|
-
updatedAt
|
|
89
|
+
// Round model - groups matches within a tournament stage
|
|
90
|
+
model Round {
|
|
91
|
+
id String @id @default(cuid())
|
|
92
|
+
createdAt DateTime @default(now())
|
|
93
|
+
updatedAt DateTime @updatedAt
|
|
94
|
+
|
|
95
|
+
tournamentId String
|
|
96
|
+
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
|
|
97
|
+
|
|
98
|
+
number Int // Round number within the stage (1, 2, 3...)
|
|
99
|
+
name String? // Optional name (e.g., "Quarterfinals", "Semifinal")
|
|
100
|
+
isFinals Boolean @default(false)
|
|
87
101
|
|
|
88
102
|
// Relations
|
|
89
|
-
|
|
90
|
-
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
|
91
|
-
tournamentId String
|
|
92
|
-
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
|
|
103
|
+
matches Match[]
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
@@unique([tournamentId, number, isFinals])
|
|
106
|
+
@@index([tournamentId])
|
|
107
|
+
@@index([tournamentId, isFinals])
|
|
108
|
+
}
|
|
97
109
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
// Match model - a single game with 1-4 players
|
|
111
|
+
model Match {
|
|
112
|
+
id String @id @default(cuid())
|
|
113
|
+
createdAt DateTime @default(now())
|
|
114
|
+
updatedAt DateTime @updatedAt
|
|
102
115
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
116
|
+
tournamentId String
|
|
117
|
+
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
|
|
118
|
+
roundId String?
|
|
119
|
+
round Round? @relation(fields: [roundId], references: [id], onDelete: SetNull)
|
|
107
120
|
|
|
108
|
-
//
|
|
109
|
-
|
|
121
|
+
number Int? // Match number within the round
|
|
122
|
+
machineName String? // Machine played on
|
|
123
|
+
|
|
124
|
+
// Relations
|
|
125
|
+
entries Entry[]
|
|
110
126
|
|
|
111
|
-
@@
|
|
127
|
+
@@index([tournamentId])
|
|
128
|
+
@@index([roundId])
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Entry model - a player's participation in a match
|
|
132
|
+
model Entry {
|
|
133
|
+
id String @id @default(cuid())
|
|
134
|
+
createdAt DateTime @default(now())
|
|
135
|
+
updatedAt DateTime @updatedAt
|
|
136
|
+
|
|
137
|
+
matchId String
|
|
138
|
+
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
|
|
139
|
+
playerId String
|
|
140
|
+
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
|
141
|
+
|
|
142
|
+
result MatchResult // WIN, LOSS, TIE
|
|
143
|
+
position Int? // Position within the match (1st, 2nd, 3rd, 4th for group games)
|
|
144
|
+
|
|
145
|
+
@@unique([matchId, playerId])
|
|
146
|
+
@@index([matchId])
|
|
147
|
+
@@index([playerId])
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Standing model - final position for qualifying or finals
|
|
151
|
+
model Standing {
|
|
152
|
+
id String @id @default(cuid())
|
|
153
|
+
createdAt DateTime @default(now())
|
|
154
|
+
updatedAt DateTime @updatedAt
|
|
155
|
+
|
|
156
|
+
tournamentId String
|
|
157
|
+
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
|
|
158
|
+
playerId String
|
|
159
|
+
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
|
160
|
+
|
|
161
|
+
position Int // Finishing position (1 = first place)
|
|
162
|
+
isFinals Boolean @default(false)
|
|
163
|
+
optedOut Boolean @default(false)
|
|
164
|
+
|
|
165
|
+
// Points (calculated from merged standings)
|
|
166
|
+
linearPoints Float? @default(0)
|
|
167
|
+
dynamicPoints Float? @default(0)
|
|
168
|
+
totalPoints Float?
|
|
169
|
+
ageInDays Int? @default(0)
|
|
170
|
+
decayMultiplier Float? @default(1.0)
|
|
171
|
+
decayedPoints Float?
|
|
172
|
+
efficiency Float?
|
|
173
|
+
|
|
174
|
+
@@unique([playerId, tournamentId, isFinals])
|
|
112
175
|
@@index([playerId])
|
|
113
176
|
@@index([tournamentId])
|
|
177
|
+
@@index([tournamentId, isFinals])
|
|
114
178
|
@@index([position])
|
|
115
179
|
}
|
|
116
180
|
|
|
181
|
+
// Enum for match results
|
|
182
|
+
enum MatchResult {
|
|
183
|
+
WIN
|
|
184
|
+
LOSS
|
|
185
|
+
TIE
|
|
186
|
+
}
|
|
187
|
+
|
|
117
188
|
// Enum for event booster types
|
|
118
189
|
enum EventBoosterType {
|
|
119
190
|
NONE
|
|
@@ -149,5 +220,122 @@ model User {
|
|
|
149
220
|
// Session management (for token revocation)
|
|
150
221
|
refreshTokenHash String?
|
|
151
222
|
|
|
223
|
+
// Policy acceptance timestamps (null = not accepted)
|
|
224
|
+
tosAcceptedAt DateTime?
|
|
225
|
+
privacyPolicyAcceptedAt DateTime?
|
|
226
|
+
codeOfConductAcceptedAt DateTime?
|
|
227
|
+
|
|
228
|
+
// API Keys
|
|
229
|
+
apiKeys ApiKey[]
|
|
230
|
+
|
|
152
231
|
@@index([email])
|
|
153
232
|
}
|
|
233
|
+
|
|
234
|
+
// ApiKey model - represents an API key for programmatic access
|
|
235
|
+
model ApiKey {
|
|
236
|
+
id String @id @default(cuid())
|
|
237
|
+
createdAt DateTime @default(now())
|
|
238
|
+
updatedAt DateTime @updatedAt
|
|
239
|
+
|
|
240
|
+
// Key identification
|
|
241
|
+
name String // User-provided name (e.g., "CI Pipeline", "Mobile App")
|
|
242
|
+
keyPrefix String // First 14 characters of the key for display/lookup
|
|
243
|
+
keyHash String // bcrypt hash of the full key
|
|
244
|
+
|
|
245
|
+
// Ownership
|
|
246
|
+
userId String
|
|
247
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
248
|
+
|
|
249
|
+
// Lifecycle
|
|
250
|
+
expiresAt DateTime? // Optional expiration date
|
|
251
|
+
lastUsedAt DateTime? // Updated on each successful authentication
|
|
252
|
+
|
|
253
|
+
@@index([userId])
|
|
254
|
+
@@index([keyPrefix])
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Location model - represents a venue where tournaments are held
|
|
258
|
+
model Location {
|
|
259
|
+
id String @id @default(cuid())
|
|
260
|
+
createdAt DateTime @default(now())
|
|
261
|
+
updatedAt DateTime @updatedAt
|
|
262
|
+
|
|
263
|
+
externalId String? @unique
|
|
264
|
+
name String
|
|
265
|
+
address String?
|
|
266
|
+
city String?
|
|
267
|
+
state String?
|
|
268
|
+
country String?
|
|
269
|
+
|
|
270
|
+
// Relations
|
|
271
|
+
tournaments Tournament[]
|
|
272
|
+
|
|
273
|
+
@@index([externalId])
|
|
274
|
+
@@index([name])
|
|
275
|
+
@@index([city])
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// OPPR Player Ranking - OPPR-specific rating and ranking data
|
|
279
|
+
// Separated from Player to allow for future alternative ranking systems
|
|
280
|
+
model OpprPlayerRanking {
|
|
281
|
+
id String @id @default(cuid())
|
|
282
|
+
createdAt DateTime @default(now())
|
|
283
|
+
updatedAt DateTime @updatedAt
|
|
284
|
+
|
|
285
|
+
// Relation to Player (one-to-one)
|
|
286
|
+
playerId String @unique
|
|
287
|
+
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
|
288
|
+
|
|
289
|
+
// Glicko Rating fields
|
|
290
|
+
rating Float @default(1500) // Glicko rating
|
|
291
|
+
ratingDeviation Float @default(200) // Rating uncertainty (RD)
|
|
292
|
+
lastRatingUpdate DateTime @default(now())
|
|
293
|
+
|
|
294
|
+
// World Ranking fields
|
|
295
|
+
ranking Int? // World ranking position (1 = best)
|
|
296
|
+
isRated Boolean @default(false) // Has 5+ events (eligible for ranking)
|
|
297
|
+
|
|
298
|
+
// Relations
|
|
299
|
+
history OpprRankingHistory[]
|
|
300
|
+
|
|
301
|
+
@@index([playerId])
|
|
302
|
+
@@index([rating])
|
|
303
|
+
@@index([ranking])
|
|
304
|
+
@@index([isRated])
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// OPPR Ranking History - Historical record of ranking/rating changes
|
|
308
|
+
model OpprRankingHistory {
|
|
309
|
+
id String @id @default(cuid())
|
|
310
|
+
createdAt DateTime @default(now())
|
|
311
|
+
|
|
312
|
+
// Relation to OpprPlayerRanking
|
|
313
|
+
opprPlayerRankingId String
|
|
314
|
+
opprPlayerRanking OpprPlayerRanking @relation(fields: [opprPlayerRankingId], references: [id], onDelete: Cascade)
|
|
315
|
+
|
|
316
|
+
// Snapshot of values at this point in time
|
|
317
|
+
rating Float
|
|
318
|
+
ratingDeviation Float
|
|
319
|
+
ranking Int?
|
|
320
|
+
isRated Boolean
|
|
321
|
+
|
|
322
|
+
// Context for the change
|
|
323
|
+
changeType OpprRankingChangeType
|
|
324
|
+
tournamentId String?
|
|
325
|
+
tournament Tournament? @relation(fields: [tournamentId], references: [id], onDelete: SetNull)
|
|
326
|
+
notes String? @db.VarChar(500)
|
|
327
|
+
|
|
328
|
+
@@index([opprPlayerRankingId])
|
|
329
|
+
@@index([createdAt])
|
|
330
|
+
@@index([tournamentId])
|
|
331
|
+
@@index([opprPlayerRankingId, createdAt])
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Enum for tracking what caused the ranking change
|
|
335
|
+
enum OpprRankingChangeType {
|
|
336
|
+
INITIAL // First ranking record created
|
|
337
|
+
TOURNAMENT_RESULT // Rating updated after tournament
|
|
338
|
+
RANKING_REFRESH // Periodic ranking recalculation
|
|
339
|
+
RD_DECAY // RD increased due to inactivity
|
|
340
|
+
MANUAL_ADJUSTMENT // Administrative correction
|
|
341
|
+
}
|