feat: implement team name normalization and enhance CSV import match detection
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 51s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 51s
This commit is contained in:
@@ -69,6 +69,50 @@ class MatchService {
|
||||
return `${seasonStartYear}/${seasonEndYear}`;
|
||||
}
|
||||
|
||||
normalizeTeamNameForMatch(name) {
|
||||
return String(name || '')
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.replace(/\s*\((?:j|m)\d+\)\s*/gi, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
async findExistingMatchForCSV(clubId, date, homeTeamName, guestTeamName) {
|
||||
if (Number.isNaN(date?.getTime?.())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const startOfDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
|
||||
const endOfDay = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
|
||||
const candidates = await Match.findAll({
|
||||
where: {
|
||||
clubId,
|
||||
date: { [Op.between]: [startOfDay, endOfDay] }
|
||||
},
|
||||
attributes: ['id', 'homeTeamId', 'guestTeamId', 'leagueId', 'locationId']
|
||||
});
|
||||
|
||||
const normalizedHome = this.normalizeTeamNameForMatch(homeTeamName);
|
||||
const normalizedGuest = this.normalizeTeamNameForMatch(guestTeamName);
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const [homeTeam, guestTeam] = await Promise.all([
|
||||
Team.findByPk(candidate.homeTeamId, { attributes: ['name'] }),
|
||||
Team.findByPk(candidate.guestTeamId, { attributes: ['name'] })
|
||||
]);
|
||||
const candidateHome = this.normalizeTeamNameForMatch(homeTeam?.name);
|
||||
const candidateGuest = this.normalizeTeamNameForMatch(guestTeam?.name);
|
||||
|
||||
if (candidateHome === normalizedHome && candidateGuest === normalizedGuest) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async importCSV(userToken, clubId, filePath) {
|
||||
await checkAccess(userToken, clubId);
|
||||
let seasonString = '';
|
||||
@@ -92,6 +136,43 @@ class MatchService {
|
||||
seasonId: season.id,
|
||||
},
|
||||
});
|
||||
const homeTeamName = this.formatTeamNameWithAgeClass(
|
||||
row['HeimMannschaft'],
|
||||
row['HeimMannschaftAltersklasse']
|
||||
);
|
||||
const guestTeamName = this.formatTeamNameWithAgeClass(
|
||||
row['GastMannschaft'],
|
||||
row['GastMannschaftAltersklasse']
|
||||
);
|
||||
let location = null;
|
||||
if (row['HalleName']?.trim()) {
|
||||
[location] = await Location.findOrCreate({
|
||||
where: {
|
||||
name: row['HalleName'],
|
||||
address: row['HalleStrasse'],
|
||||
city: row['HalleOrt'],
|
||||
zip: row['HallePLZ'],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Club teams already configured through MyTischtennis can be linked to
|
||||
// leagues with a different display name than the click-TT CSV "Staffel".
|
||||
// Update the existing match in that league so the schedule view receives
|
||||
// the venue instead of creating a disconnected duplicate in a CSV league.
|
||||
const existingMatch = await this.findExistingMatchForCSV(
|
||||
clubId,
|
||||
parsedDate,
|
||||
homeTeamName,
|
||||
guestTeamName
|
||||
);
|
||||
if (existingMatch && existingMatch.leagueId !== league.id) {
|
||||
if (location && existingMatch.locationId !== location.id) {
|
||||
await existingMatch.update({ locationId: location.id });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const homeTeamId = await this.getOrCreateTeamId(
|
||||
row['HeimMannschaft'],
|
||||
row['HeimMannschaftAltersklasse'],
|
||||
@@ -106,21 +187,13 @@ class MatchService {
|
||||
league.id,
|
||||
season.id
|
||||
);
|
||||
const [location] = await Location.findOrCreate({
|
||||
where: {
|
||||
name: row['HalleName'],
|
||||
address: row['HalleStrasse'],
|
||||
city: row['HalleOrt'],
|
||||
zip: row['HallePLZ'],
|
||||
},
|
||||
});
|
||||
matches.push({
|
||||
date: parsedDate,
|
||||
time: row['Termin'].split(' ')[1],
|
||||
homeTeamId: homeTeamId,
|
||||
guestTeamId: guestTeamId,
|
||||
leagueId: league.id,
|
||||
locationId: location.id,
|
||||
locationId: location?.id || null,
|
||||
clubId: clubId,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user