@opprs/db-prisma 2.2.1-canary.840995 → 2.2.1-canary.93558b4
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 +1182 -156
- package/dist/index.d.cts +998 -105
- package/dist/index.d.ts +998 -105
- package/dist/index.js +1069 -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/migrations/20260105010000_add_tournament_external_url/migration.sql +2 -0
- package/prisma/schema.prisma +291 -37
- package/prisma/seed.ts +107 -35
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
|
|
@@ -50,10 +44,19 @@ model Tournament {
|
|
|
50
44
|
|
|
51
45
|
// Tournament identification
|
|
52
46
|
externalId String? @unique // External ID from OPPR or other systems
|
|
47
|
+
externalUrl String? // URL to external tournament page (e.g., IFPA, Matchplay)
|
|
53
48
|
name String
|
|
54
|
-
|
|
49
|
+
description String? @db.VarChar(2000)
|
|
55
50
|
date DateTime
|
|
56
51
|
|
|
52
|
+
// Location relation
|
|
53
|
+
locationId String?
|
|
54
|
+
location Location? @relation(fields: [locationId], references: [id], onDelete: SetNull)
|
|
55
|
+
|
|
56
|
+
// Organizer relation
|
|
57
|
+
organizerId String?
|
|
58
|
+
organizer Player? @relation("OrganizedTournaments", fields: [organizerId], references: [id], onDelete: SetNull)
|
|
59
|
+
|
|
57
60
|
// Tournament configuration (stored as JSON)
|
|
58
61
|
// Contains TGPConfig structure from OPPR
|
|
59
62
|
tgpConfig Json?
|
|
@@ -72,48 +75,117 @@ model Tournament {
|
|
|
72
75
|
firstPlaceValue Float?
|
|
73
76
|
|
|
74
77
|
// Relations
|
|
75
|
-
|
|
78
|
+
rounds Round[]
|
|
79
|
+
matches Match[]
|
|
80
|
+
standings Standing[]
|
|
81
|
+
rankingHistoryRecords OpprRankingHistory[]
|
|
76
82
|
|
|
77
83
|
@@index([date])
|
|
78
84
|
@@index([eventBooster])
|
|
79
85
|
@@index([externalId])
|
|
86
|
+
@@index([locationId])
|
|
87
|
+
@@index([organizerId])
|
|
80
88
|
}
|
|
81
89
|
|
|
82
|
-
//
|
|
83
|
-
model
|
|
84
|
-
id
|
|
85
|
-
createdAt
|
|
86
|
-
updatedAt
|
|
90
|
+
// Round model - groups matches within a tournament stage
|
|
91
|
+
model Round {
|
|
92
|
+
id String @id @default(cuid())
|
|
93
|
+
createdAt DateTime @default(now())
|
|
94
|
+
updatedAt DateTime @updatedAt
|
|
95
|
+
|
|
96
|
+
tournamentId String
|
|
97
|
+
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
|
|
98
|
+
|
|
99
|
+
number Int // Round number within the stage (1, 2, 3...)
|
|
100
|
+
name String? // Optional name (e.g., "Quarterfinals", "Semifinal")
|
|
101
|
+
isFinals Boolean @default(false)
|
|
87
102
|
|
|
88
103
|
// Relations
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
tournamentId
|
|
92
|
-
|
|
104
|
+
matches Match[]
|
|
105
|
+
|
|
106
|
+
@@unique([tournamentId, number, isFinals])
|
|
107
|
+
@@index([tournamentId])
|
|
108
|
+
@@index([tournamentId, isFinals])
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Match model - a single game with 1-4 players
|
|
112
|
+
model Match {
|
|
113
|
+
id String @id @default(cuid())
|
|
114
|
+
createdAt DateTime @default(now())
|
|
115
|
+
updatedAt DateTime @updatedAt
|
|
116
|
+
|
|
117
|
+
tournamentId String
|
|
118
|
+
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
|
|
119
|
+
roundId String?
|
|
120
|
+
round Round? @relation(fields: [roundId], references: [id], onDelete: SetNull)
|
|
93
121
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
optedOut Boolean @default(false)
|
|
122
|
+
number Int? // Match number within the round
|
|
123
|
+
machineName String? // Machine played on
|
|
97
124
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
dynamicPoints Float? @default(0) // Dynamic distribution points
|
|
101
|
-
totalPoints Float? // Total points (linear + dynamic)
|
|
125
|
+
// Relations
|
|
126
|
+
entries Entry[]
|
|
102
127
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
decayedPoints Float? // Points after applying decay (defaults to totalPoints via application logic)
|
|
128
|
+
@@index([tournamentId])
|
|
129
|
+
@@index([roundId])
|
|
130
|
+
}
|
|
107
131
|
|
|
108
|
-
|
|
109
|
-
|
|
132
|
+
// Entry model - a player's participation in a match
|
|
133
|
+
model Entry {
|
|
134
|
+
id String @id @default(cuid())
|
|
135
|
+
createdAt DateTime @default(now())
|
|
136
|
+
updatedAt DateTime @updatedAt
|
|
110
137
|
|
|
111
|
-
|
|
138
|
+
matchId String
|
|
139
|
+
match Match @relation(fields: [matchId], references: [id], onDelete: Cascade)
|
|
140
|
+
playerId String
|
|
141
|
+
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
|
142
|
+
|
|
143
|
+
result MatchResult // WIN, LOSS, TIE
|
|
144
|
+
position Int? // Position within the match (1st, 2nd, 3rd, 4th for group games)
|
|
145
|
+
|
|
146
|
+
@@unique([matchId, playerId])
|
|
147
|
+
@@index([matchId])
|
|
148
|
+
@@index([playerId])
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Standing model - final position for qualifying or finals
|
|
152
|
+
model Standing {
|
|
153
|
+
id String @id @default(cuid())
|
|
154
|
+
createdAt DateTime @default(now())
|
|
155
|
+
updatedAt DateTime @updatedAt
|
|
156
|
+
|
|
157
|
+
tournamentId String
|
|
158
|
+
tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
|
|
159
|
+
playerId String
|
|
160
|
+
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
|
161
|
+
|
|
162
|
+
position Int // Finishing position (1 = first place)
|
|
163
|
+
isFinals Boolean @default(false)
|
|
164
|
+
optedOut Boolean @default(false)
|
|
165
|
+
|
|
166
|
+
// Points (calculated from merged standings)
|
|
167
|
+
linearPoints Float? @default(0)
|
|
168
|
+
dynamicPoints Float? @default(0)
|
|
169
|
+
totalPoints Float?
|
|
170
|
+
ageInDays Int? @default(0)
|
|
171
|
+
decayMultiplier Float? @default(1.0)
|
|
172
|
+
decayedPoints Float?
|
|
173
|
+
efficiency Float?
|
|
174
|
+
|
|
175
|
+
@@unique([playerId, tournamentId, isFinals])
|
|
112
176
|
@@index([playerId])
|
|
113
177
|
@@index([tournamentId])
|
|
178
|
+
@@index([tournamentId, isFinals])
|
|
114
179
|
@@index([position])
|
|
115
180
|
}
|
|
116
181
|
|
|
182
|
+
// Enum for match results
|
|
183
|
+
enum MatchResult {
|
|
184
|
+
WIN
|
|
185
|
+
LOSS
|
|
186
|
+
TIE
|
|
187
|
+
}
|
|
188
|
+
|
|
117
189
|
// Enum for event booster types
|
|
118
190
|
enum EventBoosterType {
|
|
119
191
|
NONE
|
|
@@ -129,6 +201,12 @@ enum Role {
|
|
|
129
201
|
ADMIN
|
|
130
202
|
}
|
|
131
203
|
|
|
204
|
+
// Enum for blog post status
|
|
205
|
+
enum PostStatus {
|
|
206
|
+
DRAFT
|
|
207
|
+
PUBLISHED
|
|
208
|
+
}
|
|
209
|
+
|
|
132
210
|
// User model - represents an authenticated user account
|
|
133
211
|
model User {
|
|
134
212
|
id String @id @default(cuid())
|
|
@@ -149,5 +227,181 @@ model User {
|
|
|
149
227
|
// Session management (for token revocation)
|
|
150
228
|
refreshTokenHash String?
|
|
151
229
|
|
|
230
|
+
// Policy acceptance timestamps (null = not accepted)
|
|
231
|
+
tosAcceptedAt DateTime?
|
|
232
|
+
privacyPolicyAcceptedAt DateTime?
|
|
233
|
+
codeOfConductAcceptedAt DateTime?
|
|
234
|
+
|
|
235
|
+
// Blog posts authored by this user
|
|
236
|
+
blogPosts BlogPost[]
|
|
237
|
+
|
|
238
|
+
// API Keys
|
|
239
|
+
apiKeys ApiKey[]
|
|
240
|
+
|
|
152
241
|
@@index([email])
|
|
153
242
|
}
|
|
243
|
+
|
|
244
|
+
// ApiKey model - represents an API key for programmatic access
|
|
245
|
+
model ApiKey {
|
|
246
|
+
id String @id @default(cuid())
|
|
247
|
+
createdAt DateTime @default(now())
|
|
248
|
+
updatedAt DateTime @updatedAt
|
|
249
|
+
|
|
250
|
+
// Key identification
|
|
251
|
+
name String // User-provided name (e.g., "CI Pipeline", "Mobile App")
|
|
252
|
+
keyPrefix String // First 14 characters of the key for display/lookup
|
|
253
|
+
keyHash String // bcrypt hash of the full key
|
|
254
|
+
|
|
255
|
+
// Ownership
|
|
256
|
+
userId String
|
|
257
|
+
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
258
|
+
|
|
259
|
+
// Lifecycle
|
|
260
|
+
expiresAt DateTime? // Optional expiration date
|
|
261
|
+
lastUsedAt DateTime? // Updated on each successful authentication
|
|
262
|
+
|
|
263
|
+
@@index([userId])
|
|
264
|
+
@@index([keyPrefix])
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Location model - represents a venue where tournaments are held
|
|
268
|
+
model Location {
|
|
269
|
+
id String @id @default(cuid())
|
|
270
|
+
createdAt DateTime @default(now())
|
|
271
|
+
updatedAt DateTime @updatedAt
|
|
272
|
+
|
|
273
|
+
externalId String? @unique
|
|
274
|
+
name String
|
|
275
|
+
address String?
|
|
276
|
+
city String?
|
|
277
|
+
state String?
|
|
278
|
+
country String?
|
|
279
|
+
|
|
280
|
+
// Relations
|
|
281
|
+
tournaments Tournament[]
|
|
282
|
+
|
|
283
|
+
@@index([externalId])
|
|
284
|
+
@@index([name])
|
|
285
|
+
@@index([city])
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// BlogPost model - represents a blog article
|
|
289
|
+
model BlogPost {
|
|
290
|
+
id String @id @default(cuid())
|
|
291
|
+
createdAt DateTime @default(now())
|
|
292
|
+
updatedAt DateTime @updatedAt
|
|
293
|
+
|
|
294
|
+
// Content
|
|
295
|
+
title String
|
|
296
|
+
slug String @unique
|
|
297
|
+
content String @db.Text
|
|
298
|
+
excerpt String? @db.VarChar(500)
|
|
299
|
+
|
|
300
|
+
// Status
|
|
301
|
+
status PostStatus @default(DRAFT)
|
|
302
|
+
publishedAt DateTime?
|
|
303
|
+
|
|
304
|
+
// Featured image (external URL)
|
|
305
|
+
featuredImageUrl String?
|
|
306
|
+
featuredImageAlt String?
|
|
307
|
+
|
|
308
|
+
// SEO fields
|
|
309
|
+
metaTitle String? @db.VarChar(60)
|
|
310
|
+
metaDescription String? @db.VarChar(160)
|
|
311
|
+
ogTitle String?
|
|
312
|
+
ogDescription String?
|
|
313
|
+
ogImageUrl String?
|
|
314
|
+
|
|
315
|
+
// Author (User who created the post)
|
|
316
|
+
authorId String
|
|
317
|
+
author User @relation(fields: [authorId], references: [id], onDelete: Restrict)
|
|
318
|
+
|
|
319
|
+
// Tags relation
|
|
320
|
+
tags BlogTag[]
|
|
321
|
+
|
|
322
|
+
@@index([slug])
|
|
323
|
+
@@index([status])
|
|
324
|
+
@@index([publishedAt])
|
|
325
|
+
@@index([authorId])
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// BlogTag model - for categorizing blog posts
|
|
329
|
+
model BlogTag {
|
|
330
|
+
id String @id @default(cuid())
|
|
331
|
+
createdAt DateTime @default(now())
|
|
332
|
+
updatedAt DateTime @updatedAt
|
|
333
|
+
|
|
334
|
+
name String @unique
|
|
335
|
+
slug String @unique
|
|
336
|
+
description String?
|
|
337
|
+
|
|
338
|
+
// Posts with this tag
|
|
339
|
+
posts BlogPost[]
|
|
340
|
+
|
|
341
|
+
@@index([slug])
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// OPPR Player Ranking - OPPR-specific rating and ranking data
|
|
345
|
+
// Separated from Player to allow for future alternative ranking systems
|
|
346
|
+
model OpprPlayerRanking {
|
|
347
|
+
id String @id @default(cuid())
|
|
348
|
+
createdAt DateTime @default(now())
|
|
349
|
+
updatedAt DateTime @updatedAt
|
|
350
|
+
|
|
351
|
+
// Relation to Player (one-to-one)
|
|
352
|
+
playerId String @unique
|
|
353
|
+
player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
|
|
354
|
+
|
|
355
|
+
// Glicko Rating fields
|
|
356
|
+
rating Float @default(1500) // Glicko rating
|
|
357
|
+
ratingDeviation Float @default(200) // Rating uncertainty (RD)
|
|
358
|
+
lastRatingUpdate DateTime @default(now())
|
|
359
|
+
|
|
360
|
+
// World Ranking fields
|
|
361
|
+
ranking Int? // World ranking position (1 = best)
|
|
362
|
+
isRated Boolean @default(false) // Has 5+ events (eligible for ranking)
|
|
363
|
+
|
|
364
|
+
// Relations
|
|
365
|
+
history OpprRankingHistory[]
|
|
366
|
+
|
|
367
|
+
@@index([playerId])
|
|
368
|
+
@@index([rating])
|
|
369
|
+
@@index([ranking])
|
|
370
|
+
@@index([isRated])
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// OPPR Ranking History - Historical record of ranking/rating changes
|
|
374
|
+
model OpprRankingHistory {
|
|
375
|
+
id String @id @default(cuid())
|
|
376
|
+
createdAt DateTime @default(now())
|
|
377
|
+
|
|
378
|
+
// Relation to OpprPlayerRanking
|
|
379
|
+
opprPlayerRankingId String
|
|
380
|
+
opprPlayerRanking OpprPlayerRanking @relation(fields: [opprPlayerRankingId], references: [id], onDelete: Cascade)
|
|
381
|
+
|
|
382
|
+
// Snapshot of values at this point in time
|
|
383
|
+
rating Float
|
|
384
|
+
ratingDeviation Float
|
|
385
|
+
ranking Int?
|
|
386
|
+
isRated Boolean
|
|
387
|
+
|
|
388
|
+
// Context for the change
|
|
389
|
+
changeType OpprRankingChangeType
|
|
390
|
+
tournamentId String?
|
|
391
|
+
tournament Tournament? @relation(fields: [tournamentId], references: [id], onDelete: SetNull)
|
|
392
|
+
notes String? @db.VarChar(500)
|
|
393
|
+
|
|
394
|
+
@@index([opprPlayerRankingId])
|
|
395
|
+
@@index([createdAt])
|
|
396
|
+
@@index([tournamentId])
|
|
397
|
+
@@index([opprPlayerRankingId, createdAt])
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Enum for tracking what caused the ranking change
|
|
401
|
+
enum OpprRankingChangeType {
|
|
402
|
+
INITIAL // First ranking record created
|
|
403
|
+
TOURNAMENT_RESULT // Rating updated after tournament
|
|
404
|
+
RANKING_REFRESH // Periodic ranking recalculation
|
|
405
|
+
RD_DECAY // RD increased due to inactivity
|
|
406
|
+
MANUAL_ADJUSTMENT // Administrative correction
|
|
407
|
+
}
|
package/prisma/seed.ts
CHANGED
|
@@ -15,54 +15,43 @@ async function main() {
|
|
|
15
15
|
externalId: 'player-1',
|
|
16
16
|
playerNumber: 10001,
|
|
17
17
|
name: 'Alice Champion',
|
|
18
|
-
rating: 1850,
|
|
19
|
-
ratingDeviation: 50,
|
|
20
|
-
ranking: 5,
|
|
21
|
-
isRated: true,
|
|
22
18
|
eventCount: 25,
|
|
23
19
|
},
|
|
24
20
|
{
|
|
25
21
|
externalId: 'player-2',
|
|
26
22
|
playerNumber: 10002,
|
|
27
23
|
name: 'Bob Wizard',
|
|
28
|
-
rating: 1750,
|
|
29
|
-
ratingDeviation: 60,
|
|
30
|
-
ranking: 12,
|
|
31
|
-
isRated: true,
|
|
32
24
|
eventCount: 18,
|
|
33
25
|
},
|
|
34
26
|
{
|
|
35
27
|
externalId: 'player-3',
|
|
36
28
|
playerNumber: 10003,
|
|
37
29
|
name: 'Charlie Flipper',
|
|
38
|
-
rating: 1650,
|
|
39
|
-
ratingDeviation: 75,
|
|
40
|
-
ranking: 28,
|
|
41
|
-
isRated: true,
|
|
42
30
|
eventCount: 12,
|
|
43
31
|
},
|
|
44
32
|
{
|
|
45
33
|
externalId: 'player-4',
|
|
46
34
|
playerNumber: 10004,
|
|
47
35
|
name: 'Diana Tilt',
|
|
48
|
-
rating: 1550,
|
|
49
|
-
ratingDeviation: 100,
|
|
50
|
-
ranking: 45,
|
|
51
|
-
isRated: true,
|
|
52
36
|
eventCount: 8,
|
|
53
37
|
},
|
|
54
38
|
{
|
|
55
39
|
externalId: 'player-5',
|
|
56
40
|
playerNumber: 10005,
|
|
57
41
|
name: 'Eve Plunger',
|
|
58
|
-
rating: 1300,
|
|
59
|
-
ratingDeviation: 150,
|
|
60
|
-
ranking: null,
|
|
61
|
-
isRated: false,
|
|
62
42
|
eventCount: 3,
|
|
63
43
|
},
|
|
64
44
|
];
|
|
65
45
|
|
|
46
|
+
// OPPR ranking data for each player
|
|
47
|
+
const rankingData = [
|
|
48
|
+
{ rating: 1850, ratingDeviation: 50, ranking: 5, isRated: true },
|
|
49
|
+
{ rating: 1750, ratingDeviation: 60, ranking: 12, isRated: true },
|
|
50
|
+
{ rating: 1650, ratingDeviation: 75, ranking: 28, isRated: true },
|
|
51
|
+
{ rating: 1550, ratingDeviation: 100, ranking: 45, isRated: true },
|
|
52
|
+
{ rating: 1300, ratingDeviation: 150, ranking: null, isRated: false },
|
|
53
|
+
];
|
|
54
|
+
|
|
66
55
|
const player1 = await prisma.player.upsert({
|
|
67
56
|
where: { externalId: 'player-1' },
|
|
68
57
|
update: playerData[0],
|
|
@@ -95,6 +84,18 @@ async function main() {
|
|
|
95
84
|
|
|
96
85
|
console.log(`✓ Created ${await prisma.player.count()} players`);
|
|
97
86
|
|
|
87
|
+
// Create OPPR rankings for each player
|
|
88
|
+
console.log('Creating OPPR rankings...');
|
|
89
|
+
const players = [player1, player2, player3, player4, player5];
|
|
90
|
+
for (let i = 0; i < players.length; i++) {
|
|
91
|
+
await prisma.opprPlayerRanking.upsert({
|
|
92
|
+
where: { playerId: players[i].id },
|
|
93
|
+
update: rankingData[i],
|
|
94
|
+
create: { playerId: players[i].id, ...rankingData[i] },
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
console.log(`✓ Created ${await prisma.opprPlayerRanking.count()} OPPR rankings`);
|
|
98
|
+
|
|
98
99
|
// Seed users from environment variables (development only)
|
|
99
100
|
const seedAdminEmail = process.env.SEED_ADMIN_EMAIL;
|
|
100
101
|
const seedAdminPassword = process.env.SEED_ADMIN_PASSWORD;
|
|
@@ -149,13 +150,71 @@ async function main() {
|
|
|
149
150
|
|
|
150
151
|
console.log(`✓ Created admin user (admin@example.com / ${adminPassword})`);
|
|
151
152
|
|
|
153
|
+
// Create sample locations (using upsert for idempotency)
|
|
154
|
+
console.log('Creating locations...');
|
|
155
|
+
|
|
156
|
+
const location1 = await prisma.location.upsert({
|
|
157
|
+
where: { externalId: 'location-1' },
|
|
158
|
+
update: {
|
|
159
|
+
name: 'Las Vegas Convention Center',
|
|
160
|
+
city: 'Las Vegas',
|
|
161
|
+
state: 'NV',
|
|
162
|
+
country: 'USA',
|
|
163
|
+
},
|
|
164
|
+
create: {
|
|
165
|
+
externalId: 'location-1',
|
|
166
|
+
name: 'Las Vegas Convention Center',
|
|
167
|
+
city: 'Las Vegas',
|
|
168
|
+
state: 'NV',
|
|
169
|
+
country: 'USA',
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const location2 = await prisma.location.upsert({
|
|
174
|
+
where: { externalId: 'location-2' },
|
|
175
|
+
update: {
|
|
176
|
+
name: 'Ground Kontrol',
|
|
177
|
+
address: '115 NW 5th Ave',
|
|
178
|
+
city: 'Portland',
|
|
179
|
+
state: 'OR',
|
|
180
|
+
country: 'USA',
|
|
181
|
+
},
|
|
182
|
+
create: {
|
|
183
|
+
externalId: 'location-2',
|
|
184
|
+
name: 'Ground Kontrol',
|
|
185
|
+
address: '115 NW 5th Ave',
|
|
186
|
+
city: 'Portland',
|
|
187
|
+
state: 'OR',
|
|
188
|
+
country: 'USA',
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const location3 = await prisma.location.upsert({
|
|
193
|
+
where: { externalId: 'location-3' },
|
|
194
|
+
update: {
|
|
195
|
+
name: 'Add-a-Ball Amusements',
|
|
196
|
+
city: 'Seattle',
|
|
197
|
+
state: 'WA',
|
|
198
|
+
country: 'USA',
|
|
199
|
+
},
|
|
200
|
+
create: {
|
|
201
|
+
externalId: 'location-3',
|
|
202
|
+
name: 'Add-a-Ball Amusements',
|
|
203
|
+
city: 'Seattle',
|
|
204
|
+
state: 'WA',
|
|
205
|
+
country: 'USA',
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
console.log(`✓ Created ${await prisma.location.count()} locations`);
|
|
210
|
+
|
|
152
211
|
// Create sample tournaments (using upsert for idempotency)
|
|
153
212
|
console.log('Creating tournaments...');
|
|
154
213
|
|
|
155
214
|
const tournament1Data = {
|
|
156
215
|
externalId: 'tournament-1',
|
|
157
216
|
name: 'World Pinball Championship 2024',
|
|
158
|
-
|
|
217
|
+
locationId: location1.id,
|
|
159
218
|
date: new Date('2024-03-15'),
|
|
160
219
|
eventBooster: EventBoosterType.MAJOR,
|
|
161
220
|
allowsOptOut: false,
|
|
@@ -191,7 +250,7 @@ async function main() {
|
|
|
191
250
|
const tournament2Data = {
|
|
192
251
|
externalId: 'tournament-2',
|
|
193
252
|
name: 'Spring Classics 2024',
|
|
194
|
-
|
|
253
|
+
locationId: location2.id,
|
|
195
254
|
date: new Date('2024-04-20'),
|
|
196
255
|
eventBooster: EventBoosterType.CERTIFIED,
|
|
197
256
|
allowsOptOut: true,
|
|
@@ -225,7 +284,7 @@ async function main() {
|
|
|
225
284
|
const tournament3Data = {
|
|
226
285
|
externalId: 'tournament-3',
|
|
227
286
|
name: 'Monthly League Finals',
|
|
228
|
-
|
|
287
|
+
locationId: location3.id,
|
|
229
288
|
date: new Date('2024-05-10'),
|
|
230
289
|
eventBooster: EventBoosterType.NONE,
|
|
231
290
|
allowsOptOut: false,
|
|
@@ -258,11 +317,11 @@ async function main() {
|
|
|
258
317
|
|
|
259
318
|
console.log(`✓ Created ${await prisma.tournament.count()} tournaments`);
|
|
260
319
|
|
|
261
|
-
// Create tournament
|
|
262
|
-
console.log('Creating tournament
|
|
320
|
+
// Create tournament standings (delete existing first for idempotency)
|
|
321
|
+
console.log('Creating tournament standings...');
|
|
263
322
|
|
|
264
|
-
// Delete existing
|
|
265
|
-
await prisma.
|
|
323
|
+
// Delete existing standings for seeded tournaments
|
|
324
|
+
await prisma.standing.deleteMany({
|
|
266
325
|
where: {
|
|
267
326
|
tournamentId: {
|
|
268
327
|
in: [tournament1.id, tournament2.id, tournament3.id],
|
|
@@ -270,13 +329,14 @@ async function main() {
|
|
|
270
329
|
},
|
|
271
330
|
});
|
|
272
331
|
|
|
273
|
-
// World Championship
|
|
274
|
-
await prisma.
|
|
332
|
+
// World Championship standings
|
|
333
|
+
await prisma.standing.createMany({
|
|
275
334
|
data: [
|
|
276
335
|
{
|
|
277
336
|
playerId: player1.id,
|
|
278
337
|
tournamentId: tournament1.id,
|
|
279
338
|
position: 1,
|
|
339
|
+
isFinals: true,
|
|
280
340
|
totalPoints: 411.84,
|
|
281
341
|
linearPoints: 41.18,
|
|
282
342
|
dynamicPoints: 370.66,
|
|
@@ -289,6 +349,7 @@ async function main() {
|
|
|
289
349
|
playerId: player2.id,
|
|
290
350
|
tournamentId: tournament1.id,
|
|
291
351
|
position: 2,
|
|
352
|
+
isFinals: true,
|
|
292
353
|
totalPoints: 298.45,
|
|
293
354
|
linearPoints: 41.18,
|
|
294
355
|
dynamicPoints: 257.27,
|
|
@@ -301,6 +362,7 @@ async function main() {
|
|
|
301
362
|
playerId: player3.id,
|
|
302
363
|
tournamentId: tournament1.id,
|
|
303
364
|
position: 3,
|
|
365
|
+
isFinals: true,
|
|
304
366
|
totalPoints: 215.32,
|
|
305
367
|
linearPoints: 41.18,
|
|
306
368
|
dynamicPoints: 174.14,
|
|
@@ -313,6 +375,7 @@ async function main() {
|
|
|
313
375
|
playerId: player4.id,
|
|
314
376
|
tournamentId: tournament1.id,
|
|
315
377
|
position: 5,
|
|
378
|
+
isFinals: true,
|
|
316
379
|
totalPoints: 125.18,
|
|
317
380
|
linearPoints: 41.18,
|
|
318
381
|
dynamicPoints: 84.00,
|
|
@@ -324,13 +387,14 @@ async function main() {
|
|
|
324
387
|
],
|
|
325
388
|
});
|
|
326
389
|
|
|
327
|
-
// Spring Classics
|
|
328
|
-
await prisma.
|
|
390
|
+
// Spring Classics standings
|
|
391
|
+
await prisma.standing.createMany({
|
|
329
392
|
data: [
|
|
330
393
|
{
|
|
331
394
|
playerId: player2.id,
|
|
332
395
|
tournamentId: tournament2.id,
|
|
333
396
|
position: 1,
|
|
397
|
+
isFinals: true,
|
|
334
398
|
totalPoints: 87.28,
|
|
335
399
|
linearPoints: 8.73,
|
|
336
400
|
dynamicPoints: 78.55,
|
|
@@ -343,6 +407,7 @@ async function main() {
|
|
|
343
407
|
playerId: player1.id,
|
|
344
408
|
tournamentId: tournament2.id,
|
|
345
409
|
position: 2,
|
|
410
|
+
isFinals: true,
|
|
346
411
|
totalPoints: 63.25,
|
|
347
412
|
linearPoints: 8.73,
|
|
348
413
|
dynamicPoints: 54.52,
|
|
@@ -355,6 +420,7 @@ async function main() {
|
|
|
355
420
|
playerId: player4.id,
|
|
356
421
|
tournamentId: tournament2.id,
|
|
357
422
|
position: 3,
|
|
423
|
+
isFinals: true,
|
|
358
424
|
totalPoints: 45.67,
|
|
359
425
|
linearPoints: 8.73,
|
|
360
426
|
dynamicPoints: 36.94,
|
|
@@ -367,6 +433,7 @@ async function main() {
|
|
|
367
433
|
playerId: player5.id,
|
|
368
434
|
tournamentId: tournament2.id,
|
|
369
435
|
position: 6,
|
|
436
|
+
isFinals: true,
|
|
370
437
|
totalPoints: 18.52,
|
|
371
438
|
linearPoints: 8.73,
|
|
372
439
|
dynamicPoints: 9.79,
|
|
@@ -378,13 +445,14 @@ async function main() {
|
|
|
378
445
|
],
|
|
379
446
|
});
|
|
380
447
|
|
|
381
|
-
// Monthly League
|
|
382
|
-
await prisma.
|
|
448
|
+
// Monthly League standings
|
|
449
|
+
await prisma.standing.createMany({
|
|
383
450
|
data: [
|
|
384
451
|
{
|
|
385
452
|
playerId: player3.id,
|
|
386
453
|
tournamentId: tournament3.id,
|
|
387
454
|
position: 1,
|
|
455
|
+
isFinals: true,
|
|
388
456
|
totalPoints: 28.4,
|
|
389
457
|
linearPoints: 2.84,
|
|
390
458
|
dynamicPoints: 25.56,
|
|
@@ -397,6 +465,7 @@ async function main() {
|
|
|
397
465
|
playerId: player4.id,
|
|
398
466
|
tournamentId: tournament3.id,
|
|
399
467
|
position: 2,
|
|
468
|
+
isFinals: true,
|
|
400
469
|
totalPoints: 20.58,
|
|
401
470
|
linearPoints: 2.84,
|
|
402
471
|
dynamicPoints: 17.74,
|
|
@@ -409,6 +478,7 @@ async function main() {
|
|
|
409
478
|
playerId: player5.id,
|
|
410
479
|
tournamentId: tournament3.id,
|
|
411
480
|
position: 3,
|
|
481
|
+
isFinals: true,
|
|
412
482
|
totalPoints: 14.85,
|
|
413
483
|
linearPoints: 2.84,
|
|
414
484
|
dynamicPoints: 12.01,
|
|
@@ -420,16 +490,18 @@ async function main() {
|
|
|
420
490
|
],
|
|
421
491
|
});
|
|
422
492
|
|
|
423
|
-
console.log(`✓ Created ${await prisma.
|
|
493
|
+
console.log(`✓ Created ${await prisma.standing.count()} tournament standings`);
|
|
424
494
|
|
|
425
495
|
console.log('');
|
|
426
496
|
console.log('✅ Database seeded successfully!');
|
|
427
497
|
console.log('');
|
|
428
498
|
console.log('Summary:');
|
|
429
499
|
console.log(` - ${await prisma.player.count()} players`);
|
|
500
|
+
console.log(` - ${await prisma.opprPlayerRanking.count()} OPPR rankings`);
|
|
430
501
|
console.log(` - ${await prisma.user.count()} users`);
|
|
502
|
+
console.log(` - ${await prisma.location.count()} locations`);
|
|
431
503
|
console.log(` - ${await prisma.tournament.count()} tournaments`);
|
|
432
|
-
console.log(` - ${await prisma.
|
|
504
|
+
console.log(` - ${await prisma.standing.count()} standings`);
|
|
433
505
|
}
|
|
434
506
|
|
|
435
507
|
main()
|