Spaces:
Running
Running
Return five annotated player skill ratings
Browse files- src/server.ts +24 -10
src/server.ts
CHANGED
|
@@ -107,6 +107,28 @@ const meanPlayerScore = (values: number[]) => values.length
|
|
| 107 |
? values.reduce((sum, value) => sum + value, 0) / values.length * 10
|
| 108 |
: null;
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
export async function accountRoleFor(userId: string) {
|
| 111 |
const { data, error } = await supabaseAdmin.from("users").select("account_role").eq("user_id", userId).maybeSingle();
|
| 112 |
if (error) throw new Error(`Unable to load account permissions: ${error.message}`);
|
|
@@ -522,21 +544,13 @@ app.get("/api/players-overview/:playerId", async (req, res, next) => {
|
|
| 522 |
const ownSessions = (sessions ?? []).filter((row) => row.player_id === playerId.data);
|
| 523 |
const ownActions = (actions ?? []).filter((row) => ownSessions.some((session) => session.session_id === row.session_id));
|
| 524 |
const ownScores = ownActions.map((row) => scoreMap.get(row.action_id)).filter((score): score is number => score != null && Number.isFinite(score));
|
| 525 |
-
const byAction =
|
| 526 |
-
const matching = ownActions.filter((row) => row.type === type);
|
| 527 |
-
const values = matching.map((row) => scoreMap.get(row.action_id)).filter((score): score is number => score != null && Number.isFinite(score));
|
| 528 |
-
return { axis: type, score: meanPlayerScore(values), samples: values.length };
|
| 529 |
-
});
|
| 530 |
const sessionPlayerById = new Map((sessions ?? []).map((row) => [row.session_id, row.player_id]));
|
| 531 |
const squadActions = (actions ?? []).filter((action) => {
|
| 532 |
const ownerId = sessionPlayerById.get(action.session_id);
|
| 533 |
return Boolean(ownerId) && ownerId !== playerId.data;
|
| 534 |
});
|
| 535 |
-
const squadByAction =
|
| 536 |
-
const matching = squadActions.filter((action) => action.type === type);
|
| 537 |
-
const values = matching.map((action) => scoreMap.get(action.action_id)).filter((score): score is number => score != null && Number.isFinite(score));
|
| 538 |
-
return { axis: type, score: meanPlayerScore(values), samples: values.length };
|
| 539 |
-
});
|
| 540 |
const player = overview?.find((row) => row.player_id === playerId.data);
|
| 541 |
if (!player) return res.status(404).json({ error: "Player not found." });
|
| 542 |
const sessionsForPlayer = ownSessions.map((row) => {
|
|
|
|
| 107 |
? values.reduce((sum, value) => sum + value, 0) / values.length * 10
|
| 108 |
: null;
|
| 109 |
|
| 110 |
+
const PLAYER_SKILL_CATEGORIES = [
|
| 111 |
+
{ axis: "Pass", aliases: ["pass", "passing"] },
|
| 112 |
+
{ axis: "Shoot", aliases: ["shoot", "shooting", "shot"] },
|
| 113 |
+
{ axis: "Receive", aliases: ["receive", "receiving", "reception"] },
|
| 114 |
+
{ axis: "Dribbling", aliases: ["dribble", "dribbling"] },
|
| 115 |
+
{ axis: "Pace", aliases: ["pace"] },
|
| 116 |
+
] as const;
|
| 117 |
+
|
| 118 |
+
function playerSkillRatings(
|
| 119 |
+
actions: Array<{ action_id: string; type: string }>,
|
| 120 |
+
scoreByAction: Map<string, number | null>,
|
| 121 |
+
) {
|
| 122 |
+
return PLAYER_SKILL_CATEGORIES.map(({ axis, aliases }) => {
|
| 123 |
+
const names = new Set<string>(aliases);
|
| 124 |
+
const scores = actions
|
| 125 |
+
.filter(({ type }) => names.has(type.trim().toLowerCase().replace(/[^a-z0-9]/g, "")))
|
| 126 |
+
.map(({ action_id }) => scoreByAction.get(action_id))
|
| 127 |
+
.filter((score): score is number => score != null && Number.isFinite(score));
|
| 128 |
+
return { axis, score: meanPlayerScore(scores), samples: scores.length };
|
| 129 |
+
});
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
export async function accountRoleFor(userId: string) {
|
| 133 |
const { data, error } = await supabaseAdmin.from("users").select("account_role").eq("user_id", userId).maybeSingle();
|
| 134 |
if (error) throw new Error(`Unable to load account permissions: ${error.message}`);
|
|
|
|
| 544 |
const ownSessions = (sessions ?? []).filter((row) => row.player_id === playerId.data);
|
| 545 |
const ownActions = (actions ?? []).filter((row) => ownSessions.some((session) => session.session_id === row.session_id));
|
| 546 |
const ownScores = ownActions.map((row) => scoreMap.get(row.action_id)).filter((score): score is number => score != null && Number.isFinite(score));
|
| 547 |
+
const byAction = playerSkillRatings(ownActions, scoreMap);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
const sessionPlayerById = new Map((sessions ?? []).map((row) => [row.session_id, row.player_id]));
|
| 549 |
const squadActions = (actions ?? []).filter((action) => {
|
| 550 |
const ownerId = sessionPlayerById.get(action.session_id);
|
| 551 |
return Boolean(ownerId) && ownerId !== playerId.data;
|
| 552 |
});
|
| 553 |
+
const squadByAction = playerSkillRatings(squadActions, scoreMap);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
const player = overview?.find((row) => row.player_id === playerId.data);
|
| 555 |
if (!player) return res.status(404).json({ error: "Player not found." });
|
| 556 |
const sessionsForPlayer = ownSessions.map((row) => {
|