@opprs/db-prisma 0.5.2-canary.16741f5
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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/index.cjs +573 -0
- package/dist/index.d.cts +608 -0
- package/dist/index.d.ts +608 -0
- package/dist/index.js +500 -0
- package/package.json +85 -0
- package/prisma/migrations/20251231075738_init/migration.sql +112 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +123 -0
- package/prisma/seed.ts +349 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import { PrismaClient } from "@prisma/client";
|
|
3
|
+
var globalForPrisma = globalThis;
|
|
4
|
+
var prisma = globalForPrisma.prisma ?? new PrismaClient({
|
|
5
|
+
log: process.env.NODE_ENV === "development" ? ["query", "error", "warn"] : ["error"]
|
|
6
|
+
});
|
|
7
|
+
if (process.env.NODE_ENV !== "production") {
|
|
8
|
+
globalForPrisma.prisma = prisma;
|
|
9
|
+
}
|
|
10
|
+
async function disconnect() {
|
|
11
|
+
await prisma.$disconnect();
|
|
12
|
+
}
|
|
13
|
+
async function connect() {
|
|
14
|
+
await prisma.$connect();
|
|
15
|
+
}
|
|
16
|
+
async function testConnection() {
|
|
17
|
+
try {
|
|
18
|
+
await prisma.$queryRaw`SELECT 1`;
|
|
19
|
+
return true;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error("Database connection test failed:", error);
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// src/players.ts
|
|
27
|
+
async function createPlayer(data) {
|
|
28
|
+
return prisma.player.create({
|
|
29
|
+
data
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async function findPlayerById(id, include) {
|
|
33
|
+
return prisma.player.findUnique({
|
|
34
|
+
where: { id },
|
|
35
|
+
include
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async function findPlayerByExternalId(externalId, include) {
|
|
39
|
+
return prisma.player.findUnique({
|
|
40
|
+
where: { externalId },
|
|
41
|
+
include
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function findPlayerByEmail(email, include) {
|
|
45
|
+
return prisma.player.findUnique({
|
|
46
|
+
where: { email },
|
|
47
|
+
include
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function findPlayers(options = {}) {
|
|
51
|
+
return prisma.player.findMany({
|
|
52
|
+
take: options.take,
|
|
53
|
+
skip: options.skip,
|
|
54
|
+
where: options.where,
|
|
55
|
+
orderBy: options.orderBy,
|
|
56
|
+
include: options.include
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async function getRatedPlayers(options = {}) {
|
|
60
|
+
return findPlayers({
|
|
61
|
+
...options,
|
|
62
|
+
where: { isRated: true }
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async function getTopPlayersByRating(limit = 50) {
|
|
66
|
+
return findPlayers({
|
|
67
|
+
take: limit,
|
|
68
|
+
orderBy: { rating: "desc" },
|
|
69
|
+
where: { isRated: true }
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async function getTopPlayersByRanking(limit = 50) {
|
|
73
|
+
return findPlayers({
|
|
74
|
+
take: limit,
|
|
75
|
+
orderBy: { ranking: "asc" },
|
|
76
|
+
where: {
|
|
77
|
+
isRated: true,
|
|
78
|
+
ranking: { not: null }
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function updatePlayer(id, data) {
|
|
83
|
+
return prisma.player.update({
|
|
84
|
+
where: { id },
|
|
85
|
+
data
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function updatePlayerRating(id, rating, ratingDeviation, eventCount) {
|
|
89
|
+
const updateData = {
|
|
90
|
+
rating,
|
|
91
|
+
ratingDeviation,
|
|
92
|
+
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
93
|
+
lastEventDate: /* @__PURE__ */ new Date()
|
|
94
|
+
};
|
|
95
|
+
if (eventCount !== void 0) {
|
|
96
|
+
updateData.eventCount = eventCount;
|
|
97
|
+
updateData.isRated = eventCount >= 5;
|
|
98
|
+
}
|
|
99
|
+
return updatePlayer(id, updateData);
|
|
100
|
+
}
|
|
101
|
+
async function deletePlayer(id) {
|
|
102
|
+
return prisma.player.delete({
|
|
103
|
+
where: { id }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function countPlayers(where) {
|
|
107
|
+
return prisma.player.count({ where });
|
|
108
|
+
}
|
|
109
|
+
async function getPlayerWithResults(id) {
|
|
110
|
+
const player = await prisma.player.findUnique({
|
|
111
|
+
where: { id },
|
|
112
|
+
include: {
|
|
113
|
+
tournamentResults: {
|
|
114
|
+
include: {
|
|
115
|
+
tournament: true
|
|
116
|
+
},
|
|
117
|
+
orderBy: {
|
|
118
|
+
tournament: {
|
|
119
|
+
date: "desc"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
if (!player) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
...player,
|
|
130
|
+
results: player.tournamentResults
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
async function searchPlayers(query, limit = 20) {
|
|
134
|
+
return findPlayers({
|
|
135
|
+
take: limit,
|
|
136
|
+
where: {
|
|
137
|
+
OR: [
|
|
138
|
+
{ name: { contains: query, mode: "insensitive" } },
|
|
139
|
+
{ email: { contains: query, mode: "insensitive" } }
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/tournaments.ts
|
|
146
|
+
async function createTournament(data) {
|
|
147
|
+
return prisma.tournament.create({
|
|
148
|
+
data: {
|
|
149
|
+
...data,
|
|
150
|
+
eventBooster: data.eventBooster ?? "NONE"
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
async function findTournamentById(id, include) {
|
|
155
|
+
return prisma.tournament.findUnique({
|
|
156
|
+
where: { id },
|
|
157
|
+
include
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async function findTournamentByExternalId(externalId, include) {
|
|
161
|
+
return prisma.tournament.findUnique({
|
|
162
|
+
where: { externalId },
|
|
163
|
+
include
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async function findTournaments(options = {}) {
|
|
167
|
+
return prisma.tournament.findMany({
|
|
168
|
+
take: options.take,
|
|
169
|
+
skip: options.skip,
|
|
170
|
+
where: options.where,
|
|
171
|
+
orderBy: options.orderBy,
|
|
172
|
+
include: options.include
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async function getRecentTournaments(limit = 20, include) {
|
|
176
|
+
return findTournaments({
|
|
177
|
+
take: limit,
|
|
178
|
+
orderBy: { date: "desc" },
|
|
179
|
+
include
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
async function getTournamentsByDateRange(startDate, endDate, options = {}) {
|
|
183
|
+
return findTournaments({
|
|
184
|
+
...options,
|
|
185
|
+
where: {
|
|
186
|
+
date: {
|
|
187
|
+
gte: startDate,
|
|
188
|
+
lte: endDate
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function getTournamentsByBoosterType(boosterType, options = {}) {
|
|
194
|
+
return findTournaments({
|
|
195
|
+
...options,
|
|
196
|
+
where: { eventBooster: boosterType }
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async function getMajorTournaments(limit) {
|
|
200
|
+
return findTournaments({
|
|
201
|
+
take: limit,
|
|
202
|
+
where: { eventBooster: "MAJOR" },
|
|
203
|
+
orderBy: { date: "desc" }
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function updateTournament(id, data) {
|
|
207
|
+
return prisma.tournament.update({
|
|
208
|
+
where: { id },
|
|
209
|
+
data
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function deleteTournament(id) {
|
|
213
|
+
return prisma.tournament.delete({
|
|
214
|
+
where: { id }
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async function countTournaments(where) {
|
|
218
|
+
return prisma.tournament.count({ where });
|
|
219
|
+
}
|
|
220
|
+
async function getTournamentWithResults(id) {
|
|
221
|
+
return prisma.tournament.findUnique({
|
|
222
|
+
where: { id },
|
|
223
|
+
include: {
|
|
224
|
+
results: {
|
|
225
|
+
include: {
|
|
226
|
+
player: true
|
|
227
|
+
},
|
|
228
|
+
orderBy: {
|
|
229
|
+
position: "asc"
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function searchTournaments(query, limit = 20) {
|
|
236
|
+
return findTournaments({
|
|
237
|
+
take: limit,
|
|
238
|
+
where: {
|
|
239
|
+
OR: [
|
|
240
|
+
{ name: { contains: query, mode: "insensitive" } },
|
|
241
|
+
{ location: { contains: query, mode: "insensitive" } }
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
orderBy: { date: "desc" }
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
async function getTournamentStats(id) {
|
|
248
|
+
const tournament = await getTournamentWithResults(id);
|
|
249
|
+
if (!tournament) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const playerCount = tournament.results.length;
|
|
253
|
+
if (playerCount === 0) {
|
|
254
|
+
return {
|
|
255
|
+
tournament,
|
|
256
|
+
playerCount: 0,
|
|
257
|
+
averagePoints: 0,
|
|
258
|
+
averageEfficiency: 0,
|
|
259
|
+
highestPoints: 0,
|
|
260
|
+
lowestPoints: 0
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const totalPoints = tournament.results.reduce((sum, r) => sum + (r.totalPoints || 0), 0);
|
|
264
|
+
const totalEfficiency = tournament.results.reduce((sum, r) => sum + (r.efficiency || 0), 0);
|
|
265
|
+
const allPoints = tournament.results.map((r) => r.totalPoints || 0);
|
|
266
|
+
return {
|
|
267
|
+
tournament,
|
|
268
|
+
playerCount,
|
|
269
|
+
averagePoints: totalPoints / playerCount,
|
|
270
|
+
averageEfficiency: totalEfficiency / playerCount,
|
|
271
|
+
highestPoints: Math.max(...allPoints),
|
|
272
|
+
lowestPoints: Math.min(...allPoints)
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/results.ts
|
|
277
|
+
async function createResult(data) {
|
|
278
|
+
const resultData = {
|
|
279
|
+
...data,
|
|
280
|
+
decayedPoints: data.decayedPoints ?? data.totalPoints ?? 0
|
|
281
|
+
};
|
|
282
|
+
return prisma.tournamentResult.create({
|
|
283
|
+
data: resultData
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
async function createManyResults(data) {
|
|
287
|
+
const resultsData = data.map((item) => ({
|
|
288
|
+
...item,
|
|
289
|
+
decayedPoints: item.decayedPoints ?? item.totalPoints ?? 0
|
|
290
|
+
}));
|
|
291
|
+
return prisma.tournamentResult.createMany({
|
|
292
|
+
data: resultsData
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
async function findResultById(id, include) {
|
|
296
|
+
return prisma.tournamentResult.findUnique({
|
|
297
|
+
where: { id },
|
|
298
|
+
include
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
async function findResultByPlayerAndTournament(playerId, tournamentId, include) {
|
|
302
|
+
return prisma.tournamentResult.findUnique({
|
|
303
|
+
where: {
|
|
304
|
+
playerId_tournamentId: {
|
|
305
|
+
playerId,
|
|
306
|
+
tournamentId
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
include
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
async function findResults(options = {}) {
|
|
313
|
+
return prisma.tournamentResult.findMany({
|
|
314
|
+
take: options.take,
|
|
315
|
+
skip: options.skip,
|
|
316
|
+
where: options.where,
|
|
317
|
+
orderBy: options.orderBy,
|
|
318
|
+
include: options.include
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async function getPlayerResults(playerId, options = {}) {
|
|
322
|
+
return findResults({
|
|
323
|
+
...options,
|
|
324
|
+
where: { playerId },
|
|
325
|
+
include: { tournament: true, ...options.include },
|
|
326
|
+
orderBy: { tournament: { date: "desc" } }
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
async function getTournamentResults(tournamentId, options = {}) {
|
|
330
|
+
return findResults({
|
|
331
|
+
...options,
|
|
332
|
+
where: { tournamentId },
|
|
333
|
+
include: { player: true, ...options.include },
|
|
334
|
+
orderBy: { position: "asc" }
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
async function getPlayerTopFinishes(playerId, limit = 15) {
|
|
338
|
+
return findResults({
|
|
339
|
+
where: { playerId },
|
|
340
|
+
take: limit,
|
|
341
|
+
include: { tournament: true },
|
|
342
|
+
orderBy: { decayedPoints: "desc" }
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
async function updateResult(id, data) {
|
|
346
|
+
return prisma.tournamentResult.update({
|
|
347
|
+
where: { id },
|
|
348
|
+
data
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints) {
|
|
352
|
+
const result = await findResultById(id, {
|
|
353
|
+
tournament: true
|
|
354
|
+
});
|
|
355
|
+
if (!result) {
|
|
356
|
+
throw new Error(`Result with id ${id} not found`);
|
|
357
|
+
}
|
|
358
|
+
const now = /* @__PURE__ */ new Date();
|
|
359
|
+
const tournamentDate = result.tournament.date;
|
|
360
|
+
const ageInDays = Math.floor((now.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
361
|
+
const ageInYears = ageInDays / 365;
|
|
362
|
+
let decayMultiplier = 0;
|
|
363
|
+
if (ageInYears < 1) {
|
|
364
|
+
decayMultiplier = 1;
|
|
365
|
+
} else if (ageInYears < 2) {
|
|
366
|
+
decayMultiplier = 0.75;
|
|
367
|
+
} else if (ageInYears < 3) {
|
|
368
|
+
decayMultiplier = 0.5;
|
|
369
|
+
} else {
|
|
370
|
+
decayMultiplier = 0;
|
|
371
|
+
}
|
|
372
|
+
const decayedPoints = totalPoints * decayMultiplier;
|
|
373
|
+
return updateResult(id, {
|
|
374
|
+
linearPoints,
|
|
375
|
+
dynamicPoints,
|
|
376
|
+
totalPoints,
|
|
377
|
+
ageInDays,
|
|
378
|
+
decayMultiplier,
|
|
379
|
+
decayedPoints
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
async function deleteResult(id) {
|
|
383
|
+
return prisma.tournamentResult.delete({
|
|
384
|
+
where: { id }
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
async function deleteResultsByTournament(tournamentId) {
|
|
388
|
+
return prisma.tournamentResult.deleteMany({
|
|
389
|
+
where: { tournamentId }
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
async function countResults(where) {
|
|
393
|
+
return prisma.tournamentResult.count({ where });
|
|
394
|
+
}
|
|
395
|
+
async function getPlayerStats(playerId) {
|
|
396
|
+
const results = await getPlayerResults(playerId);
|
|
397
|
+
if (results.length === 0) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
const totalPoints = results.reduce((sum, r) => sum + (r.totalPoints || 0), 0);
|
|
401
|
+
const totalDecayedPoints = results.reduce((sum, r) => sum + (r.decayedPoints || 0), 0);
|
|
402
|
+
const averagePosition = results.reduce((sum, r) => sum + r.position, 0) / results.length;
|
|
403
|
+
const averageEfficiency = results.reduce((sum, r) => sum + (r.efficiency || 0), 0) / results.length;
|
|
404
|
+
const firstPlaceFinishes = results.filter((r) => r.position === 1).length;
|
|
405
|
+
const topThreeFinishes = results.filter((r) => r.position <= 3).length;
|
|
406
|
+
return {
|
|
407
|
+
totalEvents: results.length,
|
|
408
|
+
totalPoints,
|
|
409
|
+
totalDecayedPoints,
|
|
410
|
+
averagePoints: totalPoints / results.length,
|
|
411
|
+
averagePosition,
|
|
412
|
+
averageFinish: averagePosition,
|
|
413
|
+
averageEfficiency,
|
|
414
|
+
firstPlaceFinishes,
|
|
415
|
+
topThreeFinishes,
|
|
416
|
+
bestFinish: Math.min(...results.map((r) => r.position)),
|
|
417
|
+
highestPoints: Math.max(...results.map((r) => r.totalPoints || 0))
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date()) {
|
|
421
|
+
const results = await findResults({
|
|
422
|
+
include: { tournament: true }
|
|
423
|
+
});
|
|
424
|
+
const updates = results.map((result) => {
|
|
425
|
+
const tournamentDate = result.tournament.date;
|
|
426
|
+
const ageInDays = Math.floor(
|
|
427
|
+
(referenceDate.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
428
|
+
);
|
|
429
|
+
const ageInYears = ageInDays / 365;
|
|
430
|
+
let decayMultiplier = 0;
|
|
431
|
+
if (ageInYears < 1) {
|
|
432
|
+
decayMultiplier = 1;
|
|
433
|
+
} else if (ageInYears < 2) {
|
|
434
|
+
decayMultiplier = 0.75;
|
|
435
|
+
} else if (ageInYears < 3) {
|
|
436
|
+
decayMultiplier = 0.5;
|
|
437
|
+
} else {
|
|
438
|
+
decayMultiplier = 0;
|
|
439
|
+
}
|
|
440
|
+
const decayedPoints = (result.totalPoints || 0) * decayMultiplier;
|
|
441
|
+
return prisma.tournamentResult.update({
|
|
442
|
+
where: { id: result.id },
|
|
443
|
+
data: {
|
|
444
|
+
ageInDays,
|
|
445
|
+
decayMultiplier,
|
|
446
|
+
decayedPoints
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
return Promise.all(updates);
|
|
451
|
+
}
|
|
452
|
+
export {
|
|
453
|
+
connect,
|
|
454
|
+
countPlayers,
|
|
455
|
+
countResults,
|
|
456
|
+
countTournaments,
|
|
457
|
+
createManyResults,
|
|
458
|
+
createPlayer,
|
|
459
|
+
createResult,
|
|
460
|
+
createTournament,
|
|
461
|
+
deletePlayer,
|
|
462
|
+
deleteResult,
|
|
463
|
+
deleteResultsByTournament,
|
|
464
|
+
deleteTournament,
|
|
465
|
+
disconnect,
|
|
466
|
+
findPlayerByEmail,
|
|
467
|
+
findPlayerByExternalId,
|
|
468
|
+
findPlayerById,
|
|
469
|
+
findPlayers,
|
|
470
|
+
findResultById,
|
|
471
|
+
findResultByPlayerAndTournament,
|
|
472
|
+
findResults,
|
|
473
|
+
findTournamentByExternalId,
|
|
474
|
+
findTournamentById,
|
|
475
|
+
findTournaments,
|
|
476
|
+
getMajorTournaments,
|
|
477
|
+
getPlayerResults,
|
|
478
|
+
getPlayerStats,
|
|
479
|
+
getPlayerTopFinishes,
|
|
480
|
+
getPlayerWithResults,
|
|
481
|
+
getRatedPlayers,
|
|
482
|
+
getRecentTournaments,
|
|
483
|
+
getTopPlayersByRanking,
|
|
484
|
+
getTopPlayersByRating,
|
|
485
|
+
getTournamentResults,
|
|
486
|
+
getTournamentStats,
|
|
487
|
+
getTournamentWithResults,
|
|
488
|
+
getTournamentsByBoosterType,
|
|
489
|
+
getTournamentsByDateRange,
|
|
490
|
+
prisma,
|
|
491
|
+
recalculateTimeDecay,
|
|
492
|
+
searchPlayers,
|
|
493
|
+
searchTournaments,
|
|
494
|
+
testConnection,
|
|
495
|
+
updatePlayer,
|
|
496
|
+
updatePlayerRating,
|
|
497
|
+
updateResult,
|
|
498
|
+
updateResultPoints,
|
|
499
|
+
updateTournament
|
|
500
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opprs/db-prisma",
|
|
3
|
+
"version": "0.5.2-canary.16741f5",
|
|
4
|
+
"description": "Database backend for OPPR (Open Pinball Player Ranking System) using Prisma and PostgreSQL",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"oppr",
|
|
7
|
+
"pinball",
|
|
8
|
+
"ranking",
|
|
9
|
+
"database",
|
|
10
|
+
"prisma",
|
|
11
|
+
"postgresql",
|
|
12
|
+
"tournament"
|
|
13
|
+
],
|
|
14
|
+
"author": "Mitch McAffee",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/themcaffee/OPPR",
|
|
19
|
+
"directory": "packages/db-prisma"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./dist/index.cjs",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"require": "./dist/index.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"prisma",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@prisma/client": "^6.2.0"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@testcontainers/postgresql": "^11.11.0",
|
|
43
|
+
"@types/node": "^22.10.5",
|
|
44
|
+
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
45
|
+
"@typescript-eslint/parser": "^8.19.1",
|
|
46
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
47
|
+
"@vitest/ui": "^4.0.16",
|
|
48
|
+
"eslint": "^9.17.0",
|
|
49
|
+
"prettier": "^3.4.2",
|
|
50
|
+
"prisma": "^6.2.0",
|
|
51
|
+
"tsup": "^8.3.5",
|
|
52
|
+
"tsx": "^4.19.2",
|
|
53
|
+
"typescript": "^5.7.2",
|
|
54
|
+
"vitest": "^4.0.16"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@opprs/core": "^0.5.2-canary.16741f5"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18.0.0"
|
|
61
|
+
},
|
|
62
|
+
"prisma": {
|
|
63
|
+
"seed": "tsx prisma/seed.ts"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
67
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
68
|
+
"typecheck": "tsc --noEmit",
|
|
69
|
+
"lint": "eslint src --ext .ts",
|
|
70
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
71
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
72
|
+
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
73
|
+
"test": "vitest run",
|
|
74
|
+
"test:watch": "vitest watch",
|
|
75
|
+
"test:ui": "vitest --ui",
|
|
76
|
+
"test:coverage": "vitest run --coverage",
|
|
77
|
+
"db:generate": "prisma generate",
|
|
78
|
+
"db:push": "prisma db push",
|
|
79
|
+
"db:migrate": "prisma migrate dev",
|
|
80
|
+
"db:migrate:prod": "prisma migrate deploy",
|
|
81
|
+
"db:studio": "prisma studio",
|
|
82
|
+
"db:seed": "tsx prisma/seed.ts",
|
|
83
|
+
"db:reset": "prisma migrate reset"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
-- CreateEnum
|
|
2
|
+
CREATE TYPE "EventBoosterType" AS ENUM ('NONE', 'CERTIFIED', 'CERTIFIED_PLUS', 'CHAMPIONSHIP_SERIES', 'MAJOR');
|
|
3
|
+
|
|
4
|
+
-- CreateTable
|
|
5
|
+
CREATE TABLE "Player" (
|
|
6
|
+
"id" TEXT NOT NULL,
|
|
7
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
9
|
+
"externalId" TEXT,
|
|
10
|
+
"name" TEXT,
|
|
11
|
+
"email" TEXT,
|
|
12
|
+
"rating" DOUBLE PRECISION NOT NULL DEFAULT 1500,
|
|
13
|
+
"ratingDeviation" DOUBLE PRECISION NOT NULL DEFAULT 200,
|
|
14
|
+
"ranking" INTEGER,
|
|
15
|
+
"isRated" BOOLEAN NOT NULL DEFAULT false,
|
|
16
|
+
"eventCount" INTEGER NOT NULL DEFAULT 0,
|
|
17
|
+
"lastRatingUpdate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
18
|
+
"lastEventDate" TIMESTAMP(3),
|
|
19
|
+
|
|
20
|
+
CONSTRAINT "Player_pkey" PRIMARY KEY ("id")
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
-- CreateTable
|
|
24
|
+
CREATE TABLE "Tournament" (
|
|
25
|
+
"id" TEXT NOT NULL,
|
|
26
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
27
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
28
|
+
"externalId" TEXT,
|
|
29
|
+
"name" TEXT NOT NULL,
|
|
30
|
+
"location" TEXT,
|
|
31
|
+
"date" TIMESTAMP(3) NOT NULL,
|
|
32
|
+
"tgpConfig" JSONB,
|
|
33
|
+
"eventBooster" "EventBoosterType" NOT NULL DEFAULT 'NONE',
|
|
34
|
+
"allowsOptOut" BOOLEAN NOT NULL DEFAULT false,
|
|
35
|
+
"baseValue" DOUBLE PRECISION,
|
|
36
|
+
"tvaRating" DOUBLE PRECISION,
|
|
37
|
+
"tvaRanking" DOUBLE PRECISION,
|
|
38
|
+
"totalTVA" DOUBLE PRECISION,
|
|
39
|
+
"tgp" DOUBLE PRECISION,
|
|
40
|
+
"eventBoosterMultiplier" DOUBLE PRECISION,
|
|
41
|
+
"firstPlaceValue" DOUBLE PRECISION,
|
|
42
|
+
|
|
43
|
+
CONSTRAINT "Tournament_pkey" PRIMARY KEY ("id")
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
-- CreateTable
|
|
47
|
+
CREATE TABLE "TournamentResult" (
|
|
48
|
+
"id" TEXT NOT NULL,
|
|
49
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
50
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
51
|
+
"playerId" TEXT NOT NULL,
|
|
52
|
+
"tournamentId" TEXT NOT NULL,
|
|
53
|
+
"position" INTEGER NOT NULL,
|
|
54
|
+
"optedOut" BOOLEAN NOT NULL DEFAULT false,
|
|
55
|
+
"linearPoints" DOUBLE PRECISION DEFAULT 0,
|
|
56
|
+
"dynamicPoints" DOUBLE PRECISION DEFAULT 0,
|
|
57
|
+
"totalPoints" DOUBLE PRECISION,
|
|
58
|
+
"ageInDays" INTEGER DEFAULT 0,
|
|
59
|
+
"decayMultiplier" DOUBLE PRECISION DEFAULT 1.0,
|
|
60
|
+
"decayedPoints" DOUBLE PRECISION,
|
|
61
|
+
"efficiency" DOUBLE PRECISION,
|
|
62
|
+
|
|
63
|
+
CONSTRAINT "TournamentResult_pkey" PRIMARY KEY ("id")
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
-- CreateIndex
|
|
67
|
+
CREATE UNIQUE INDEX "Player_externalId_key" ON "Player"("externalId");
|
|
68
|
+
|
|
69
|
+
-- CreateIndex
|
|
70
|
+
CREATE UNIQUE INDEX "Player_email_key" ON "Player"("email");
|
|
71
|
+
|
|
72
|
+
-- CreateIndex
|
|
73
|
+
CREATE INDEX "Player_email_idx" ON "Player"("email");
|
|
74
|
+
|
|
75
|
+
-- CreateIndex
|
|
76
|
+
CREATE INDEX "Player_externalId_idx" ON "Player"("externalId");
|
|
77
|
+
|
|
78
|
+
-- CreateIndex
|
|
79
|
+
CREATE INDEX "Player_rating_idx" ON "Player"("rating");
|
|
80
|
+
|
|
81
|
+
-- CreateIndex
|
|
82
|
+
CREATE INDEX "Player_ranking_idx" ON "Player"("ranking");
|
|
83
|
+
|
|
84
|
+
-- CreateIndex
|
|
85
|
+
CREATE UNIQUE INDEX "Tournament_externalId_key" ON "Tournament"("externalId");
|
|
86
|
+
|
|
87
|
+
-- CreateIndex
|
|
88
|
+
CREATE INDEX "Tournament_date_idx" ON "Tournament"("date");
|
|
89
|
+
|
|
90
|
+
-- CreateIndex
|
|
91
|
+
CREATE INDEX "Tournament_eventBooster_idx" ON "Tournament"("eventBooster");
|
|
92
|
+
|
|
93
|
+
-- CreateIndex
|
|
94
|
+
CREATE INDEX "Tournament_externalId_idx" ON "Tournament"("externalId");
|
|
95
|
+
|
|
96
|
+
-- CreateIndex
|
|
97
|
+
CREATE INDEX "TournamentResult_playerId_idx" ON "TournamentResult"("playerId");
|
|
98
|
+
|
|
99
|
+
-- CreateIndex
|
|
100
|
+
CREATE INDEX "TournamentResult_tournamentId_idx" ON "TournamentResult"("tournamentId");
|
|
101
|
+
|
|
102
|
+
-- CreateIndex
|
|
103
|
+
CREATE INDEX "TournamentResult_position_idx" ON "TournamentResult"("position");
|
|
104
|
+
|
|
105
|
+
-- CreateIndex
|
|
106
|
+
CREATE UNIQUE INDEX "TournamentResult_playerId_tournamentId_key" ON "TournamentResult"("playerId", "tournamentId");
|
|
107
|
+
|
|
108
|
+
-- AddForeignKey
|
|
109
|
+
ALTER TABLE "TournamentResult" ADD CONSTRAINT "TournamentResult_playerId_fkey" FOREIGN KEY ("playerId") REFERENCES "Player"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
110
|
+
|
|
111
|
+
-- AddForeignKey
|
|
112
|
+
ALTER TABLE "TournamentResult" ADD CONSTRAINT "TournamentResult_tournamentId_fkey" FOREIGN KEY ("tournamentId") REFERENCES "Tournament"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|