Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
[![GitHub last commit](https://img.shields.io/github/last-commit/softwaremagico/KendoTournamentManager)](https://github.com/softwaremagico/KendoTournamentManager)
[![Issues](https://img.shields.io/github/issues/softwaremagico/KendoTournamentManager.svg)](https://github.com/softwaremagico/KendoTournamentManager/issues)
[![CircleCI](https://circleci.com/gh/softwaremagico/KendoTournamentManager.svg?style=shield)](https://circleci.com/gh/softwaremagico/KendoTournamentManager)
[![Time](https://img.shields.io/badge/development-717.5h-blueviolet.svg)]()
[![Time](https://img.shields.io/badge/development-724.5h-blueviolet.svg)]()

[![Powered by](https://img.shields.io/badge/powered%20by%20java-orange.svg?logo=OpenJDK&logoColor=white)]()
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=kendo-tournament-backend&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=kendo-tournament-backend)
Expand Down
2 changes: 1 addition & 1 deletion backend/kendo-tournament-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<parent>
<artifactId>kendo-tournament-backend</artifactId>
<groupId>com.softwaremagico</groupId>
<version>2.18.1</version>
<version>2.19.0</version>
</parent>


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package com.softwaremagico.kt.core.controller;

/*-
* #%L
* Kendo Tournament Manager (Core)
* %%
* Copyright (C) 2021 - 2025 SoftwareMagico
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/

import com.softwaremagico.kt.core.controller.models.ClubDTO;
import com.softwaremagico.kt.core.controller.models.ParticipantDTO;
import com.softwaremagico.kt.core.controller.models.TeamDTO;
import com.softwaremagico.kt.core.converters.ClubConverter;
import com.softwaremagico.kt.core.converters.ParticipantConverter;
import com.softwaremagico.kt.core.converters.TeamConverter;
import com.softwaremagico.kt.core.converters.models.ClubConverterRequest;
import com.softwaremagico.kt.core.converters.models.ParticipantConverterRequest;
import com.softwaremagico.kt.core.converters.models.TeamConverterRequest;
import com.softwaremagico.kt.core.csv.ClubCsv;
import com.softwaremagico.kt.core.csv.ParticipantCsv;
import com.softwaremagico.kt.core.csv.TeamCsv;
import com.softwaremagico.kt.core.exceptions.InvalidCsvFieldException;
import com.softwaremagico.kt.core.providers.ClubProvider;
import com.softwaremagico.kt.core.providers.ParticipantProvider;
import com.softwaremagico.kt.core.providers.RoleProvider;
import com.softwaremagico.kt.core.providers.TeamProvider;
import com.softwaremagico.kt.logger.KendoTournamentLogger;
import com.softwaremagico.kt.persistence.entities.Club;
import com.softwaremagico.kt.persistence.entities.Participant;
import com.softwaremagico.kt.persistence.entities.Role;
import com.softwaremagico.kt.persistence.entities.Team;
import com.softwaremagico.kt.persistence.values.RoleType;
import org.springframework.stereotype.Controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Controller
public class CsvController {

private final ClubCsv clubCsv;
private final ClubProvider clubProvider;
private final ClubConverter clubConverter;

private final ParticipantCsv participantCsv;
private final ParticipantProvider participantProvider;
private final ParticipantConverter participantConverter;

private final TeamCsv teamCsv;
private final TeamProvider teamProvider;
private final TeamConverter teamConverter;

private final RoleProvider roleProvider;

public CsvController(ClubCsv clubCsv, ClubProvider clubProvider, ClubConverter clubConverter,
ParticipantCsv participantCsv, ParticipantProvider participantProvider, ParticipantConverter participantConverter,
TeamCsv teamCsv, TeamProvider teamProvider, TeamConverter teamConverter, RoleProvider roleProvider) {
this.clubCsv = clubCsv;
this.clubProvider = clubProvider;
this.clubConverter = clubConverter;
this.participantCsv = participantCsv;
this.participantProvider = participantProvider;
this.participantConverter = participantConverter;
this.teamCsv = teamCsv;
this.teamProvider = teamProvider;
this.teamConverter = teamConverter;
this.roleProvider = roleProvider;
}


public List<ClubDTO> addClubs(String csvContent, String uploadedBy) {
final List<Club> clubs = clubCsv.readCSV(csvContent);
final List<ClubDTO> failedClubs = new ArrayList<>();
for (Club club : clubs) {
try {
if (club.getName() != null && club.getCity() != null) {
final Optional<Club> storedClub = clubProvider.findBy(club.getName(), club.getCity());
if (storedClub.isPresent()) {
KendoTournamentLogger.warning(this.getClass(), "Club '" + club.getName() + "' from '"
+ club.getCity() + "' already exists. Will be updated.");
club.setId(storedClub.get().getId());
club.setUpdatedBy(uploadedBy);
} else {
club.setCreatedBy(uploadedBy);
}
clubProvider.save(club);
} else {
KendoTournamentLogger.warning(this.getClass(), "Club with invalid name and/or city.");
failedClubs.add(clubConverter.convert(new ClubConverterRequest(club)));
}
} catch (Exception e) {
KendoTournamentLogger.errorMessage(this.getClass(), e);
failedClubs.add(clubConverter.convert(new ClubConverterRequest(club)));
}
}
return failedClubs;
}


public List<ParticipantDTO> addParticipants(String csvContent, String uploadedBy) {
final List<Participant> participants = participantCsv.readCSV(csvContent);
final List<ParticipantDTO> failedParticipants = new ArrayList<>();
for (Participant participant : participants) {
try {
if (participantProvider.findByIdCard(participant.getIdCard()).isPresent()) {
KendoTournamentLogger.severe(this.getClass().getName(), "Participant '" + participant.getIdCard() + "' with name '"
+ participant.getName() + " " + participant.getLastname() + "' already exists.");
participant.setUpdatedBy(uploadedBy);
failedParticipants.add(participantConverter.convert(new ParticipantConverterRequest(participant)));
} else {
participant.setCreatedBy(uploadedBy);
participantProvider.save(participant);
}
} catch (Exception e) {
KendoTournamentLogger.severe(this.getClass().getName(), "Error when inserting '" + participant + "'.");
KendoTournamentLogger.errorMessage(this.getClass(), e);
failedParticipants.add(participantConverter.convert(new ParticipantConverterRequest(participant)));
}
}
return failedParticipants;
}


public List<TeamDTO> addTeams(String csvContent, String uploadedBy) {
final List<Team> teams = teamCsv.readCSV(csvContent);
final List<TeamDTO> failedTeams = new ArrayList<>();
for (Team team : teams) {
if (team.getTournament() == null) {
KendoTournamentLogger.severe(this.getClass().getName(), "Team '" + team.getName() + "' has assigned a tournament that does not exists.");
failedTeams.add(teamConverter.convert(new TeamConverterRequest(team)));
continue;
}
if (team.getMembers().size() > team.getTournament().getTeamSize()) {
throw new InvalidCsvFieldException(this.getClass(), "Team size is incorrect!", null);
}
setTeamMemberRoles(team);
try {
final Optional<Team> storedTeam = teamProvider.get(team.getTournament(), team.getName());
if (storedTeam.isPresent()) {
KendoTournamentLogger.warning(this.getClass(), "Team '" + team.getName() + "' already exists on tournament '"
+ team.getTournament().getName() + "'. Will be updated.");
team.setId(storedTeam.get().getId());
team.setUpdatedBy(uploadedBy);
} else {
team.setCreatedBy(uploadedBy);
}
teamProvider.save(team);
} catch (Exception e) {
KendoTournamentLogger.errorMessage(this.getClass(), e);
failedTeams.add(teamConverter.convert(new TeamConverterRequest(team)));
}
}
return failedTeams;
}

private void setTeamMemberRoles(Team team) {
if (team.getTournament() == null) {
return;
}
//Define roles for team members.
team.getMembers().forEach(member -> {
final Role role = roleProvider.get(team.getTournament(), member);
if (role == null) {
roleProvider.save(new Role(team.getTournament(), member, RoleType.COMPETITOR));
} else {
role.setRoleType(RoleType.COMPETITOR);
roleProvider.save(role);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.softwaremagico.kt.core.csv;

/*-
* #%L
* Kendo Tournament Manager (Core)
* %%
* Copyright (C) 2021 - 2025 SoftwareMagico
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/

import com.softwaremagico.kt.persistence.entities.Club;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class ClubCsv extends CsvReader<Club> {
private static final String NAME_HEADER = "name";
private static final String COUNTRY_HEADER = "country";
private static final String CITY_HEADER = "city";
private static final String ADDRESS_HEADER = "address";
private static final String EMAIL_HEADER = "email";
private static final String PHONE_HEADER = "phone";
private static final String WEB_HEADER = "web";

@Override
public List<Club> readCSV(String csvContent) {
final String[] headers = getHeaders(csvContent);
checkHeaders(headers, NAME_HEADER, COUNTRY_HEADER, CITY_HEADER, ADDRESS_HEADER, EMAIL_HEADER, PHONE_HEADER, WEB_HEADER);
final String[] content = getContent(csvContent);
final List<Club> clubs = new ArrayList<>();

final int nameIndex = getHeaderIndex(headers, NAME_HEADER);
final int countryIndex = getHeaderIndex(headers, COUNTRY_HEADER);
final int cityIndex = getHeaderIndex(headers, CITY_HEADER);
final int addressIndex = getHeaderIndex(headers, ADDRESS_HEADER);
final int emailIndex = getHeaderIndex(headers, EMAIL_HEADER);
final int phoneIndex = getHeaderIndex(headers, PHONE_HEADER);
final int webIndex = getHeaderIndex(headers, WEB_HEADER);


for (String clubLine : content) {
final Club club = new Club();
club.setName(getField(clubLine, nameIndex));
club.setCountry(getField(clubLine, countryIndex));
club.setCity(getField(clubLine, cityIndex));
club.setAddress(getField(clubLine, addressIndex));
club.setEmail(getField(clubLine, emailIndex));
club.setPhone(getField(clubLine, phoneIndex));
club.setWeb(getField(clubLine, webIndex));
clubs.add(club);
}
return clubs;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.softwaremagico.kt.core.csv;

/*-
* #%L
* Kendo Tournament Manager (Core)
* %%
* Copyright (C) 2021 - 2025 SoftwareMagico
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/

import com.softwaremagico.kt.core.exceptions.InvalidCsvFieldException;
import com.softwaremagico.kt.persistence.entities.Element;

import java.util.Arrays;
import java.util.List;

public abstract class CsvReader<E extends Element> {
private static final String CSV_SEPARATOR = ";";
private static final String LINE_SEPARATOR = "\\r?\\n";

public abstract List<E> readCSV(String csvContent);

protected void checkHeaders(String[] fileHeaders, String... elementHeaders) throws InvalidCsvFieldException {
for (String header : fileHeaders) {
if (getHeaderIndex(elementHeaders, header) < 0) {
throw new InvalidCsvFieldException(this.getClass(), "Invalid header '" + header + "'.", header);
}
}
}

public String[] getHeaders(String content) {
final String[] lines = content.replace("#", "").split(LINE_SEPARATOR);
if (lines.length > 1) {
return lines[0].split(CSV_SEPARATOR);
}
return new String[0];
}


public String[] getContent(String content) {
final String[] lines = content.split("\\r?\\n");
if (lines.length > 1) {
return Arrays.copyOfRange(lines, 1, lines.length);
}
return new String[0];
}

public int getHeaderIndex(String[] headers, String header) {
for (int i = 0; i < headers.length; i++) {
if (header != null && headers[i] != null && headers[i].trim().equalsIgnoreCase(header.trim())) {
return i;
}
}
return -1;
}

public String getField(String line, int index) {
if (index < 0) {
return null;
}
final String[] columns = line.split(CSV_SEPARATOR);
if (index < columns.length) {
return columns[index].trim();
}
return null;
}
}
Loading
Loading