feat(TournamentStats): refine internal tournament scoring and enhance UI features
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 36s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 36s
- Updated the scoring logic for internal tournaments to reflect percentage-based placements, improving clarity and fairness in rankings. - Refactored the `groupPointsFromRankings` function to `groupPercentFromRankings` for better readability and accuracy in calculations. - Enhanced the InternalTournamentStats component with a new PDF export feature and improved dialog positioning for better user experience. - Updated localization strings across multiple languages to align with the new scoring system and UI enhancements, ensuring better accessibility and understanding for users.
This commit is contained in:
@@ -59,7 +59,7 @@ export const getTournaments = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** Ranglisten interne Einzel-Turniere (Gruppen- + K.-o.-Punkte) */
|
||||
/** Ranglisten interne Einzel-Turniere (Gruppen-% + K.-o.-Bonus wie im Service) */
|
||||
export const getInternalTournamentStats = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubId } = req.params;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/**
|
||||
* Statistik-Punkte für interne Einzel-Turniere (Gruppenphase + K.-o.-Runde).
|
||||
* Statistik-Werte für interne Einzel-Turniere (Gruppenphase + K.-o.-Runde).
|
||||
*
|
||||
* Gruppe: 1. Platz = 100, 2. = 99, 3. = 98, … (Platzierung aus API: 1 = bestes Ergebnis).
|
||||
* Gleiche Platzierung = gleiche Punkte. Ab Platz 101 wird mit 0 gekappt.
|
||||
* K.-o.: Wer die K.-o.-Runde erreicht: höchste Gruppenpunkte dieser Klasse + 1,
|
||||
* danach +1 pro gewonnenes K.-o.-Spiel.
|
||||
* Gruppe: Platzierung als Prozent relativ zur Feldgröße N (alle mit Platzierung > 0),
|
||||
* Formel (N − pos) / (N − 1) × 100 für N ≥ 2 (1. = 100 %, Letzter = 0 %),
|
||||
* bei N = 1: 100 %. Nur Vereinsmitglieder werden gewertet; N zählt alle Platzierten.
|
||||
* K.-o.: Wer die K.-o.-Runde erreicht: höchster Gruppen-Prozentwert dieser Klasse + 1,
|
||||
* danach +1 pro gewonnenes K.-o.-Spiel (wie bisher, jetzt auf %-Skala).
|
||||
*/
|
||||
|
||||
export function parseWinnerFromMatch(match) {
|
||||
@@ -50,19 +51,32 @@ export function parseWinnerFromMatch(match) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<{ position: number, id: number }>} rankings – Platz 1 = bestes Ergebnis; id = Turnier-Mitglied-ID
|
||||
* @returns {Map<number, number>} tournamentMemberId -> Punkte (1. Platz 100, 2. 99, …)
|
||||
* @param {Array<{ position: number, id: number }>} rankings – zu wertende Spieler; id = Turnier-Mitglied-ID
|
||||
* @param {number} nInGroup – Anzahl Teilnehmer in der Gruppe mit Platzierung > 0 (inkl. Externe)
|
||||
* @returns {Map<number, number>} tournamentMemberId -> Prozent 0–100 (Gleitkomma)
|
||||
*/
|
||||
export function groupPointsFromRankings(rankings) {
|
||||
export function groupPercentFromRankings(rankings, nInGroup) {
|
||||
const map = new Map();
|
||||
if (!rankings || rankings.length === 0) return map;
|
||||
const N = Number(nInGroup);
|
||||
if (!rankings || rankings.length === 0 || !Number.isFinite(N) || N < 1) return map;
|
||||
|
||||
if (N === 1) {
|
||||
for (const r of rankings) {
|
||||
const id = r.id;
|
||||
if (id == null) continue;
|
||||
map.set(Number(id), 100);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
for (const r of rankings) {
|
||||
const pos = Number(r.position);
|
||||
if (!Number.isFinite(pos) || pos < 1) continue;
|
||||
const id = r.id;
|
||||
if (id == null) continue;
|
||||
const pts = Math.max(0, 101 - pos);
|
||||
map.set(Number(id), pts);
|
||||
const raw = ((N - pos) / (N - 1)) * 100;
|
||||
const pct = Math.max(0, Math.min(100, raw));
|
||||
map.set(Number(id), pct);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@@ -130,12 +144,15 @@ export function computeInternalSinglesStatsForTournament({
|
||||
});
|
||||
|
||||
const groupPointsByTm = new Map();
|
||||
let maxGroupPoints = 0;
|
||||
let maxGroupPct = 0;
|
||||
|
||||
for (const g of classGroups) {
|
||||
const parts = g.participants || [];
|
||||
if (parts.length === 0) continue;
|
||||
|
||||
const nInGroup = parts.filter((p) => Number(p.position) > 0).length;
|
||||
if (nInGroup < 1) continue;
|
||||
|
||||
const singlesRankings = parts
|
||||
.filter((p) => !p.isExternal && p.id && tmToMemberId.has(Number(p.id)))
|
||||
.map((p) => ({
|
||||
@@ -144,10 +161,10 @@ export function computeInternalSinglesStatsForTournament({
|
||||
}))
|
||||
.filter((r) => r.position > 0);
|
||||
|
||||
const m = groupPointsFromRankings(singlesRankings);
|
||||
const m = groupPercentFromRankings(singlesRankings, nInGroup);
|
||||
for (const [tmId, pts] of m) {
|
||||
groupPointsByTm.set(tmId, pts);
|
||||
if (pts > maxGroupPoints) maxGroupPoints = pts;
|
||||
if (pts > maxGroupPct) maxGroupPct = pts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +201,7 @@ export function computeInternalSinglesStatsForTournament({
|
||||
const inKo = playedKo.has(tmId);
|
||||
let total;
|
||||
if (inKo) {
|
||||
total = maxGroupPoints + 1 + wins;
|
||||
total = maxGroupPct + 1 + wins;
|
||||
} else {
|
||||
total = gPts;
|
||||
}
|
||||
@@ -199,7 +216,7 @@ export function computeInternalSinglesStatsForTournament({
|
||||
}
|
||||
|
||||
export default {
|
||||
groupPointsFromRankings,
|
||||
groupPercentFromRankings,
|
||||
computeInternalSinglesStatsForTournament,
|
||||
parseWinnerFromMatch,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user