mirror of
https://github.com/AJMicke/KickerELO.git
synced 2026-03-11 13:31:02 +01:00
Merge remote-tracking branch 'origin/master' into sso
I hate large merges!
This commit is contained in:
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -5,10 +5,11 @@ updates:
|
|||||||
directory: "/" # Location of package manifests
|
directory: "/" # Location of package manifests
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
target-branch: "develop"
|
target-branch: "master"
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
target-branch: "develop"
|
target-branch: "master"
|
||||||
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@ target/
|
|||||||
!**/src/test/**/target/
|
!**/src/test/**/target/
|
||||||
data.mv.db
|
data.mv.db
|
||||||
src/main/bundles
|
src/main/bundles
|
||||||
|
node_modules
|
||||||
|
|
||||||
# for secrets
|
# for secrets
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ To build the project and run the application with the embedded H2 database, use
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
mvn clean package
|
mvn clean package
|
||||||
mvn spring-boot:run
|
mvn spring-boot:run -Ptest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
9
pom.xml
9
pom.xml
@@ -11,7 +11,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>3.4.4</version>
|
<version>3.5.0</version>
|
||||||
<relativePath/> <!-- lookup parent from repository -->
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<maven.compiler.source>23</maven.compiler.source>
|
<maven.compiler.source>23</maven.compiler.source>
|
||||||
<maven.compiler.target>23</maven.compiler.target>
|
<maven.compiler.target>23</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<vaadin.version>24.7.1</vaadin.version>
|
<vaadin.version>24.7.5</vaadin.version>
|
||||||
<spring.profiles.active>test</spring.profiles.active>
|
<spring.profiles.active>test</spring.profiles.active>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -46,11 +46,6 @@
|
|||||||
<groupId>com.vaadin</groupId>
|
<groupId>com.vaadin</groupId>
|
||||||
<artifactId>vaadin-spring-boot-starter</artifactId>
|
<artifactId>vaadin-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.appreciated</groupId>
|
|
||||||
<artifactId>apexcharts</artifactId>
|
|
||||||
<version>24.0.2</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mariadb.jdbc</groupId>
|
<groupId>org.mariadb.jdbc</groupId>
|
||||||
|
|||||||
@@ -69,4 +69,10 @@ public class Spieler {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Spieler)) return false;
|
||||||
|
return this.id == ((Spieler) o).id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.kickerelo.kickerelo.repository;
|
|||||||
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
|
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
@@ -12,7 +14,14 @@ public interface Ergebnis2vs2Repository extends JpaRepository<Ergebnis2vs2, Long
|
|||||||
int countByVerliererHinten(Spieler verliererHinten);
|
int countByVerliererHinten(Spieler verliererHinten);
|
||||||
int countByVerliererVorn(Spieler verlierVon);
|
int countByVerliererVorn(Spieler verlierVon);
|
||||||
|
|
||||||
int countByGewinnerVornOrGewinnerHinten(Spieler gewinnerVon, Spieler gewinnerHinten);
|
|
||||||
int countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(Spieler gewinnerVon, Spieler gewinnerHinten,
|
int countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(Spieler gewinnerVon, Spieler gewinnerHinten,
|
||||||
Spieler verliererVon, Spieler verliererHinten);
|
Spieler verliererVon, Spieler verliererHinten);
|
||||||
|
|
||||||
|
int countByGewinnerVornOrVerliererVorn(Spieler gewinnerVon, Spieler verliererHinten);
|
||||||
|
|
||||||
|
@Query("select avg(case when e.verliererVorn = :s then (e.toreVerlierer - 10) when e.gewinnerVorn = :s then (10 - e.toreVerlierer) end) from Ergebnis2vs2 e where e.gewinnerVorn = :s or e.verliererVorn = :s")
|
||||||
|
Float avgGoalDiffFront(@Param("s") Spieler s);
|
||||||
|
|
||||||
|
@Query("select avg(case when e.verliererHinten = :s then (e.toreVerlierer - 10) when e.gewinnerHinten = :s then (10 - e.toreVerlierer) end) from Ergebnis2vs2 e where e.gewinnerHinten = :s or e.verliererHinten = :s")
|
||||||
|
Float avgGoalDiffBack(@Param("s") Spieler s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.kickerelo.kickerelo.service;
|
package org.kickerelo.kickerelo.service;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
|
import org.kickerelo.kickerelo.util.EloChange1vs1;
|
||||||
|
import org.kickerelo.kickerelo.util.EloChange2vs2;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
|
||||||
@@ -18,7 +20,7 @@ public class EloCalculationService {
|
|||||||
* @param verlierer The entity representing the losing player
|
* @param verlierer The entity representing the losing player
|
||||||
* @param toreVerlierer The number of goals of the losing player
|
* @param toreVerlierer The number of goals of the losing player
|
||||||
*/
|
*/
|
||||||
public void updateElo1vs1(Spieler gewinner, Spieler verlierer, short toreVerlierer) {
|
public EloChange1vs1 updateElo1vs1(Spieler gewinner, Spieler verlierer, short toreVerlierer) {
|
||||||
final float baseK = 50;
|
final float baseK = 50;
|
||||||
final float reductionPerGoal = 0.1f * baseK;
|
final float reductionPerGoal = 0.1f * baseK;
|
||||||
final float finalK = baseK - (reductionPerGoal * toreVerlierer);
|
final float finalK = baseK - (reductionPerGoal * toreVerlierer);
|
||||||
@@ -28,6 +30,8 @@ public class EloCalculationService {
|
|||||||
|
|
||||||
gewinner.setElo1vs1(gewinner.getElo1vs1() + eloChange);
|
gewinner.setElo1vs1(gewinner.getElo1vs1() + eloChange);
|
||||||
verlierer.setElo1vs1(verlierer.getElo1vs1() - eloChange);
|
verlierer.setElo1vs1(verlierer.getElo1vs1() - eloChange);
|
||||||
|
|
||||||
|
return new EloChange1vs1(eloChange, -eloChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +42,7 @@ public class EloCalculationService {
|
|||||||
* @param verliererHinten The losing defensive player
|
* @param verliererHinten The losing defensive player
|
||||||
* @param toreVerlierer The number of goals of the losing teams
|
* @param toreVerlierer The number of goals of the losing teams
|
||||||
*/
|
*/
|
||||||
public void updateElo2vs2(Spieler gewinnerVorn, Spieler gewinnerHinten, Spieler verliererVorn, Spieler verliererHinten, short toreVerlierer) {
|
public EloChange2vs2 updateElo2vs2(Spieler gewinnerVorn, Spieler gewinnerHinten, Spieler verliererVorn, Spieler verliererHinten, short toreVerlierer) {
|
||||||
final float baseK = 100;
|
final float baseK = 100;
|
||||||
final double adjustedK = baseK * (1 - (0.1 * toreVerlierer));
|
final double adjustedK = baseK * (1 - (0.1 * toreVerlierer));
|
||||||
var totalWinnerElo = gewinnerVorn.getElo2vs2() + gewinnerHinten.getElo2vs2();
|
var totalWinnerElo = gewinnerVorn.getElo2vs2() + gewinnerHinten.getElo2vs2();
|
||||||
@@ -60,6 +64,8 @@ public class EloCalculationService {
|
|||||||
gewinnerHinten.setElo2vs2((float) (gewinnerHinten.getElo2vs2() + winner2EloChange));
|
gewinnerHinten.setElo2vs2((float) (gewinnerHinten.getElo2vs2() + winner2EloChange));
|
||||||
verliererVorn.setElo2vs2((float) (verliererVorn.getElo2vs2() + loser1EloChange));
|
verliererVorn.setElo2vs2((float) (verliererVorn.getElo2vs2() + loser1EloChange));
|
||||||
verliererHinten.setElo2vs2((float) (verliererHinten.getElo2vs2() + loser2EloChange));
|
verliererHinten.setElo2vs2((float) (verliererHinten.getElo2vs2() + loser2EloChange));
|
||||||
|
|
||||||
|
return new EloChange2vs2(winner1EloChange, winner2EloChange, loser1EloChange, loser2EloChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getInitialElo1vs1() {
|
public float getInitialElo1vs1() {
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.kickerelo.kickerelo.service;
|
||||||
|
|
||||||
|
import org.kickerelo.kickerelo.util.EloChange1vs1;
|
||||||
|
import org.kickerelo.kickerelo.util.EloChange2vs2;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class EloChangeTracker {
|
||||||
|
static Map<Long, EloChange1vs1> changes1vs1 = new HashMap<>();
|
||||||
|
static Map<Long, EloChange2vs2> changes2vs2 = new HashMap<>();
|
||||||
|
|
||||||
|
public static void put1vs1Result(Long gameId, EloChange1vs1 change) {
|
||||||
|
changes1vs1.put(gameId, change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void put2vs2Result(long gameId, EloChange2vs2 change) {
|
||||||
|
changes2vs2.put(gameId, change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EloChange1vs1 get1vs1Result(long gameId) {
|
||||||
|
var result = changes1vs1.get(gameId);
|
||||||
|
assert result != null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EloChange2vs2 get2vs2Result(long gameId) {
|
||||||
|
var result = changes2vs2.get(gameId);
|
||||||
|
assert result != null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package org.kickerelo.kickerelo.service;
|
|||||||
|
|
||||||
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
|
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
|
||||||
import org.kickerelo.kickerelo.exception.InvalidDataException;
|
import org.kickerelo.kickerelo.exception.InvalidDataException;
|
||||||
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
|
|
||||||
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
|
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
|
||||||
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
|
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
@@ -10,10 +9,11 @@ import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
|
|||||||
import org.kickerelo.kickerelo.repository.Ergebnis1vs1Repository;
|
import org.kickerelo.kickerelo.repository.Ergebnis1vs1Repository;
|
||||||
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
|
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
|
||||||
import org.kickerelo.kickerelo.repository.SpielerRepository;
|
import org.kickerelo.kickerelo.repository.SpielerRepository;
|
||||||
import org.kickerelo.kickerelo.util.Ergebnis1vs1TimeComparator;
|
import org.kickerelo.kickerelo.util.EloChange1vs1;
|
||||||
import org.kickerelo.kickerelo.util.Ergebnis2vs2TimeComparator;
|
import org.kickerelo.kickerelo.util.EloChange2vs2;
|
||||||
import org.kickerelo.kickerelo.util.Spieler1vs1EloComparator;
|
import org.kickerelo.kickerelo.util.comparator.Ergebnis1vs1TimeComparator;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.kickerelo.kickerelo.util.comparator.Ergebnis2vs2TimeComparator;
|
||||||
|
import org.kickerelo.kickerelo.util.comparator.SpielerNameComparator;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -25,14 +25,22 @@ import java.util.stream.Stream;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class KickerEloService {
|
public class KickerEloService {
|
||||||
@Autowired
|
private final Ergebnis1vs1Repository ergebnis1vs1Repository;
|
||||||
private Ergebnis1vs1Repository ergebnis1vs1Repository;
|
private final Ergebnis2vs2Repository ergebnis2vs2Repository;
|
||||||
@Autowired
|
private final SpielerRepository spielerRepository;
|
||||||
private Ergebnis2vs2Repository ergebnis2vs2Repository;
|
private final EloCalculationService eloCalculationService;
|
||||||
@Autowired
|
|
||||||
private SpielerRepository spielerRepository;
|
public KickerEloService(Ergebnis1vs1Repository ergebnis1vs1Repository,
|
||||||
@Autowired
|
Ergebnis2vs2Repository ergebnis2vs2Repository,
|
||||||
private EloCalculationService eloCalculationService;
|
SpielerRepository spielerRepository,
|
||||||
|
EloCalculationService eloCalculationService) {
|
||||||
|
this.ergebnis1vs1Repository = ergebnis1vs1Repository;
|
||||||
|
this.ergebnis2vs2Repository = ergebnis2vs2Repository;
|
||||||
|
this.spielerRepository = spielerRepository;
|
||||||
|
this.eloCalculationService = eloCalculationService;
|
||||||
|
recalculateAll1vs1();
|
||||||
|
recalculateAll2vs2();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return List of all player names sorted alphabetically
|
* @return List of all player names sorted alphabetically
|
||||||
@@ -45,68 +53,61 @@ public class KickerEloService {
|
|||||||
* @return List of all player entities sorted by 1 vs 1 ELO
|
* @return List of all player entities sorted by 1 vs 1 ELO
|
||||||
*/
|
*/
|
||||||
public List<Spieler> getSpielerEntities() {
|
public List<Spieler> getSpielerEntities() {
|
||||||
return spielerRepository.findAll().stream().sorted(new Spieler1vs1EloComparator()).toList();
|
return spielerRepository.findAll().stream().sorted(new SpielerNameComparator()).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter a result of a 1 vs 1 game
|
* Enter a result of a 1 vs 1 game
|
||||||
* @param gewinnerName The name of the winning player
|
* @param gewinner The winning player
|
||||||
* @param verliererName The name of the losing player
|
* @param verlierer The losing player
|
||||||
* @param toreVerlierer The number of goals of the loser
|
* @param toreVerlierer The number of goals of the loser
|
||||||
*/
|
*/
|
||||||
public void enterResult1vs1(String gewinnerName, String verliererName,
|
public void enterResult1vs1(Spieler gewinner, Spieler verlierer,
|
||||||
short toreVerlierer) {
|
short toreVerlierer) {
|
||||||
// Check if the inputs are valid
|
// Check if the inputs are valid
|
||||||
if (gewinnerName == null || verliererName == null) {
|
if (gewinner == null || verlierer == null) {
|
||||||
throw new PlayerNameNotSetException("Alle Namen müssen gesetzt sein");
|
throw new PlayerNameNotSetException("Alle Namen müssen gesetzt sein");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gewinnerName.equals(verliererName)) {
|
if (gewinner.equals(verlierer)) {
|
||||||
throw new DuplicatePlayerException("winner and loser identical");
|
throw new DuplicatePlayerException("winner and loser identical");
|
||||||
}
|
}
|
||||||
if (toreVerlierer > 9 || toreVerlierer < 0) {
|
if (toreVerlierer > 9 || toreVerlierer < 0) {
|
||||||
throw new InvalidDataException("too many goals");
|
throw new InvalidDataException("too many goals");
|
||||||
}
|
}
|
||||||
|
|
||||||
Spieler gewinner = spielerRepository.findByName(gewinnerName)
|
|
||||||
.orElseThrow(() -> new NoSuchPlayerException(gewinnerName));
|
|
||||||
|
|
||||||
Spieler verlierer = spielerRepository.findByName(verliererName)
|
|
||||||
.orElseThrow(() -> new NoSuchPlayerException(verliererName));
|
|
||||||
|
|
||||||
|
|
||||||
Ergebnis1vs1 ergebnis = new Ergebnis1vs1(gewinner, verlierer, toreVerlierer);
|
Ergebnis1vs1 ergebnis = new Ergebnis1vs1(gewinner, verlierer, toreVerlierer);
|
||||||
ergebnis1vs1Repository.save(ergebnis);
|
ergebnis = ergebnis1vs1Repository.save(ergebnis);
|
||||||
|
|
||||||
eloCalculationService.updateElo1vs1(gewinner, verlierer, toreVerlierer);
|
EloChange1vs1 change = eloCalculationService.updateElo1vs1(gewinner, verlierer, toreVerlierer);
|
||||||
|
EloChangeTracker.put1vs1Result(ergebnis.getId(), change);
|
||||||
spielerRepository.save(gewinner);
|
spielerRepository.save(gewinner);
|
||||||
spielerRepository.save(verlierer);
|
spielerRepository.save(verlierer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter the result of a 2 vs 2 game
|
* Enter the result of a 2 vs 2 game
|
||||||
* @param gewinnerNameVorn Name of the winning offensive player
|
* @param gewinnerVorn winning offensive player
|
||||||
* @param gewinnerNameHinten Name of the winning defensive player
|
* @param gewinnerHinten winning defensive player
|
||||||
* @param verliererNameVorn Name of the losing offensive player
|
* @param verliererVorn losing offensive player
|
||||||
* @param verliererNameHinten Name of the losing defensive player
|
* @param verliererHinten losing defensive player
|
||||||
* @param toreVerlierer Number of goals of the losing team
|
* @param toreVerlierer Number of goals of the losing team
|
||||||
*/
|
*/
|
||||||
public void enterResult2vs2(String gewinnerNameVorn, String gewinnerNameHinten,
|
public void enterResult2vs2(Spieler gewinnerVorn, Spieler gewinnerHinten,
|
||||||
String verliererNameVorn, String verliererNameHinten,
|
Spieler verliererVorn, Spieler verliererHinten,
|
||||||
short toreVerlierer) {
|
short toreVerlierer) {
|
||||||
// Check if the inputs are valid
|
// Check if the inputs are valid
|
||||||
if (gewinnerNameVorn == null || gewinnerNameHinten == null
|
if (gewinnerVorn == null || gewinnerHinten == null
|
||||||
|| verliererNameVorn == null || verliererNameHinten == null) {
|
|| verliererVorn == null || verliererHinten == null) {
|
||||||
throw new PlayerNameNotSetException("Alle Namen müssen gesetzt sein");
|
throw new PlayerNameNotSetException("Alle Namen müssen gesetzt sein");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gewinnerNameVorn.equals(gewinnerNameHinten) ||
|
if (gewinnerVorn.equals(gewinnerHinten) ||
|
||||||
gewinnerNameVorn.equals(verliererNameVorn) ||
|
gewinnerVorn.equals(verliererVorn) ||
|
||||||
gewinnerNameVorn.equals(verliererNameHinten) ||
|
gewinnerVorn.equals(verliererHinten) ||
|
||||||
gewinnerNameHinten.equals(verliererNameVorn) ||
|
gewinnerHinten.equals(verliererVorn) ||
|
||||||
gewinnerNameHinten.equals(verliererNameHinten) ||
|
gewinnerHinten.equals(verliererHinten) ||
|
||||||
verliererNameVorn.equals(verliererNameHinten)) {
|
verliererVorn.equals(verliererHinten)) {
|
||||||
throw new DuplicatePlayerException("players must not be identical");
|
throw new DuplicatePlayerException("players must not be identical");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,22 +115,11 @@ public class KickerEloService {
|
|||||||
throw new InvalidDataException("too many loser goals");
|
throw new InvalidDataException("too many loser goals");
|
||||||
}
|
}
|
||||||
|
|
||||||
Spieler gewinnerVorn = spielerRepository.findByName(gewinnerNameVorn)
|
|
||||||
.orElseThrow(() -> new NoSuchPlayerException(gewinnerNameVorn));
|
|
||||||
|
|
||||||
Spieler gewinnerHinten = spielerRepository.findByName(gewinnerNameHinten)
|
|
||||||
.orElseThrow(() -> new NoSuchPlayerException(gewinnerNameHinten));
|
|
||||||
|
|
||||||
Spieler verliererVorn = spielerRepository.findByName(verliererNameVorn)
|
|
||||||
.orElseThrow(() -> new NoSuchPlayerException(verliererNameVorn));
|
|
||||||
|
|
||||||
Spieler verliererHinten = spielerRepository.findByName(verliererNameHinten)
|
|
||||||
.orElseThrow(() -> new NoSuchPlayerException(verliererNameHinten));
|
|
||||||
|
|
||||||
Ergebnis2vs2 ergebnis = new Ergebnis2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
|
Ergebnis2vs2 ergebnis = new Ergebnis2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
|
||||||
ergebnis2vs2Repository.save(ergebnis);
|
ergebnis = ergebnis2vs2Repository.save(ergebnis);
|
||||||
|
|
||||||
eloCalculationService.updateElo2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
|
EloChange2vs2 change = eloCalculationService.updateElo2vs2(gewinnerVorn, gewinnerHinten, verliererVorn, verliererHinten, toreVerlierer);
|
||||||
|
EloChangeTracker.put2vs2Result(ergebnis.getId(), change);
|
||||||
spielerRepository.save(gewinnerVorn);
|
spielerRepository.save(gewinnerVorn);
|
||||||
spielerRepository.save(gewinnerHinten);
|
spielerRepository.save(gewinnerHinten);
|
||||||
spielerRepository.save(verliererVorn);
|
spielerRepository.save(verliererVorn);
|
||||||
@@ -169,9 +159,10 @@ public class KickerEloService {
|
|||||||
}
|
}
|
||||||
Stream<Ergebnis1vs1> results = ergebnis1vs1Repository.findAll().stream().sorted(new Ergebnis1vs1TimeComparator());
|
Stream<Ergebnis1vs1> results = ergebnis1vs1Repository.findAll().stream().sorted(new Ergebnis1vs1TimeComparator());
|
||||||
results.forEach(r -> {
|
results.forEach(r -> {
|
||||||
eloCalculationService.updateElo1vs1(players.get(r.getGewinner().getId()),
|
EloChange1vs1 c = eloCalculationService.updateElo1vs1(players.get(r.getGewinner().getId()),
|
||||||
players.get(r.getVerlierer().getId()),
|
players.get(r.getVerlierer().getId()),
|
||||||
r.getToreVerlierer());
|
r.getToreVerlierer());
|
||||||
|
EloChangeTracker.put1vs1Result(r.getId(), c);
|
||||||
});
|
});
|
||||||
spielerRepository.saveAll(players.values());
|
spielerRepository.saveAll(players.values());
|
||||||
}
|
}
|
||||||
@@ -187,11 +178,12 @@ public class KickerEloService {
|
|||||||
}
|
}
|
||||||
Stream<Ergebnis2vs2> results = ergebnis2vs2Repository.findAll().stream().sorted(new Ergebnis2vs2TimeComparator());
|
Stream<Ergebnis2vs2> results = ergebnis2vs2Repository.findAll().stream().sorted(new Ergebnis2vs2TimeComparator());
|
||||||
results.forEach(r -> {
|
results.forEach(r -> {
|
||||||
eloCalculationService.updateElo2vs2(players.get(r.getGewinnerVorn().getId()),
|
EloChange2vs2 c = eloCalculationService.updateElo2vs2(players.get(r.getGewinnerVorn().getId()),
|
||||||
players.get(r.getGewinnerHinten().getId()),
|
players.get(r.getGewinnerHinten().getId()),
|
||||||
players.get(r.getVerliererVorn().getId()),
|
players.get(r.getVerliererVorn().getId()),
|
||||||
players.get(r.getVerliererHinten().getId()),
|
players.get(r.getVerliererHinten().getId()),
|
||||||
r.getToreVerlierer());
|
r.getToreVerlierer());
|
||||||
|
EloChangeTracker.put2vs2Result(r.getId(), c);
|
||||||
});
|
});
|
||||||
spielerRepository.saveAll(players.values());
|
spielerRepository.saveAll(players.values());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class Stat2vs2Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Float getFrontRate(Spieler s) {
|
public Float getFrontRate(Spieler s) {
|
||||||
int numFront = ergebnis2vs2Repository.countByGewinnerVornOrGewinnerHinten(s, s);
|
int numFront = ergebnis2vs2Repository.countByGewinnerVornOrVerliererVorn(s, s);
|
||||||
int numAll = ergebnis2vs2Repository.countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(s, s, s, s);
|
int numAll = ergebnis2vs2Repository.countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(s, s, s, s);
|
||||||
return (float) numFront / numAll;
|
return (float) numFront / numAll;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package org.kickerelo.kickerelo.util;
|
||||||
|
|
||||||
|
public record EloChange1vs1(double winnerEloChange,
|
||||||
|
double loserEloChange) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.kickerelo.kickerelo.util;
|
||||||
|
|
||||||
|
public record EloChange2vs2(double winnerFrontEloChange,
|
||||||
|
double winnerBackEloChange,
|
||||||
|
double loserFrontEloChange,
|
||||||
|
double loserBackEloChange) {
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.kickerelo.kickerelo.util;
|
package org.kickerelo.kickerelo.util.comparator;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
|
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.kickerelo.kickerelo.util;
|
package org.kickerelo.kickerelo.util.comparator;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
|
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.kickerelo.kickerelo.util;
|
package org.kickerelo.kickerelo.util.comparator;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.kickerelo.kickerelo.util;
|
package org.kickerelo.kickerelo.util.comparator;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.kickerelo.kickerelo.util.comparator;
|
||||||
|
|
||||||
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public class SpielerNameComparator implements Comparator<Spieler> {
|
||||||
|
@Override
|
||||||
|
public int compare(Spieler o1, Spieler o2) {
|
||||||
|
return o1.getName().compareTo(o2.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
49
src/main/java/org/kickerelo/kickerelo/views/Chart.java
Normal file
49
src/main/java/org/kickerelo/kickerelo/views/Chart.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package org.kickerelo.kickerelo.views;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.Component;
|
||||||
|
import com.vaadin.flow.component.UI;
|
||||||
|
import com.vaadin.flow.component.Tag;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
|
||||||
|
@Tag("canvas")
|
||||||
|
public class Chart extends Component {
|
||||||
|
|
||||||
|
public Chart(List<String> xvalues, List<Float> yvalues) {
|
||||||
|
setId("chart");
|
||||||
|
|
||||||
|
UI.getCurrent().getPage().addJavaScript("https://cdn.jsdelivr.net/npm/chart.js");
|
||||||
|
|
||||||
|
JSONArray x = new JSONArray(xvalues);
|
||||||
|
JSONArray y = new JSONArray(yvalues);
|
||||||
|
|
||||||
|
String js = "";
|
||||||
|
|
||||||
|
// Dark mode setting
|
||||||
|
js += "if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {";
|
||||||
|
js += "Chart.defaults.color = 'hsla(214, 96%, 96%, 0.9)';";
|
||||||
|
js += "Chart.defaults.borderColor = 'hsla(214, 78%, 88%, 0.5)';";
|
||||||
|
js += "}";
|
||||||
|
|
||||||
|
// Font size
|
||||||
|
js += "Chart.defaults.font.size = 16;";
|
||||||
|
|
||||||
|
// Scales
|
||||||
|
js += "Chart.defaults.scales.category.ticks.autoSkip = false;";
|
||||||
|
js += "Chart.defaults.scales.category.offset = true;";
|
||||||
|
|
||||||
|
// Chart
|
||||||
|
js += "new Chart(document.getElementById('chart'), {type: 'line', ";
|
||||||
|
js += "options: {showLine: false, pointRadius: 7, plugins: { legend: { display: false}}, layout: { padding: 10}}, ";
|
||||||
|
|
||||||
|
// Data
|
||||||
|
js += "data: { labels: " + x + ", datasets:[{data: " + y + ", ";
|
||||||
|
js += "borderColor: 'hsl(214, 90%, 48%)', backgroundColor: 'hsl(214, 90%, 77%)'}]}});";
|
||||||
|
|
||||||
|
getElement().executeJs(js);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package org.kickerelo.kickerelo.views;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
|
||||||
import org.kickerelo.kickerelo.util.Spieler1vs1EloComparator;
|
|
||||||
|
|
||||||
import com.github.appreciated.apexcharts.ApexChartsBuilder;
|
|
||||||
import com.github.appreciated.apexcharts.config.Theme;
|
|
||||||
import com.github.appreciated.apexcharts.config.builder.ChartBuilder;
|
|
||||||
import com.github.appreciated.apexcharts.config.builder.XAxisBuilder;
|
|
||||||
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.theme.Mode;
|
|
||||||
import com.github.appreciated.apexcharts.config.theme.Monochrome;
|
|
||||||
import com.github.appreciated.apexcharts.config.xaxis.Labels;
|
|
||||||
import com.github.appreciated.apexcharts.helper.Series;
|
|
||||||
import com.vaadin.flow.router.Route;
|
|
||||||
|
|
||||||
@Route(value = "app/chart1vs1")
|
|
||||||
public class Chart1vs1 extends ApexChartsBuilder {
|
|
||||||
public Chart1vs1(List<Spieler> l) {
|
|
||||||
Theme theme = new Theme();
|
|
||||||
Monochrome monochrome = new Monochrome();
|
|
||||||
monochrome.setEnabled(true);
|
|
||||||
theme.setMode(Mode.DARK);
|
|
||||||
theme.setMonochrome(monochrome);
|
|
||||||
Labels labels = new Labels();
|
|
||||||
labels.setRotate(270d);
|
|
||||||
labels.setShow(true);
|
|
||||||
labels.setRotateAlways(false);
|
|
||||||
|
|
||||||
|
|
||||||
withChart(ChartBuilder.get().withType(Type.SCATTER)
|
|
||||||
.withZoom(ZoomBuilder.get().withEnabled(true).withType(ZoomType.XY).build()).build())
|
|
||||||
.withSeries(new Series<>("ELO",
|
|
||||||
l.stream().sorted(new Spieler1vs1EloComparator()).map(Spieler::getElo1vs1).toArray()
|
|
||||||
))
|
|
||||||
.withXaxis(XAxisBuilder.get().withCategories(l.stream().sorted(new Spieler1vs1EloComparator())
|
|
||||||
.map(Spieler::getName).toList()).withLabels(labels).build())
|
|
||||||
.withYaxis(YAxisBuilder.get().build())
|
|
||||||
.withTheme(theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package org.kickerelo.kickerelo.views;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
|
||||||
import org.kickerelo.kickerelo.util.Spieler2vs2EloComparator;
|
|
||||||
|
|
||||||
import com.github.appreciated.apexcharts.ApexChartsBuilder;
|
|
||||||
import com.github.appreciated.apexcharts.config.Theme;
|
|
||||||
import com.github.appreciated.apexcharts.config.builder.ChartBuilder;
|
|
||||||
import com.github.appreciated.apexcharts.config.builder.XAxisBuilder;
|
|
||||||
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.theme.Mode;
|
|
||||||
import com.github.appreciated.apexcharts.config.theme.Monochrome;
|
|
||||||
import com.github.appreciated.apexcharts.config.xaxis.Labels;
|
|
||||||
import com.github.appreciated.apexcharts.helper.Series;
|
|
||||||
import com.vaadin.flow.router.Route;
|
|
||||||
|
|
||||||
@Route(value = "app/chart2vs2")
|
|
||||||
public class Chart2vs2 extends ApexChartsBuilder {
|
|
||||||
public Chart2vs2(List<Spieler> l) {
|
|
||||||
Theme theme = new Theme();
|
|
||||||
Monochrome monochrome = new Monochrome();
|
|
||||||
monochrome.setEnabled(true);
|
|
||||||
theme.setMode(Mode.DARK);
|
|
||||||
theme.setMonochrome(monochrome);
|
|
||||||
Labels labels = new Labels();
|
|
||||||
labels.setRotate(270d);
|
|
||||||
labels.setShow(true);
|
|
||||||
labels.setRotateAlways(false);
|
|
||||||
|
|
||||||
withChart(ChartBuilder.get().withType(Type.SCATTER)
|
|
||||||
.withZoom(ZoomBuilder.get().withEnabled(true).withType(ZoomType.XY).build()).build())
|
|
||||||
.withSeries(new Series<>("ELO",
|
|
||||||
l.stream().sorted(new Spieler2vs2EloComparator()).map(Spieler::getElo2vs2).toArray()
|
|
||||||
))
|
|
||||||
.withXaxis(XAxisBuilder.get().withCategories(l.stream().sorted(new Spieler2vs2EloComparator()).map(Spieler::getName).toList()).withLabels(labels).build())
|
|
||||||
.withYaxis(YAxisBuilder.get().build())
|
|
||||||
.withTheme(theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.kickerelo.kickerelo.views;
|
package org.kickerelo.kickerelo.views;
|
||||||
|
|
||||||
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
|
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
|
||||||
import org.kickerelo.kickerelo.exception.InvalidDataException;
|
import org.kickerelo.kickerelo.exception.InvalidDataException;
|
||||||
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
|
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
|
||||||
@@ -21,12 +22,12 @@ public class Enter1vs1View extends VerticalLayout {
|
|||||||
public Enter1vs1View(KickerEloService eloService) {
|
public Enter1vs1View(KickerEloService eloService) {
|
||||||
H2 subheading = new H2("1 vs 1 Ergebnis");
|
H2 subheading = new H2("1 vs 1 Ergebnis");
|
||||||
|
|
||||||
ComboBox<String> winnerSelect = new ComboBox<>("Gewinner");
|
ComboBox<Spieler> winnerSelect = new ComboBox<>("Gewinner");
|
||||||
winnerSelect.setItems(eloService.getSpielerNamen());
|
winnerSelect.setItems(eloService.getSpielerEntities());
|
||||||
winnerSelect.setPlaceholder("Spieler auswählen");
|
winnerSelect.setPlaceholder("Spieler auswählen");
|
||||||
|
|
||||||
ComboBox<String> loserSelect = new ComboBox<>("Verlierer");
|
ComboBox<Spieler> loserSelect = new ComboBox<>("Verlierer");
|
||||||
loserSelect.setItems(eloService.getSpielerNamen());
|
loserSelect.setItems(eloService.getSpielerEntities());
|
||||||
loserSelect.setPlaceholder("Spieler auswählen");
|
loserSelect.setPlaceholder("Spieler auswählen");
|
||||||
|
|
||||||
IntegerField loserGoals = new IntegerField("Tore des Verlierers");
|
IntegerField loserGoals = new IntegerField("Tore des Verlierers");
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.kickerelo.kickerelo.views;
|
package org.kickerelo.kickerelo.views;
|
||||||
|
|
||||||
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
|
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
|
||||||
import org.kickerelo.kickerelo.exception.InvalidDataException;
|
import org.kickerelo.kickerelo.exception.InvalidDataException;
|
||||||
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
|
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
|
||||||
@@ -20,20 +21,20 @@ public class Enter2vs2View extends VerticalLayout {
|
|||||||
public Enter2vs2View(KickerEloService eloService) {
|
public Enter2vs2View(KickerEloService eloService) {
|
||||||
H2 subheading = new H2("2 vs 2 Ergebnis");
|
H2 subheading = new H2("2 vs 2 Ergebnis");
|
||||||
|
|
||||||
ComboBox<String> winnerFrontSelect = new ComboBox<>("Gewinner vorne");
|
ComboBox<Spieler> winnerFrontSelect = new ComboBox<>("Gewinner vorne");
|
||||||
winnerFrontSelect.setItems(eloService.getSpielerNamen());
|
winnerFrontSelect.setItems(eloService.getSpielerEntities());
|
||||||
winnerFrontSelect.setPlaceholder("Spieler auswählen");
|
winnerFrontSelect.setPlaceholder("Spieler auswählen");
|
||||||
|
|
||||||
ComboBox<String> winnerBackSelect = new ComboBox<>("Gewinner hinten");
|
ComboBox<Spieler> winnerBackSelect = new ComboBox<>("Gewinner hinten");
|
||||||
winnerBackSelect.setItems(eloService.getSpielerNamen());
|
winnerBackSelect.setItems(eloService.getSpielerEntities());
|
||||||
winnerBackSelect.setPlaceholder("Spieler auswählen");
|
winnerBackSelect.setPlaceholder("Spieler auswählen");
|
||||||
|
|
||||||
ComboBox<String> loserFrontSelect = new ComboBox<>("Verlierer vorne");
|
ComboBox<Spieler> loserFrontSelect = new ComboBox<>("Verlierer vorne");
|
||||||
loserFrontSelect.setItems(eloService.getSpielerNamen());
|
loserFrontSelect.setItems(eloService.getSpielerEntities());
|
||||||
loserFrontSelect.setPlaceholder("Spieler auswählen");
|
loserFrontSelect.setPlaceholder("Spieler auswählen");
|
||||||
|
|
||||||
ComboBox<String> loserBackSelect = new ComboBox<>("Verlierer hinten");
|
ComboBox<Spieler> loserBackSelect = new ComboBox<>("Verlierer hinten");
|
||||||
loserBackSelect.setItems(eloService.getSpielerNamen());
|
loserBackSelect.setItems(eloService.getSpielerEntities());
|
||||||
loserBackSelect.setPlaceholder("Spieler auswählen");
|
loserBackSelect.setPlaceholder("Spieler auswählen");
|
||||||
|
|
||||||
IntegerField loserGoals = new IntegerField("Tore des Verlierers");
|
IntegerField loserGoals = new IntegerField("Tore des Verlierers");
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.kickerelo.kickerelo.views;
|
package org.kickerelo.kickerelo.views;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.repository.SpielerRepository;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.kickerelo.kickerelo.repository.SpielerRepository;
|
||||||
|
import org.kickerelo.kickerelo.util.comparator.Spieler1vs1EloComparator;
|
||||||
|
|
||||||
import com.github.appreciated.apexcharts.ApexCharts;
|
|
||||||
import com.vaadin.flow.component.Unit;
|
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.H2;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
@@ -11,14 +13,17 @@ import com.vaadin.flow.router.Route;
|
|||||||
@Route("app/graph1vs1")
|
@Route("app/graph1vs1")
|
||||||
public class Graph1vs1View extends VerticalLayout {
|
public class Graph1vs1View extends VerticalLayout {
|
||||||
|
|
||||||
ApexCharts chart1vs1;
|
|
||||||
public Graph1vs1View(SpielerRepository repo) {
|
public Graph1vs1View(SpielerRepository repo) {
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
H2 subheading = new H2("1 vs 1 Elo");
|
H2 subheading = new H2("1 vs 1 Elo");
|
||||||
chart1vs1 = new Chart1vs1(repo.getSpielerWith1vs1Games()).build();
|
|
||||||
chart1vs1.setWidth(100, Unit.PERCENTAGE);
|
|
||||||
chart1vs1.setHeight(100, Unit.PERCENTAGE);
|
|
||||||
|
|
||||||
add(subheading, chart1vs1);
|
List<String> names = new ArrayList<>();
|
||||||
|
List<Float> elo = new ArrayList<>();
|
||||||
|
|
||||||
|
repo.getSpielerWith1vs1Games().stream().sorted(new Spieler1vs1EloComparator()).forEach((s) -> {names.add(s.getName()); elo.add(s.getElo1vs1());});
|
||||||
|
|
||||||
|
Chart chart = new Chart(names, elo);
|
||||||
|
|
||||||
|
add(subheading, chart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
package org.kickerelo.kickerelo.views;
|
package org.kickerelo.kickerelo.views;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.repository.SpielerRepository;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.kickerelo.kickerelo.repository.SpielerRepository;
|
||||||
|
import org.kickerelo.kickerelo.util.comparator.Spieler2vs2EloComparator;
|
||||||
|
|
||||||
import com.github.appreciated.apexcharts.ApexCharts;
|
|
||||||
import com.vaadin.flow.component.Unit;
|
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.H2;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
|
|
||||||
@Route("app/graph2vs2")
|
@Route("app/graph2vs2")
|
||||||
public class Graph2vs2View extends VerticalLayout {
|
public class Graph2vs2View extends VerticalLayout {
|
||||||
ApexCharts chart2vs2;
|
|
||||||
public Graph2vs2View(SpielerRepository repo) {
|
public Graph2vs2View(SpielerRepository repo) {
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
H2 subheading = new H2("2 vs 2 Elo");
|
H2 subheading = new H2("2 vs 2 Elo");
|
||||||
chart2vs2 = new Chart2vs2(repo.getSpielerWith2vs2Games()).build();
|
|
||||||
chart2vs2.setWidth(100, Unit.PERCENTAGE);
|
|
||||||
chart2vs2.setHeight(100, Unit.PERCENTAGE);
|
|
||||||
|
|
||||||
add(subheading, chart2vs2);
|
List<String> names = new ArrayList<>();
|
||||||
|
List<Float> elo = new ArrayList<>();
|
||||||
|
|
||||||
|
repo.getSpielerWith2vs2Games().stream().sorted(new Spieler2vs2EloComparator()).forEach((s) -> {names.add(s.getName()); elo.add(s.getElo2vs2());});
|
||||||
|
|
||||||
|
Chart chart = new Chart(names, elo);
|
||||||
|
|
||||||
|
add(subheading, chart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,22 @@ import com.vaadin.flow.component.html.H2;
|
|||||||
import com.vaadin.flow.component.icon.Icon;
|
import com.vaadin.flow.component.icon.Icon;
|
||||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
import com.vaadin.flow.data.provider.SortDirection;
|
import com.vaadin.flow.data.provider.SortDirection;
|
||||||
|
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||||
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
|
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
|
||||||
import com.vaadin.flow.data.value.ValueChangeMode;
|
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
|
|
||||||
@Route("app/history1vs1")
|
@Route("app/history1vs1")
|
||||||
public class History1vs1View extends VerticalLayout {
|
public class History1vs1View extends HistoryView {
|
||||||
|
|
||||||
|
private final Ergebnis1vs1Repository repo;
|
||||||
|
|
||||||
List<Ergebnis1vs1> res;
|
List<Ergebnis1vs1> res;
|
||||||
public History1vs1View(Ergebnis1vs1Repository repo) {
|
public History1vs1View(Ergebnis1vs1Repository repo) {
|
||||||
|
this.repo = repo;
|
||||||
|
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
H2 subheading = new H2("Spiele 1 vs 1");
|
H2 subheading = new H2("Spiele 1 vs 1");
|
||||||
res = repo.findAll();
|
res = repo.findAll();
|
||||||
@@ -50,8 +55,10 @@ public class History1vs1View extends VerticalLayout {
|
|||||||
grid.removeColumnByKey("id");
|
grid.removeColumnByKey("id");
|
||||||
Grid.Column<Ergebnis1vs1> winnerColumn = grid.getColumnByKey("gewinner");
|
Grid.Column<Ergebnis1vs1> winnerColumn = grid.getColumnByKey("gewinner");
|
||||||
winnerColumn.setHeader("Gewinner");
|
winnerColumn.setHeader("Gewinner");
|
||||||
|
winnerColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType1vs1.GEWINNER)));
|
||||||
Grid.Column<Ergebnis1vs1> loserColumn = grid.getColumnByKey("verlierer");
|
Grid.Column<Ergebnis1vs1> loserColumn = grid.getColumnByKey("verlierer");
|
||||||
loserColumn.setHeader("Verlierer");
|
loserColumn.setHeader("Verlierer");
|
||||||
|
loserColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType1vs1.VERLIERER)));
|
||||||
Grid.Column<Ergebnis1vs1> goals = grid.getColumnByKey("toreVerlierer");
|
Grid.Column<Ergebnis1vs1> goals = grid.getColumnByKey("toreVerlierer");
|
||||||
goals.setHeader("Verlierertore");
|
goals.setHeader("Verlierertore");
|
||||||
Grid.Column<Ergebnis1vs1> timestamp = grid.getColumnByKey("timestamp");
|
Grid.Column<Ergebnis1vs1> timestamp = grid.getColumnByKey("timestamp");
|
||||||
|
|||||||
@@ -13,16 +13,21 @@ import com.vaadin.flow.component.html.Paragraph;
|
|||||||
import com.vaadin.flow.component.icon.Icon;
|
import com.vaadin.flow.component.icon.Icon;
|
||||||
import com.vaadin.flow.component.icon.VaadinIcon;
|
import com.vaadin.flow.component.icon.VaadinIcon;
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
import com.vaadin.flow.data.provider.SortDirection;
|
import com.vaadin.flow.data.provider.SortDirection;
|
||||||
|
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||||
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
|
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
|
||||||
import com.vaadin.flow.data.value.ValueChangeMode;
|
import com.vaadin.flow.data.value.ValueChangeMode;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
|
|
||||||
|
|
||||||
@Route("app/history2vs2")
|
@Route("app/history2vs2")
|
||||||
public class History2vs2View extends VerticalLayout {
|
public class History2vs2View extends HistoryView {
|
||||||
|
|
||||||
|
private final Ergebnis2vs2Repository repo;
|
||||||
|
|
||||||
public History2vs2View(Ergebnis2vs2Repository repo) {
|
public History2vs2View(Ergebnis2vs2Repository repo) {
|
||||||
|
this.repo = repo;
|
||||||
setSizeFull();
|
setSizeFull();
|
||||||
H2 subheading = new H2("Spiele 2 vs 2");
|
H2 subheading = new H2("Spiele 2 vs 2");
|
||||||
List<Ergebnis2vs2> res = repo.findAll();
|
List<Ergebnis2vs2> res = repo.findAll();
|
||||||
@@ -64,12 +69,16 @@ public class History2vs2View extends VerticalLayout {
|
|||||||
grid.removeColumnByKey("id");
|
grid.removeColumnByKey("id");
|
||||||
Grid.Column<Ergebnis2vs2> winnerFront = grid.getColumnByKey("gewinnerVorn");
|
Grid.Column<Ergebnis2vs2> winnerFront = grid.getColumnByKey("gewinnerVorn");
|
||||||
winnerFront.setHeader("Gewinner vorne");
|
winnerFront.setHeader("Gewinner vorne");
|
||||||
|
winnerFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.GEWINNER_VORN)));
|
||||||
Grid.Column<Ergebnis2vs2> winnerBack = grid.getColumnByKey("gewinnerHinten");
|
Grid.Column<Ergebnis2vs2> winnerBack = grid.getColumnByKey("gewinnerHinten");
|
||||||
winnerBack.setHeader("Gewinner hinten");
|
winnerBack.setHeader("Gewinner hinten");
|
||||||
|
winnerBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.GEWINNER_HINTEN)));
|
||||||
Grid.Column<Ergebnis2vs2> loserFront = grid.getColumnByKey("verliererVorn");
|
Grid.Column<Ergebnis2vs2> loserFront = grid.getColumnByKey("verliererVorn");
|
||||||
loserFront.setHeader("Verlierer vorne");
|
loserFront.setHeader("Verlierer vorne");
|
||||||
|
loserFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.VERLIERER_VORN)));
|
||||||
Grid.Column<Ergebnis2vs2> loserBack = grid.getColumnByKey("verliererHinten");
|
Grid.Column<Ergebnis2vs2> loserBack = grid.getColumnByKey("verliererHinten");
|
||||||
loserBack.setHeader("Verlierer hinten");
|
loserBack.setHeader("Verlierer hinten");
|
||||||
|
loserBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.VERLIERER_HINTEN)));
|
||||||
Grid.Column<Ergebnis2vs2> goals = grid.getColumnByKey("toreVerlierer");
|
Grid.Column<Ergebnis2vs2> goals = grid.getColumnByKey("toreVerlierer");
|
||||||
goals.setHeader("Verlierertore");
|
goals.setHeader("Verlierertore");
|
||||||
Grid.Column<Ergebnis2vs2> timestamp = grid.getColumnByKey("timestamp");
|
Grid.Column<Ergebnis2vs2> timestamp = grid.getColumnByKey("timestamp");
|
||||||
|
|||||||
57
src/main/java/org/kickerelo/kickerelo/views/HistoryView.java
Normal file
57
src/main/java/org/kickerelo/kickerelo/views/HistoryView.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package org.kickerelo.kickerelo.views;
|
||||||
|
|
||||||
|
import com.vaadin.flow.component.html.Div;
|
||||||
|
import com.vaadin.flow.component.html.Span;
|
||||||
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
|
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||||
|
import org.kickerelo.kickerelo.data.Ergebnis1vs1;
|
||||||
|
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
|
||||||
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
|
import org.kickerelo.kickerelo.service.EloChangeTracker;
|
||||||
|
import org.kickerelo.kickerelo.util.EloChange1vs1;
|
||||||
|
import org.kickerelo.kickerelo.util.EloChange2vs2;
|
||||||
|
|
||||||
|
public class HistoryView extends VerticalLayout {
|
||||||
|
|
||||||
|
enum PlayerType1vs1 {
|
||||||
|
GEWINNER,
|
||||||
|
VERLIERER
|
||||||
|
}
|
||||||
|
enum PlayerType2vs2 {
|
||||||
|
GEWINNER_VORN,
|
||||||
|
GEWINNER_HINTEN,
|
||||||
|
VERLIERER_VORN,
|
||||||
|
VERLIERER_HINTEN
|
||||||
|
}
|
||||||
|
|
||||||
|
static Div formatEloChange(Ergebnis1vs1 ergebnis, PlayerType1vs1 type) {
|
||||||
|
EloChange1vs1 change = EloChangeTracker.get1vs1Result(ergebnis.getId());
|
||||||
|
return switch (type) {
|
||||||
|
case GEWINNER -> formatEloChange(ergebnis.getGewinner(), change.winnerEloChange());
|
||||||
|
case VERLIERER -> formatEloChange(ergebnis.getVerlierer(), change.loserEloChange());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Div formatEloChange(Ergebnis2vs2 ergebnis, PlayerType2vs2 type) {
|
||||||
|
EloChange2vs2 change = EloChangeTracker.get2vs2Result(ergebnis.getId());
|
||||||
|
return switch (type) {
|
||||||
|
case GEWINNER_VORN -> formatEloChange(ergebnis.getGewinnerVorn(), change.winnerFrontEloChange());
|
||||||
|
case GEWINNER_HINTEN -> formatEloChange(ergebnis.getGewinnerHinten(), change.winnerBackEloChange());
|
||||||
|
case VERLIERER_VORN -> formatEloChange(ergebnis.getVerliererVorn(), change.loserFrontEloChange());
|
||||||
|
case VERLIERER_HINTEN -> formatEloChange(ergebnis.getVerliererHinten(), change.loserBackEloChange());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Div formatEloChange(Spieler spieler, double change) {
|
||||||
|
Div div = new Div();
|
||||||
|
|
||||||
|
Span nameSpan = new Span(spieler.getName() + " ");
|
||||||
|
String formattedChange = String.format("%+.2f", change);
|
||||||
|
Span changeSpan = new Span(formattedChange);
|
||||||
|
changeSpan.addClassNames(LumoUtility.FontSize.SMALL, LumoUtility.TextColor.SECONDARY, LumoUtility.FontWeight.LIGHT);
|
||||||
|
|
||||||
|
div.add(nameSpan, changeSpan);
|
||||||
|
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.kickerelo.kickerelo.views;
|
package org.kickerelo.kickerelo.views;
|
||||||
|
|
||||||
import org.kickerelo.kickerelo.data.Spieler;
|
import org.kickerelo.kickerelo.data.Spieler;
|
||||||
|
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
|
||||||
import org.kickerelo.kickerelo.service.KickerEloService;
|
import org.kickerelo.kickerelo.service.KickerEloService;
|
||||||
import org.kickerelo.kickerelo.service.Stat2vs2Service;
|
import org.kickerelo.kickerelo.service.Stat2vs2Service;
|
||||||
import org.kickerelo.kickerelo.util.Position;
|
import org.kickerelo.kickerelo.util.Position;
|
||||||
@@ -8,6 +9,7 @@ import org.kickerelo.kickerelo.util.Position;
|
|||||||
import com.vaadin.flow.component.combobox.ComboBox;
|
import com.vaadin.flow.component.combobox.ComboBox;
|
||||||
import com.vaadin.flow.component.html.H2;
|
import com.vaadin.flow.component.html.H2;
|
||||||
import com.vaadin.flow.component.html.NativeLabel;
|
import com.vaadin.flow.component.html.NativeLabel;
|
||||||
|
import com.vaadin.flow.component.html.Paragraph;
|
||||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
|
||||||
import com.vaadin.flow.component.progressbar.ProgressBar;
|
import com.vaadin.flow.component.progressbar.ProgressBar;
|
||||||
import com.vaadin.flow.component.progressbar.ProgressBarVariant;
|
import com.vaadin.flow.component.progressbar.ProgressBarVariant;
|
||||||
@@ -17,29 +19,43 @@ import com.vaadin.flow.router.Route;
|
|||||||
public class Stat2vs2View extends VerticalLayout {
|
public class Stat2vs2View extends VerticalLayout {
|
||||||
Stat2vs2Service stat2vs2Service;
|
Stat2vs2Service stat2vs2Service;
|
||||||
KickerEloService kickerEloService;
|
KickerEloService kickerEloService;
|
||||||
|
Ergebnis2vs2Repository repo;
|
||||||
H2 subheading;
|
H2 subheading;
|
||||||
ComboBox<Spieler> selector;
|
ComboBox<Spieler> selector;
|
||||||
|
Paragraph generalInfo = new Paragraph();
|
||||||
ProgressBar frontRate = new ProgressBar();
|
ProgressBar frontRate = new ProgressBar();
|
||||||
NativeLabel frontRateText = new NativeLabel();
|
NativeLabel frontRateText = new NativeLabel();
|
||||||
ProgressBar winRateFront = new ProgressBar();
|
ProgressBar winRateFront = new ProgressBar();
|
||||||
NativeLabel winRateFrontText = new NativeLabel();
|
NativeLabel winRateFrontText = new NativeLabel();
|
||||||
ProgressBar winRateBack = new ProgressBar();
|
ProgressBar winRateBack = new ProgressBar();
|
||||||
NativeLabel winRateBackText = new NativeLabel();
|
NativeLabel winRateBackText = new NativeLabel();
|
||||||
|
Paragraph goalDiffBack = new Paragraph();
|
||||||
|
Paragraph goalDiffFront = new Paragraph();
|
||||||
|
|
||||||
public Stat2vs2View(Stat2vs2Service service, KickerEloService kickerService) {
|
public Stat2vs2View(Stat2vs2Service service, KickerEloService kickerService, Ergebnis2vs2Repository repo) {
|
||||||
this.stat2vs2Service = service;
|
this.stat2vs2Service = service;
|
||||||
this.kickerEloService = kickerService;
|
this.kickerEloService = kickerService;
|
||||||
subheading = new H2("2 vs 2 Ergebnis");
|
this.repo = repo;
|
||||||
|
subheading = new H2("2 vs 2 Spielerstatistik");
|
||||||
selector = new ComboBox<>("Spieler");
|
selector = new ComboBox<>("Spieler");
|
||||||
selector.setItems(kickerService.getSpielerEntities());
|
selector.setItems(kickerService.getSpielerEntities());
|
||||||
selector.addValueChangeListener(event -> updateData(selector.getValue()));
|
selector.addValueChangeListener(event -> updateData(selector.getValue()));
|
||||||
add(subheading, selector, frontRateText, frontRate, winRateFrontText, winRateFront, winRateBackText, winRateBack);
|
add(subheading, selector, generalInfo, frontRateText, frontRate, winRateFrontText, winRateFront, winRateBackText, winRateBack, goalDiffBack, goalDiffFront);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateData(Spieler s) {
|
private void updateData(Spieler s) {
|
||||||
|
updateGeneralInfo(s);
|
||||||
updateFrontRate(s);
|
updateFrontRate(s);
|
||||||
updateFrontWinrate(s);
|
updateFrontWinrate(s);
|
||||||
updateBackWinrate(s);
|
updateBackWinrate(s);
|
||||||
|
updateGoalDiffs(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateGeneralInfo(Spieler s) {
|
||||||
|
int anzahl = repo.countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(s, s, s, s);
|
||||||
|
float elo = s.getElo2vs2();
|
||||||
|
String text = String.format("%.2f", elo) + " Elo bei " + anzahl + " Spielen";
|
||||||
|
generalInfo.setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFrontRate(Spieler s) {
|
private void updateFrontRate(Spieler s) {
|
||||||
@@ -66,4 +82,14 @@ public class Stat2vs2View extends VerticalLayout {
|
|||||||
winRateBack.addThemeVariants((winRate > 0.5f ? ProgressBarVariant.LUMO_SUCCESS : ProgressBarVariant.LUMO_ERROR));
|
winRateBack.addThemeVariants((winRate > 0.5f ? ProgressBarVariant.LUMO_SUCCESS : ProgressBarVariant.LUMO_ERROR));
|
||||||
winRateBackText.setText(winRate.isNaN() ? text + "-" : text + String.format("%.2f", winRate * 100) + "%");
|
winRateBackText.setText(winRate.isNaN() ? text + "-" : text + String.format("%.2f", winRate * 100) + "%");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateGoalDiffs(Spieler s) {
|
||||||
|
String text = "Mittlere Tordifferenz hinten: ";
|
||||||
|
Float backDiff = repo.avgGoalDiffBack(s);
|
||||||
|
goalDiffBack.setText(backDiff.isNaN() ? text + "-" : text + String.format("%.2f", backDiff));
|
||||||
|
text = "Mittlere Tordifferenz vorne: ";
|
||||||
|
Float frontDiff = repo.avgGoalDiffFront(s);
|
||||||
|
goalDiffFront.setText(frontDiff.isNaN() ? text + "-" : text + String.format("%.2f", frontDiff));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,3 +18,6 @@ spring.security.oauth2.client.registration.oidc.redirect-uri={baseUrl}/login/oau
|
|||||||
spring.security.oauth2.client.provider.oidc.issuer-uri=${OIDC_ISSUER_URI}
|
spring.security.oauth2.client.provider.oidc.issuer-uri=${OIDC_ISSUER_URI}
|
||||||
|
|
||||||
vaadin.urlMapping=/app/*
|
vaadin.urlMapping=/app/*
|
||||||
|
|
||||||
|
# In prod mode, never add the test data to the database
|
||||||
|
spring.sql.init.mode=never
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ logging.level.org.atmosphere = warn
|
|||||||
logging.level.org.springframework.security=DEBUG
|
logging.level.org.springframework.security=DEBUG
|
||||||
spring.mustache.check-template-location = false
|
spring.mustache.check-template-location = false
|
||||||
|
|
||||||
spring.datasource.url=jdbc:h2:file:./data
|
|
||||||
spring.datasource.driver-class-name=org.h2.Driver
|
spring.datasource.driver-class-name=org.h2.Driver
|
||||||
spring.datasource.username=sa
|
spring.datasource.username=sa
|
||||||
spring.datasource.password=
|
spring.datasource.password=
|
||||||
|
|||||||
231
src/main/resources/data.sql
Normal file
231
src/main/resources/data.sql
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
INSERT INTO spieler (id, name, elo1vs1, elo2vs2, elo_alt) VALUES
|
||||||
|
(1, 'Alfons', 1500, 1500, 0),
|
||||||
|
(2, 'Bertha', 1500, 1500, 0),
|
||||||
|
(3, 'Claude', 1500, 1500, 0),
|
||||||
|
(4, 'Doris', 1500, 1500, 0),
|
||||||
|
(5, 'Emil', 1500, 1500, 0),
|
||||||
|
(6, 'Friederike', 1500, 1500, 0),
|
||||||
|
(7, 'Gisela', 1500, 1500, 0),
|
||||||
|
(8, 'Heinrich', 1500, 1500, 0),
|
||||||
|
(9, 'Ingrid', 1500, 1500, 0),
|
||||||
|
(10, 'Johann', 1500, 1500, 0),
|
||||||
|
(11, 'Käthe', 1500, 1500, 0),
|
||||||
|
(12, 'Lorenz', 1500, 1500, 0),
|
||||||
|
(13, 'Marie', 1500, 1500, 0),
|
||||||
|
(14, 'Noah', 1500, 1500, 0),
|
||||||
|
(15, 'Odile', 1500, 1500, 0),
|
||||||
|
(16, 'Paula', 1500, 1500, 0),
|
||||||
|
(17, 'Quirin', 1500, 1500, 0),
|
||||||
|
(18, 'Roland', 1500, 1500, 0),
|
||||||
|
(19, 'Sophie', 1500, 1500, 0),
|
||||||
|
(20, 'Torsten', 1500, 1500, 0),
|
||||||
|
(21, 'Ulrich', 1500, 1500, 0),
|
||||||
|
(22, 'Veronika', 1500, 1500, 0),
|
||||||
|
(23, 'Werner', 1500, 1500, 0),
|
||||||
|
(24, 'Xaver', 1500, 1500, 0),
|
||||||
|
(25, 'Yseult', 1500, 1500, 0),
|
||||||
|
(26, 'Zacharias', 1500, 1500, 0);
|
||||||
|
|
||||||
|
INSERT INTO ergebnis1vs1 (id, gewinner, verlierer, tore_verlierer, zeitpunkt) VALUES
|
||||||
|
(1, 5, 12, 2, '2024-11-01 10:00:00'),
|
||||||
|
(2, 18, 7, 1, '2024-11-02 11:30:00'),
|
||||||
|
(3, 3, 25, 0, '2024-11-03 13:45:00'),
|
||||||
|
(4, 22, 1, 3, '2024-11-04 15:00:00'),
|
||||||
|
(5, 10, 15, 2, '2024-11-05 16:15:00'),
|
||||||
|
(6, 7, 20, 4, '2024-11-06 17:30:00'),
|
||||||
|
(7, 2, 14, 1, '2024-11-07 10:45:00'),
|
||||||
|
(8, 11, 23, 0, '2024-11-08 12:00:00'),
|
||||||
|
(9, 26, 9, 5, '2024-11-09 14:15:00'),
|
||||||
|
(10, 16, 4, 3, '2024-11-10 16:30:00'),
|
||||||
|
(11, 13, 21, 2, '2024-11-11 10:00:00'),
|
||||||
|
(12, 8, 19, 1, '2024-11-12 11:30:00'),
|
||||||
|
(13, 1, 6, 0, '2024-11-13 13:45:00'),
|
||||||
|
(14, 24, 17, 3, '2024-11-14 15:00:00'),
|
||||||
|
(15, 20, 5, 2, '2024-11-15 16:15:00'),
|
||||||
|
(16, 9, 2, 4, '2024-11-16 17:30:00'),
|
||||||
|
(17, 14, 10, 1, '2024-11-17 10:45:00'),
|
||||||
|
(18, 23, 18, 0, '2024-11-18 12:00:00'),
|
||||||
|
(19, 4, 11, 5, '2024-11-19 14:15:00'),
|
||||||
|
(20, 21, 3, 3, '2024-11-20 16:30:00'),
|
||||||
|
(21, 19, 26, 2, '2024-11-21 10:00:00'),
|
||||||
|
(22, 6, 16, 1, '2024-11-22 11:30:00'),
|
||||||
|
(23, 17, 13, 0, '2024-11-23 13:45:00'),
|
||||||
|
(24, 5, 24, 3, '2024-11-24 15:00:00'),
|
||||||
|
(25, 12, 7, 2, '2024-11-25 16:15:00'),
|
||||||
|
(26, 25, 22, 4, '2024-11-26 17:30:00'),
|
||||||
|
(27, 15, 1, 1, '2024-11-27 10:45:00'),
|
||||||
|
(28, 2, 10, 0, '2024-11-28 12:00:00'),
|
||||||
|
(29, 23, 14, 5, '2024-11-29 14:15:00'),
|
||||||
|
(30, 11, 20, 3, '2024-11-30 16:30:00'),
|
||||||
|
(31, 18, 9, 2, '2024-12-01 10:00:00'),
|
||||||
|
(32, 26, 13, 1, '2024-12-02 11:30:00'),
|
||||||
|
(33, 4, 19, 0, '2024-12-03 13:45:00'),
|
||||||
|
(34, 21, 6, 3, '2024-12-04 15:00:00'),
|
||||||
|
(35, 16, 17, 2, '2024-12-05 16:15:00'),
|
||||||
|
(36, 7, 24, 4, '2024-12-06 17:30:00'),
|
||||||
|
(37, 1, 12, 1, '2024-12-07 10:45:00'),
|
||||||
|
(38, 10, 25, 0, '2024-12-08 12:00:00'),
|
||||||
|
(39, 14, 3, 5, '2024-12-09 14:15:00'),
|
||||||
|
(40, 20, 2, 3, '2024-12-10 16:30:00'),
|
||||||
|
(41, 9, 23, 2, '2024-12-11 10:00:00'),
|
||||||
|
(42, 13, 11, 1, '2024-12-12 11:30:00'),
|
||||||
|
(43, 22, 18, 0, '2024-12-13 13:45:00'),
|
||||||
|
(44, 24, 15, 3, '2024-12-14 15:00:00'),
|
||||||
|
(45, 5, 16, 2, '2024-12-15 16:15:00'),
|
||||||
|
(46, 17, 26, 4, '2024-12-16 17:30:00'),
|
||||||
|
(47, 3, 19, 1, '2024-12-17 10:45:00'),
|
||||||
|
(48, 6, 4, 0, '2024-12-18 12:00:00'),
|
||||||
|
(49, 11, 21, 5, '2024-12-19 14:15:00'),
|
||||||
|
(50, 25, 7, 3, '2024-12-20 16:30:00'),
|
||||||
|
(51, 12, 1, 2, '2024-12-21 10:00:00'),
|
||||||
|
(52, 15, 18, 1, '2024-12-22 11:30:00'),
|
||||||
|
(53, 2, 22, 0, '2024-12-23 13:45:00'),
|
||||||
|
(54, 10, 20, 3, '2024-12-24 15:00:00'),
|
||||||
|
(55, 14, 9, 2, '2024-12-25 16:15:00'),
|
||||||
|
(56, 23, 26, 4, '2024-12-26 17:30:00'),
|
||||||
|
(57, 19, 16, 1, '2024-12-27 10:45:00'),
|
||||||
|
(58, 4, 13, 0, '2024-12-28 12:00:00'),
|
||||||
|
(59, 21, 17, 5, '2024-12-29 14:15:00'),
|
||||||
|
(60, 6, 5, 3, '2024-12-30 16:30:00'),
|
||||||
|
(61, 20, 12, 2, '2025-01-01 10:00:00'),
|
||||||
|
(62, 7, 10, 1, '2025-01-02 11:30:00'),
|
||||||
|
(63, 1, 2, 0, '2025-01-03 13:45:00'),
|
||||||
|
(64, 25, 14, 3, '2025-01-04 15:00:00'),
|
||||||
|
(65, 18, 11, 2, '2025-01-05 16:15:00'),
|
||||||
|
(66, 22, 23, 4, '2025-01-06 17:30:00'),
|
||||||
|
(67, 9, 4, 1, '2025-01-07 10:45:00'),
|
||||||
|
(68, 16, 19, 0, '2025-01-08 12:00:00'),
|
||||||
|
(69, 13, 21, 5, '2025-01-09 14:15:00'),
|
||||||
|
(70, 26, 3, 3, '2025-01-10 16:30:00'),
|
||||||
|
(71, 5, 17, 2, '2025-01-11 10:00:00'),
|
||||||
|
(72, 12, 24, 1, '2025-01-12 11:30:00'),
|
||||||
|
(73, 15, 6, 0, '2025-01-13 13:45:00'),
|
||||||
|
(74, 2, 20, 3, '2025-01-14 15:00:00'),
|
||||||
|
(75, 10, 1, 2, '2025-01-15 16:15:00'),
|
||||||
|
(76, 14, 7, 4, '2025-01-16 17:30:00'),
|
||||||
|
(77, 23, 18, 1, '2025-01-17 10:45:00'),
|
||||||
|
(78, 11, 9, 0, '2025-01-18 12:00:00'),
|
||||||
|
(79, 21, 22, 5, '2025-01-19 14:15:00'),
|
||||||
|
(80, 4, 25, 3, '2025-01-20 16:30:00'),
|
||||||
|
(81, 19, 13, 2, '2025-02-01 10:00:00'),
|
||||||
|
(82, 6, 16, 1, '2025-02-02 11:30:00'),
|
||||||
|
(83, 17, 26, 0, '2025-02-03 13:45:00'),
|
||||||
|
(84, 1, 5, 3, '2025-02-04 15:00:00'),
|
||||||
|
(85, 24, 12, 2, '2025-02-05 16:15:00'),
|
||||||
|
(86, 7, 15, 4, '2025-02-06 17:30:00'),
|
||||||
|
(87, 2, 10, 1, '2025-02-07 10:45:00'),
|
||||||
|
(88, 20, 14, 0, '2025-02-08 12:00:00'),
|
||||||
|
(89, 9, 23, 5, '2025-02-09 14:15:00'),
|
||||||
|
(90, 18, 11, 3, '2025-02-10 16:30:00'),
|
||||||
|
(91, 3, 21, 2, '2025-03-01 10:00:00'),
|
||||||
|
(92, 25, 19, 1, '2025-03-02 11:30:00'),
|
||||||
|
(93, 16, 4, 0, '2025-03-03 13:45:00'),
|
||||||
|
(94, 22, 6, 3, '2025-03-04 15:00:00'),
|
||||||
|
(95, 13, 17, 2, '2025-03-05 16:15:00'),
|
||||||
|
(96, 26, 1, 4, '2025-03-06 17:30:00'),
|
||||||
|
(97, 5, 2, 1, '2025-03-07 10:45:00'),
|
||||||
|
(98, 10, 24, 0, '2025-03-08 12:00:00'),
|
||||||
|
(99, 14, 12, 5, '2025-03-09 14:15:00'),
|
||||||
|
(100, 23, 7, 3, '2025-03-10 16:30:00');
|
||||||
|
|
||||||
|
INSERT INTO ergebnis2vs2 (id, gewinner_vorn, gewinner_hinten, verlierer_vorn, verlierer_hinten, tore_verlierer, zeitpunkt) VALUES
|
||||||
|
(1, 5, 12, 1, 18, 2, '2024-11-01 10:00:00'),
|
||||||
|
(2, 7, 20, 3, 25, 1, '2024-11-02 11:30:00'),
|
||||||
|
(3, 22, 1, 10, 15, 0, '2024-11-03 13:45:00'),
|
||||||
|
(4, 11, 23, 2, 14, 3, '2024-11-04 15:00:00'),
|
||||||
|
(5, 26, 9, 16, 4, 2, '2024-11-05 16:15:00'),
|
||||||
|
(6, 13, 21, 8, 19, 4, '2024-11-06 17:30:00'),
|
||||||
|
(7, 1, 6, 24, 17, 1, '2024-11-07 10:45:00'),
|
||||||
|
(8, 20, 5, 9, 2, 0, '2024-11-08 12:00:00'),
|
||||||
|
(9, 14, 10, 23, 18, 5, '2024-11-09 14:15:00'),
|
||||||
|
(10, 4, 11, 21, 3, 3, '2024-11-10 16:30:00'),
|
||||||
|
(11, 19, 26, 6, 16, 2, '2024-11-11 10:00:00'),
|
||||||
|
(12, 17, 13, 5, 24, 1, '2024-11-12 11:30:00'),
|
||||||
|
(13, 12, 7, 25, 22, 0, '2024-11-13 13:45:00'),
|
||||||
|
(14, 15, 1, 2, 10, 3, '2024-11-14 15:00:00'),
|
||||||
|
(15, 23, 14, 11, 20, 2, '2024-11-15 16:15:00'),
|
||||||
|
(16, 18, 9, 26, 13, 4, '2024-11-16 17:30:00'),
|
||||||
|
(17, 4, 19, 21, 6, 1, '2024-11-17 10:45:00'),
|
||||||
|
(18, 16, 17, 7, 24, 0, '2024-11-18 12:00:00'),
|
||||||
|
(19, 1, 12, 10, 25, 5, '2024-11-19 14:15:00'),
|
||||||
|
(20, 14, 3, 20, 2, 3, '2024-11-20 16:30:00'),
|
||||||
|
(21, 9, 23, 13, 11, 2, '2024-11-21 10:00:00'),
|
||||||
|
(22, 22, 18, 24, 15, 1, '2024-11-22 11:30:00'),
|
||||||
|
(23, 5, 16, 17, 26, 0, '2024-11-23 13:45:00'),
|
||||||
|
(24, 3, 19, 6, 4, 3, '2024-11-24 15:00:00'),
|
||||||
|
(25, 11, 21, 25, 7, 2, '2024-11-25 16:15:00'),
|
||||||
|
(26, 12, 1, 15, 18, 4, '2024-11-26 17:30:00'),
|
||||||
|
(27, 2, 22, 10, 20, 1, '2024-11-27 10:45:00'),
|
||||||
|
(28, 14, 9, 23, 26, 0, '2024-11-28 12:00:00'),
|
||||||
|
(29, 19, 16, 4, 13, 5, '2024-11-29 14:15:00'),
|
||||||
|
(30, 21, 17, 6, 5, 3, '2024-11-30 16:30:00'),
|
||||||
|
(31, 20, 12, 7, 10, 2, '2024-12-01 10:00:00'),
|
||||||
|
(32, 1, 2, 25, 14, 1, '2024-12-02 11:30:00'),
|
||||||
|
(33, 18, 11, 22, 23, 0, '2024-12-03 13:45:00'),
|
||||||
|
(34, 9, 4, 16, 19, 3, '2024-12-04 15:00:00'),
|
||||||
|
(35, 13, 21, 26, 3, 2, '2024-12-05 16:15:00'),
|
||||||
|
(36, 5, 17, 12, 24, 4, '2024-12-06 17:30:00'),
|
||||||
|
(37, 15, 6, 2, 20, 1, '2024-12-07 10:45:00'),
|
||||||
|
(38, 10, 1, 14, 7, 0, '2024-12-08 12:00:00'),
|
||||||
|
(39, 23, 18, 11, 9, 5, '2024-12-09 14:15:00'),
|
||||||
|
(40, 21, 22, 4, 25, 3, '2024-12-10 16:30:00'),
|
||||||
|
(41, 19, 13, 6, 16, 2, '2024-12-11 10:00:00'),
|
||||||
|
(42, 17, 26, 1, 5, 1, '2024-12-12 11:30:00'),
|
||||||
|
(43, 24, 12, 7, 15, 0, '2024-12-13 13:45:00'),
|
||||||
|
(44, 2, 10, 20, 14, 3, '2024-12-14 15:00:00'),
|
||||||
|
(45, 9, 23, 18, 11, 2, '2024-12-15 16:15:00'),
|
||||||
|
(46, 3, 21, 25, 19, 4, '2024-12-16 17:30:00'),
|
||||||
|
(47, 16, 4, 22, 6, 1, '2024-12-17 10:45:00'),
|
||||||
|
(48, 13, 17, 26, 1, 0, '2024-12-18 12:00:00'),
|
||||||
|
(49, 5, 2, 10, 24, 5, '2024-12-19 14:15:00'),
|
||||||
|
(50, 14, 12, 23, 7, 3, '2024-12-20 16:30:00'),
|
||||||
|
(51, 18, 25, 15, 3, 2, '2024-12-21 10:00:00'),
|
||||||
|
(52, 22, 1, 7, 11, 1, '2024-12-22 11:30:00'),
|
||||||
|
(53, 20, 9, 13, 24, 0, '2024-12-23 13:45:00'),
|
||||||
|
(54, 4, 16, 2, 21, 3, '2024-12-24 15:00:00'),
|
||||||
|
(55, 6, 19, 17, 10, 2, '2024-12-25 16:15:00'),
|
||||||
|
(56, 26, 5, 1, 14, 4, '2024-12-26 17:30:00'),
|
||||||
|
(57, 12, 23, 11, 18, 1, '2024-12-27 10:45:00'),
|
||||||
|
(58, 2, 25, 20, 3, 0, '2024-12-28 12:00:00'),
|
||||||
|
(59, 15, 22, 7, 13, 5, '2024-12-29 14:15:00'),
|
||||||
|
(60, 24, 9, 16, 21, 3, '2024-12-30 16:30:00'),
|
||||||
|
(61, 10, 17, 4, 6, 2, '2025-01-01 10:00:00'),
|
||||||
|
(62, 1, 26, 19, 5, 1, '2025-01-02 11:30:00'),
|
||||||
|
(63, 11, 12, 14, 2, 0, '2025-01-03 13:45:00'),
|
||||||
|
(64, 18, 20, 23, 25, 3, '2025-01-04 15:00:00'),
|
||||||
|
(65, 3, 7, 21, 16, 2, '2025-01-05 16:15:00'),
|
||||||
|
(66, 22, 13, 1, 24, 4, '2025-01-06 17:30:00'),
|
||||||
|
(67, 5, 9, 17, 18, 1, '2025-01-07 10:45:00'),
|
||||||
|
(68, 10, 15, 2, 26, 0, '2025-01-08 12:00:00'),
|
||||||
|
(69, 14, 11, 20, 4, 5, '2025-01-09 14:15:00'),
|
||||||
|
(70, 23, 19, 12, 21, 3, '2025-01-10 16:30:00'),
|
||||||
|
(71, 6, 25, 3, 1, 2, '2025-01-11 10:00:00'),
|
||||||
|
(72, 16, 7, 22, 10, 1, '2025-01-12 11:30:00'),
|
||||||
|
(73, 17, 13, 14, 15, 0, '2025-01-13 13:45:00'),
|
||||||
|
(74, 18, 24, 5, 2, 3, '2025-01-14 15:00:00'),
|
||||||
|
(75, 11, 20, 23, 26, 2, '2025-01-15 16:15:00'),
|
||||||
|
(76, 9, 21, 4, 19, 4, '2025-01-16 17:30:00'),
|
||||||
|
(77, 12, 1, 6, 17, 1, '2025-01-17 10:45:00'),
|
||||||
|
(78, 25, 10, 3, 22, 0, '2025-01-18 12:00:00'),
|
||||||
|
(79, 7, 13, 15, 14, 5, '2025-01-19 14:15:00'),
|
||||||
|
(80, 2, 18, 24, 20, 3, '2025-01-20 16:30:00'),
|
||||||
|
(81, 16, 11, 5, 23, 2, '2025-02-01 10:00:00'),
|
||||||
|
(82, 1, 4, 9, 26, 1, '2025-02-02 11:30:00'),
|
||||||
|
(83, 19, 21, 12, 17, 0, '2025-02-03 13:45:00'),
|
||||||
|
(84, 6, 22, 13, 2, 3, '2025-02-04 15:00:00'),
|
||||||
|
(85, 20, 7, 10, 14, 2, '2025-02-05 16:15:00'),
|
||||||
|
(86, 25, 15, 1, 18, 4, '2025-02-06 17:30:00'),
|
||||||
|
(87, 23, 3, 24, 11, 1, '2025-02-07 10:45:00'),
|
||||||
|
(88, 5, 26, 16, 9, 0, '2025-02-08 12:00:00'),
|
||||||
|
(89, 12, 4, 17, 21, 5, '2025-02-09 14:15:00'),
|
||||||
|
(90, 13, 6, 2, 19, 3, '2025-02-10 16:30:00'),
|
||||||
|
(91, 10, 22, 18, 7, 2, '2025-03-01 10:00:00'),
|
||||||
|
(92, 14, 25, 11, 1, 1, '2025-03-02 11:30:00'),
|
||||||
|
(93, 20, 23, 3, 16, 0, '2025-03-03 13:45:00'),
|
||||||
|
(94, 9, 17, 26, 13, 3, '2025-03-04 15:00:00'),
|
||||||
|
(95, 21, 5, 24, 12, 2, '2025-03-05 16:15:00'),
|
||||||
|
(96, 4, 15, 2, 10, 4, '2025-03-06 17:30:00'),
|
||||||
|
(97, 6, 1, 18, 14, 1, '2025-03-07 10:45:00'),
|
||||||
|
(98, 19, 23, 7, 25, 0, '2025-03-08 12:00:00'),
|
||||||
|
(99, 11, 26, 22, 3, 5, '2025-03-09 14:15:00'),
|
||||||
|
(100, 16, 12, 5, 17, 3, '2025-03-10 16:30:00');
|
||||||
58
src/main/resources/schema.sql
Normal file
58
src/main/resources/schema.sql
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
CREATE SEQUENCE ergebnis1vs1_seq INCREMENT BY 50 START WITH 1;
|
||||||
|
|
||||||
|
CREATE SEQUENCE ergebnis2vs2_seq INCREMENT BY 50 START WITH 1;
|
||||||
|
|
||||||
|
CREATE SEQUENCE spieler_seq INCREMENT BY 50 START WITH 1;
|
||||||
|
|
||||||
|
CREATE TABLE ergebnis1vs1
|
||||||
|
(
|
||||||
|
id BIGINT NOT NULL,
|
||||||
|
gewinner INT NOT NULL,
|
||||||
|
verlierer INT NOT NULL,
|
||||||
|
tore_verlierer SMALLINT NOT NULL,
|
||||||
|
zeitpunkt datetime NULL,
|
||||||
|
CONSTRAINT pk_ergebnis1vs1 PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE ergebnis2vs2
|
||||||
|
(
|
||||||
|
id BIGINT NOT NULL,
|
||||||
|
gewinner_vorn INT NOT NULL,
|
||||||
|
gewinner_hinten INT NOT NULL,
|
||||||
|
verlierer_vorn INT NOT NULL,
|
||||||
|
verlierer_hinten INT NOT NULL,
|
||||||
|
tore_verlierer SMALLINT NOT NULL,
|
||||||
|
zeitpunkt datetime NOT NULL,
|
||||||
|
CONSTRAINT pk_ergebnis2vs2 PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE spieler
|
||||||
|
(
|
||||||
|
id INT NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
elo1vs1 FLOAT NOT NULL,
|
||||||
|
elo2vs2 FLOAT NOT NULL,
|
||||||
|
elo_alt FLOAT NULL,
|
||||||
|
CONSTRAINT pk_spieler PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE spieler
|
||||||
|
ADD CONSTRAINT uc_spieler_name UNIQUE (name);
|
||||||
|
|
||||||
|
ALTER TABLE ergebnis1vs1
|
||||||
|
ADD CONSTRAINT FK_ERGEBNIS1VS1_ON_GEWINNER FOREIGN KEY (gewinner) REFERENCES spieler (id);
|
||||||
|
|
||||||
|
ALTER TABLE ergebnis1vs1
|
||||||
|
ADD CONSTRAINT FK_ERGEBNIS1VS1_ON_VERLIERER FOREIGN KEY (verlierer) REFERENCES spieler (id);
|
||||||
|
|
||||||
|
ALTER TABLE ergebnis2vs2
|
||||||
|
ADD CONSTRAINT FK_ERGEBNIS2VS2_ON_GEWINNER_HINTEN FOREIGN KEY (gewinner_hinten) REFERENCES spieler (id);
|
||||||
|
|
||||||
|
ALTER TABLE ergebnis2vs2
|
||||||
|
ADD CONSTRAINT FK_ERGEBNIS2VS2_ON_GEWINNER_VORN FOREIGN KEY (gewinner_vorn) REFERENCES spieler (id);
|
||||||
|
|
||||||
|
ALTER TABLE ergebnis2vs2
|
||||||
|
ADD CONSTRAINT FK_ERGEBNIS2VS2_ON_VERLIERER_HINTEN FOREIGN KEY (verlierer_hinten) REFERENCES spieler (id);
|
||||||
|
|
||||||
|
ALTER TABLE ergebnis2vs2
|
||||||
|
ADD CONSTRAINT FK_ERGEBNIS2VS2_ON_VERLIERER_VORN FOREIGN KEY (verlierer_vorn) REFERENCES spieler (id);
|
||||||
Reference in New Issue
Block a user