@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.
@@ -21,25 +21,19 @@ model Player {
21
21
  playerNumber Int @unique // 5-digit unique identifier (10000-99999)
22
22
  name String?
23
23
 
24
- // OPPR Rating fields
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
- tournamentResults TournamentResult[]
37
- user User?
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
- location String?
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
- results TournamentResult[]
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
- // Tournament Result - junction table linking players to tournaments
83
- model TournamentResult {
84
- id String @id @default(cuid())
85
- createdAt DateTime @default(now())
86
- updatedAt DateTime @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
- playerId String
90
- player Player @relation(fields: [playerId], references: [id], onDelete: Cascade)
91
- tournamentId String
92
- tournament Tournament @relation(fields: [tournamentId], references: [id], onDelete: Cascade)
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
- // Result data
95
- position Int // Finishing position (1 = first place)
96
- optedOut Boolean @default(false)
122
+ number Int? // Match number within the round
123
+ machineName String? // Machine played on
97
124
 
98
- // Points awarded
99
- linearPoints Float? @default(0) // Linear distribution points
100
- dynamicPoints Float? @default(0) // Dynamic distribution points
101
- totalPoints Float? // Total points (linear + dynamic)
125
+ // Relations
126
+ entries Entry[]
102
127
 
103
- // Time decay tracking
104
- ageInDays Int? @default(0) // Age of event in days at calculation time
105
- decayMultiplier Float? @default(1.0) // Time decay multiplier (1.0, 0.75, 0.5, or 0.0)
106
- decayedPoints Float? // Points after applying decay (defaults to totalPoints via application logic)
128
+ @@index([tournamentId])
129
+ @@index([roundId])
130
+ }
107
131
 
108
- // Efficiency tracking
109
- efficiency Float? // Performance efficiency percentage
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
- @@unique([playerId, tournamentId])
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
- location: 'Las Vegas, NV',
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
- location: 'Portland, OR',
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
- location: 'Seattle, WA',
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 results (delete existing first for idempotency)
262
- console.log('Creating tournament results...');
320
+ // Create tournament standings (delete existing first for idempotency)
321
+ console.log('Creating tournament standings...');
263
322
 
264
- // Delete existing results for seeded tournaments
265
- await prisma.tournamentResult.deleteMany({
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 results
274
- await prisma.tournamentResult.createMany({
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 results
328
- await prisma.tournamentResult.createMany({
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 results
382
- await prisma.tournamentResult.createMany({
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.tournamentResult.count()} tournament results`);
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.tournamentResult.count()} tournament results`);
504
+ console.log(` - ${await prisma.standing.count()} standings`);
433
505
  }
434
506
 
435
507
  main()