From f4411a4ee5c2b0a3f65359cc20e3b5b20fb15181 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 3 Nov 2025 09:35:04 +0100 Subject: [PATCH] Enhance club rankings retrieval and member TTR updates Updated the getClubRankings method in myTischtennisClient to include an optional parameter for current rankings. Modified memberService to fetch both current and quarterly TTR values, improving member data accuracy. Enhanced the TeamManagementView to display (Q)TTR values for better user visibility. Added error handling for QTTR retrieval, ensuring robustness in member updates. --- backend/clients/myTischtennisClient.js | 4 +- backend/services/memberService.js | 70 ++++++++++++++++------- frontend/src/views/TeamManagementView.vue | 20 +++++++ 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/backend/clients/myTischtennisClient.js b/backend/clients/myTischtennisClient.js index a7d9ad4..9b2206c 100644 --- a/backend/clients/myTischtennisClient.js +++ b/backend/clients/myTischtennisClient.js @@ -190,14 +190,14 @@ class MyTischtennisClient { * @param {string} fedNickname - Federation nickname (e.g., "HeTTV") * @returns {Promise} Rankings with player entries (all pages) */ - async getClubRankings(cookie, clubId, fedNickname) { + async getClubRankings(cookie, clubId, fedNickname, currentRanking = 'yes') { const allEntries = []; let currentPage = 0; let hasMorePages = true; while (hasMorePages) { - const endpoint = `/rankings/andro-rangliste?all-players=on&clubnr=${clubId}&fednickname=${fedNickname}&results-per-page=100&page=${currentPage}&_data=routes%2F%24`; + const endpoint = `/rankings/andro-rangliste?all-players=on&clubnr=${clubId}&fednickname=${fedNickname}¤t-ranking=${currentRanking}&results-per-page=100&page=${currentPage}&_data=routes%2F%24`; const result = await this.authenticatedRequest(endpoint, cookie, { diff --git a/backend/services/memberService.js b/backend/services/memberService.js index 10dab03..10af219 100644 --- a/backend/services/memberService.js +++ b/backend/services/memberService.js @@ -219,28 +219,42 @@ class MemberService { }; } - // 2. Rangliste vom Verein abrufen - const rankings = await myTischtennisClient.getClubRankings( + // 2. Ranglisten vom Verein abrufen + // TTR (aktuell) + const rankingsCurrent = await myTischtennisClient.getClubRankings( session.cookie, account.clubId, - account.fedNickname + account.fedNickname, + 'yes' + ); + + // QTTR (Quartalswert) + const rankingsQuarter = await myTischtennisClient.getClubRankings( + session.cookie, + account.clubId, + account.fedNickname, + 'no' ); - if (!rankings.success) { + if (!rankingsCurrent.success) { return { status: 500, response: { - message: rankings.error || 'Fehler beim Abrufen der Rangliste', + message: rankingsCurrent.error || 'Fehler beim Abrufen der Rangliste (aktuell)', updated: 0, errors: [], debug: { clubId: account.clubId, fedNickname: account.fedNickname, - rankingsError: rankings.error + rankingsError: rankingsCurrent.error } } }; } + if (!rankingsQuarter.success) { + // QTTR optional; nicht hart abbrechen, aber vermerken + console.warn('[updateRatingsFromMyTischtennis] - QTTR Abruf fehlgeschlagen:', rankingsQuarter.error); + } // 3. Alle Mitglieder des Clubs laden const members = await Member.findAll({ where: { clubId } }); @@ -250,29 +264,45 @@ class MemberService { const notFound = []; const matched = []; - // 4. Für jedes Mitglied TTR aktualisieren + // Maps für schnelleres Matching + const mapByName = (entries) => { + const m = new Map(); + for (const e of (entries || [])) { + const key = `${(e.firstname||'').toLowerCase()}|${(e.lastname||'').toLowerCase()}`; + if (!m.has(key)) m.set(key, e); + } + return m; + }; + + const currentMap = mapByName(rankingsCurrent.entries); + const quarterMap = rankingsQuarter.success ? mapByName(rankingsQuarter.entries) : new Map(); + + // 4. Für jedes Mitglied TTR und QTTR aktualisieren for (const member of members) { const firstName = member.firstName; const lastName = member.lastName; - - // Suche nach Match in rankings entries - const rankingEntry = rankings.entries.find(entry => - entry.firstname.toLowerCase() === firstName.toLowerCase() && - entry.lastname.toLowerCase() === lastName.toLowerCase() - ); - - if (rankingEntry) { + const key = `${(firstName||'').toLowerCase()}|${(lastName||'').toLowerCase()}`; + const rankingEntry = currentMap.get(key); + const rankingQuarterEntry = quarterMap.get(key); + + if (rankingEntry || rankingQuarterEntry) { try { - // fedRank ist der TTR-Wert const oldTtr = member.ttr; - member.ttr = rankingEntry.fedRank; - // TODO: QTTR muss von einem anderen Endpoint geholt werden + const oldQttr = member.qttr; + if (rankingEntry && typeof rankingEntry.fedRank === 'number') { + member.ttr = rankingEntry.fedRank; + } + if (rankingQuarterEntry && typeof rankingQuarterEntry.fedRank === 'number') { + member.qttr = rankingQuarterEntry.fedRank; + } await member.save(); updated++; matched.push({ name: `${firstName} ${lastName}`, oldTtr: oldTtr, - newTtr: rankingEntry.fedRank + newTtr: member.ttr, + oldQttr: oldQttr, + newQttr: member.qttr }); } catch (error) { console.error(`[updateRatingsFromMyTischtennis] - Error updating ${firstName} ${lastName}:`, error); @@ -304,7 +334,7 @@ class MemberService { matched: matched, notFound: notFound, errors: errors, - totalEntries: rankings.entries.length, + totalEntries: rankingsCurrent.entries.length, totalMembers: members.length } }; diff --git a/frontend/src/views/TeamManagementView.vue b/frontend/src/views/TeamManagementView.vue index 96c58d3..c141c5c 100644 --- a/frontend/src/views/TeamManagementView.vue +++ b/frontend/src/views/TeamManagementView.vue @@ -67,6 +67,7 @@ Spieler + (Q)TTR Saison {{ isSecondHalf ? 'Rückrunde' : 'Vorrunde' }} @@ -76,6 +77,12 @@ {{ stat.firstName }} {{ stat.lastName }} + + + {{ memberById[stat.memberId].qttr ?? memberById[stat.memberId].ttr ?? '–' }} + + + {{ stat.totalSeason }} {{ isSecondHalf ? stat.totalSecondHalf : stat.totalFirstHalf }} @@ -392,6 +399,7 @@ export default { // Player Stats const playerStats = ref([]); const loadingStats = ref(false); + const memberById = ref({}); // Computed const selectedClub = computed(() => store.state.currentClub); @@ -1012,6 +1020,18 @@ export default { loadingStats.value = true; try { + // Mitglieder mit (Q)TTR laden, um Werte anzuzeigen + try { + const membersResp = await apiClient.get(`/clubmembers/get/${selectedClub.value}/true`); + const map = {}; + for (const m of membersResp.data || []) { + map[m.id] = { ttr: m.ttr ?? null, qttr: m.qttr ?? null }; + } + memberById.value = map; + } catch (e) { + memberById.value = {}; + } + const response = await apiClient.get( `/matches/leagues/${selectedClub.value}/stats/${teamToEdit.value.leagueId}`, {