5
0
mirror of https://github.com/AJMicke/KickerELO.git synced 2026-03-11 21:41:02 +01:00

Add admin panel, input checks

This commit is contained in:
Anton Micke
2025-02-07 17:11:15 +01:00
parent f25c9a33a2
commit ff17b0444c
18 changed files with 175 additions and 176 deletions

View File

@@ -0,0 +1,7 @@
package org.kickerelo.kickerelo.exception;
public class InvalidDataException extends RuntimeException {
public InvalidDataException(String message) {
super(message);
}
}

View File

@@ -29,10 +29,11 @@ public class KickerAppLayout extends AppLayout {
RouterLink playerList = new RouterLink("Spielerliste", PlayerListView.class);
RouterLink graph1vs1 = new RouterLink("Graph 1 vs 1", Graph1vs1View.class);
RouterLink graph2vs2 = new RouterLink("Graph 2 vs 2", Graph2vs2View.class);
RouterLink admin = new RouterLink("Verwaltung", AdminView.class);
Tabs tabs = new Tabs(new Tab(enter1vs1), new Tab(enter2vs2), new Tab(playerList), new Tab(graph1vs1), new Tab(graph2vs2));
Tabs tabs = new Tabs(new Tab(enter1vs1), new Tab(enter2vs2), new Tab(playerList), new Tab(graph1vs1), new Tab(graph2vs2), new Tab(admin));
tabs.setOrientation(Tabs.Orientation.VERTICAL);
addToDrawer(tabs);
}

View File

@@ -1,55 +0,0 @@
package org.kickerelo.kickerelo.model;
public class ResultInfo1vs1 {
float oldEloWinner;
float oldEloLoser;
float newEloWinner;
float newEloLoser;
short loserGoals;
public ResultInfo1vs1(float oldEloWinner, short loserGoals, float oldEloLoser) {
this.oldEloWinner = oldEloWinner;
this.loserGoals = loserGoals;
this.oldEloLoser = oldEloLoser;
}
public float getOldEloWinner() {
return oldEloWinner;
}
public void setOldEloWinner(float oldEloWinner) {
this.oldEloWinner = oldEloWinner;
}
public float getOldEloLoser() {
return oldEloLoser;
}
public void setOldEloLoser(float oldEloLoser) {
this.oldEloLoser = oldEloLoser;
}
public float getNewEloWinner() {
return newEloWinner;
}
public void setNewEloWinner(float newEloWinner) {
this.newEloWinner = newEloWinner;
}
public float getNewEloLoser() {
return newEloLoser;
}
public void setNewEloLoser(float newEloLoser) {
this.newEloLoser = newEloLoser;
}
public short getLoserGoals() {
return loserGoals;
}
public void setLoserGoals(short loserGoals) {
this.loserGoals = loserGoals;
}
}

View File

@@ -1,93 +0,0 @@
package org.kickerelo.kickerelo.model;
public class ResultInfo2vs2 {
float oldEloWinnerFront;
float oldEloWinnerBack;
float oldEloLoserFront;
float oldEloLoserBack;
float newEloWinnerFront;
float newEloWinnerBack;
float newEloLoserFront;
float newEloLoserBack;
short loserGoals;
public ResultInfo2vs2(float oldEloWinnerFront, float oldEloWinnerBack, float oldEloLoserFront, float oldEloLoserBack, short loserGoals) {
this.oldEloWinnerFront = oldEloWinnerFront;
this.oldEloWinnerBack = oldEloWinnerBack;
this.oldEloLoserFront = oldEloLoserFront;
this.oldEloLoserBack = oldEloLoserBack;
this.loserGoals = loserGoals;
}
public float getOldEloWinnerFront() {
return oldEloWinnerFront;
}
public void setOldEloWinnerFront(float oldEloWinnerFront) {
this.oldEloWinnerFront = oldEloWinnerFront;
}
public float getOldEloWinnerBack() {
return oldEloWinnerBack;
}
public void setOldEloWinnerBack(float oldEloWinnerBack) {
this.oldEloWinnerBack = oldEloWinnerBack;
}
public float getOldEloLoserFront() {
return oldEloLoserFront;
}
public void setOldEloLoserFront(float oldEloLoserFront) {
this.oldEloLoserFront = oldEloLoserFront;
}
public float getOldEloLoserBack() {
return oldEloLoserBack;
}
public void setOldEloLoserBack(float oldEloLoserBack) {
this.oldEloLoserBack = oldEloLoserBack;
}
public float getNewEloWinnerFront() {
return newEloWinnerFront;
}
public void setNewEloWinnerFront(float newEloWinnerFront) {
this.newEloWinnerFront = newEloWinnerFront;
}
public float getNewEloWinnerBack() {
return newEloWinnerBack;
}
public void setNewEloWinnerBack(float newEloWinnerBack) {
this.newEloWinnerBack = newEloWinnerBack;
}
public float getNewEloLoserFront() {
return newEloLoserFront;
}
public void setNewEloLoserFront(float newEloLoserFront) {
this.newEloLoserFront = newEloLoserFront;
}
public float getNewEloLoserBack() {
return newEloLoserBack;
}
public void setNewEloLoserBack(float newEloLoserBack) {
this.newEloLoserBack = newEloLoserBack;
}
public short getLoserGoals() {
return loserGoals;
}
public void setLoserGoals(short loserGoals) {
this.loserGoals = loserGoals;
}
}

