@opprs/db-prisma 2.2.1-canary.ccfb3c5 → 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/dist/index.cjs
CHANGED
|
@@ -20,31 +20,77 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
MAX_API_KEYS_PER_USER: () => MAX_API_KEYS_PER_USER,
|
|
24
|
+
applyRDDecayForInactivePlayers: () => applyRDDecayForInactivePlayers,
|
|
23
25
|
connect: () => connect,
|
|
26
|
+
countEntries: () => countEntries,
|
|
27
|
+
countLocations: () => countLocations,
|
|
28
|
+
countMatches: () => countMatches,
|
|
29
|
+
countOpprPlayerRankings: () => countOpprPlayerRankings,
|
|
30
|
+
countOpprRankingHistory: () => countOpprRankingHistory,
|
|
24
31
|
countPlayers: () => countPlayers,
|
|
25
|
-
|
|
32
|
+
countRounds: () => countRounds,
|
|
33
|
+
countStandings: () => countStandings,
|
|
26
34
|
countTournaments: () => countTournaments,
|
|
35
|
+
countUserApiKeys: () => countUserApiKeys,
|
|
27
36
|
countUsers: () => countUsers,
|
|
28
|
-
|
|
37
|
+
createApiKey: () => createApiKey,
|
|
38
|
+
createEntry: () => createEntry,
|
|
39
|
+
createLocation: () => createLocation,
|
|
40
|
+
createManyEntries: () => createManyEntries,
|
|
41
|
+
createManyMatches: () => createManyMatches,
|
|
42
|
+
createManyRounds: () => createManyRounds,
|
|
43
|
+
createManyStandings: () => createManyStandings,
|
|
44
|
+
createMatch: () => createMatch,
|
|
45
|
+
createOpprPlayerRanking: () => createOpprPlayerRanking,
|
|
46
|
+
createOpprRankingHistory: () => createOpprRankingHistory,
|
|
29
47
|
createPlayer: () => createPlayer,
|
|
30
|
-
|
|
48
|
+
createRound: () => createRound,
|
|
49
|
+
createStanding: () => createStanding,
|
|
31
50
|
createTournament: () => createTournament,
|
|
32
51
|
createUser: () => createUser,
|
|
33
52
|
createUserWithPlayer: () => createUserWithPlayer,
|
|
53
|
+
deleteApiKey: () => deleteApiKey,
|
|
54
|
+
deleteEntriesByMatch: () => deleteEntriesByMatch,
|
|
55
|
+
deleteEntry: () => deleteEntry,
|
|
56
|
+
deleteLocation: () => deleteLocation,
|
|
57
|
+
deleteMatch: () => deleteMatch,
|
|
58
|
+
deleteMatchesByRound: () => deleteMatchesByRound,
|
|
59
|
+
deleteMatchesByTournament: () => deleteMatchesByTournament,
|
|
60
|
+
deleteOpprPlayerRanking: () => deleteOpprPlayerRanking,
|
|
34
61
|
deletePlayer: () => deletePlayer,
|
|
35
|
-
|
|
36
|
-
|
|
62
|
+
deleteRound: () => deleteRound,
|
|
63
|
+
deleteRoundsByTournament: () => deleteRoundsByTournament,
|
|
64
|
+
deleteStanding: () => deleteStanding,
|
|
65
|
+
deleteStandingsByTournament: () => deleteStandingsByTournament,
|
|
37
66
|
deleteTournament: () => deleteTournament,
|
|
38
67
|
deleteUser: () => deleteUser,
|
|
68
|
+
deleteUserApiKey: () => deleteUserApiKey,
|
|
39
69
|
disconnect: () => disconnect,
|
|
70
|
+
findApiKeyById: () => findApiKeyById,
|
|
71
|
+
findApiKeysByPrefix: () => findApiKeysByPrefix,
|
|
72
|
+
findEntries: () => findEntries,
|
|
73
|
+
findEntryById: () => findEntryById,
|
|
74
|
+
findEntryByMatchAndPlayer: () => findEntryByMatchAndPlayer,
|
|
75
|
+
findLocationByExternalId: () => findLocationByExternalId,
|
|
76
|
+
findLocationById: () => findLocationById,
|
|
77
|
+
findLocations: () => findLocations,
|
|
78
|
+
findMatchById: () => findMatchById,
|
|
79
|
+
findMatches: () => findMatches,
|
|
80
|
+
findOpprPlayerRankingById: () => findOpprPlayerRankingById,
|
|
81
|
+
findOpprPlayerRankingByPlayerId: () => findOpprPlayerRankingByPlayerId,
|
|
82
|
+
findOpprPlayerRankings: () => findOpprPlayerRankings,
|
|
40
83
|
findPlayerByExternalId: () => findPlayerByExternalId,
|
|
41
84
|
findPlayerById: () => findPlayerById,
|
|
42
85
|
findPlayerByPlayerNumber: () => findPlayerByPlayerNumber,
|
|
43
86
|
findPlayerByUserEmail: () => findPlayerByUserEmail,
|
|
44
87
|
findPlayers: () => findPlayers,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
88
|
+
findRoundById: () => findRoundById,
|
|
89
|
+
findRoundByTournamentAndNumber: () => findRoundByTournamentAndNumber,
|
|
90
|
+
findRounds: () => findRounds,
|
|
91
|
+
findStandingById: () => findStandingById,
|
|
92
|
+
findStandingByPlayerAndTournament: () => findStandingByPlayerAndTournament,
|
|
93
|
+
findStandings: () => findStandings,
|
|
48
94
|
findTournamentByExternalId: () => findTournamentByExternalId,
|
|
49
95
|
findTournamentById: () => findTournamentById,
|
|
50
96
|
findTournaments: () => findTournaments,
|
|
@@ -52,36 +98,66 @@ __export(index_exports, {
|
|
|
52
98
|
findUserById: () => findUserById,
|
|
53
99
|
findUsers: () => findUsers,
|
|
54
100
|
generateUniquePlayerNumber: () => generateUniquePlayerNumber,
|
|
101
|
+
getFinalsRounds: () => getFinalsRounds,
|
|
102
|
+
getFinalsStandings: () => getFinalsStandings,
|
|
103
|
+
getLatestOpprRankingHistory: () => getLatestOpprRankingHistory,
|
|
104
|
+
getLocationWithTournaments: () => getLocationWithTournaments,
|
|
55
105
|
getMajorTournaments: () => getMajorTournaments,
|
|
56
|
-
|
|
106
|
+
getMatchEntries: () => getMatchEntries,
|
|
107
|
+
getMatchWithEntries: () => getMatchWithEntries,
|
|
108
|
+
getMergedStandings: () => getMergedStandings,
|
|
109
|
+
getOpprRankingHistory: () => getOpprRankingHistory,
|
|
110
|
+
getOpprRankingHistoryByDateRange: () => getOpprRankingHistoryByDateRange,
|
|
111
|
+
getOrCreateOpprPlayerRanking: () => getOrCreateOpprPlayerRanking,
|
|
112
|
+
getPlayerEntries: () => getPlayerEntries,
|
|
113
|
+
getPlayerEntryStats: () => getPlayerEntryStats,
|
|
114
|
+
getPlayerStandings: () => getPlayerStandings,
|
|
57
115
|
getPlayerStats: () => getPlayerStats,
|
|
58
116
|
getPlayerTopFinishes: () => getPlayerTopFinishes,
|
|
117
|
+
getPlayerTournamentEntries: () => getPlayerTournamentEntries,
|
|
118
|
+
getPlayerTournamentMatches: () => getPlayerTournamentMatches,
|
|
59
119
|
getPlayerWithResults: () => getPlayerWithResults,
|
|
60
|
-
|
|
120
|
+
getQualifyingRounds: () => getQualifyingRounds,
|
|
121
|
+
getQualifyingStandings: () => getQualifyingStandings,
|
|
122
|
+
getRatedOpprPlayers: () => getRatedOpprPlayers,
|
|
61
123
|
getRecentTournaments: () => getRecentTournaments,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
124
|
+
getRoundMatches: () => getRoundMatches,
|
|
125
|
+
getRoundWithMatches: () => getRoundWithMatches,
|
|
126
|
+
getTopPlayersByOpprRanking: () => getTopPlayersByOpprRanking,
|
|
127
|
+
getTopPlayersByOpprRating: () => getTopPlayersByOpprRating,
|
|
128
|
+
getTournamentMatches: () => getTournamentMatches,
|
|
129
|
+
getTournamentRounds: () => getTournamentRounds,
|
|
130
|
+
getTournamentStandings: () => getTournamentStandings,
|
|
65
131
|
getTournamentStats: () => getTournamentStats,
|
|
132
|
+
getTournamentWithMatches: () => getTournamentWithMatches,
|
|
66
133
|
getTournamentWithResults: () => getTournamentWithResults,
|
|
67
134
|
getTournamentsByBoosterType: () => getTournamentsByBoosterType,
|
|
68
135
|
getTournamentsByDateRange: () => getTournamentsByDateRange,
|
|
136
|
+
getUserApiKeys: () => getUserApiKeys,
|
|
69
137
|
getUserByEmailWithPlayer: () => getUserByEmailWithPlayer,
|
|
70
138
|
getUserWithPlayer: () => getUserWithPlayer,
|
|
71
139
|
isValidPlayerNumber: () => isValidPlayerNumber,
|
|
72
140
|
linkPlayerToUser: () => linkPlayerToUser,
|
|
73
141
|
prisma: () => prisma,
|
|
74
142
|
recalculateTimeDecay: () => recalculateTimeDecay,
|
|
143
|
+
searchLocations: () => searchLocations,
|
|
75
144
|
searchPlayers: () => searchPlayers,
|
|
76
145
|
searchTournaments: () => searchTournaments,
|
|
77
146
|
testConnection: () => testConnection,
|
|
147
|
+
updateApiKeyLastUsed: () => updateApiKeyLastUsed,
|
|
148
|
+
updateEntry: () => updateEntry,
|
|
149
|
+
updateLocation: () => updateLocation,
|
|
150
|
+
updateMatch: () => updateMatch,
|
|
151
|
+
updateOpprPlayerRanking: () => updateOpprPlayerRanking,
|
|
152
|
+
updateOpprRatingAfterTournament: () => updateOpprRatingAfterTournament,
|
|
78
153
|
updatePlayer: () => updatePlayer,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
154
|
+
updateRound: () => updateRound,
|
|
155
|
+
updateStanding: () => updateStanding,
|
|
156
|
+
updateStandingPoints: () => updateStandingPoints,
|
|
82
157
|
updateTournament: () => updateTournament,
|
|
83
158
|
updateUser: () => updateUser,
|
|
84
|
-
updateUserRefreshToken: () => updateUserRefreshToken
|
|
159
|
+
updateUserRefreshToken: () => updateUserRefreshToken,
|
|
160
|
+
updateWorldRankings: () => updateWorldRankings
|
|
85
161
|
});
|
|
86
162
|
module.exports = __toCommonJS(index_exports);
|
|
87
163
|
|
|
@@ -180,48 +256,12 @@ async function findPlayers(options = {}) {
|
|
|
180
256
|
include: options.include
|
|
181
257
|
});
|
|
182
258
|
}
|
|
183
|
-
async function getRatedPlayers(options = {}) {
|
|
184
|
-
return findPlayers({
|
|
185
|
-
...options,
|
|
186
|
-
where: { isRated: true }
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
async function getTopPlayersByRating(limit = 50) {
|
|
190
|
-
return findPlayers({
|
|
191
|
-
take: limit,
|
|
192
|
-
orderBy: { rating: "desc" },
|
|
193
|
-
where: { isRated: true }
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
async function getTopPlayersByRanking(limit = 50) {
|
|
197
|
-
return findPlayers({
|
|
198
|
-
take: limit,
|
|
199
|
-
orderBy: { ranking: "asc" },
|
|
200
|
-
where: {
|
|
201
|
-
isRated: true,
|
|
202
|
-
ranking: { not: null }
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
259
|
async function updatePlayer(id, data) {
|
|
207
260
|
return prisma.player.update({
|
|
208
261
|
where: { id },
|
|
209
262
|
data
|
|
210
263
|
});
|
|
211
264
|
}
|
|
212
|
-
async function updatePlayerRating(id, rating, ratingDeviation, eventCount) {
|
|
213
|
-
const updateData = {
|
|
214
|
-
rating,
|
|
215
|
-
ratingDeviation,
|
|
216
|
-
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
217
|
-
lastEventDate: /* @__PURE__ */ new Date()
|
|
218
|
-
};
|
|
219
|
-
if (eventCount !== void 0) {
|
|
220
|
-
updateData.eventCount = eventCount;
|
|
221
|
-
updateData.isRated = eventCount >= 5;
|
|
222
|
-
}
|
|
223
|
-
return updatePlayer(id, updateData);
|
|
224
|
-
}
|
|
225
265
|
async function deletePlayer(id) {
|
|
226
266
|
return prisma.player.delete({
|
|
227
267
|
where: { id }
|
|
@@ -234,7 +274,7 @@ async function getPlayerWithResults(id) {
|
|
|
234
274
|
const player = await prisma.player.findUnique({
|
|
235
275
|
where: { id },
|
|
236
276
|
include: {
|
|
237
|
-
|
|
277
|
+
standings: {
|
|
238
278
|
include: {
|
|
239
279
|
tournament: true
|
|
240
280
|
},
|
|
@@ -251,7 +291,7 @@ async function getPlayerWithResults(id) {
|
|
|
251
291
|
}
|
|
252
292
|
return {
|
|
253
293
|
...player,
|
|
254
|
-
results: player.
|
|
294
|
+
results: player.standings
|
|
255
295
|
};
|
|
256
296
|
}
|
|
257
297
|
async function searchPlayers(query, limit = 20) {
|
|
@@ -263,6 +303,212 @@ async function searchPlayers(query, limit = 20) {
|
|
|
263
303
|
});
|
|
264
304
|
}
|
|
265
305
|
|
|
306
|
+
// src/oppr-rankings.ts
|
|
307
|
+
async function getOrCreateOpprPlayerRanking(playerId) {
|
|
308
|
+
const existing = await prisma.opprPlayerRanking.findUnique({
|
|
309
|
+
where: { playerId }
|
|
310
|
+
});
|
|
311
|
+
if (existing) return existing;
|
|
312
|
+
return prisma.opprPlayerRanking.create({
|
|
313
|
+
data: { playerId }
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
async function createOpprPlayerRanking(data) {
|
|
317
|
+
return prisma.opprPlayerRanking.create({
|
|
318
|
+
data
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async function findOpprPlayerRankingById(id, include) {
|
|
322
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
323
|
+
where: { id },
|
|
324
|
+
include
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
async function findOpprPlayerRankingByPlayerId(playerId, include) {
|
|
328
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
329
|
+
where: { playerId },
|
|
330
|
+
include
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
async function findOpprPlayerRankings(options = {}) {
|
|
334
|
+
return prisma.opprPlayerRanking.findMany({
|
|
335
|
+
take: options.take,
|
|
336
|
+
skip: options.skip,
|
|
337
|
+
where: options.where,
|
|
338
|
+
orderBy: options.orderBy,
|
|
339
|
+
include: options.include
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
async function getTopPlayersByOpprRating(limit = 50) {
|
|
343
|
+
return prisma.opprPlayerRanking.findMany({
|
|
344
|
+
take: limit,
|
|
345
|
+
where: { isRated: true },
|
|
346
|
+
orderBy: { rating: "desc" },
|
|
347
|
+
include: { player: true }
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
async function getTopPlayersByOpprRanking(limit = 50) {
|
|
351
|
+
return prisma.opprPlayerRanking.findMany({
|
|
352
|
+
take: limit,
|
|
353
|
+
where: {
|
|
354
|
+
isRated: true,
|
|
355
|
+
ranking: { not: null }
|
|
356
|
+
},
|
|
357
|
+
orderBy: { ranking: "asc" },
|
|
358
|
+
include: { player: true }
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
async function getRatedOpprPlayers(options = {}) {
|
|
362
|
+
return prisma.opprPlayerRanking.findMany({
|
|
363
|
+
...options,
|
|
364
|
+
where: { isRated: true },
|
|
365
|
+
include: { player: true, ...options.include }
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
async function updateOpprPlayerRanking(playerId, data) {
|
|
369
|
+
return prisma.opprPlayerRanking.update({
|
|
370
|
+
where: { playerId },
|
|
371
|
+
data: {
|
|
372
|
+
...data,
|
|
373
|
+
lastRatingUpdate: data.lastRatingUpdate ?? /* @__PURE__ */ new Date()
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
async function updateOpprRatingAfterTournament(playerId, newRating, newRD, tournamentId, eventCount) {
|
|
378
|
+
const ranking = await getOrCreateOpprPlayerRanking(playerId);
|
|
379
|
+
const isRated = eventCount !== void 0 ? eventCount >= 5 : ranking.isRated;
|
|
380
|
+
const updated = await prisma.opprPlayerRanking.update({
|
|
381
|
+
where: { playerId },
|
|
382
|
+
data: {
|
|
383
|
+
rating: newRating,
|
|
384
|
+
ratingDeviation: newRD,
|
|
385
|
+
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
386
|
+
isRated
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
await createOpprRankingHistory({
|
|
390
|
+
opprPlayerRankingId: ranking.id,
|
|
391
|
+
rating: newRating,
|
|
392
|
+
ratingDeviation: newRD,
|
|
393
|
+
ranking: updated.ranking ?? void 0,
|
|
394
|
+
isRated,
|
|
395
|
+
changeType: "TOURNAMENT_RESULT",
|
|
396
|
+
tournamentId
|
|
397
|
+
});
|
|
398
|
+
return updated;
|
|
399
|
+
}
|
|
400
|
+
async function updateWorldRankings(rankings) {
|
|
401
|
+
await prisma.$transaction(async (tx) => {
|
|
402
|
+
for (const { playerId, ranking } of rankings) {
|
|
403
|
+
const opprRanking = await tx.opprPlayerRanking.findUnique({
|
|
404
|
+
where: { playerId }
|
|
405
|
+
});
|
|
406
|
+
if (opprRanking) {
|
|
407
|
+
await tx.opprPlayerRanking.update({
|
|
408
|
+
where: { playerId },
|
|
409
|
+
data: { ranking }
|
|
410
|
+
});
|
|
411
|
+
await tx.opprRankingHistory.create({
|
|
412
|
+
data: {
|
|
413
|
+
opprPlayerRankingId: opprRanking.id,
|
|
414
|
+
rating: opprRanking.rating,
|
|
415
|
+
ratingDeviation: opprRanking.ratingDeviation,
|
|
416
|
+
ranking,
|
|
417
|
+
isRated: opprRanking.isRated,
|
|
418
|
+
changeType: "RANKING_REFRESH"
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
async function applyRDDecayForInactivePlayers(thresholdDays = 30, decayPerDay = 0.3, maxRD = 200) {
|
|
426
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
427
|
+
cutoffDate.setDate(cutoffDate.getDate() - thresholdDays);
|
|
428
|
+
const inactivePlayers = await prisma.opprPlayerRanking.findMany({
|
|
429
|
+
where: {
|
|
430
|
+
lastRatingUpdate: { lt: cutoffDate },
|
|
431
|
+
ratingDeviation: { lt: maxRD }
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
let updatedCount = 0;
|
|
435
|
+
await prisma.$transaction(async (tx) => {
|
|
436
|
+
for (const ranking of inactivePlayers) {
|
|
437
|
+
const daysSinceUpdate = Math.floor(
|
|
438
|
+
(Date.now() - ranking.lastRatingUpdate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
439
|
+
);
|
|
440
|
+
const newRD = Math.min(ranking.ratingDeviation + daysSinceUpdate * decayPerDay, maxRD);
|
|
441
|
+
await tx.opprPlayerRanking.update({
|
|
442
|
+
where: { id: ranking.id },
|
|
443
|
+
data: { ratingDeviation: newRD }
|
|
444
|
+
});
|
|
445
|
+
await tx.opprRankingHistory.create({
|
|
446
|
+
data: {
|
|
447
|
+
opprPlayerRankingId: ranking.id,
|
|
448
|
+
rating: ranking.rating,
|
|
449
|
+
ratingDeviation: newRD,
|
|
450
|
+
ranking: ranking.ranking,
|
|
451
|
+
isRated: ranking.isRated,
|
|
452
|
+
changeType: "RD_DECAY",
|
|
453
|
+
notes: `RD increased from ${ranking.ratingDeviation.toFixed(1)} to ${newRD.toFixed(1)} due to ${daysSinceUpdate} days of inactivity`
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
updatedCount++;
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
return updatedCount;
|
|
460
|
+
}
|
|
461
|
+
async function deleteOpprPlayerRanking(playerId) {
|
|
462
|
+
return prisma.opprPlayerRanking.delete({
|
|
463
|
+
where: { playerId }
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
async function countOpprPlayerRankings(where) {
|
|
467
|
+
return prisma.opprPlayerRanking.count({ where });
|
|
468
|
+
}
|
|
469
|
+
async function createOpprRankingHistory(data) {
|
|
470
|
+
return prisma.opprRankingHistory.create({
|
|
471
|
+
data
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
async function getOpprRankingHistory(playerId, limit) {
|
|
475
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
476
|
+
if (!ranking) return [];
|
|
477
|
+
return prisma.opprRankingHistory.findMany({
|
|
478
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
479
|
+
orderBy: { createdAt: "desc" },
|
|
480
|
+
take: limit,
|
|
481
|
+
include: { tournament: true }
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
async function getOpprRankingHistoryByDateRange(playerId, startDate, endDate) {
|
|
485
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
486
|
+
if (!ranking) return [];
|
|
487
|
+
return prisma.opprRankingHistory.findMany({
|
|
488
|
+
where: {
|
|
489
|
+
opprPlayerRankingId: ranking.id,
|
|
490
|
+
createdAt: {
|
|
491
|
+
gte: startDate,
|
|
492
|
+
lte: endDate
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
orderBy: { createdAt: "asc" },
|
|
496
|
+
include: { tournament: true }
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
async function getLatestOpprRankingHistory(playerId) {
|
|
500
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
501
|
+
if (!ranking) return null;
|
|
502
|
+
return prisma.opprRankingHistory.findFirst({
|
|
503
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
504
|
+
orderBy: { createdAt: "desc" },
|
|
505
|
+
include: { tournament: true }
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
async function countOpprRankingHistory(where) {
|
|
509
|
+
return prisma.opprRankingHistory.count({ where });
|
|
510
|
+
}
|
|
511
|
+
|
|
266
512
|
// src/tournaments.ts
|
|
267
513
|
async function createTournament(data) {
|
|
268
514
|
return prisma.tournament.create({
|
|
@@ -342,13 +588,33 @@ async function getTournamentWithResults(id) {
|
|
|
342
588
|
return prisma.tournament.findUnique({
|
|
343
589
|
where: { id },
|
|
344
590
|
include: {
|
|
345
|
-
|
|
591
|
+
standings: {
|
|
346
592
|
include: {
|
|
347
593
|
player: true
|
|
348
594
|
},
|
|
349
|
-
orderBy: {
|
|
350
|
-
|
|
351
|
-
|
|
595
|
+
orderBy: [{ isFinals: "desc" }, { position: "asc" }]
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
async function getTournamentWithMatches(id) {
|
|
601
|
+
return prisma.tournament.findUnique({
|
|
602
|
+
where: { id },
|
|
603
|
+
include: {
|
|
604
|
+
rounds: {
|
|
605
|
+
include: {
|
|
606
|
+
matches: {
|
|
607
|
+
include: {
|
|
608
|
+
entries: {
|
|
609
|
+
include: {
|
|
610
|
+
player: true
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
orderBy: { number: "asc" }
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
orderBy: [{ isFinals: "asc" }, { number: "asc" }]
|
|
352
618
|
}
|
|
353
619
|
}
|
|
354
620
|
});
|
|
@@ -359,7 +625,7 @@ async function searchTournaments(query, limit = 20) {
|
|
|
359
625
|
where: {
|
|
360
626
|
OR: [
|
|
361
627
|
{ name: { contains: query, mode: "insensitive" } },
|
|
362
|
-
{ location: { contains: query, mode: "insensitive" } }
|
|
628
|
+
{ location: { name: { contains: query, mode: "insensitive" } } }
|
|
363
629
|
]
|
|
364
630
|
},
|
|
365
631
|
orderBy: { date: "desc" }
|
|
@@ -370,7 +636,7 @@ async function getTournamentStats(id) {
|
|
|
370
636
|
if (!tournament) {
|
|
371
637
|
return null;
|
|
372
638
|
}
|
|
373
|
-
const playerCount = tournament.
|
|
639
|
+
const playerCount = tournament.standings.length;
|
|
374
640
|
if (playerCount === 0) {
|
|
375
641
|
return {
|
|
376
642
|
tournament,
|
|
@@ -381,9 +647,9 @@ async function getTournamentStats(id) {
|
|
|
381
647
|
lowestPoints: 0
|
|
382
648
|
};
|
|
383
649
|
}
|
|
384
|
-
const totalPoints = tournament.
|
|
385
|
-
const totalEfficiency = tournament.
|
|
386
|
-
const allPoints = tournament.
|
|
650
|
+
const totalPoints = tournament.standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
651
|
+
const totalEfficiency = tournament.standings.reduce((sum, s) => sum + (s.efficiency || 0), 0);
|
|
652
|
+
const allPoints = tournament.standings.map((s) => s.totalPoints || 0);
|
|
387
653
|
return {
|
|
388
654
|
tournament,
|
|
389
655
|
playerCount,
|
|
@@ -394,44 +660,364 @@ async function getTournamentStats(id) {
|
|
|
394
660
|
};
|
|
395
661
|
}
|
|
396
662
|
|
|
397
|
-
// src/
|
|
398
|
-
async function
|
|
399
|
-
|
|
663
|
+
// src/rounds.ts
|
|
664
|
+
async function createRound(data) {
|
|
665
|
+
return prisma.round.create({
|
|
666
|
+
data: {
|
|
667
|
+
...data,
|
|
668
|
+
isFinals: data.isFinals ?? false
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
async function createManyRounds(data) {
|
|
673
|
+
const roundsData = data.map((item) => ({
|
|
674
|
+
...item,
|
|
675
|
+
isFinals: item.isFinals ?? false
|
|
676
|
+
}));
|
|
677
|
+
return prisma.round.createMany({
|
|
678
|
+
data: roundsData
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
async function findRoundById(id, include) {
|
|
682
|
+
return prisma.round.findUnique({
|
|
683
|
+
where: { id },
|
|
684
|
+
include
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
async function findRoundByTournamentAndNumber(tournamentId, number, isFinals, include) {
|
|
688
|
+
return prisma.round.findUnique({
|
|
689
|
+
where: {
|
|
690
|
+
tournamentId_number_isFinals: {
|
|
691
|
+
tournamentId,
|
|
692
|
+
number,
|
|
693
|
+
isFinals
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
include
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
async function findRounds(options = {}) {
|
|
700
|
+
return prisma.round.findMany({
|
|
701
|
+
take: options.take,
|
|
702
|
+
skip: options.skip,
|
|
703
|
+
where: options.where,
|
|
704
|
+
orderBy: options.orderBy,
|
|
705
|
+
include: options.include
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
async function getTournamentRounds(tournamentId, options = {}) {
|
|
709
|
+
return findRounds({
|
|
710
|
+
...options,
|
|
711
|
+
where: { tournamentId },
|
|
712
|
+
orderBy: options.orderBy ?? [{ isFinals: "asc" }, { number: "asc" }]
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
async function getQualifyingRounds(tournamentId, options = {}) {
|
|
716
|
+
return findRounds({
|
|
717
|
+
...options,
|
|
718
|
+
where: { tournamentId, isFinals: false },
|
|
719
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
async function getFinalsRounds(tournamentId, options = {}) {
|
|
723
|
+
return findRounds({
|
|
724
|
+
...options,
|
|
725
|
+
where: { tournamentId, isFinals: true },
|
|
726
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
async function updateRound(id, data) {
|
|
730
|
+
return prisma.round.update({
|
|
731
|
+
where: { id },
|
|
732
|
+
data
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
async function deleteRound(id) {
|
|
736
|
+
return prisma.round.delete({
|
|
737
|
+
where: { id }
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
async function deleteRoundsByTournament(tournamentId) {
|
|
741
|
+
return prisma.round.deleteMany({
|
|
742
|
+
where: { tournamentId }
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
async function countRounds(where) {
|
|
746
|
+
return prisma.round.count({ where });
|
|
747
|
+
}
|
|
748
|
+
async function getRoundWithMatches(id) {
|
|
749
|
+
return prisma.round.findUnique({
|
|
750
|
+
where: { id },
|
|
751
|
+
include: {
|
|
752
|
+
matches: {
|
|
753
|
+
include: {
|
|
754
|
+
entries: {
|
|
755
|
+
include: {
|
|
756
|
+
player: true
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
orderBy: {
|
|
761
|
+
number: "asc"
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// src/matches.ts
|
|
769
|
+
async function createMatch(data) {
|
|
770
|
+
return prisma.match.create({
|
|
771
|
+
data
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
async function createManyMatches(data) {
|
|
775
|
+
return prisma.match.createMany({
|
|
776
|
+
data
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
async function findMatchById(id, include) {
|
|
780
|
+
return prisma.match.findUnique({
|
|
781
|
+
where: { id },
|
|
782
|
+
include
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
async function findMatches(options = {}) {
|
|
786
|
+
return prisma.match.findMany({
|
|
787
|
+
take: options.take,
|
|
788
|
+
skip: options.skip,
|
|
789
|
+
where: options.where,
|
|
790
|
+
orderBy: options.orderBy,
|
|
791
|
+
include: options.include
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
async function getTournamentMatches(tournamentId, options = {}) {
|
|
795
|
+
return findMatches({
|
|
796
|
+
...options,
|
|
797
|
+
where: { tournamentId },
|
|
798
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
async function getRoundMatches(roundId, options = {}) {
|
|
802
|
+
return findMatches({
|
|
803
|
+
...options,
|
|
804
|
+
where: { roundId },
|
|
805
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
async function updateMatch(id, data) {
|
|
809
|
+
return prisma.match.update({
|
|
810
|
+
where: { id },
|
|
811
|
+
data
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
async function deleteMatch(id) {
|
|
815
|
+
return prisma.match.delete({
|
|
816
|
+
where: { id }
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
async function deleteMatchesByTournament(tournamentId) {
|
|
820
|
+
return prisma.match.deleteMany({
|
|
821
|
+
where: { tournamentId }
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
async function deleteMatchesByRound(roundId) {
|
|
825
|
+
return prisma.match.deleteMany({
|
|
826
|
+
where: { roundId }
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
async function countMatches(where) {
|
|
830
|
+
return prisma.match.count({ where });
|
|
831
|
+
}
|
|
832
|
+
async function getMatchWithEntries(id) {
|
|
833
|
+
return prisma.match.findUnique({
|
|
834
|
+
where: { id },
|
|
835
|
+
include: {
|
|
836
|
+
entries: {
|
|
837
|
+
include: {
|
|
838
|
+
player: true
|
|
839
|
+
},
|
|
840
|
+
orderBy: {
|
|
841
|
+
position: "asc"
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
round: true
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
async function getPlayerTournamentMatches(playerId, tournamentId, include) {
|
|
849
|
+
return findMatches({
|
|
850
|
+
where: {
|
|
851
|
+
tournamentId,
|
|
852
|
+
entries: {
|
|
853
|
+
some: {
|
|
854
|
+
playerId
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
include: include ?? {
|
|
859
|
+
entries: {
|
|
860
|
+
include: {
|
|
861
|
+
player: true
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
round: true
|
|
865
|
+
},
|
|
866
|
+
orderBy: [{ round: { number: "asc" } }, { number: "asc" }]
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// src/entries.ts
|
|
871
|
+
async function createEntry(data) {
|
|
872
|
+
return prisma.entry.create({
|
|
873
|
+
data
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
async function createManyEntries(data) {
|
|
877
|
+
return prisma.entry.createMany({
|
|
878
|
+
data
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
async function findEntryById(id, include) {
|
|
882
|
+
return prisma.entry.findUnique({
|
|
883
|
+
where: { id },
|
|
884
|
+
include
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
async function findEntryByMatchAndPlayer(matchId, playerId, include) {
|
|
888
|
+
return prisma.entry.findUnique({
|
|
889
|
+
where: {
|
|
890
|
+
matchId_playerId: {
|
|
891
|
+
matchId,
|
|
892
|
+
playerId
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
include
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
async function findEntries(options = {}) {
|
|
899
|
+
return prisma.entry.findMany({
|
|
900
|
+
take: options.take,
|
|
901
|
+
skip: options.skip,
|
|
902
|
+
where: options.where,
|
|
903
|
+
orderBy: options.orderBy,
|
|
904
|
+
include: options.include
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
async function getMatchEntries(matchId, options = {}) {
|
|
908
|
+
return findEntries({
|
|
909
|
+
...options,
|
|
910
|
+
where: { matchId },
|
|
911
|
+
include: options.include ?? { player: true },
|
|
912
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
async function getPlayerEntries(playerId, options = {}) {
|
|
916
|
+
return findEntries({
|
|
917
|
+
...options,
|
|
918
|
+
where: { playerId },
|
|
919
|
+
include: options.include ?? { match: { include: { round: true, tournament: true } } }
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
async function getPlayerTournamentEntries(playerId, tournamentId, include) {
|
|
923
|
+
return findEntries({
|
|
924
|
+
where: {
|
|
925
|
+
playerId,
|
|
926
|
+
match: {
|
|
927
|
+
tournamentId
|
|
928
|
+
}
|
|
929
|
+
},
|
|
930
|
+
include: include ?? {
|
|
931
|
+
match: {
|
|
932
|
+
include: {
|
|
933
|
+
round: true,
|
|
934
|
+
entries: {
|
|
935
|
+
include: {
|
|
936
|
+
player: true
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
async function updateEntry(id, data) {
|
|
945
|
+
return prisma.entry.update({
|
|
946
|
+
where: { id },
|
|
947
|
+
data
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
async function deleteEntry(id) {
|
|
951
|
+
return prisma.entry.delete({
|
|
952
|
+
where: { id }
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
async function deleteEntriesByMatch(matchId) {
|
|
956
|
+
return prisma.entry.deleteMany({
|
|
957
|
+
where: { matchId }
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
async function countEntries(where) {
|
|
961
|
+
return prisma.entry.count({ where });
|
|
962
|
+
}
|
|
963
|
+
async function getPlayerEntryStats(playerId) {
|
|
964
|
+
const entries = await getPlayerEntries(playerId);
|
|
965
|
+
if (entries.length === 0) {
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
const wins = entries.filter((e) => e.result === "WIN").length;
|
|
969
|
+
const losses = entries.filter((e) => e.result === "LOSS").length;
|
|
970
|
+
const ties = entries.filter((e) => e.result === "TIE").length;
|
|
971
|
+
return {
|
|
972
|
+
totalMatches: entries.length,
|
|
973
|
+
wins,
|
|
974
|
+
losses,
|
|
975
|
+
ties,
|
|
976
|
+
winRate: wins / entries.length
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// src/standings.ts
|
|
981
|
+
async function createStanding(data) {
|
|
982
|
+
const standingData = {
|
|
400
983
|
...data,
|
|
984
|
+
isFinals: data.isFinals ?? false,
|
|
401
985
|
decayedPoints: data.decayedPoints ?? data.totalPoints ?? 0
|
|
402
986
|
};
|
|
403
|
-
return prisma.
|
|
404
|
-
data:
|
|
987
|
+
return prisma.standing.create({
|
|
988
|
+
data: standingData
|
|
405
989
|
});
|
|
406
990
|
}
|
|
407
|
-
async function
|
|
408
|
-
const
|
|
991
|
+
async function createManyStandings(data) {
|
|
992
|
+
const standingsData = data.map((item) => ({
|
|
409
993
|
...item,
|
|
994
|
+
isFinals: item.isFinals ?? false,
|
|
410
995
|
decayedPoints: item.decayedPoints ?? item.totalPoints ?? 0
|
|
411
996
|
}));
|
|
412
|
-
return prisma.
|
|
413
|
-
data:
|
|
997
|
+
return prisma.standing.createMany({
|
|
998
|
+
data: standingsData
|
|
414
999
|
});
|
|
415
1000
|
}
|
|
416
|
-
async function
|
|
417
|
-
return prisma.
|
|
1001
|
+
async function findStandingById(id, include) {
|
|
1002
|
+
return prisma.standing.findUnique({
|
|
418
1003
|
where: { id },
|
|
419
1004
|
include
|
|
420
1005
|
});
|
|
421
1006
|
}
|
|
422
|
-
async function
|
|
423
|
-
return prisma.
|
|
1007
|
+
async function findStandingByPlayerAndTournament(playerId, tournamentId, isFinals, include) {
|
|
1008
|
+
return prisma.standing.findUnique({
|
|
424
1009
|
where: {
|
|
425
|
-
|
|
1010
|
+
playerId_tournamentId_isFinals: {
|
|
426
1011
|
playerId,
|
|
427
|
-
tournamentId
|
|
1012
|
+
tournamentId,
|
|
1013
|
+
isFinals
|
|
428
1014
|
}
|
|
429
1015
|
},
|
|
430
1016
|
include
|
|
431
1017
|
});
|
|
432
1018
|
}
|
|
433
|
-
async function
|
|
434
|
-
return prisma.
|
|
1019
|
+
async function findStandings(options = {}) {
|
|
1020
|
+
return prisma.standing.findMany({
|
|
435
1021
|
take: options.take,
|
|
436
1022
|
skip: options.skip,
|
|
437
1023
|
where: options.where,
|
|
@@ -439,45 +1025,85 @@ async function findResults(options = {}) {
|
|
|
439
1025
|
include: options.include
|
|
440
1026
|
});
|
|
441
1027
|
}
|
|
442
|
-
async function
|
|
443
|
-
return
|
|
1028
|
+
async function getPlayerStandings(playerId, options = {}) {
|
|
1029
|
+
return findStandings({
|
|
444
1030
|
...options,
|
|
445
1031
|
where: { playerId },
|
|
446
1032
|
include: { tournament: true, ...options.include },
|
|
447
1033
|
orderBy: { tournament: { date: "desc" } }
|
|
448
1034
|
});
|
|
449
1035
|
}
|
|
450
|
-
async function
|
|
451
|
-
return
|
|
1036
|
+
async function getTournamentStandings(tournamentId, options = {}) {
|
|
1037
|
+
return findStandings({
|
|
452
1038
|
...options,
|
|
453
1039
|
where: { tournamentId },
|
|
454
1040
|
include: { player: true, ...options.include },
|
|
455
|
-
orderBy: { position: "asc" }
|
|
1041
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
456
1042
|
});
|
|
457
1043
|
}
|
|
1044
|
+
async function getQualifyingStandings(tournamentId, options = {}) {
|
|
1045
|
+
return findStandings({
|
|
1046
|
+
...options,
|
|
1047
|
+
where: { tournamentId, isFinals: false },
|
|
1048
|
+
include: { player: true, ...options.include },
|
|
1049
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
async function getFinalsStandings(tournamentId, options = {}) {
|
|
1053
|
+
return findStandings({
|
|
1054
|
+
...options,
|
|
1055
|
+
where: { tournamentId, isFinals: true },
|
|
1056
|
+
include: { player: true, ...options.include },
|
|
1057
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
async function getMergedStandings(tournamentId) {
|
|
1061
|
+
const [finals, qualifying] = await Promise.all([
|
|
1062
|
+
findStandings({
|
|
1063
|
+
where: { tournamentId, isFinals: true },
|
|
1064
|
+
orderBy: { position: "asc" },
|
|
1065
|
+
include: { player: true }
|
|
1066
|
+
}),
|
|
1067
|
+
findStandings({
|
|
1068
|
+
where: { tournamentId, isFinals: false },
|
|
1069
|
+
orderBy: { position: "asc" },
|
|
1070
|
+
include: { player: true }
|
|
1071
|
+
})
|
|
1072
|
+
]);
|
|
1073
|
+
const finalistIds = new Set(finals.map((s) => s.playerId));
|
|
1074
|
+
const nonFinalists = qualifying.filter((s) => !finalistIds.has(s.playerId));
|
|
1075
|
+
return [
|
|
1076
|
+
...finals.map((s) => ({ ...s, mergedPosition: s.position, isFinalist: true })),
|
|
1077
|
+
...nonFinalists.map((s, i) => ({
|
|
1078
|
+
...s,
|
|
1079
|
+
mergedPosition: finals.length + i + 1,
|
|
1080
|
+
isFinalist: false
|
|
1081
|
+
}))
|
|
1082
|
+
];
|
|
1083
|
+
}
|
|
458
1084
|
async function getPlayerTopFinishes(playerId, limit = 15) {
|
|
459
|
-
return
|
|
1085
|
+
return findStandings({
|
|
460
1086
|
where: { playerId },
|
|
461
1087
|
take: limit,
|
|
462
1088
|
include: { tournament: true },
|
|
463
1089
|
orderBy: { decayedPoints: "desc" }
|
|
464
1090
|
});
|
|
465
1091
|
}
|
|
466
|
-
async function
|
|
467
|
-
return prisma.
|
|
1092
|
+
async function updateStanding(id, data) {
|
|
1093
|
+
return prisma.standing.update({
|
|
468
1094
|
where: { id },
|
|
469
1095
|
data
|
|
470
1096
|
});
|
|
471
1097
|
}
|
|
472
|
-
async function
|
|
473
|
-
const
|
|
1098
|
+
async function updateStandingPoints(id, linearPoints, dynamicPoints, totalPoints) {
|
|
1099
|
+
const standing = await findStandingById(id, {
|
|
474
1100
|
tournament: true
|
|
475
1101
|
});
|
|
476
|
-
if (!
|
|
477
|
-
throw new Error(`
|
|
1102
|
+
if (!standing) {
|
|
1103
|
+
throw new Error(`Standing with id ${id} not found`);
|
|
478
1104
|
}
|
|
479
1105
|
const now = /* @__PURE__ */ new Date();
|
|
480
|
-
const tournamentDate =
|
|
1106
|
+
const tournamentDate = standing.tournament.date;
|
|
481
1107
|
const ageInDays = Math.floor((now.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
482
1108
|
const ageInYears = ageInDays / 365;
|
|
483
1109
|
let decayMultiplier = 0;
|
|
@@ -491,7 +1117,7 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
491
1117
|
decayMultiplier = 0;
|
|
492
1118
|
}
|
|
493
1119
|
const decayedPoints = totalPoints * decayMultiplier;
|
|
494
|
-
return
|
|
1120
|
+
return updateStanding(id, {
|
|
495
1121
|
linearPoints,
|
|
496
1122
|
dynamicPoints,
|
|
497
1123
|
totalPoints,
|
|
@@ -500,50 +1126,50 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
500
1126
|
decayedPoints
|
|
501
1127
|
});
|
|
502
1128
|
}
|
|
503
|
-
async function
|
|
504
|
-
return prisma.
|
|
1129
|
+
async function deleteStanding(id) {
|
|
1130
|
+
return prisma.standing.delete({
|
|
505
1131
|
where: { id }
|
|
506
1132
|
});
|
|
507
1133
|
}
|
|
508
|
-
async function
|
|
509
|
-
return prisma.
|
|
1134
|
+
async function deleteStandingsByTournament(tournamentId) {
|
|
1135
|
+
return prisma.standing.deleteMany({
|
|
510
1136
|
where: { tournamentId }
|
|
511
1137
|
});
|
|
512
1138
|
}
|
|
513
|
-
async function
|
|
514
|
-
return prisma.
|
|
1139
|
+
async function countStandings(where) {
|
|
1140
|
+
return prisma.standing.count({ where });
|
|
515
1141
|
}
|
|
516
1142
|
async function getPlayerStats(playerId) {
|
|
517
|
-
const
|
|
518
|
-
if (
|
|
1143
|
+
const standings = await getPlayerStandings(playerId);
|
|
1144
|
+
if (standings.length === 0) {
|
|
519
1145
|
return null;
|
|
520
1146
|
}
|
|
521
|
-
const totalPoints =
|
|
522
|
-
const totalDecayedPoints =
|
|
523
|
-
const averagePosition =
|
|
524
|
-
const averageEfficiency =
|
|
525
|
-
const firstPlaceFinishes =
|
|
526
|
-
const topThreeFinishes =
|
|
1147
|
+
const totalPoints = standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
1148
|
+
const totalDecayedPoints = standings.reduce((sum, s) => sum + (s.decayedPoints || 0), 0);
|
|
1149
|
+
const averagePosition = standings.reduce((sum, s) => sum + s.position, 0) / standings.length;
|
|
1150
|
+
const averageEfficiency = standings.reduce((sum, s) => sum + (s.efficiency || 0), 0) / standings.length;
|
|
1151
|
+
const firstPlaceFinishes = standings.filter((s) => s.position === 1).length;
|
|
1152
|
+
const topThreeFinishes = standings.filter((s) => s.position <= 3).length;
|
|
527
1153
|
return {
|
|
528
|
-
totalEvents:
|
|
1154
|
+
totalEvents: standings.length,
|
|
529
1155
|
totalPoints,
|
|
530
1156
|
totalDecayedPoints,
|
|
531
|
-
averagePoints: totalPoints /
|
|
1157
|
+
averagePoints: totalPoints / standings.length,
|
|
532
1158
|
averagePosition,
|
|
533
1159
|
averageFinish: averagePosition,
|
|
534
1160
|
averageEfficiency,
|
|
535
1161
|
firstPlaceFinishes,
|
|
536
1162
|
topThreeFinishes,
|
|
537
|
-
bestFinish: Math.min(...
|
|
538
|
-
highestPoints: Math.max(...
|
|
1163
|
+
bestFinish: Math.min(...standings.map((s) => s.position)),
|
|
1164
|
+
highestPoints: Math.max(...standings.map((s) => s.totalPoints || 0))
|
|
539
1165
|
};
|
|
540
1166
|
}
|
|
541
1167
|
async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date()) {
|
|
542
|
-
const
|
|
1168
|
+
const standings = await findStandings({
|
|
543
1169
|
include: { tournament: true }
|
|
544
1170
|
});
|
|
545
|
-
const updates =
|
|
546
|
-
const tournamentDate =
|
|
1171
|
+
const updates = standings.map((standing) => {
|
|
1172
|
+
const tournamentDate = standing.tournament.date;
|
|
547
1173
|
const ageInDays = Math.floor(
|
|
548
1174
|
(referenceDate.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
549
1175
|
);
|
|
@@ -558,9 +1184,9 @@ async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date())
|
|
|
558
1184
|
} else {
|
|
559
1185
|
decayMultiplier = 0;
|
|
560
1186
|
}
|
|
561
|
-
const decayedPoints = (
|
|
562
|
-
return prisma.
|
|
563
|
-
where: { id:
|
|
1187
|
+
const decayedPoints = (standing.totalPoints || 0) * decayMultiplier;
|
|
1188
|
+
return prisma.standing.update({
|
|
1189
|
+
where: { id: standing.id },
|
|
564
1190
|
data: {
|
|
565
1191
|
ageInDays,
|
|
566
1192
|
decayMultiplier,
|
|
@@ -597,10 +1223,6 @@ async function createUserWithPlayer(userData, playerData) {
|
|
|
597
1223
|
id: true,
|
|
598
1224
|
playerNumber: true,
|
|
599
1225
|
name: true,
|
|
600
|
-
rating: true,
|
|
601
|
-
ratingDeviation: true,
|
|
602
|
-
ranking: true,
|
|
603
|
-
isRated: true,
|
|
604
1226
|
eventCount: true
|
|
605
1227
|
}
|
|
606
1228
|
}
|
|
@@ -630,10 +1252,6 @@ async function getUserWithPlayer(id) {
|
|
|
630
1252
|
id: true,
|
|
631
1253
|
playerNumber: true,
|
|
632
1254
|
name: true,
|
|
633
|
-
rating: true,
|
|
634
|
-
ratingDeviation: true,
|
|
635
|
-
ranking: true,
|
|
636
|
-
isRated: true,
|
|
637
1255
|
eventCount: true
|
|
638
1256
|
}
|
|
639
1257
|
}
|
|
@@ -653,10 +1271,6 @@ async function getUserByEmailWithPlayer(email) {
|
|
|
653
1271
|
id: true,
|
|
654
1272
|
playerNumber: true,
|
|
655
1273
|
name: true,
|
|
656
|
-
rating: true,
|
|
657
|
-
ratingDeviation: true,
|
|
658
|
-
ranking: true,
|
|
659
|
-
isRated: true,
|
|
660
1274
|
eventCount: true
|
|
661
1275
|
}
|
|
662
1276
|
}
|
|
@@ -699,10 +1313,6 @@ async function findUsers(params) {
|
|
|
699
1313
|
id: true,
|
|
700
1314
|
playerNumber: true,
|
|
701
1315
|
name: true,
|
|
702
|
-
rating: true,
|
|
703
|
-
ratingDeviation: true,
|
|
704
|
-
ranking: true,
|
|
705
|
-
isRated: true,
|
|
706
1316
|
eventCount: true
|
|
707
1317
|
}
|
|
708
1318
|
}
|
|
@@ -731,10 +1341,6 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
731
1341
|
id: true,
|
|
732
1342
|
playerNumber: true,
|
|
733
1343
|
name: true,
|
|
734
|
-
rating: true,
|
|
735
|
-
ratingDeviation: true,
|
|
736
|
-
ranking: true,
|
|
737
|
-
isRated: true,
|
|
738
1344
|
eventCount: true
|
|
739
1345
|
}
|
|
740
1346
|
}
|
|
@@ -743,33 +1349,204 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
743
1349
|
return user;
|
|
744
1350
|
});
|
|
745
1351
|
}
|
|
1352
|
+
|
|
1353
|
+
// src/api-keys.ts
|
|
1354
|
+
var MAX_API_KEYS_PER_USER = 5;
|
|
1355
|
+
async function createApiKey(data) {
|
|
1356
|
+
return prisma.apiKey.create({ data });
|
|
1357
|
+
}
|
|
1358
|
+
async function findApiKeyById(id) {
|
|
1359
|
+
return prisma.apiKey.findUnique({ where: { id } });
|
|
1360
|
+
}
|
|
1361
|
+
async function findApiKeysByPrefix(keyPrefix) {
|
|
1362
|
+
const keys = await prisma.apiKey.findMany({
|
|
1363
|
+
where: { keyPrefix },
|
|
1364
|
+
include: {
|
|
1365
|
+
user: {
|
|
1366
|
+
select: {
|
|
1367
|
+
id: true,
|
|
1368
|
+
email: true,
|
|
1369
|
+
role: true
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
return keys;
|
|
1375
|
+
}
|
|
1376
|
+
async function getUserApiKeys(userId) {
|
|
1377
|
+
return prisma.apiKey.findMany({
|
|
1378
|
+
where: { userId },
|
|
1379
|
+
select: {
|
|
1380
|
+
id: true,
|
|
1381
|
+
name: true,
|
|
1382
|
+
keyPrefix: true,
|
|
1383
|
+
expiresAt: true,
|
|
1384
|
+
lastUsedAt: true,
|
|
1385
|
+
createdAt: true
|
|
1386
|
+
},
|
|
1387
|
+
orderBy: { createdAt: "desc" }
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
async function countUserApiKeys(userId) {
|
|
1391
|
+
return prisma.apiKey.count({ where: { userId } });
|
|
1392
|
+
}
|
|
1393
|
+
async function updateApiKeyLastUsed(id) {
|
|
1394
|
+
await prisma.apiKey.update({
|
|
1395
|
+
where: { id },
|
|
1396
|
+
data: { lastUsedAt: /* @__PURE__ */ new Date() }
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
async function deleteApiKey(id) {
|
|
1400
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1401
|
+
}
|
|
1402
|
+
async function deleteUserApiKey(id, userId) {
|
|
1403
|
+
const key = await prisma.apiKey.findFirst({
|
|
1404
|
+
where: { id, userId }
|
|
1405
|
+
});
|
|
1406
|
+
if (!key) {
|
|
1407
|
+
return null;
|
|
1408
|
+
}
|
|
1409
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// src/locations.ts
|
|
1413
|
+
async function createLocation(data) {
|
|
1414
|
+
return prisma.location.create({
|
|
1415
|
+
data
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
async function findLocationById(id, include) {
|
|
1419
|
+
return prisma.location.findUnique({
|
|
1420
|
+
where: { id },
|
|
1421
|
+
include
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
async function findLocationByExternalId(externalId, include) {
|
|
1425
|
+
return prisma.location.findUnique({
|
|
1426
|
+
where: { externalId },
|
|
1427
|
+
include
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
async function findLocations(options = {}) {
|
|
1431
|
+
return prisma.location.findMany({
|
|
1432
|
+
take: options.take,
|
|
1433
|
+
skip: options.skip,
|
|
1434
|
+
where: options.where,
|
|
1435
|
+
orderBy: options.orderBy,
|
|
1436
|
+
include: options.include
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
async function searchLocations(query, limit = 20) {
|
|
1440
|
+
return findLocations({
|
|
1441
|
+
take: limit,
|
|
1442
|
+
where: {
|
|
1443
|
+
OR: [
|
|
1444
|
+
{ name: { contains: query, mode: "insensitive" } },
|
|
1445
|
+
{ city: { contains: query, mode: "insensitive" } }
|
|
1446
|
+
]
|
|
1447
|
+
},
|
|
1448
|
+
orderBy: { name: "asc" }
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
async function updateLocation(id, data) {
|
|
1452
|
+
return prisma.location.update({
|
|
1453
|
+
where: { id },
|
|
1454
|
+
data
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
async function deleteLocation(id) {
|
|
1458
|
+
return prisma.location.delete({
|
|
1459
|
+
where: { id }
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
async function countLocations(where) {
|
|
1463
|
+
return prisma.location.count({ where });
|
|
1464
|
+
}
|
|
1465
|
+
async function getLocationWithTournaments(id) {
|
|
1466
|
+
return prisma.location.findUnique({
|
|
1467
|
+
where: { id },
|
|
1468
|
+
include: {
|
|
1469
|
+
tournaments: {
|
|
1470
|
+
orderBy: {
|
|
1471
|
+
date: "desc"
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
746
1477
|
// Annotate the CommonJS export names for ESM import in node:
|
|
747
1478
|
0 && (module.exports = {
|
|
1479
|
+
MAX_API_KEYS_PER_USER,
|
|
1480
|
+
applyRDDecayForInactivePlayers,
|
|
748
1481
|
connect,
|
|
1482
|
+
countEntries,
|
|
1483
|
+
countLocations,
|
|
1484
|
+
countMatches,
|
|
1485
|
+
countOpprPlayerRankings,
|
|
1486
|
+
countOpprRankingHistory,
|
|
749
1487
|
countPlayers,
|
|
750
|
-
|
|
1488
|
+
countRounds,
|
|
1489
|
+
countStandings,
|
|
751
1490
|
countTournaments,
|
|
1491
|
+
countUserApiKeys,
|
|
752
1492
|
countUsers,
|
|
753
|
-
|
|
1493
|
+
createApiKey,
|
|
1494
|
+
createEntry,
|
|
1495
|
+
createLocation,
|
|
1496
|
+
createManyEntries,
|
|
1497
|
+
createManyMatches,
|
|
1498
|
+
createManyRounds,
|
|
1499
|
+
createManyStandings,
|
|
1500
|
+
createMatch,
|
|
1501
|
+
createOpprPlayerRanking,
|
|
1502
|
+
createOpprRankingHistory,
|
|
754
1503
|
createPlayer,
|
|
755
|
-
|
|
1504
|
+
createRound,
|
|
1505
|
+
createStanding,
|
|
756
1506
|
createTournament,
|
|
757
1507
|
createUser,
|
|
758
1508
|
createUserWithPlayer,
|
|
1509
|
+
deleteApiKey,
|
|
1510
|
+
deleteEntriesByMatch,
|
|
1511
|
+
deleteEntry,
|
|
1512
|
+
deleteLocation,
|
|
1513
|
+
deleteMatch,
|
|
1514
|
+
deleteMatchesByRound,
|
|
1515
|
+
deleteMatchesByTournament,
|
|
1516
|
+
deleteOpprPlayerRanking,
|
|
759
1517
|
deletePlayer,
|
|
760
|
-
|
|
761
|
-
|
|
1518
|
+
deleteRound,
|
|
1519
|
+
deleteRoundsByTournament,
|
|
1520
|
+
deleteStanding,
|
|
1521
|
+
deleteStandingsByTournament,
|
|
762
1522
|
deleteTournament,
|
|
763
1523
|
deleteUser,
|
|
1524
|
+
deleteUserApiKey,
|
|
764
1525
|
disconnect,
|
|
1526
|
+
findApiKeyById,
|
|
1527
|
+
findApiKeysByPrefix,
|
|
1528
|
+
findEntries,
|
|
1529
|
+
findEntryById,
|
|
1530
|
+
findEntryByMatchAndPlayer,
|
|
1531
|
+
findLocationByExternalId,
|
|
1532
|
+
findLocationById,
|
|
1533
|
+
findLocations,
|
|
1534
|
+
findMatchById,
|
|
1535
|
+
findMatches,
|
|
1536
|
+
findOpprPlayerRankingById,
|
|
1537
|
+
findOpprPlayerRankingByPlayerId,
|
|
1538
|
+
findOpprPlayerRankings,
|
|
765
1539
|
findPlayerByExternalId,
|
|
766
1540
|
findPlayerById,
|
|
767
1541
|
findPlayerByPlayerNumber,
|
|
768
1542
|
findPlayerByUserEmail,
|
|
769
1543
|
findPlayers,
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
1544
|
+
findRoundById,
|
|
1545
|
+
findRoundByTournamentAndNumber,
|
|
1546
|
+
findRounds,
|
|
1547
|
+
findStandingById,
|
|
1548
|
+
findStandingByPlayerAndTournament,
|
|
1549
|
+
findStandings,
|
|
773
1550
|
findTournamentByExternalId,
|
|
774
1551
|
findTournamentById,
|
|
775
1552
|
findTournaments,
|
|
@@ -777,34 +1554,64 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
777
1554
|
findUserById,
|
|
778
1555
|
findUsers,
|
|
779
1556
|
generateUniquePlayerNumber,
|
|
1557
|
+
getFinalsRounds,
|
|
1558
|
+
getFinalsStandings,
|
|
1559
|
+
getLatestOpprRankingHistory,
|
|
1560
|
+
getLocationWithTournaments,
|
|
780
1561
|
getMajorTournaments,
|
|
781
|
-
|
|
1562
|
+
getMatchEntries,
|
|
1563
|
+
getMatchWithEntries,
|
|
1564
|
+
getMergedStandings,
|
|
1565
|
+
getOpprRankingHistory,
|
|
1566
|
+
getOpprRankingHistoryByDateRange,
|
|
1567
|
+
getOrCreateOpprPlayerRanking,
|
|
1568
|
+
getPlayerEntries,
|
|
1569
|
+
getPlayerEntryStats,
|
|
1570
|
+
getPlayerStandings,
|
|
782
1571
|
getPlayerStats,
|
|
783
1572
|
getPlayerTopFinishes,
|
|
1573
|
+
getPlayerTournamentEntries,
|
|
1574
|
+
getPlayerTournamentMatches,
|
|
784
1575
|
getPlayerWithResults,
|
|
785
|
-
|
|
1576
|
+
getQualifyingRounds,
|
|
1577
|
+
getQualifyingStandings,
|
|
1578
|
+
getRatedOpprPlayers,
|
|
786
1579
|
getRecentTournaments,
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
1580
|
+
getRoundMatches,
|
|
1581
|
+
getRoundWithMatches,
|
|
1582
|
+
getTopPlayersByOpprRanking,
|
|
1583
|
+
getTopPlayersByOpprRating,
|
|
1584
|
+
getTournamentMatches,
|
|
1585
|
+
getTournamentRounds,
|
|
1586
|
+
getTournamentStandings,
|
|
790
1587
|
getTournamentStats,
|
|
1588
|
+
getTournamentWithMatches,
|
|
791
1589
|
getTournamentWithResults,
|
|
792
1590
|
getTournamentsByBoosterType,
|
|
793
1591
|
getTournamentsByDateRange,
|
|
1592
|
+
getUserApiKeys,
|
|
794
1593
|
getUserByEmailWithPlayer,
|
|
795
1594
|
getUserWithPlayer,
|
|
796
1595
|
isValidPlayerNumber,
|
|
797
1596
|
linkPlayerToUser,
|
|
798
1597
|
prisma,
|
|
799
1598
|
recalculateTimeDecay,
|
|
1599
|
+
searchLocations,
|
|
800
1600
|
searchPlayers,
|
|
801
1601
|
searchTournaments,
|
|
802
1602
|
testConnection,
|
|
1603
|
+
updateApiKeyLastUsed,
|
|
1604
|
+
updateEntry,
|
|
1605
|
+
updateLocation,
|
|
1606
|
+
updateMatch,
|
|
1607
|
+
updateOpprPlayerRanking,
|
|
1608
|
+
updateOpprRatingAfterTournament,
|
|
803
1609
|
updatePlayer,
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1610
|
+
updateRound,
|
|
1611
|
+
updateStanding,
|
|
1612
|
+
updateStandingPoints,
|
|
807
1613
|
updateTournament,
|
|
808
1614
|
updateUser,
|
|
809
|
-
updateUserRefreshToken
|
|
1615
|
+
updateUserRefreshToken,
|
|
1616
|
+
updateWorldRankings
|
|
810
1617
|
});
|