refactor(clickTtTournamentRegistrationService): optimize tournament search and link retrieval logic

- Streamlined the tournament search process by caching the results page URL to avoid redundant searches.
- Enhanced the link retrieval mechanism to focus on table rows, improving accuracy in finding relevant tournament links.
- Updated scoring criteria for link selection to better prioritize relevant entries based on tournament and competition context.
This commit is contained in:
Torsten Schulz (local)
2026-03-11 21:10:52 +01:00
parent fb39aa0e8b
commit 8776c01e47

View File

@@ -152,11 +152,20 @@ class ClickTtTournamentRegistrationService {
trace
});
await clickTtPlayerRegistrationService._selectClubContext(page, associationMemberNumber, trace);
await this._openTournamentSearch(page, trace);
await this._filterTournamentSearch(page, tournament, trace);
const resultsPageUrl = page.url();
const processedCompetitions = [];
for (const group of competitionGroups) {
await this._openTournamentSearch(page, trace);
await this._filterTournamentSearch(page, tournament, trace);
if (page.url() !== resultsPageUrl) {
clickTtPlayerRegistrationService._trace(trace, 'step', {
name: 'open-search-results',
url: resultsPageUrl
});
await page.goto(resultsPageUrl, { waitUntil: 'domcontentloaded' });
await clickTtPlayerRegistrationService._dismissConsentOverlays(page, trace);
}
await this._openCompetitionRegistration(page, tournament, group.competition, trace);
await this._selectCompetitionMembers(page, group.entries, trace);
await this._openControlStep(page, trace);
@@ -244,79 +253,8 @@ class ClickTtTournamentRegistrationService {
}
const titleProfile = getTitleSearchProfile(tournament.title || '');
const contentRoot = page.locator('#content-row1, #content, #container').first();
const tournamentHref = await contentRoot.locator('a').evaluateAll((anchors, criteria) => {
const normalize = (value) => String(value || '')
.normalize('NFKC')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
const tokenize = (value) => normalize(value)
.split(/[^a-z0-9äöüß]+/i)
.map((token) => token.trim())
.filter((token) => token.length >= 3)
.filter((token) => !new Set([
'und', 'der', 'die', 'das', 'des', 'den', 'dem', 'von', 'für', 'mit',
'ein', 'eine', 'einer', 'eines', 'zum', 'zur', 'im', 'am', 'an'
]).has(token));
const wantedTitle = normalize(criteria.tournamentTitle);
const wantedTokens = Array.isArray(criteria.tournamentTokens) ? criteria.tournamentTokens : [];
const wantedTailTokens = Array.isArray(criteria.tournamentTailTokens) ? criteria.tournamentTailTokens : [];
let bestHref = null;
let bestScore = -1;
for (const anchor of anchors) {
const href = anchor.getAttribute('href');
if (!href) continue;
if (/dataProtection|legalNotice|logout|contact/i.test(href)) continue;
const text = normalize(anchor.textContent || '');
const contextText = normalize(anchor.closest('tr, li, div, td')?.textContent || '');
const combinedText = `${text} ${contextText}`.trim();
const combinedTokens = new Set(tokenize(combinedText));
let score = 0;
if (wantedTitle && combinedText.includes(wantedTitle)) score += 100;
if (text && wantedTitle && text.includes(wantedTitle)) score += 30;
for (const token of wantedTokens) {
if (combinedTokens.has(token)) score += 3;
}
for (const token of wantedTailTokens) {
if (combinedTokens.has(token)) score += 8;
}
if (/turnier|meisterschaft|rangliste|pokal/i.test(combinedText)) score += 10;
if (/anmelden|meldung|teilnehmer/i.test(combinedText)) score += 5;
if (score > bestScore) {
bestScore = score;
bestHref = href;
}
}
return bestHref;
}, {
tournamentTitle: tournament.title || '',
tournamentTokens: titleProfile.tokens,
tournamentTailTokens: titleProfile.tailTokens
});
if (tournamentHref) {
clickTtPlayerRegistrationService._trace(trace, 'step', {
name: 'click',
label: tournament.title || 'Turnier',
selector: `a[href="${tournamentHref}"]`
});
await contentRoot.locator(`a[href="${tournamentHref}"]`).first().click();
await page.waitForLoadState('domcontentloaded');
}
const href = await page.locator('#content-row1 a, #content a, #container a').evaluateAll((anchors, criteria) => {
const href = await page.locator('#content-row1 tr, #content tr, #container tr').evaluateAll((rows, criteria) => {
const normalize = (value) => String(value || '')
.normalize('NFKC')
.replace(/\s+/g, ' ')
@@ -339,19 +277,26 @@ class ClickTtTournamentRegistrationService {
let bestHref = null;
let bestScore = -1;
for (const anchor of anchors) {
for (const row of rows) {
const rowText = normalize(row.textContent || '');
if (!rowText.includes(wantedCompetition)) continue;
if (wantedTournament && !rowText.includes(wantedTournament)) continue;
const anchor = Array.from(row.querySelectorAll('a[href]')).find((candidate) =>
/anmeldung|anmelden|melden/i.test(normalize(candidate.textContent || ''))
);
if (!anchor) continue;
const href = anchor.getAttribute('href');
if (!href) continue;
if (/dataProtection|legalNotice|logout|contact/i.test(href)) continue;
const text = normalize(anchor.textContent || '');
if (!text.includes(wantedCompetition)) continue;
const contextText = normalize(anchor.closest('tr, li, div, td')?.textContent || '');
const combinedText = `${text} ${contextText}`.trim();
const linkText = normalize(anchor.textContent || '');
const combinedText = `${rowText} ${linkText}`.trim();
const combinedTokens = new Set(tokenize(combinedText));
let score = 0;
if (wantedTournament && combinedText.includes(wantedTournament)) score += 100;
if (/anmeldung|anmelden|melden/.test(linkText)) score += 20;
for (const token of wantedTournamentTokens) {
if (combinedTokens.has(token)) score += 3;