View File

@@ -1,5 +1,8 @@
package org.kickerelo.kickerelo.repository;
import java.util.List;
import java.util.stream.Stream;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

View File

@@ -4,6 +4,8 @@ import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.stream.Stream;
@Repository
public interface Ergebnis2vs2Repository extends JpaRepository<Ergebnis2vs2, Long> {
}

View File

@@ -1,20 +1,32 @@
package org.kickerelo.kickerelo.service;
import org.kickerelo.kickerelo.model.ResultInfo1vs1;
import org.kickerelo.kickerelo.model.ResultInfo2vs2;
import org.kickerelo.kickerelo.data.Spieler;
import org.springframework.stereotype.Service;
@Service
public class EloCalculationService {
public void updateElo1vs1(ResultInfo1vs1 result) {
result.setNewEloWinner(result.getOldEloWinner() + 1);
result.setNewEloLoser(result.getOldEloLoser() - 1);
final float initialElo1vs1 = 1500;
final float initialElo2vs2 = 1500;
public void updateElo1vs1(Spieler gewinner, Spieler verlierer, short toreVerlierer) {
gewinner.setElo1vs1(gewinner.getElo1vs1() + 10 - toreVerlierer);
verlierer.setElo1vs1(verlierer.getElo1vs1() - 10 + toreVerlierer);
}
public void updateElo2vs2(ResultInfo2vs2 result) {
result.setNewEloWinnerFront(result.getOldEloWinnerFront() + 1);
result.setNewEloWinnerBack(result.getOldEloWinnerBack() + 1);
result.setNewEloLoserFront(result.getOldEloLoserFront() - 1);
result.setNewEloLoserBack(result.getOldEloLoserBack() - 1);
public void updateElo2vs2(Spieler gewinnerVorn, Spieler gewinnerHinten, Spieler verliererVorn, Spieler verliererHinten, short toreVerlierer) {
gewinnerVorn.setElo2vs2(gewinnerVorn.getElo2vs2() + 10 - toreVerlierer);
gewinnerHinten.setElo2vs2(gewinnerHinten.getElo2vs2() + 10 - toreVerlierer);
verliererVorn.setElo2vs2(verliererVorn.getElo2vs2() - 10 + toreVerlierer);
verliererHinten.setElo2vs2(verliererHinten.getElo2vs2());
}
public float getInitialElo1vs1() {
return initialElo1vs1;
}
public float getInitialElo2vs2() {
return initialElo2vs2;
}
}

View File

@@ -1,21 +1,24 @@
package org.kickerelo.kickerelo.service;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.model.ResultInfo1vs1;
import org.kickerelo.kickerelo.model.ResultInfo2vs2;
import org.kickerelo.kickerelo.repository.Ergebnis1vs1Repository;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import org.kickerelo.kickerelo.repository.SpielerRepository;
import org.kickerelo.kickerelo.util.Ergebnis1vs1TimeComparator;
import org.kickerelo.kickerelo.util.Ergebnis2vs2TimeComparator;
import org.kickerelo.kickerelo.util.Spieler1vs1EloComparator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Stream;
@Service
public class KickerEloService {
@@ -45,6 +48,9 @@ public class KickerEloService {
if (gewinnerName.equals(verliererName)) {
throw new DuplicatePlayerException("winner and loser identical");
}
if (toreVerlierer > 9 || toreVerlierer < 0) {
throw new InvalidDataException("too many goals");
}
Spieler gewinner = spielerRepository.findByName(gewinnerName)
.orElseThrow(() -> new NoSuchPlayerException(gewinnerName));
@@ -57,11 +63,8 @@ public class KickerEloService {
ergebnis1vs1Repository.save(ergebnis);
ResultInfo1vs1 result = new ResultInfo1vs1(gewinner.getElo1vs1(), ergebnis.getToreVerlierer(), verlierer.getElo1vs1());
eloCalculationService.updateElo1vs1(result);
gewinner.setElo1vs1(result.getNewEloWinner());
eloCalculationService.updateElo1vs1(gewinner, verlierer, toreVerlierer);
spielerRepository.save(gewinner);
verlierer.setElo1vs1(result.getNewEloLoser());
spielerRepository.save(verlierer);
}
@@ -84,6 +87,10 @@ public class KickerEloService {
throw new DuplicatePlayerException("players must not be identical");
}
if (toreVerlierer > 9 || toreVerlierer < 0) {
throw new InvalidDataException("too many loser goals");
}
Spieler gewinnerVorn = spielerRepository.findByName(gewinnerNameVorn)
.orElseThrow(() -> new NoSuchPlayerException(gewinnerNameVorn));
@@ -100,22 +107,56 @@ public class KickerEloService {
ergebnis2vs2Repository.save(ergebnis);
ResultInfo2vs2 result = new ResultInfo2vs2(gewinnerVorn.getElo2vs2(), gewinnerHinten.getElo2vs2(), verliererVorn.getElo2vs2(), verliererHinten.getElo2vs2(), toreVerlierer);
eloCalculationService.updateElo2vs2(result);
gewinnerVorn.setElo2vs2(result.getNewEloWinnerFront());
eloCalculationService.updateElo2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
spielerRepository.save(gewinnerVorn);
gewinnerHinten.setElo2vs2(result.getNewEloWinnerBack());
spielerRepository.save(gewinnerHinten);
verliererVorn.setElo2vs2(result.getNewEloLoserFront());
spielerRepository.save(verliererVorn);
verliererHinten.setElo2vs2(result.getNewEloLoserBack());
spielerRepository.save(verliererHinten);
}
public void addSpieler(String name) {
if (name == null || name.isBlank()) {
throw new PlayerNameNotSetException("Leerer Name");
}
if (getSpielerNamen().contains(name)) {
throw new DuplicatePlayerException("players must not be identical");
}
Spieler spieler = new Spieler();
spieler.setName(name);
spieler.setElo1vs1(1500);
spieler.setElo1vs1(eloCalculationService.getInitialElo1vs1());
spieler.setElo2vs2(eloCalculationService.getInitialElo2vs2());
spielerRepository.save(spieler);
}
public void recalculateAll1vs1() {
HashMap<Integer, Spieler> players = new HashMap<>();
for (Spieler spieler : spielerRepository.findAll()) {
spieler.setElo1vs1(eloCalculationService.getInitialElo1vs1());
players.put(spieler.getId(), spieler);
}
Stream<Ergebnis1vs1> results = ergebnis1vs1Repository.findAll().stream().sorted(new Ergebnis1vs1TimeComparator());
results.forEach(r -> {
eloCalculationService.updateElo1vs1(players.get(r.getGewinner().getId()),
players.get(r.getVerlierer().getId()),
r.getToreVerlierer());
});
spielerRepository.saveAll(players.values());
}
public void recalculateAll2vs2() {
HashMap<Integer, Spieler> players = new HashMap<>();
for (Spieler spieler : spielerRepository.findAll()) {
spieler.setElo2vs2(eloCalculationService.getInitialElo2vs2());
players.put(spieler.getId(), spieler);
}
Stream<Ergebnis2vs2> results = ergebnis2vs2Repository.findAll().stream().sorted(new Ergebnis2vs2TimeComparator());
results.forEach(r -> {
eloCalculationService.updateElo2vs2(players.get(r.getGewinnerVorn().getId()),
players.get(r.getGewinnerHinten().getId()),
players.get(r.getVerliererVorn().getId()),
players.get(r.getVerliererHinten().getId()),
r.getToreVerlierer());
});
spielerRepository.saveAll(players.values());
}
}

View File

@@ -0,0 +1,12 @@
package org.kickerelo.kickerelo.util;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
import java.util.Comparator;
public class Ergebnis1vs1TimeComparator implements Comparator<Ergebnis1vs1> {
@Override
public int compare(Ergebnis1vs1 o1, Ergebnis1vs1 o2) {
return o1.getTimestamp().compareTo(o2.getTimestamp());
}
}

View File

@@ -0,0 +1,12 @@
package org.kickerelo.kickerelo.util;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import java.util.Comparator;
public class Ergebnis2vs2TimeComparator implements Comparator<Ergebnis2vs2> {
@Override
public int compare(Ergebnis2vs2 o1, Ergebnis2vs2 o2) {
return o1.getTimestamp().compareTo(o2.getTimestamp());
}
}

View File

@@ -7,6 +7,6 @@ import java.util.Comparator;
public class Spieler1vs1EloComparator implements Comparator<Spieler> {
@Override
public int compare(Spieler o1, Spieler o2) {
return Float.compare(o1.getElo1vs1(), o2.getElo1vs1());
return Float.compare(o2.getElo1vs1(), o1.getElo1vs1());
}
}

View File

@@ -7,6 +7,6 @@ import java.util.Comparator;
public class Spieler2vs2EloComparator implements Comparator<Spieler> {
@Override
public int compare(Spieler o1, Spieler o2) {
return Float.compare(o1.getElo2vs2(), o2.getElo2vs2());
return Float.compare(o2.getElo2vs2(), o1.getElo2vs2());
}
}

View File

@@ -0,0 +1,50 @@
package org.kickerelo.kickerelo.views;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.service.KickerEloService;
@Route("admin")
public class AdminView extends VerticalLayout {
public AdminView(KickerEloService service) {
H2 subheader = new H2("Verwaltung");
TextField spielername = new TextField("Spielername");
spielername.addClassName("bordered");
// Button click listeners can be defined as lambda expressions
Button addPlayerButton = new Button("Spieler hinzufügen", e -> {
try {
service.addSpieler(spielername.getValue());
} catch (PlayerNameNotSetException err) {
Notification.show("Spielername darf nicht leer sein").addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
catch (DuplicatePlayerException err) {
Notification.show("Spieler existiert bereits").addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
Notification.show("Spieler gespeichert").addThemeVariants(NotificationVariant.LUMO_SUCCESS);
});
Button recalc1vs1Button = new Button("1 vs 1 Elo neu berechnen", e -> {
Notification.show("Recalculating Elo").addThemeVariants(NotificationVariant.LUMO_WARNING);
service.recalculateAll1vs1();
Notification.show("Recalculating finished").addThemeVariants(NotificationVariant.LUMO_SUCCESS);
});
Button recalc2vs2Button = new Button("2 vs 2 Elo neu berechnen", e -> {
Notification.show("Recalculating Elo").addThemeVariants(NotificationVariant.LUMO_WARNING);
service.recalculateAll2vs2();
Notification.show("Recalculating finished").addThemeVariants(NotificationVariant.LUMO_SUCCESS);
});
add(spielername, spielername, addPlayerButton, recalc1vs1Button, recalc2vs2Button);
}
}

View File

@@ -7,6 +7,7 @@ import com.github.appreciated.apexcharts.config.builder.YAxisBuilder;
import com.github.appreciated.apexcharts.config.chart.Type;
import com.github.appreciated.apexcharts.config.chart.builder.ZoomBuilder;
import com.github.appreciated.apexcharts.config.chart.zoom.ZoomType;
import com.github.appreciated.apexcharts.config.yaxis.Title;
import com.github.appreciated.apexcharts.helper.Series;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.util.Spieler1vs1EloComparator;

View File

@@ -9,6 +9,7 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.service.KickerEloService;
@@ -43,6 +44,8 @@ public class Enter1vs1View extends VerticalLayout {
Notification.show("Alle Spieler müssen paarweise verschieden sein").addThemeVariants(NotificationVariant.LUMO_ERROR);
} catch (PlayerNameNotSetException err) {
Notification.show("Alle Spieler müssen gesetzt sein").addThemeVariants(NotificationVariant.LUMO_ERROR);
} catch (InvalidDataException err) {
Notification.show("Verliertore falsch").addThemeVariants(NotificationVariant.LUMO_ERROR);
}
});

View File

@@ -9,6 +9,7 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.router.Route;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
import org.kickerelo.kickerelo.service.KickerEloService;
@@ -50,6 +51,8 @@ public class Enter2vs2View extends VerticalLayout {
Notification.show("Alle Spieler müssen paarweise verschieden sein").addThemeVariants(NotificationVariant.LUMO_ERROR);
} catch (PlayerNameNotSetException err) {
Notification.show("Alle Spieler müssen gesetzt sein").addThemeVariants(NotificationVariant.LUMO_ERROR);
} catch (InvalidDataException err) {
Notification.show("Verliertore falsch").addThemeVariants(NotificationVariant.LUMO_ERROR);
}
});

View File

@@ -14,7 +14,7 @@ public class Graph1vs1View extends VerticalLayout {
public Graph1vs1View(KickerEloService service) {
H2 subheading = new H2("1 vs 1 Elo");
chart1vs1 = new Chart1vs1(service.getSpielerEntities());
add(chart1vs1.build(), subheading);
add(subheading, chart1vs1.build());
}
}

View File

@@ -12,6 +12,6 @@ public class Graph2vs2View extends VerticalLayout {
public Graph2vs2View(KickerEloService service) {
H2 subheading = new H2("2 vs 2 Elo");
chart2vs2 = new Chart2vs2(service.getSpielerEntities());
add(chart2vs2.build(), subheading);
add(subheading, chart2vs2.build());
}
}