@opprs/db-prisma 2.2.1 → 2.5.0
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 +1233 -158
- package/dist/index.d.cts +1012 -106
- package/dist/index.d.ts +1012 -106
- package/dist/index.js +1117 -141
- package/package.json +1 -1
- package/prisma/migrations/20260104000000_add_player_number/migration.sql +23 -0
- 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 +292 -37
- package/prisma/seed.ts +157 -48
package/dist/index.cjs
CHANGED
|
@@ -20,65 +20,164 @@ 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
|
+
countBlogPosts: () => countBlogPosts,
|
|
27
|
+
countBlogTags: () => countBlogTags,
|
|
28
|
+
countEntries: () => countEntries,
|
|
29
|
+
countLocations: () => countLocations,
|
|
30
|
+
countMatches: () => countMatches,
|
|
31
|
+
countOpprPlayerRankings: () => countOpprPlayerRankings,
|
|
32
|
+
countOpprRankingHistory: () => countOpprRankingHistory,
|
|
24
33
|
countPlayers: () => countPlayers,
|
|
25
|
-
|
|
34
|
+
countPublishedBlogPosts: () => countPublishedBlogPosts,
|
|
35
|
+
countRounds: () => countRounds,
|
|
36
|
+
countStandings: () => countStandings,
|
|
26
37
|
countTournaments: () => countTournaments,
|
|
38
|
+
countUserApiKeys: () => countUserApiKeys,
|
|
27
39
|
countUsers: () => countUsers,
|
|
28
|
-
|
|
40
|
+
createApiKey: () => createApiKey,
|
|
41
|
+
createBlogPost: () => createBlogPost,
|
|
42
|
+
createBlogTag: () => createBlogTag,
|
|
43
|
+
createEntry: () => createEntry,
|
|
44
|
+
createLocation: () => createLocation,
|
|
45
|
+
createManyEntries: () => createManyEntries,
|
|
46
|
+
createManyMatches: () => createManyMatches,
|
|
47
|
+
createManyRounds: () => createManyRounds,
|
|
48
|
+
createManyStandings: () => createManyStandings,
|
|
49
|
+
createMatch: () => createMatch,
|
|
50
|
+
createOpprPlayerRanking: () => createOpprPlayerRanking,
|
|
51
|
+
createOpprRankingHistory: () => createOpprRankingHistory,
|
|
29
52
|
createPlayer: () => createPlayer,
|
|
30
|
-
|
|
53
|
+
createRound: () => createRound,
|
|
54
|
+
createStanding: () => createStanding,
|
|
31
55
|
createTournament: () => createTournament,
|
|
32
56
|
createUser: () => createUser,
|
|
33
57
|
createUserWithPlayer: () => createUserWithPlayer,
|
|
58
|
+
deleteApiKey: () => deleteApiKey,
|
|
59
|
+
deleteBlogPost: () => deleteBlogPost,
|
|
60
|
+
deleteBlogTag: () => deleteBlogTag,
|
|
61
|
+
deleteEntriesByMatch: () => deleteEntriesByMatch,
|
|
62
|
+
deleteEntry: () => deleteEntry,
|
|
63
|
+
deleteLocation: () => deleteLocation,
|
|
64
|
+
deleteMatch: () => deleteMatch,
|
|
65
|
+
deleteMatchesByRound: () => deleteMatchesByRound,
|
|
66
|
+
deleteMatchesByTournament: () => deleteMatchesByTournament,
|
|
67
|
+
deleteOpprPlayerRanking: () => deleteOpprPlayerRanking,
|
|
34
68
|
deletePlayer: () => deletePlayer,
|
|
35
|
-
|
|
36
|
-
|
|
69
|
+
deleteRound: () => deleteRound,
|
|
70
|
+
deleteRoundsByTournament: () => deleteRoundsByTournament,
|
|
71
|
+
deleteStanding: () => deleteStanding,
|
|
72
|
+
deleteStandingsByTournament: () => deleteStandingsByTournament,
|
|
37
73
|
deleteTournament: () => deleteTournament,
|
|
38
74
|
deleteUser: () => deleteUser,
|
|
75
|
+
deleteUserApiKey: () => deleteUserApiKey,
|
|
39
76
|
disconnect: () => disconnect,
|
|
77
|
+
findApiKeyById: () => findApiKeyById,
|
|
78
|
+
findApiKeysByPrefix: () => findApiKeysByPrefix,
|
|
79
|
+
findBlogPostById: () => findBlogPostById,
|
|
80
|
+
findBlogPostBySlug: () => findBlogPostBySlug,
|
|
81
|
+
findBlogPosts: () => findBlogPosts,
|
|
82
|
+
findBlogTagById: () => findBlogTagById,
|
|
83
|
+
findBlogTagBySlug: () => findBlogTagBySlug,
|
|
84
|
+
findBlogTags: () => findBlogTags,
|
|
85
|
+
findEntries: () => findEntries,
|
|
86
|
+
findEntryById: () => findEntryById,
|
|
87
|
+
findEntryByMatchAndPlayer: () => findEntryByMatchAndPlayer,
|
|
88
|
+
findLocationByExternalId: () => findLocationByExternalId,
|
|
89
|
+
findLocationById: () => findLocationById,
|
|
90
|
+
findLocations: () => findLocations,
|
|
91
|
+
findMatchById: () => findMatchById,
|
|
92
|
+
findMatches: () => findMatches,
|
|
93
|
+
findOpprPlayerRankingById: () => findOpprPlayerRankingById,
|
|
94
|
+
findOpprPlayerRankingByPlayerId: () => findOpprPlayerRankingByPlayerId,
|
|
95
|
+
findOpprPlayerRankings: () => findOpprPlayerRankings,
|
|
40
96
|
findPlayerByExternalId: () => findPlayerByExternalId,
|
|
41
97
|
findPlayerById: () => findPlayerById,
|
|
98
|
+
findPlayerByPlayerNumber: () => findPlayerByPlayerNumber,
|
|
42
99
|
findPlayerByUserEmail: () => findPlayerByUserEmail,
|
|
43
100
|
findPlayers: () => findPlayers,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
101
|
+
findPublishedBlogPosts: () => findPublishedBlogPosts,
|
|
102
|
+
findRoundById: () => findRoundById,
|
|
103
|
+
findRoundByTournamentAndNumber: () => findRoundByTournamentAndNumber,
|
|
104
|
+
findRounds: () => findRounds,
|
|
105
|
+
findStandingById: () => findStandingById,
|
|
106
|
+
findStandingByPlayerAndTournament: () => findStandingByPlayerAndTournament,
|
|
107
|
+
findStandings: () => findStandings,
|
|
47
108
|
findTournamentByExternalId: () => findTournamentByExternalId,
|
|
48
109
|
findTournamentById: () => findTournamentById,
|
|
49
110
|
findTournaments: () => findTournaments,
|
|
50
111
|
findUserByEmail: () => findUserByEmail,
|
|
51
112
|
findUserById: () => findUserById,
|
|
52
113
|
findUsers: () => findUsers,
|
|
114
|
+
generateUniquePlayerNumber: () => generateUniquePlayerNumber,
|
|
115
|
+
getBlogTagWithPostCount: () => getBlogTagWithPostCount,
|
|
116
|
+
getBlogTagsWithPostCounts: () => getBlogTagsWithPostCounts,
|
|
117
|
+
getFinalsRounds: () => getFinalsRounds,
|
|
118
|
+
getFinalsStandings: () => getFinalsStandings,
|
|
119
|
+
getLatestOpprRankingHistory: () => getLatestOpprRankingHistory,
|
|
120
|
+
getLocationWithTournaments: () => getLocationWithTournaments,
|
|
53
121
|
getMajorTournaments: () => getMajorTournaments,
|
|
54
|
-
|
|
122
|
+
getMatchEntries: () => getMatchEntries,
|
|
123
|
+
getMatchWithEntries: () => getMatchWithEntries,
|
|
124
|
+
getMergedStandings: () => getMergedStandings,
|
|
125
|
+
getOpprRankingHistory: () => getOpprRankingHistory,
|
|
126
|
+
getOpprRankingHistoryByDateRange: () => getOpprRankingHistoryByDateRange,
|
|
127
|
+
getOrCreateOpprPlayerRanking: () => getOrCreateOpprPlayerRanking,
|
|
128
|
+
getPlayerEntries: () => getPlayerEntries,
|
|
129
|
+
getPlayerEntryStats: () => getPlayerEntryStats,
|
|
130
|
+
getPlayerStandings: () => getPlayerStandings,
|
|
55
131
|
getPlayerStats: () => getPlayerStats,
|
|
56
132
|
getPlayerTopFinishes: () => getPlayerTopFinishes,
|
|
133
|
+
getPlayerTournamentEntries: () => getPlayerTournamentEntries,
|
|
134
|
+
getPlayerTournamentMatches: () => getPlayerTournamentMatches,
|
|
57
135
|
getPlayerWithResults: () => getPlayerWithResults,
|
|
58
|
-
|
|
136
|
+
getQualifyingRounds: () => getQualifyingRounds,
|
|
137
|
+
getQualifyingStandings: () => getQualifyingStandings,
|
|
138
|
+
getRatedOpprPlayers: () => getRatedOpprPlayers,
|
|
59
139
|
getRecentTournaments: () => getRecentTournaments,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
140
|
+
getRoundMatches: () => getRoundMatches,
|
|
141
|
+
getRoundWithMatches: () => getRoundWithMatches,
|
|
142
|
+
getTopPlayersByOpprRanking: () => getTopPlayersByOpprRanking,
|
|
143
|
+
getTopPlayersByOpprRating: () => getTopPlayersByOpprRating,
|
|
144
|
+
getTournamentMatches: () => getTournamentMatches,
|
|
145
|
+
getTournamentRounds: () => getTournamentRounds,
|
|
146
|
+
getTournamentStandings: () => getTournamentStandings,
|
|
63
147
|
getTournamentStats: () => getTournamentStats,
|
|
148
|
+
getTournamentWithMatches: () => getTournamentWithMatches,
|
|
64
149
|
getTournamentWithResults: () => getTournamentWithResults,
|
|
65
150
|
getTournamentsByBoosterType: () => getTournamentsByBoosterType,
|
|
66
151
|
getTournamentsByDateRange: () => getTournamentsByDateRange,
|
|
152
|
+
getUserApiKeys: () => getUserApiKeys,
|
|
67
153
|
getUserByEmailWithPlayer: () => getUserByEmailWithPlayer,
|
|
68
154
|
getUserWithPlayer: () => getUserWithPlayer,
|
|
155
|
+
isValidPlayerNumber: () => isValidPlayerNumber,
|
|
69
156
|
linkPlayerToUser: () => linkPlayerToUser,
|
|
70
157
|
prisma: () => prisma,
|
|
71
158
|
recalculateTimeDecay: () => recalculateTimeDecay,
|
|
159
|
+
searchBlogPosts: () => searchBlogPosts,
|
|
160
|
+
searchBlogTags: () => searchBlogTags,
|
|
161
|
+
searchLocations: () => searchLocations,
|
|
72
162
|
searchPlayers: () => searchPlayers,
|
|
73
163
|
searchTournaments: () => searchTournaments,
|
|
74
164
|
testConnection: () => testConnection,
|
|
165
|
+
updateApiKeyLastUsed: () => updateApiKeyLastUsed,
|
|
166
|
+
updateBlogPost: () => updateBlogPost,
|
|
167
|
+
updateBlogTag: () => updateBlogTag,
|
|
168
|
+
updateEntry: () => updateEntry,
|
|
169
|
+
updateLocation: () => updateLocation,
|
|
170
|
+
updateMatch: () => updateMatch,
|
|
171
|
+
updateOpprPlayerRanking: () => updateOpprPlayerRanking,
|
|
172
|
+
updateOpprRatingAfterTournament: () => updateOpprRatingAfterTournament,
|
|
75
173
|
updatePlayer: () => updatePlayer,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
174
|
+
updateRound: () => updateRound,
|
|
175
|
+
updateStanding: () => updateStanding,
|
|
176
|
+
updateStandingPoints: () => updateStandingPoints,
|
|
79
177
|
updateTournament: () => updateTournament,
|
|
80
178
|
updateUser: () => updateUser,
|
|
81
|
-
updateUserRefreshToken: () => updateUserRefreshToken
|
|
179
|
+
updateUserRefreshToken: () => updateUserRefreshToken,
|
|
180
|
+
updateWorldRankings: () => updateWorldRankings
|
|
82
181
|
});
|
|
83
182
|
module.exports = __toCommonJS(index_exports);
|
|
84
183
|
|
|
@@ -107,10 +206,40 @@ async function testConnection() {
|
|
|
107
206
|
}
|
|
108
207
|
}
|
|
109
208
|
|
|
209
|
+
// src/player-number.ts
|
|
210
|
+
var MIN_PLAYER_NUMBER = 1e4;
|
|
211
|
+
var MAX_PLAYER_NUMBER = 99999;
|
|
212
|
+
var MAX_RETRIES = 10;
|
|
213
|
+
function generateRandomPlayerNumber() {
|
|
214
|
+
return Math.floor(Math.random() * (MAX_PLAYER_NUMBER - MIN_PLAYER_NUMBER + 1)) + MIN_PLAYER_NUMBER;
|
|
215
|
+
}
|
|
216
|
+
async function generateUniquePlayerNumber() {
|
|
217
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
218
|
+
const candidate = generateRandomPlayerNumber();
|
|
219
|
+
const existing = await prisma.player.findUnique({
|
|
220
|
+
where: { playerNumber: candidate },
|
|
221
|
+
select: { id: true }
|
|
222
|
+
});
|
|
223
|
+
if (!existing) {
|
|
224
|
+
return candidate;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
throw new Error(
|
|
228
|
+
`Failed to generate unique player number after ${MAX_RETRIES} attempts. Consider increasing the number range or implementing a different allocation strategy.`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
function isValidPlayerNumber(playerNumber) {
|
|
232
|
+
return Number.isInteger(playerNumber) && playerNumber >= MIN_PLAYER_NUMBER && playerNumber <= MAX_PLAYER_NUMBER;
|
|
233
|
+
}
|
|
234
|
+
|
|
110
235
|
// src/players.ts
|
|
111
236
|
async function createPlayer(data) {
|
|
237
|
+
const playerNumber = data.playerNumber ?? await generateUniquePlayerNumber();
|
|
112
238
|
return prisma.player.create({
|
|
113
|
-
data
|
|
239
|
+
data: {
|
|
240
|
+
...data,
|
|
241
|
+
playerNumber
|
|
242
|
+
}
|
|
114
243
|
});
|
|
115
244
|
}
|
|
116
245
|
async function findPlayerById(id, include) {
|
|
@@ -125,6 +254,12 @@ async function findPlayerByExternalId(externalId, include) {
|
|
|
125
254
|
include
|
|
126
255
|
});
|
|
127
256
|
}
|
|
257
|
+
async function findPlayerByPlayerNumber(playerNumber, include) {
|
|
258
|
+
return prisma.player.findUnique({
|
|
259
|
+
where: { playerNumber },
|
|
260
|
+
include
|
|
261
|
+
});
|
|
262
|
+
}
|
|
128
263
|
async function findPlayerByUserEmail(email, include) {
|
|
129
264
|
const user = await prisma.user.findUnique({
|
|
130
265
|
where: { email },
|
|
@@ -141,48 +276,12 @@ async function findPlayers(options = {}) {
|
|
|
141
276
|
include: options.include
|
|
142
277
|
});
|
|
143
278
|
}
|
|
144
|
-
async function getRatedPlayers(options = {}) {
|
|
145
|
-
return findPlayers({
|
|
146
|
-
...options,
|
|
147
|
-
where: { isRated: true }
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
async function getTopPlayersByRating(limit = 50) {
|
|
151
|
-
return findPlayers({
|
|
152
|
-
take: limit,
|
|
153
|
-
orderBy: { rating: "desc" },
|
|
154
|
-
where: { isRated: true }
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
async function getTopPlayersByRanking(limit = 50) {
|
|
158
|
-
return findPlayers({
|
|
159
|
-
take: limit,
|
|
160
|
-
orderBy: { ranking: "asc" },
|
|
161
|
-
where: {
|
|
162
|
-
isRated: true,
|
|
163
|
-
ranking: { not: null }
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
279
|
async function updatePlayer(id, data) {
|
|
168
280
|
return prisma.player.update({
|
|
169
281
|
where: { id },
|
|
170
282
|
data
|
|
171
283
|
});
|
|
172
284
|
}
|
|
173
|
-
async function updatePlayerRating(id, rating, ratingDeviation, eventCount) {
|
|
174
|
-
const updateData = {
|
|
175
|
-
rating,
|
|
176
|
-
ratingDeviation,
|
|
177
|
-
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
178
|
-
lastEventDate: /* @__PURE__ */ new Date()
|
|
179
|
-
};
|
|
180
|
-
if (eventCount !== void 0) {
|
|
181
|
-
updateData.eventCount = eventCount;
|
|
182
|
-
updateData.isRated = eventCount >= 5;
|
|
183
|
-
}
|
|
184
|
-
return updatePlayer(id, updateData);
|
|
185
|
-
}
|
|
186
285
|
async function deletePlayer(id) {
|
|
187
286
|
return prisma.player.delete({
|
|
188
287
|
where: { id }
|
|
@@ -195,7 +294,7 @@ async function getPlayerWithResults(id) {
|
|
|
195
294
|
const player = await prisma.player.findUnique({
|
|
196
295
|
where: { id },
|
|
197
296
|
include: {
|
|
198
|
-
|
|
297
|
+
standings: {
|
|
199
298
|
include: {
|
|
200
299
|
tournament: true
|
|
201
300
|
},
|
|
@@ -212,7 +311,7 @@ async function getPlayerWithResults(id) {
|
|
|
212
311
|
}
|
|
213
312
|
return {
|
|
214
313
|
...player,
|
|
215
|
-
results: player.
|
|
314
|
+
results: player.standings
|
|
216
315
|
};
|
|
217
316
|
}
|
|
218
317
|
async function searchPlayers(query, limit = 20) {
|
|
@@ -224,6 +323,212 @@ async function searchPlayers(query, limit = 20) {
|
|
|
224
323
|
});
|
|
225
324
|
}
|
|
226
325
|
|
|
326
|
+
// src/oppr-rankings.ts
|
|
327
|
+
async function getOrCreateOpprPlayerRanking(playerId) {
|
|
328
|
+
const existing = await prisma.opprPlayerRanking.findUnique({
|
|
329
|
+
where: { playerId }
|
|
330
|
+
});
|
|
331
|
+
if (existing) return existing;
|
|
332
|
+
return prisma.opprPlayerRanking.create({
|
|
333
|
+
data: { playerId }
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
async function createOpprPlayerRanking(data) {
|
|
337
|
+
return prisma.opprPlayerRanking.create({
|
|
338
|
+
data
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
async function findOpprPlayerRankingById(id, include) {
|
|
342
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
343
|
+
where: { id },
|
|
344
|
+
include
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
async function findOpprPlayerRankingByPlayerId(playerId, include) {
|
|
348
|
+
return prisma.opprPlayerRanking.findUnique({
|
|
349
|
+
where: { playerId },
|
|
350
|
+
include
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
async function findOpprPlayerRankings(options = {}) {
|
|
354
|
+
return prisma.opprPlayerRanking.findMany({
|
|
355
|
+
take: options.take,
|
|
356
|
+
skip: options.skip,
|
|
357
|
+
where: options.where,
|
|
358
|
+
orderBy: options.orderBy,
|
|
359
|
+
include: options.include
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
async function getTopPlayersByOpprRating(limit = 50) {
|
|
363
|
+
return prisma.opprPlayerRanking.findMany({
|
|
364
|
+
take: limit,
|
|
365
|
+
where: { isRated: true },
|
|
366
|
+
orderBy: { rating: "desc" },
|
|
367
|
+
include: { player: true }
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
async function getTopPlayersByOpprRanking(limit = 50) {
|
|
371
|
+
return prisma.opprPlayerRanking.findMany({
|
|
372
|
+
take: limit,
|
|
373
|
+
where: {
|
|
374
|
+
isRated: true,
|
|
375
|
+
ranking: { not: null }
|
|
376
|
+
},
|
|
377
|
+
orderBy: { ranking: "asc" },
|
|
378
|
+
include: { player: true }
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
async function getRatedOpprPlayers(options = {}) {
|
|
382
|
+
return prisma.opprPlayerRanking.findMany({
|
|
383
|
+
...options,
|
|
384
|
+
where: { isRated: true },
|
|
385
|
+
include: { player: true, ...options.include }
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
async function updateOpprPlayerRanking(playerId, data) {
|
|
389
|
+
return prisma.opprPlayerRanking.update({
|
|
390
|
+
where: { playerId },
|
|
391
|
+
data: {
|
|
392
|
+
...data,
|
|
393
|
+
lastRatingUpdate: data.lastRatingUpdate ?? /* @__PURE__ */ new Date()
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
async function updateOpprRatingAfterTournament(playerId, newRating, newRD, tournamentId, eventCount) {
|
|
398
|
+
const ranking = await getOrCreateOpprPlayerRanking(playerId);
|
|
399
|
+
const isRated = eventCount !== void 0 ? eventCount >= 5 : ranking.isRated;
|
|
400
|
+
const updated = await prisma.opprPlayerRanking.update({
|
|
401
|
+
where: { playerId },
|
|
402
|
+
data: {
|
|
403
|
+
rating: newRating,
|
|
404
|
+
ratingDeviation: newRD,
|
|
405
|
+
lastRatingUpdate: /* @__PURE__ */ new Date(),
|
|
406
|
+
isRated
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
await createOpprRankingHistory({
|
|
410
|
+
opprPlayerRankingId: ranking.id,
|
|
411
|
+
rating: newRating,
|
|
412
|
+
ratingDeviation: newRD,
|
|
413
|
+
ranking: updated.ranking ?? void 0,
|
|
414
|
+
isRated,
|
|
415
|
+
changeType: "TOURNAMENT_RESULT",
|
|
416
|
+
tournamentId
|
|
417
|
+
});
|
|
418
|
+
return updated;
|
|
419
|
+
}
|
|
420
|
+
async function updateWorldRankings(rankings) {
|
|
421
|
+
await prisma.$transaction(async (tx) => {
|
|
422
|
+
for (const { playerId, ranking } of rankings) {
|
|
423
|
+
const opprRanking = await tx.opprPlayerRanking.findUnique({
|
|
424
|
+
where: { playerId }
|
|
425
|
+
});
|
|
426
|
+
if (opprRanking) {
|
|
427
|
+
await tx.opprPlayerRanking.update({
|
|
428
|
+
where: { playerId },
|
|
429
|
+
data: { ranking }
|
|
430
|
+
});
|
|
431
|
+
await tx.opprRankingHistory.create({
|
|
432
|
+
data: {
|
|
433
|
+
opprPlayerRankingId: opprRanking.id,
|
|
434
|
+
rating: opprRanking.rating,
|
|
435
|
+
ratingDeviation: opprRanking.ratingDeviation,
|
|
436
|
+
ranking,
|
|
437
|
+
isRated: opprRanking.isRated,
|
|
438
|
+
changeType: "RANKING_REFRESH"
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
async function applyRDDecayForInactivePlayers(thresholdDays = 30, decayPerDay = 0.3, maxRD = 200) {
|
|
446
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
447
|
+
cutoffDate.setDate(cutoffDate.getDate() - thresholdDays);
|
|
448
|
+
const inactivePlayers = await prisma.opprPlayerRanking.findMany({
|
|
449
|
+
where: {
|
|
450
|
+
lastRatingUpdate: { lt: cutoffDate },
|
|
451
|
+
ratingDeviation: { lt: maxRD }
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
let updatedCount = 0;
|
|
455
|
+
await prisma.$transaction(async (tx) => {
|
|
456
|
+
for (const ranking of inactivePlayers) {
|
|
457
|
+
const daysSinceUpdate = Math.floor(
|
|
458
|
+
(Date.now() - ranking.lastRatingUpdate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
459
|
+
);
|
|
460
|
+
const newRD = Math.min(ranking.ratingDeviation + daysSinceUpdate * decayPerDay, maxRD);
|
|
461
|
+
await tx.opprPlayerRanking.update({
|
|
462
|
+
where: { id: ranking.id },
|
|
463
|
+
data: { ratingDeviation: newRD }
|
|
464
|
+
});
|
|
465
|
+
await tx.opprRankingHistory.create({
|
|
466
|
+
data: {
|
|
467
|
+
opprPlayerRankingId: ranking.id,
|
|
468
|
+
rating: ranking.rating,
|
|
469
|
+
ratingDeviation: newRD,
|
|
470
|
+
ranking: ranking.ranking,
|
|
471
|
+
isRated: ranking.isRated,
|
|
472
|
+
changeType: "RD_DECAY",
|
|
473
|
+
notes: `RD increased from ${ranking.ratingDeviation.toFixed(1)} to ${newRD.toFixed(1)} due to ${daysSinceUpdate} days of inactivity`
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
updatedCount++;
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
return updatedCount;
|
|
480
|
+
}
|
|
481
|
+
async function deleteOpprPlayerRanking(playerId) {
|
|
482
|
+
return prisma.opprPlayerRanking.delete({
|
|
483
|
+
where: { playerId }
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
async function countOpprPlayerRankings(where) {
|
|
487
|
+
return prisma.opprPlayerRanking.count({ where });
|
|
488
|
+
}
|
|
489
|
+
async function createOpprRankingHistory(data) {
|
|
490
|
+
return prisma.opprRankingHistory.create({
|
|
491
|
+
data
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
async function getOpprRankingHistory(playerId, limit) {
|
|
495
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
496
|
+
if (!ranking) return [];
|
|
497
|
+
return prisma.opprRankingHistory.findMany({
|
|
498
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
499
|
+
orderBy: { createdAt: "desc" },
|
|
500
|
+
take: limit,
|
|
501
|
+
include: { tournament: true }
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
async function getOpprRankingHistoryByDateRange(playerId, startDate, endDate) {
|
|
505
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
506
|
+
if (!ranking) return [];
|
|
507
|
+
return prisma.opprRankingHistory.findMany({
|
|
508
|
+
where: {
|
|
509
|
+
opprPlayerRankingId: ranking.id,
|
|
510
|
+
createdAt: {
|
|
511
|
+
gte: startDate,
|
|
512
|
+
lte: endDate
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
orderBy: { createdAt: "asc" },
|
|
516
|
+
include: { tournament: true }
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
async function getLatestOpprRankingHistory(playerId) {
|
|
520
|
+
const ranking = await findOpprPlayerRankingByPlayerId(playerId);
|
|
521
|
+
if (!ranking) return null;
|
|
522
|
+
return prisma.opprRankingHistory.findFirst({
|
|
523
|
+
where: { opprPlayerRankingId: ranking.id },
|
|
524
|
+
orderBy: { createdAt: "desc" },
|
|
525
|
+
include: { tournament: true }
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
async function countOpprRankingHistory(where) {
|
|
529
|
+
return prisma.opprRankingHistory.count({ where });
|
|
530
|
+
}
|
|
531
|
+
|
|
227
532
|
// src/tournaments.ts
|
|
228
533
|
async function createTournament(data) {
|
|
229
534
|
return prisma.tournament.create({
|
|
@@ -303,13 +608,33 @@ async function getTournamentWithResults(id) {
|
|
|
303
608
|
return prisma.tournament.findUnique({
|
|
304
609
|
where: { id },
|
|
305
610
|
include: {
|
|
306
|
-
|
|
611
|
+
standings: {
|
|
307
612
|
include: {
|
|
308
613
|
player: true
|
|
309
614
|
},
|
|
310
|
-
orderBy: {
|
|
311
|
-
|
|
312
|
-
|
|
615
|
+
orderBy: [{ isFinals: "desc" }, { position: "asc" }]
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
async function getTournamentWithMatches(id) {
|
|
621
|
+
return prisma.tournament.findUnique({
|
|
622
|
+
where: { id },
|
|
623
|
+
include: {
|
|
624
|
+
rounds: {
|
|
625
|
+
include: {
|
|
626
|
+
matches: {
|
|
627
|
+
include: {
|
|
628
|
+
entries: {
|
|
629
|
+
include: {
|
|
630
|
+
player: true
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
orderBy: { number: "asc" }
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
orderBy: [{ isFinals: "asc" }, { number: "asc" }]
|
|
313
638
|
}
|
|
314
639
|
}
|
|
315
640
|
});
|
|
@@ -320,7 +645,7 @@ async function searchTournaments(query, limit = 20) {
|
|
|
320
645
|
where: {
|
|
321
646
|
OR: [
|
|
322
647
|
{ name: { contains: query, mode: "insensitive" } },
|
|
323
|
-
{ location: { contains: query, mode: "insensitive" } }
|
|
648
|
+
{ location: { name: { contains: query, mode: "insensitive" } } }
|
|
324
649
|
]
|
|
325
650
|
},
|
|
326
651
|
orderBy: { date: "desc" }
|
|
@@ -331,7 +656,7 @@ async function getTournamentStats(id) {
|
|
|
331
656
|
if (!tournament) {
|
|
332
657
|
return null;
|
|
333
658
|
}
|
|
334
|
-
const playerCount = tournament.
|
|
659
|
+
const playerCount = tournament.standings.length;
|
|
335
660
|
if (playerCount === 0) {
|
|
336
661
|
return {
|
|
337
662
|
tournament,
|
|
@@ -342,9 +667,9 @@ async function getTournamentStats(id) {
|
|
|
342
667
|
lowestPoints: 0
|
|
343
668
|
};
|
|
344
669
|
}
|
|
345
|
-
const totalPoints = tournament.
|
|
346
|
-
const totalEfficiency = tournament.
|
|
347
|
-
const allPoints = tournament.
|
|
670
|
+
const totalPoints = tournament.standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
671
|
+
const totalEfficiency = tournament.standings.reduce((sum, s) => sum + (s.efficiency || 0), 0);
|
|
672
|
+
const allPoints = tournament.standings.map((s) => s.totalPoints || 0);
|
|
348
673
|
return {
|
|
349
674
|
tournament,
|
|
350
675
|
playerCount,
|
|
@@ -355,44 +680,364 @@ async function getTournamentStats(id) {
|
|
|
355
680
|
};
|
|
356
681
|
}
|
|
357
682
|
|
|
358
|
-
// src/
|
|
359
|
-
async function
|
|
360
|
-
|
|
683
|
+
// src/rounds.ts
|
|
684
|
+
async function createRound(data) {
|
|
685
|
+
return prisma.round.create({
|
|
686
|
+
data: {
|
|
687
|
+
...data,
|
|
688
|
+
isFinals: data.isFinals ?? false
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
async function createManyRounds(data) {
|
|
693
|
+
const roundsData = data.map((item) => ({
|
|
694
|
+
...item,
|
|
695
|
+
isFinals: item.isFinals ?? false
|
|
696
|
+
}));
|
|
697
|
+
return prisma.round.createMany({
|
|
698
|
+
data: roundsData
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
async function findRoundById(id, include) {
|
|
702
|
+
return prisma.round.findUnique({
|
|
703
|
+
where: { id },
|
|
704
|
+
include
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
async function findRoundByTournamentAndNumber(tournamentId, number, isFinals, include) {
|
|
708
|
+
return prisma.round.findUnique({
|
|
709
|
+
where: {
|
|
710
|
+
tournamentId_number_isFinals: {
|
|
711
|
+
tournamentId,
|
|
712
|
+
number,
|
|
713
|
+
isFinals
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
include
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
async function findRounds(options = {}) {
|
|
720
|
+
return prisma.round.findMany({
|
|
721
|
+
take: options.take,
|
|
722
|
+
skip: options.skip,
|
|
723
|
+
where: options.where,
|
|
724
|
+
orderBy: options.orderBy,
|
|
725
|
+
include: options.include
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
async function getTournamentRounds(tournamentId, options = {}) {
|
|
729
|
+
return findRounds({
|
|
730
|
+
...options,
|
|
731
|
+
where: { tournamentId },
|
|
732
|
+
orderBy: options.orderBy ?? [{ isFinals: "asc" }, { number: "asc" }]
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
async function getQualifyingRounds(tournamentId, options = {}) {
|
|
736
|
+
return findRounds({
|
|
737
|
+
...options,
|
|
738
|
+
where: { tournamentId, isFinals: false },
|
|
739
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
async function getFinalsRounds(tournamentId, options = {}) {
|
|
743
|
+
return findRounds({
|
|
744
|
+
...options,
|
|
745
|
+
where: { tournamentId, isFinals: true },
|
|
746
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
async function updateRound(id, data) {
|
|
750
|
+
return prisma.round.update({
|
|
751
|
+
where: { id },
|
|
752
|
+
data
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
async function deleteRound(id) {
|
|
756
|
+
return prisma.round.delete({
|
|
757
|
+
where: { id }
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
async function deleteRoundsByTournament(tournamentId) {
|
|
761
|
+
return prisma.round.deleteMany({
|
|
762
|
+
where: { tournamentId }
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
async function countRounds(where) {
|
|
766
|
+
return prisma.round.count({ where });
|
|
767
|
+
}
|
|
768
|
+
async function getRoundWithMatches(id) {
|
|
769
|
+
return prisma.round.findUnique({
|
|
770
|
+
where: { id },
|
|
771
|
+
include: {
|
|
772
|
+
matches: {
|
|
773
|
+
include: {
|
|
774
|
+
entries: {
|
|
775
|
+
include: {
|
|
776
|
+
player: true
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
},
|
|
780
|
+
orderBy: {
|
|
781
|
+
number: "asc"
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/matches.ts
|
|
789
|
+
async function createMatch(data) {
|
|
790
|
+
return prisma.match.create({
|
|
791
|
+
data
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
async function createManyMatches(data) {
|
|
795
|
+
return prisma.match.createMany({
|
|
796
|
+
data
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
async function findMatchById(id, include) {
|
|
800
|
+
return prisma.match.findUnique({
|
|
801
|
+
where: { id },
|
|
802
|
+
include
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
async function findMatches(options = {}) {
|
|
806
|
+
return prisma.match.findMany({
|
|
807
|
+
take: options.take,
|
|
808
|
+
skip: options.skip,
|
|
809
|
+
where: options.where,
|
|
810
|
+
orderBy: options.orderBy,
|
|
811
|
+
include: options.include
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
async function getTournamentMatches(tournamentId, options = {}) {
|
|
815
|
+
return findMatches({
|
|
816
|
+
...options,
|
|
817
|
+
where: { tournamentId },
|
|
818
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
async function getRoundMatches(roundId, options = {}) {
|
|
822
|
+
return findMatches({
|
|
823
|
+
...options,
|
|
824
|
+
where: { roundId },
|
|
825
|
+
orderBy: options.orderBy ?? { number: "asc" }
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
async function updateMatch(id, data) {
|
|
829
|
+
return prisma.match.update({
|
|
830
|
+
where: { id },
|
|
831
|
+
data
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
async function deleteMatch(id) {
|
|
835
|
+
return prisma.match.delete({
|
|
836
|
+
where: { id }
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
async function deleteMatchesByTournament(tournamentId) {
|
|
840
|
+
return prisma.match.deleteMany({
|
|
841
|
+
where: { tournamentId }
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
async function deleteMatchesByRound(roundId) {
|
|
845
|
+
return prisma.match.deleteMany({
|
|
846
|
+
where: { roundId }
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
async function countMatches(where) {
|
|
850
|
+
return prisma.match.count({ where });
|
|
851
|
+
}
|
|
852
|
+
async function getMatchWithEntries(id) {
|
|
853
|
+
return prisma.match.findUnique({
|
|
854
|
+
where: { id },
|
|
855
|
+
include: {
|
|
856
|
+
entries: {
|
|
857
|
+
include: {
|
|
858
|
+
player: true
|
|
859
|
+
},
|
|
860
|
+
orderBy: {
|
|
861
|
+
position: "asc"
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
round: true
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
async function getPlayerTournamentMatches(playerId, tournamentId, include) {
|
|
869
|
+
return findMatches({
|
|
870
|
+
where: {
|
|
871
|
+
tournamentId,
|
|
872
|
+
entries: {
|
|
873
|
+
some: {
|
|
874
|
+
playerId
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
},
|
|
878
|
+
include: include ?? {
|
|
879
|
+
entries: {
|
|
880
|
+
include: {
|
|
881
|
+
player: true
|
|
882
|
+
}
|
|
883
|
+
},
|
|
884
|
+
round: true
|
|
885
|
+
},
|
|
886
|
+
orderBy: [{ round: { number: "asc" } }, { number: "asc" }]
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// src/entries.ts
|
|
891
|
+
async function createEntry(data) {
|
|
892
|
+
return prisma.entry.create({
|
|
893
|
+
data
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
async function createManyEntries(data) {
|
|
897
|
+
return prisma.entry.createMany({
|
|
898
|
+
data
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
async function findEntryById(id, include) {
|
|
902
|
+
return prisma.entry.findUnique({
|
|
903
|
+
where: { id },
|
|
904
|
+
include
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
async function findEntryByMatchAndPlayer(matchId, playerId, include) {
|
|
908
|
+
return prisma.entry.findUnique({
|
|
909
|
+
where: {
|
|
910
|
+
matchId_playerId: {
|
|
911
|
+
matchId,
|
|
912
|
+
playerId
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
include
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
async function findEntries(options = {}) {
|
|
919
|
+
return prisma.entry.findMany({
|
|
920
|
+
take: options.take,
|
|
921
|
+
skip: options.skip,
|
|
922
|
+
where: options.where,
|
|
923
|
+
orderBy: options.orderBy,
|
|
924
|
+
include: options.include
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
async function getMatchEntries(matchId, options = {}) {
|
|
928
|
+
return findEntries({
|
|
929
|
+
...options,
|
|
930
|
+
where: { matchId },
|
|
931
|
+
include: options.include ?? { player: true },
|
|
932
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
async function getPlayerEntries(playerId, options = {}) {
|
|
936
|
+
return findEntries({
|
|
937
|
+
...options,
|
|
938
|
+
where: { playerId },
|
|
939
|
+
include: options.include ?? { match: { include: { round: true, tournament: true } } }
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
async function getPlayerTournamentEntries(playerId, tournamentId, include) {
|
|
943
|
+
return findEntries({
|
|
944
|
+
where: {
|
|
945
|
+
playerId,
|
|
946
|
+
match: {
|
|
947
|
+
tournamentId
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
include: include ?? {
|
|
951
|
+
match: {
|
|
952
|
+
include: {
|
|
953
|
+
round: true,
|
|
954
|
+
entries: {
|
|
955
|
+
include: {
|
|
956
|
+
player: true
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
async function updateEntry(id, data) {
|
|
965
|
+
return prisma.entry.update({
|
|
966
|
+
where: { id },
|
|
967
|
+
data
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
async function deleteEntry(id) {
|
|
971
|
+
return prisma.entry.delete({
|
|
972
|
+
where: { id }
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
async function deleteEntriesByMatch(matchId) {
|
|
976
|
+
return prisma.entry.deleteMany({
|
|
977
|
+
where: { matchId }
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
async function countEntries(where) {
|
|
981
|
+
return prisma.entry.count({ where });
|
|
982
|
+
}
|
|
983
|
+
async function getPlayerEntryStats(playerId) {
|
|
984
|
+
const entries = await getPlayerEntries(playerId);
|
|
985
|
+
if (entries.length === 0) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
const wins = entries.filter((e) => e.result === "WIN").length;
|
|
989
|
+
const losses = entries.filter((e) => e.result === "LOSS").length;
|
|
990
|
+
const ties = entries.filter((e) => e.result === "TIE").length;
|
|
991
|
+
return {
|
|
992
|
+
totalMatches: entries.length,
|
|
993
|
+
wins,
|
|
994
|
+
losses,
|
|
995
|
+
ties,
|
|
996
|
+
winRate: wins / entries.length
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// src/standings.ts
|
|
1001
|
+
async function createStanding(data) {
|
|
1002
|
+
const standingData = {
|
|
361
1003
|
...data,
|
|
1004
|
+
isFinals: data.isFinals ?? false,
|
|
362
1005
|
decayedPoints: data.decayedPoints ?? data.totalPoints ?? 0
|
|
363
1006
|
};
|
|
364
|
-
return prisma.
|
|
365
|
-
data:
|
|
1007
|
+
return prisma.standing.create({
|
|
1008
|
+
data: standingData
|
|
366
1009
|
});
|
|
367
1010
|
}
|
|
368
|
-
async function
|
|
369
|
-
const
|
|
1011
|
+
async function createManyStandings(data) {
|
|
1012
|
+
const standingsData = data.map((item) => ({
|
|
370
1013
|
...item,
|
|
1014
|
+
isFinals: item.isFinals ?? false,
|
|
371
1015
|
decayedPoints: item.decayedPoints ?? item.totalPoints ?? 0
|
|
372
1016
|
}));
|
|
373
|
-
return prisma.
|
|
374
|
-
data:
|
|
1017
|
+
return prisma.standing.createMany({
|
|
1018
|
+
data: standingsData
|
|
375
1019
|
});
|
|
376
1020
|
}
|
|
377
|
-
async function
|
|
378
|
-
return prisma.
|
|
1021
|
+
async function findStandingById(id, include) {
|
|
1022
|
+
return prisma.standing.findUnique({
|
|
379
1023
|
where: { id },
|
|
380
1024
|
include
|
|
381
1025
|
});
|
|
382
1026
|
}
|
|
383
|
-
async function
|
|
384
|
-
return prisma.
|
|
1027
|
+
async function findStandingByPlayerAndTournament(playerId, tournamentId, isFinals, include) {
|
|
1028
|
+
return prisma.standing.findUnique({
|
|
385
1029
|
where: {
|
|
386
|
-
|
|
1030
|
+
playerId_tournamentId_isFinals: {
|
|
387
1031
|
playerId,
|
|
388
|
-
tournamentId
|
|
1032
|
+
tournamentId,
|
|
1033
|
+
isFinals
|
|
389
1034
|
}
|
|
390
1035
|
},
|
|
391
1036
|
include
|
|
392
1037
|
});
|
|
393
1038
|
}
|
|
394
|
-
async function
|
|
395
|
-
return prisma.
|
|
1039
|
+
async function findStandings(options = {}) {
|
|
1040
|
+
return prisma.standing.findMany({
|
|
396
1041
|
take: options.take,
|
|
397
1042
|
skip: options.skip,
|
|
398
1043
|
where: options.where,
|
|
@@ -400,45 +1045,85 @@ async function findResults(options = {}) {
|
|
|
400
1045
|
include: options.include
|
|
401
1046
|
});
|
|
402
1047
|
}
|
|
403
|
-
async function
|
|
404
|
-
return
|
|
1048
|
+
async function getPlayerStandings(playerId, options = {}) {
|
|
1049
|
+
return findStandings({
|
|
405
1050
|
...options,
|
|
406
1051
|
where: { playerId },
|
|
407
1052
|
include: { tournament: true, ...options.include },
|
|
408
1053
|
orderBy: { tournament: { date: "desc" } }
|
|
409
1054
|
});
|
|
410
1055
|
}
|
|
411
|
-
async function
|
|
412
|
-
return
|
|
1056
|
+
async function getTournamentStandings(tournamentId, options = {}) {
|
|
1057
|
+
return findStandings({
|
|
413
1058
|
...options,
|
|
414
1059
|
where: { tournamentId },
|
|
415
1060
|
include: { player: true, ...options.include },
|
|
416
|
-
orderBy: { position: "asc" }
|
|
1061
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
async function getQualifyingStandings(tournamentId, options = {}) {
|
|
1065
|
+
return findStandings({
|
|
1066
|
+
...options,
|
|
1067
|
+
where: { tournamentId, isFinals: false },
|
|
1068
|
+
include: { player: true, ...options.include },
|
|
1069
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
417
1070
|
});
|
|
418
1071
|
}
|
|
1072
|
+
async function getFinalsStandings(tournamentId, options = {}) {
|
|
1073
|
+
return findStandings({
|
|
1074
|
+
...options,
|
|
1075
|
+
where: { tournamentId, isFinals: true },
|
|
1076
|
+
include: { player: true, ...options.include },
|
|
1077
|
+
orderBy: options.orderBy ?? { position: "asc" }
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
async function getMergedStandings(tournamentId) {
|
|
1081
|
+
const [finals, qualifying] = await Promise.all([
|
|
1082
|
+
findStandings({
|
|
1083
|
+
where: { tournamentId, isFinals: true },
|
|
1084
|
+
orderBy: { position: "asc" },
|
|
1085
|
+
include: { player: true }
|
|
1086
|
+
}),
|
|
1087
|
+
findStandings({
|
|
1088
|
+
where: { tournamentId, isFinals: false },
|
|
1089
|
+
orderBy: { position: "asc" },
|
|
1090
|
+
include: { player: true }
|
|
1091
|
+
})
|
|
1092
|
+
]);
|
|
1093
|
+
const finalistIds = new Set(finals.map((s) => s.playerId));
|
|
1094
|
+
const nonFinalists = qualifying.filter((s) => !finalistIds.has(s.playerId));
|
|
1095
|
+
return [
|
|
1096
|
+
...finals.map((s) => ({ ...s, mergedPosition: s.position, isFinalist: true })),
|
|
1097
|
+
...nonFinalists.map((s, i) => ({
|
|
1098
|
+
...s,
|
|
1099
|
+
mergedPosition: finals.length + i + 1,
|
|
1100
|
+
isFinalist: false
|
|
1101
|
+
}))
|
|
1102
|
+
];
|
|
1103
|
+
}
|
|
419
1104
|
async function getPlayerTopFinishes(playerId, limit = 15) {
|
|
420
|
-
return
|
|
1105
|
+
return findStandings({
|
|
421
1106
|
where: { playerId },
|
|
422
1107
|
take: limit,
|
|
423
1108
|
include: { tournament: true },
|
|
424
1109
|
orderBy: { decayedPoints: "desc" }
|
|
425
1110
|
});
|
|
426
1111
|
}
|
|
427
|
-
async function
|
|
428
|
-
return prisma.
|
|
1112
|
+
async function updateStanding(id, data) {
|
|
1113
|
+
return prisma.standing.update({
|
|
429
1114
|
where: { id },
|
|
430
1115
|
data
|
|
431
1116
|
});
|
|
432
1117
|
}
|
|
433
|
-
async function
|
|
434
|
-
const
|
|
1118
|
+
async function updateStandingPoints(id, linearPoints, dynamicPoints, totalPoints) {
|
|
1119
|
+
const standing = await findStandingById(id, {
|
|
435
1120
|
tournament: true
|
|
436
1121
|
});
|
|
437
|
-
if (!
|
|
438
|
-
throw new Error(`
|
|
1122
|
+
if (!standing) {
|
|
1123
|
+
throw new Error(`Standing with id ${id} not found`);
|
|
439
1124
|
}
|
|
440
1125
|
const now = /* @__PURE__ */ new Date();
|
|
441
|
-
const tournamentDate =
|
|
1126
|
+
const tournamentDate = standing.tournament.date;
|
|
442
1127
|
const ageInDays = Math.floor((now.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
443
1128
|
const ageInYears = ageInDays / 365;
|
|
444
1129
|
let decayMultiplier = 0;
|
|
@@ -452,7 +1137,7 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
452
1137
|
decayMultiplier = 0;
|
|
453
1138
|
}
|
|
454
1139
|
const decayedPoints = totalPoints * decayMultiplier;
|
|
455
|
-
return
|
|
1140
|
+
return updateStanding(id, {
|
|
456
1141
|
linearPoints,
|
|
457
1142
|
dynamicPoints,
|
|
458
1143
|
totalPoints,
|
|
@@ -461,50 +1146,50 @@ async function updateResultPoints(id, linearPoints, dynamicPoints, totalPoints)
|
|
|
461
1146
|
decayedPoints
|
|
462
1147
|
});
|
|
463
1148
|
}
|
|
464
|
-
async function
|
|
465
|
-
return prisma.
|
|
1149
|
+
async function deleteStanding(id) {
|
|
1150
|
+
return prisma.standing.delete({
|
|
466
1151
|
where: { id }
|
|
467
1152
|
});
|
|
468
1153
|
}
|
|
469
|
-
async function
|
|
470
|
-
return prisma.
|
|
1154
|
+
async function deleteStandingsByTournament(tournamentId) {
|
|
1155
|
+
return prisma.standing.deleteMany({
|
|
471
1156
|
where: { tournamentId }
|
|
472
1157
|
});
|
|
473
1158
|
}
|
|
474
|
-
async function
|
|
475
|
-
return prisma.
|
|
1159
|
+
async function countStandings(where) {
|
|
1160
|
+
return prisma.standing.count({ where });
|
|
476
1161
|
}
|
|
477
1162
|
async function getPlayerStats(playerId) {
|
|
478
|
-
const
|
|
479
|
-
if (
|
|
1163
|
+
const standings = await getPlayerStandings(playerId);
|
|
1164
|
+
if (standings.length === 0) {
|
|
480
1165
|
return null;
|
|
481
1166
|
}
|
|
482
|
-
const totalPoints =
|
|
483
|
-
const totalDecayedPoints =
|
|
484
|
-
const averagePosition =
|
|
485
|
-
const averageEfficiency =
|
|
486
|
-
const firstPlaceFinishes =
|
|
487
|
-
const topThreeFinishes =
|
|
1167
|
+
const totalPoints = standings.reduce((sum, s) => sum + (s.totalPoints || 0), 0);
|
|
1168
|
+
const totalDecayedPoints = standings.reduce((sum, s) => sum + (s.decayedPoints || 0), 0);
|
|
1169
|
+
const averagePosition = standings.reduce((sum, s) => sum + s.position, 0) / standings.length;
|
|
1170
|
+
const averageEfficiency = standings.reduce((sum, s) => sum + (s.efficiency || 0), 0) / standings.length;
|
|
1171
|
+
const firstPlaceFinishes = standings.filter((s) => s.position === 1).length;
|
|
1172
|
+
const topThreeFinishes = standings.filter((s) => s.position <= 3).length;
|
|
488
1173
|
return {
|
|
489
|
-
totalEvents:
|
|
1174
|
+
totalEvents: standings.length,
|
|
490
1175
|
totalPoints,
|
|
491
1176
|
totalDecayedPoints,
|
|
492
|
-
averagePoints: totalPoints /
|
|
1177
|
+
averagePoints: totalPoints / standings.length,
|
|
493
1178
|
averagePosition,
|
|
494
1179
|
averageFinish: averagePosition,
|
|
495
1180
|
averageEfficiency,
|
|
496
1181
|
firstPlaceFinishes,
|
|
497
1182
|
topThreeFinishes,
|
|
498
|
-
bestFinish: Math.min(...
|
|
499
|
-
highestPoints: Math.max(...
|
|
1183
|
+
bestFinish: Math.min(...standings.map((s) => s.position)),
|
|
1184
|
+
highestPoints: Math.max(...standings.map((s) => s.totalPoints || 0))
|
|
500
1185
|
};
|
|
501
1186
|
}
|
|
502
1187
|
async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date()) {
|
|
503
|
-
const
|
|
1188
|
+
const standings = await findStandings({
|
|
504
1189
|
include: { tournament: true }
|
|
505
1190
|
});
|
|
506
|
-
const updates =
|
|
507
|
-
const tournamentDate =
|
|
1191
|
+
const updates = standings.map((standing) => {
|
|
1192
|
+
const tournamentDate = standing.tournament.date;
|
|
508
1193
|
const ageInDays = Math.floor(
|
|
509
1194
|
(referenceDate.getTime() - tournamentDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
510
1195
|
);
|
|
@@ -519,9 +1204,9 @@ async function recalculateTimeDecay(referenceDate = /* @__PURE__ */ new Date())
|
|
|
519
1204
|
} else {
|
|
520
1205
|
decayMultiplier = 0;
|
|
521
1206
|
}
|
|
522
|
-
const decayedPoints = (
|
|
523
|
-
return prisma.
|
|
524
|
-
where: { id:
|
|
1207
|
+
const decayedPoints = (standing.totalPoints || 0) * decayMultiplier;
|
|
1208
|
+
return prisma.standing.update({
|
|
1209
|
+
where: { id: standing.id },
|
|
525
1210
|
data: {
|
|
526
1211
|
ageInDays,
|
|
527
1212
|
decayMultiplier,
|
|
@@ -540,9 +1225,11 @@ async function createUser(data) {
|
|
|
540
1225
|
}
|
|
541
1226
|
async function createUserWithPlayer(userData, playerData) {
|
|
542
1227
|
return prisma.$transaction(async (tx) => {
|
|
1228
|
+
const playerNumber = await generateUniquePlayerNumber();
|
|
543
1229
|
const player = await tx.player.create({
|
|
544
1230
|
data: {
|
|
545
|
-
name: playerData.name
|
|
1231
|
+
name: playerData.name,
|
|
1232
|
+
playerNumber
|
|
546
1233
|
}
|
|
547
1234
|
});
|
|
548
1235
|
const user = await tx.user.create({
|
|
@@ -554,11 +1241,8 @@ async function createUserWithPlayer(userData, playerData) {
|
|
|
554
1241
|
player: {
|
|
555
1242
|
select: {
|
|
556
1243
|
id: true,
|
|
1244
|
+
playerNumber: true,
|
|
557
1245
|
name: true,
|
|
558
|
-
rating: true,
|
|
559
|
-
ratingDeviation: true,
|
|
560
|
-
ranking: true,
|
|
561
|
-
isRated: true,
|
|
562
1246
|
eventCount: true
|
|
563
1247
|
}
|
|
564
1248
|
}
|
|
@@ -586,11 +1270,8 @@ async function getUserWithPlayer(id) {
|
|
|
586
1270
|
player: {
|
|
587
1271
|
select: {
|
|
588
1272
|
id: true,
|
|
1273
|
+
playerNumber: true,
|
|
589
1274
|
name: true,
|
|
590
|
-
rating: true,
|
|
591
|
-
ratingDeviation: true,
|
|
592
|
-
ranking: true,
|
|
593
|
-
isRated: true,
|
|
594
1275
|
eventCount: true
|
|
595
1276
|
}
|
|
596
1277
|
}
|
|
@@ -608,11 +1289,8 @@ async function getUserByEmailWithPlayer(email) {
|
|
|
608
1289
|
player: {
|
|
609
1290
|
select: {
|
|
610
1291
|
id: true,
|
|
1292
|
+
playerNumber: true,
|
|
611
1293
|
name: true,
|
|
612
|
-
rating: true,
|
|
613
|
-
ratingDeviation: true,
|
|
614
|
-
ranking: true,
|
|
615
|
-
isRated: true,
|
|
616
1294
|
eventCount: true
|
|
617
1295
|
}
|
|
618
1296
|
}
|
|
@@ -653,11 +1331,8 @@ async function findUsers(params) {
|
|
|
653
1331
|
player: {
|
|
654
1332
|
select: {
|
|
655
1333
|
id: true,
|
|
1334
|
+
playerNumber: true,
|
|
656
1335
|
name: true,
|
|
657
|
-
rating: true,
|
|
658
|
-
ratingDeviation: true,
|
|
659
|
-
ranking: true,
|
|
660
|
-
isRated: true,
|
|
661
1336
|
eventCount: true
|
|
662
1337
|
}
|
|
663
1338
|
}
|
|
@@ -684,11 +1359,8 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
684
1359
|
player: {
|
|
685
1360
|
select: {
|
|
686
1361
|
id: true,
|
|
1362
|
+
playerNumber: true,
|
|
687
1363
|
name: true,
|
|
688
|
-
rating: true,
|
|
689
|
-
ratingDeviation: true,
|
|
690
|
-
ranking: true,
|
|
691
|
-
isRated: true,
|
|
692
1364
|
eventCount: true
|
|
693
1365
|
}
|
|
694
1366
|
}
|
|
@@ -697,65 +1369,468 @@ async function linkPlayerToUser(userId, playerId) {
|
|
|
697
1369
|
return user;
|
|
698
1370
|
});
|
|
699
1371
|
}
|
|
1372
|
+
|
|
1373
|
+
// src/api-keys.ts
|
|
1374
|
+
var MAX_API_KEYS_PER_USER = 5;
|
|
1375
|
+
async function createApiKey(data) {
|
|
1376
|
+
return prisma.apiKey.create({ data });
|
|
1377
|
+
}
|
|
1378
|
+
async function findApiKeyById(id) {
|
|
1379
|
+
return prisma.apiKey.findUnique({ where: { id } });
|
|
1380
|
+
}
|
|
1381
|
+
async function findApiKeysByPrefix(keyPrefix) {
|
|
1382
|
+
const keys = await prisma.apiKey.findMany({
|
|
1383
|
+
where: { keyPrefix },
|
|
1384
|
+
include: {
|
|
1385
|
+
user: {
|
|
1386
|
+
select: {
|
|
1387
|
+
id: true,
|
|
1388
|
+
email: true,
|
|
1389
|
+
role: true
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
return keys;
|
|
1395
|
+
}
|
|
1396
|
+
async function getUserApiKeys(userId) {
|
|
1397
|
+
return prisma.apiKey.findMany({
|
|
1398
|
+
where: { userId },
|
|
1399
|
+
select: {
|
|
1400
|
+
id: true,
|
|
1401
|
+
name: true,
|
|
1402
|
+
keyPrefix: true,
|
|
1403
|
+
expiresAt: true,
|
|
1404
|
+
lastUsedAt: true,
|
|
1405
|
+
createdAt: true
|
|
1406
|
+
},
|
|
1407
|
+
orderBy: { createdAt: "desc" }
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
async function countUserApiKeys(userId) {
|
|
1411
|
+
return prisma.apiKey.count({ where: { userId } });
|
|
1412
|
+
}
|
|
1413
|
+
async function updateApiKeyLastUsed(id) {
|
|
1414
|
+
await prisma.apiKey.update({
|
|
1415
|
+
where: { id },
|
|
1416
|
+
data: { lastUsedAt: /* @__PURE__ */ new Date() }
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
async function deleteApiKey(id) {
|
|
1420
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1421
|
+
}
|
|
1422
|
+
async function deleteUserApiKey(id, userId) {
|
|
1423
|
+
const key = await prisma.apiKey.findFirst({
|
|
1424
|
+
where: { id, userId }
|
|
1425
|
+
});
|
|
1426
|
+
if (!key) {
|
|
1427
|
+
return null;
|
|
1428
|
+
}
|
|
1429
|
+
return prisma.apiKey.delete({ where: { id } });
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// src/locations.ts
|
|
1433
|
+
async function createLocation(data) {
|
|
1434
|
+
return prisma.location.create({
|
|
1435
|
+
data
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
async function findLocationById(id, include) {
|
|
1439
|
+
return prisma.location.findUnique({
|
|
1440
|
+
where: { id },
|
|
1441
|
+
include
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
async function findLocationByExternalId(externalId, include) {
|
|
1445
|
+
return prisma.location.findUnique({
|
|
1446
|
+
where: { externalId },
|
|
1447
|
+
include
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
async function findLocations(options = {}) {
|
|
1451
|
+
return prisma.location.findMany({
|
|
1452
|
+
take: options.take,
|
|
1453
|
+
skip: options.skip,
|
|
1454
|
+
where: options.where,
|
|
1455
|
+
orderBy: options.orderBy,
|
|
1456
|
+
include: options.include
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
async function searchLocations(query, limit = 20) {
|
|
1460
|
+
return findLocations({
|
|
1461
|
+
take: limit,
|
|
1462
|
+
where: {
|
|
1463
|
+
OR: [
|
|
1464
|
+
{ name: { contains: query, mode: "insensitive" } },
|
|
1465
|
+
{ city: { contains: query, mode: "insensitive" } }
|
|
1466
|
+
]
|
|
1467
|
+
},
|
|
1468
|
+
orderBy: { name: "asc" }
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
async function updateLocation(id, data) {
|
|
1472
|
+
return prisma.location.update({
|
|
1473
|
+
where: { id },
|
|
1474
|
+
data
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
async function deleteLocation(id) {
|
|
1478
|
+
return prisma.location.delete({
|
|
1479
|
+
where: { id }
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
async function countLocations(where) {
|
|
1483
|
+
return prisma.location.count({ where });
|
|
1484
|
+
}
|
|
1485
|
+
async function getLocationWithTournaments(id) {
|
|
1486
|
+
return prisma.location.findUnique({
|
|
1487
|
+
where: { id },
|
|
1488
|
+
include: {
|
|
1489
|
+
tournaments: {
|
|
1490
|
+
orderBy: {
|
|
1491
|
+
date: "desc"
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// src/blog-posts.ts
|
|
1499
|
+
var defaultInclude = {
|
|
1500
|
+
author: {
|
|
1501
|
+
select: { id: true, email: true }
|
|
1502
|
+
},
|
|
1503
|
+
tags: {
|
|
1504
|
+
select: { id: true, name: true, slug: true }
|
|
1505
|
+
}
|
|
1506
|
+
};
|
|
1507
|
+
async function createBlogPost(data) {
|
|
1508
|
+
const { tagIds, ...postData } = data;
|
|
1509
|
+
return prisma.blogPost.create({
|
|
1510
|
+
data: {
|
|
1511
|
+
...postData,
|
|
1512
|
+
tags: tagIds?.length ? { connect: tagIds.map((id) => ({ id })) } : void 0
|
|
1513
|
+
},
|
|
1514
|
+
include: defaultInclude
|
|
1515
|
+
});
|
|
1516
|
+
}
|
|
1517
|
+
async function findBlogPostById(id) {
|
|
1518
|
+
return prisma.blogPost.findUnique({
|
|
1519
|
+
where: { id },
|
|
1520
|
+
include: defaultInclude
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
async function findBlogPostBySlug(slug) {
|
|
1524
|
+
return prisma.blogPost.findUnique({
|
|
1525
|
+
where: { slug },
|
|
1526
|
+
include: defaultInclude
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
async function findBlogPosts(options = {}) {
|
|
1530
|
+
return prisma.blogPost.findMany({
|
|
1531
|
+
take: options.take,
|
|
1532
|
+
skip: options.skip,
|
|
1533
|
+
where: options.where,
|
|
1534
|
+
orderBy: options.orderBy ?? { createdAt: "desc" },
|
|
1535
|
+
include: options.include ?? defaultInclude
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
async function findPublishedBlogPosts(options = {}) {
|
|
1539
|
+
const { tagSlug, ...restOptions } = options;
|
|
1540
|
+
const where = {
|
|
1541
|
+
status: "PUBLISHED",
|
|
1542
|
+
publishedAt: { not: null },
|
|
1543
|
+
...tagSlug && {
|
|
1544
|
+
tags: {
|
|
1545
|
+
some: { slug: tagSlug }
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
return findBlogPosts({
|
|
1550
|
+
...restOptions,
|
|
1551
|
+
where,
|
|
1552
|
+
orderBy: options.orderBy ?? { publishedAt: "desc" }
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
async function searchBlogPosts(query, limit = 20, publishedOnly = true) {
|
|
1556
|
+
const where = {
|
|
1557
|
+
OR: [
|
|
1558
|
+
{ title: { contains: query, mode: "insensitive" } },
|
|
1559
|
+
{ excerpt: { contains: query, mode: "insensitive" } }
|
|
1560
|
+
],
|
|
1561
|
+
...publishedOnly && {
|
|
1562
|
+
status: "PUBLISHED",
|
|
1563
|
+
publishedAt: { not: null }
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
return findBlogPosts({
|
|
1567
|
+
take: limit,
|
|
1568
|
+
where,
|
|
1569
|
+
orderBy: { publishedAt: "desc" }
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
async function updateBlogPost(id, data) {
|
|
1573
|
+
const { tagIds, ...postData } = data;
|
|
1574
|
+
return prisma.blogPost.update({
|
|
1575
|
+
where: { id },
|
|
1576
|
+
data: {
|
|
1577
|
+
...postData,
|
|
1578
|
+
// If tagIds is provided, replace all tags
|
|
1579
|
+
...tagIds !== void 0 && {
|
|
1580
|
+
tags: {
|
|
1581
|
+
set: tagIds.map((tagId) => ({ id: tagId }))
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
},
|
|
1585
|
+
include: defaultInclude
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
async function deleteBlogPost(id) {
|
|
1589
|
+
return prisma.blogPost.delete({
|
|
1590
|
+
where: { id }
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
async function countBlogPosts(where) {
|
|
1594
|
+
return prisma.blogPost.count({ where });
|
|
1595
|
+
}
|
|
1596
|
+
async function countPublishedBlogPosts(tagSlug) {
|
|
1597
|
+
return countBlogPosts({
|
|
1598
|
+
status: "PUBLISHED",
|
|
1599
|
+
publishedAt: { not: null },
|
|
1600
|
+
...tagSlug && {
|
|
1601
|
+
tags: {
|
|
1602
|
+
some: { slug: tagSlug }
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// src/blog-tags.ts
|
|
1609
|
+
async function createBlogTag(data) {
|
|
1610
|
+
return prisma.blogTag.create({
|
|
1611
|
+
data
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
async function findBlogTagById(id) {
|
|
1615
|
+
return prisma.blogTag.findUnique({
|
|
1616
|
+
where: { id }
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
async function findBlogTagBySlug(slug) {
|
|
1620
|
+
return prisma.blogTag.findUnique({
|
|
1621
|
+
where: { slug }
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
async function findBlogTags(options = {}) {
|
|
1625
|
+
return prisma.blogTag.findMany({
|
|
1626
|
+
take: options.take,
|
|
1627
|
+
skip: options.skip,
|
|
1628
|
+
where: options.where,
|
|
1629
|
+
orderBy: options.orderBy ?? { name: "asc" },
|
|
1630
|
+
include: options.include
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
async function searchBlogTags(query, limit = 20) {
|
|
1634
|
+
return findBlogTags({
|
|
1635
|
+
take: limit,
|
|
1636
|
+
where: {
|
|
1637
|
+
name: { contains: query, mode: "insensitive" }
|
|
1638
|
+
},
|
|
1639
|
+
orderBy: { name: "asc" }
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
async function updateBlogTag(id, data) {
|
|
1643
|
+
return prisma.blogTag.update({
|
|
1644
|
+
where: { id },
|
|
1645
|
+
data
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
async function deleteBlogTag(id) {
|
|
1649
|
+
return prisma.blogTag.delete({
|
|
1650
|
+
where: { id }
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
async function countBlogTags(where) {
|
|
1654
|
+
return prisma.blogTag.count({ where });
|
|
1655
|
+
}
|
|
1656
|
+
async function getBlogTagWithPostCount(id) {
|
|
1657
|
+
return prisma.blogTag.findUnique({
|
|
1658
|
+
where: { id },
|
|
1659
|
+
include: {
|
|
1660
|
+
_count: {
|
|
1661
|
+
select: { posts: true }
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
async function getBlogTagsWithPostCounts() {
|
|
1667
|
+
return prisma.blogTag.findMany({
|
|
1668
|
+
include: {
|
|
1669
|
+
_count: {
|
|
1670
|
+
select: { posts: true }
|
|
1671
|
+
}
|
|
1672
|
+
},
|
|
1673
|
+
orderBy: { name: "asc" }
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
700
1676
|
// Annotate the CommonJS export names for ESM import in node:
|
|
701
1677
|
0 && (module.exports = {
|
|
1678
|
+
MAX_API_KEYS_PER_USER,
|
|
1679
|
+
applyRDDecayForInactivePlayers,
|
|
702
1680
|
connect,
|
|
1681
|
+
countBlogPosts,
|
|
1682
|
+
countBlogTags,
|
|
1683
|
+
countEntries,
|
|
1684
|
+
countLocations,
|
|
1685
|
+
countMatches,
|
|
1686
|
+
countOpprPlayerRankings,
|
|
1687
|
+
countOpprRankingHistory,
|
|
703
1688
|
countPlayers,
|
|
704
|
-
|
|
1689
|
+
countPublishedBlogPosts,
|
|
1690
|
+
countRounds,
|
|
1691
|
+
countStandings,
|
|
705
1692
|
countTournaments,
|
|
1693
|
+
countUserApiKeys,
|
|
706
1694
|
countUsers,
|
|
707
|
-
|
|
1695
|
+
createApiKey,
|
|
1696
|
+
createBlogPost,
|
|
1697
|
+
createBlogTag,
|
|
1698
|
+
createEntry,
|
|
1699
|
+
createLocation,
|
|
1700
|
+
createManyEntries,
|
|
1701
|
+
createManyMatches,
|
|
1702
|
+
createManyRounds,
|
|
1703
|
+
createManyStandings,
|
|
1704
|
+
createMatch,
|
|
1705
|
+
createOpprPlayerRanking,
|
|
1706
|
+
createOpprRankingHistory,
|
|
708
1707
|
createPlayer,
|
|
709
|
-
|
|
1708
|
+
createRound,
|
|
1709
|
+
createStanding,
|
|
710
1710
|
createTournament,
|
|
711
1711
|
createUser,
|
|
712
1712
|
createUserWithPlayer,
|
|
1713
|
+
deleteApiKey,
|
|
1714
|
+
deleteBlogPost,
|
|
1715
|
+
deleteBlogTag,
|
|
1716
|
+
deleteEntriesByMatch,
|
|
1717
|
+
deleteEntry,
|
|
1718
|
+
deleteLocation,
|
|
1719
|
+
deleteMatch,
|
|
1720
|
+
deleteMatchesByRound,
|
|
1721
|
+
deleteMatchesByTournament,
|
|
1722
|
+
deleteOpprPlayerRanking,
|
|
713
1723
|
deletePlayer,
|
|
714
|
-
|
|
715
|
-
|
|
1724
|
+
deleteRound,
|
|
1725
|
+
deleteRoundsByTournament,
|
|
1726
|
+
deleteStanding,
|
|
1727
|
+
deleteStandingsByTournament,
|
|
716
1728
|
deleteTournament,
|
|
717
1729
|
deleteUser,
|
|
1730
|
+
deleteUserApiKey,
|
|
718
1731
|
disconnect,
|
|
1732
|
+
findApiKeyById,
|
|
1733
|
+
findApiKeysByPrefix,
|
|
1734
|
+
findBlogPostById,
|
|
1735
|
+
findBlogPostBySlug,
|
|
1736
|
+
findBlogPosts,
|
|
1737
|
+
findBlogTagById,
|
|
1738
|
+
findBlogTagBySlug,
|
|
1739
|
+
findBlogTags,
|
|
1740
|
+
findEntries,
|
|
1741
|
+
findEntryById,
|
|
1742
|
+
findEntryByMatchAndPlayer,
|
|
1743
|
+
findLocationByExternalId,
|
|
1744
|
+
findLocationById,
|
|
1745
|
+
findLocations,
|
|
1746
|
+
findMatchById,
|
|
1747
|
+
findMatches,
|
|
1748
|
+
findOpprPlayerRankingById,
|
|
1749
|
+
findOpprPlayerRankingByPlayerId,
|
|
1750
|
+
findOpprPlayerRankings,
|
|
719
1751
|
findPlayerByExternalId,
|
|
720
1752
|
findPlayerById,
|
|
1753
|
+
findPlayerByPlayerNumber,
|
|
721
1754
|
findPlayerByUserEmail,
|
|
722
1755
|
findPlayers,
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
1756
|
+
findPublishedBlogPosts,
|
|
1757
|
+
findRoundById,
|
|
1758
|
+
findRoundByTournamentAndNumber,
|
|
1759
|
+
findRounds,
|
|
1760
|
+
findStandingById,
|
|
1761
|
+
findStandingByPlayerAndTournament,
|
|
1762
|
+
findStandings,
|
|
726
1763
|
findTournamentByExternalId,
|
|
727
1764
|
findTournamentById,
|
|
728
1765
|
findTournaments,
|
|
729
1766
|
findUserByEmail,
|
|
730
1767
|
findUserById,
|
|
731
1768
|
findUsers,
|
|
1769
|
+
generateUniquePlayerNumber,
|
|
1770
|
+
getBlogTagWithPostCount,
|
|
1771
|
+
getBlogTagsWithPostCounts,
|
|
1772
|
+
getFinalsRounds,
|
|
1773
|
+
getFinalsStandings,
|
|
1774
|
+
getLatestOpprRankingHistory,
|
|
1775
|
+
getLocationWithTournaments,
|
|
732
1776
|
getMajorTournaments,
|
|
733
|
-
|
|
1777
|
+
getMatchEntries,
|
|
1778
|
+
getMatchWithEntries,
|
|
1779
|
+
getMergedStandings,
|
|
1780
|
+
getOpprRankingHistory,
|
|
1781
|
+
getOpprRankingHistoryByDateRange,
|
|
1782
|
+
getOrCreateOpprPlayerRanking,
|
|
1783
|
+
getPlayerEntries,
|
|
1784
|
+
getPlayerEntryStats,
|
|
1785
|
+
getPlayerStandings,
|
|
734
1786
|
getPlayerStats,
|
|
735
1787
|
getPlayerTopFinishes,
|
|
1788
|
+
getPlayerTournamentEntries,
|
|
1789
|
+
getPlayerTournamentMatches,
|
|
736
1790
|
getPlayerWithResults,
|
|
737
|
-
|
|
1791
|
+
getQualifyingRounds,
|
|
1792
|
+
getQualifyingStandings,
|
|
1793
|
+
getRatedOpprPlayers,
|
|
738
1794
|
getRecentTournaments,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
1795
|
+
getRoundMatches,
|
|
1796
|
+
getRoundWithMatches,
|
|
1797
|
+
getTopPlayersByOpprRanking,
|
|
1798
|
+
getTopPlayersByOpprRating,
|
|
1799
|
+
getTournamentMatches,
|
|
1800
|
+
getTournamentRounds,
|
|
1801
|
+
getTournamentStandings,
|
|
742
1802
|
getTournamentStats,
|
|
1803
|
+
getTournamentWithMatches,
|
|
743
1804
|
getTournamentWithResults,
|
|
744
1805
|
getTournamentsByBoosterType,
|
|
745
1806
|
getTournamentsByDateRange,
|
|
1807
|
+
getUserApiKeys,
|
|
746
1808
|
getUserByEmailWithPlayer,
|
|
747
1809
|
getUserWithPlayer,
|
|
1810
|
+
isValidPlayerNumber,
|
|
748
1811
|
linkPlayerToUser,
|
|
749
1812
|
prisma,
|
|
750
1813
|
recalculateTimeDecay,
|
|
1814
|
+
searchBlogPosts,
|
|
1815
|
+
searchBlogTags,
|
|
1816
|
+
searchLocations,
|
|
751
1817
|
searchPlayers,
|
|
752
1818
|
searchTournaments,
|
|
753
1819
|
testConnection,
|
|
1820
|
+
updateApiKeyLastUsed,
|
|
1821
|
+
updateBlogPost,
|
|
1822
|
+
updateBlogTag,
|
|
1823
|
+
updateEntry,
|
|
1824
|
+
updateLocation,
|
|
1825
|
+
updateMatch,
|
|
1826
|
+
updateOpprPlayerRanking,
|
|
1827
|
+
updateOpprRatingAfterTournament,
|
|
754
1828
|
updatePlayer,
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1829
|
+
updateRound,
|
|
1830
|
+
updateStanding,
|
|
1831
|
+
updateStandingPoints,
|
|
758
1832
|
updateTournament,
|
|
759
1833
|
updateUser,
|
|
760
|
-
updateUserRefreshToken
|
|
1834
|
+
updateUserRefreshToken,
|
|
1835
|
+
updateWorldRankings
|
|
761
1836
|
});
|