5
0
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:
2025-05-31 18:47:38 +02:00
32 changed files with 639 additions and 211 deletions

View File

@@ -5,10 +5,11 @@ updates:
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
target-branch: "develop"
target-branch: "master"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
target-branch: "develop"
target-branch: "master"

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ target/
!**/src/test/**/target/
data.mv.db
src/main/bundles
node_modules
# for secrets
.env

View File

@@ -30,7 +30,7 @@ To build the project and run the application with the embedded H2 database, use
```sh
mvn clean package
mvn spring-boot:run
mvn spring-boot:run -Ptest
```

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
<version>3.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
@@ -19,7 +19,7 @@
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<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>
</properties>
@@ -46,11 +46,6 @@
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.appreciated</groupId>
<artifactId>apexcharts</artifactId>
<version>24.0.2</version>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>

View File

@@ -69,4 +69,10 @@ public class Spieler {
public String toString() {
return this.name;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Spieler)) return false;
return this.id == ((Spieler) o).id;
}
}

View File

@@ -3,6 +3,8 @@ package org.kickerelo.kickerelo.repository;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;
import org.kickerelo.kickerelo.data.Spieler;
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;
@Repository
@@ -12,7 +14,14 @@ public interface Ergebnis2vs2Repository extends JpaRepository<Ergebnis2vs2, Long
int countByVerliererHinten(Spieler verliererHinten);
int countByVerliererVorn(Spieler verlierVon);
int countByGewinnerVornOrGewinnerHinten(Spieler gewinnerVon, Spieler gewinnerHinten);
int countByGewinnerVornOrGewinnerHintenOrVerliererVornOrVerliererHinten(Spieler gewinnerVon, Spieler gewinnerHinten,
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);
}

View File

@@ -1,6 +1,8 @@
package org.kickerelo.kickerelo.service;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.util.EloChange1vs1;
import org.kickerelo.kickerelo.util.EloChange2vs2;
import org.springframework.stereotype.Service;
@@ -18,7 +20,7 @@ public class EloCalculationService {
* @param verlierer The entity representing 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 reductionPerGoal = 0.1f * baseK;
final float finalK = baseK - (reductionPerGoal * toreVerlierer);
@@ -28,6 +30,8 @@ public class EloCalculationService {
gewinner.setElo1vs1(gewinner.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 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 double adjustedK = baseK * (1 - (0.1 * toreVerlierer));
var totalWinnerElo = gewinnerVorn.getElo2vs2() + gewinnerHinten.getElo2vs2();
@@ -60,6 +64,8 @@ public class EloCalculationService {
gewinnerHinten.setElo2vs2((float) (gewinnerHinten.getElo2vs2() + winner2EloChange));
verliererVorn.setElo2vs2((float) (verliererVorn.getElo2vs2() + loser1EloChange));
verliererHinten.setElo2vs2((float) (verliererHinten.getElo2vs2() + loser2EloChange));
return new EloChange2vs2(winner1EloChange, winner2EloChange, loser1EloChange, loser2EloChange);
}
public float getInitialElo1vs1() {

View File

@@ -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;
}
}

View File

@@ -2,7 +2,6 @@ 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;
@@ -10,10 +9,11 @@ import org.kickerelo.kickerelo.exception.PlayerNameNotSetException;
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.kickerelo.kickerelo.util.EloChange1vs1;
import org.kickerelo.kickerelo.util.EloChange2vs2;
import org.kickerelo.kickerelo.util.comparator.Ergebnis1vs1TimeComparator;
import org.kickerelo.kickerelo.util.comparator.Ergebnis2vs2TimeComparator;
import org.kickerelo.kickerelo.util.comparator.SpielerNameComparator;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@@ -25,14 +25,22 @@ import java.util.stream.Stream;
*/
@Service
public class KickerEloService {
@Autowired
private Ergebnis1vs1Repository ergebnis1vs1Repository;
@Autowired
private Ergebnis2vs2Repository ergebnis2vs2Repository;
@Autowired
private SpielerRepository spielerRepository;
@Autowired
private EloCalculationService eloCalculationService;
private final Ergebnis1vs1Repository ergebnis1vs1Repository;
private final Ergebnis2vs2Repository ergebnis2vs2Repository;
private final SpielerRepository spielerRepository;
private final EloCalculationService eloCalculationService;
public KickerEloService(Ergebnis1vs1Repository ergebnis1vs1Repository,
Ergebnis2vs2Repository ergebnis2vs2Repository,
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
@@ -45,68 +53,61 @@ public class KickerEloService {
* @return List of all player entities sorted by 1 vs 1 ELO
*/
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
* @param gewinnerName The name of the winning player
* @param verliererName The name of the losing player
* @param gewinner The winning player
* @param verlierer The losing player
* @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) {
// 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");
}
if (gewinnerName.equals(verliererName)) {
if (gewinner.equals(verlierer)) {
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));
Spieler verlierer = spielerRepository.findByName(verliererName)
.orElseThrow(() -> new NoSuchPlayerException(verliererName));
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(verlierer);
}
/**
* Enter the result of a 2 vs 2 game
* @param gewinnerNameVorn Name of the winning offensive player
* @param gewinnerNameHinten Name of the winning defensive player
* @param verliererNameVorn Name of the losing offensive player
* @param verliererNameHinten Name of the losing defensive player
* @param gewinnerVorn winning offensive player
* @param gewinnerHinten winning defensive player
* @param verliererVorn losing offensive player
* @param verliererHinten losing defensive player
* @param toreVerlierer Number of goals of the losing team
*/
public void enterResult2vs2(String gewinnerNameVorn, String gewinnerNameHinten,
String verliererNameVorn, String verliererNameHinten,
public void enterResult2vs2(Spieler gewinnerVorn, Spieler gewinnerHinten,
Spieler verliererVorn, Spieler verliererHinten,
short toreVerlierer) {
// Check if the inputs are valid
if (gewinnerNameVorn == null || gewinnerNameHinten == null
|| verliererNameVorn == null || verliererNameHinten == null) {
if (gewinnerVorn == null || gewinnerHinten == null
|| verliererVorn == null || verliererHinten == null) {
throw new PlayerNameNotSetException("Alle Namen müssen gesetzt sein");
}
if (gewinnerNameVorn.equals(gewinnerNameHinten) ||
gewinnerNameVorn.equals(verliererNameVorn) ||
gewinnerNameVorn.equals(verliererNameHinten) ||
gewinnerNameHinten.equals(verliererNameVorn) ||
gewinnerNameHinten.equals(verliererNameHinten) ||
verliererNameVorn.equals(verliererNameHinten)) {
if (gewinnerVorn.equals(gewinnerHinten) ||
gewinnerVorn.equals(verliererVorn) ||
gewinnerVorn.equals(verliererHinten) ||
gewinnerHinten.equals(verliererVorn) ||
gewinnerHinten.equals(verliererHinten) ||
verliererVorn.equals(verliererHinten)) {
throw new DuplicatePlayerException("players must not be identical");
}
@@ -114,22 +115,11 @@ public class KickerEloService {
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);
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(gewinnerHinten);
spielerRepository.save(verliererVorn);
@@ -169,9 +159,10 @@ public class KickerEloService {
}
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());
EloChange1vs1 c = eloCalculationService.updateElo1vs1(players.get(r.getGewinner().getId()),
players.get(r.getVerlierer().getId()),
r.getToreVerlierer());
EloChangeTracker.put1vs1Result(r.getId(), c);
});
spielerRepository.saveAll(players.values());
}
@@ -187,11 +178,12 @@ public class KickerEloService {
}
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());
EloChange2vs2 c = eloCalculationService.updateElo2vs2(players.get(r.getGewinnerVorn().getId()),
players.get(r.getGewinnerHinten().getId()),
players.get(r.getVerliererVorn().getId()),
players.get(r.getVerliererHinten().getId()),
r.getToreVerlierer());
EloChangeTracker.put2vs2Result(r.getId(), c);
});
spielerRepository.saveAll(players.values());
}

View File

@@ -35,7 +35,7 @@ public class Stat2vs2Service {
}
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);
return (float) numFront / numAll;
}

View File

@@ -0,0 +1,5 @@
package org.kickerelo.kickerelo.util;
public record EloChange1vs1(double winnerEloChange,
double loserEloChange) {
}

View File

@@ -0,0 +1,7 @@
package org.kickerelo.kickerelo.util;
public record EloChange2vs2(double winnerFrontEloChange,
double winnerBackEloChange,
double loserFrontEloChange,
double loserBackEloChange) {
}

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Ergebnis1vs1;

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Ergebnis2vs2;

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Spieler;

View File

@@ -1,4 +1,4 @@
package org.kickerelo.kickerelo.util;
package org.kickerelo.kickerelo.util.comparator;
import org.kickerelo.kickerelo.data.Spieler;

View File

@@ -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());
}
}

View 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -1,5 +1,6 @@
package org.kickerelo.kickerelo.views;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
@@ -21,12 +22,12 @@ public class Enter1vs1View extends VerticalLayout {
public Enter1vs1View(KickerEloService eloService) {
H2 subheading = new H2("1 vs 1 Ergebnis");
ComboBox<String> winnerSelect = new ComboBox<>("Gewinner");
winnerSelect.setItems(eloService.getSpielerNamen());
ComboBox<Spieler> winnerSelect = new ComboBox<>("Gewinner");
winnerSelect.setItems(eloService.getSpielerEntities());
winnerSelect.setPlaceholder("Spieler auswählen");
ComboBox<String> loserSelect = new ComboBox<>("Verlierer");
loserSelect.setItems(eloService.getSpielerNamen());
ComboBox<Spieler> loserSelect = new ComboBox<>("Verlierer");
loserSelect.setItems(eloService.getSpielerEntities());
loserSelect.setPlaceholder("Spieler auswählen");
IntegerField loserGoals = new IntegerField("Tore des Verlierers");

View File

@@ -1,5 +1,6 @@
package org.kickerelo.kickerelo.views;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.exception.DuplicatePlayerException;
import org.kickerelo.kickerelo.exception.InvalidDataException;
import org.kickerelo.kickerelo.exception.NoSuchPlayerException;
@@ -20,20 +21,20 @@ public class Enter2vs2View extends VerticalLayout {
public Enter2vs2View(KickerEloService eloService) {
H2 subheading = new H2("2 vs 2 Ergebnis");
ComboBox<String> winnerFrontSelect = new ComboBox<>("Gewinner vorne");
winnerFrontSelect.setItems(eloService.getSpielerNamen());
ComboBox<Spieler> winnerFrontSelect = new ComboBox<>("Gewinner vorne");
winnerFrontSelect.setItems(eloService.getSpielerEntities());
winnerFrontSelect.setPlaceholder("Spieler auswählen");
ComboBox<String> winnerBackSelect = new ComboBox<>("Gewinner hinten");
winnerBackSelect.setItems(eloService.getSpielerNamen());
ComboBox<Spieler> winnerBackSelect = new ComboBox<>("Gewinner hinten");
winnerBackSelect.setItems(eloService.getSpielerEntities());
winnerBackSelect.setPlaceholder("Spieler auswählen");
ComboBox<String> loserFrontSelect = new ComboBox<>("Verlierer vorne");
loserFrontSelect.setItems(eloService.getSpielerNamen());
ComboBox<Spieler> loserFrontSelect = new ComboBox<>("Verlierer vorne");
loserFrontSelect.setItems(eloService.getSpielerEntities());
loserFrontSelect.setPlaceholder("Spieler auswählen");
ComboBox<String> loserBackSelect = new ComboBox<>("Verlierer hinten");
loserBackSelect.setItems(eloService.getSpielerNamen());
ComboBox<Spieler> loserBackSelect = new ComboBox<>("Verlierer hinten");
loserBackSelect.setItems(eloService.getSpielerEntities());
loserBackSelect.setPlaceholder("Spieler auswählen");
IntegerField loserGoals = new IntegerField("Tore des Verlierers");

View File

@@ -1,9 +1,11 @@
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.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
@@ -11,14 +13,17 @@ import com.vaadin.flow.router.Route;
@Route("app/graph1vs1")
public class Graph1vs1View extends VerticalLayout {
ApexCharts chart1vs1;
public Graph1vs1View(SpielerRepository repo) {
setSizeFull();
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);
}
}

View File

@@ -1,23 +1,29 @@
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.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
@Route("app/graph2vs2")
public class Graph2vs2View extends VerticalLayout {
ApexCharts chart2vs2;
public Graph2vs2View(SpielerRepository repo) {
setSizeFull();
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);
}
}

View File

@@ -12,17 +12,22 @@ import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
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.data.provider.SortDirection;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
@Route("app/history1vs1")
public class History1vs1View extends VerticalLayout {
public class History1vs1View extends HistoryView {
private final Ergebnis1vs1Repository repo;
List<Ergebnis1vs1> res;
public History1vs1View(Ergebnis1vs1Repository repo) {
this.repo = repo;
setSizeFull();
H2 subheading = new H2("Spiele 1 vs 1");
res = repo.findAll();
@@ -50,8 +55,10 @@ public class History1vs1View extends VerticalLayout {
grid.removeColumnByKey("id");
Grid.Column<Ergebnis1vs1> winnerColumn = grid.getColumnByKey("gewinner");
winnerColumn.setHeader("Gewinner");
winnerColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType1vs1.GEWINNER)));
Grid.Column<Ergebnis1vs1> loserColumn = grid.getColumnByKey("verlierer");
loserColumn.setHeader("Verlierer");
loserColumn.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType1vs1.VERLIERER)));
Grid.Column<Ergebnis1vs1> goals = grid.getColumnByKey("toreVerlierer");
goals.setHeader("Verlierertore");
Grid.Column<Ergebnis1vs1> timestamp = grid.getColumnByKey("timestamp");

View File

@@ -13,16 +13,21 @@ import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
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.data.provider.SortDirection;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LocalDateTimeRenderer;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
@Route("app/history2vs2")
public class History2vs2View extends VerticalLayout {
public class History2vs2View extends HistoryView {
private final Ergebnis2vs2Repository repo;
public History2vs2View(Ergebnis2vs2Repository repo) {
this.repo = repo;
setSizeFull();
H2 subheading = new H2("Spiele 2 vs 2");
List<Ergebnis2vs2> res = repo.findAll();
@@ -64,12 +69,16 @@ public class History2vs2View extends VerticalLayout {
grid.removeColumnByKey("id");
Grid.Column<Ergebnis2vs2> winnerFront = grid.getColumnByKey("gewinnerVorn");
winnerFront.setHeader("Gewinner vorne");
winnerFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.GEWINNER_VORN)));
Grid.Column<Ergebnis2vs2> winnerBack = grid.getColumnByKey("gewinnerHinten");
winnerBack.setHeader("Gewinner hinten");
winnerBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.GEWINNER_HINTEN)));
Grid.Column<Ergebnis2vs2> loserFront = grid.getColumnByKey("verliererVorn");
loserFront.setHeader("Verlierer vorne");
loserFront.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.VERLIERER_VORN)));
Grid.Column<Ergebnis2vs2> loserBack = grid.getColumnByKey("verliererHinten");
loserBack.setHeader("Verlierer hinten");
loserBack.setRenderer(new ComponentRenderer<>(ergebnis -> formatEloChange(ergebnis, PlayerType2vs2.VERLIERER_HINTEN)));
Grid.Column<Ergebnis2vs2> goals = grid.getColumnByKey("toreVerlierer");
goals.setHeader("Verlierertore");
Grid.Column<Ergebnis2vs2> timestamp = grid.getColumnByKey("timestamp");

View 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;
}
}

View File

@@ -1,6 +1,7 @@
package org.kickerelo.kickerelo.views;
import org.kickerelo.kickerelo.data.Spieler;
import org.kickerelo.kickerelo.repository.Ergebnis2vs2Repository;
import org.kickerelo.kickerelo.service.KickerEloService;
import org.kickerelo.kickerelo.service.Stat2vs2Service;
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.html.H2;
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.progressbar.ProgressBar;
import com.vaadin.flow.component.progressbar.ProgressBarVariant;
@@ -17,29 +19,43 @@ import com.vaadin.flow.router.Route;
public class Stat2vs2View extends VerticalLayout {
Stat2vs2Service stat2vs2Service;
KickerEloService kickerEloService;
Ergebnis2vs2Repository repo;
H2 subheading;
ComboBox<Spieler> selector;
Paragraph generalInfo = new Paragraph();
ProgressBar frontRate = new ProgressBar();
NativeLabel frontRateText = new NativeLabel();
ProgressBar winRateFront = new ProgressBar();
NativeLabel winRateFrontText = new NativeLabel();
ProgressBar winRateBack = new ProgressBar();
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.kickerEloService = kickerService;
subheading = new H2("2 vs 2 Ergebnis");
this.repo = repo;
subheading = new H2("2 vs 2 Spielerstatistik");
selector = new ComboBox<>("Spieler");
selector.setItems(kickerService.getSpielerEntities());
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) {
updateGeneralInfo(s);
updateFrontRate(s);
updateFrontWinrate(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) {
@@ -66,4 +82,14 @@ public class Stat2vs2View extends VerticalLayout {
winRateBack.addThemeVariants((winRate > 0.5f ? ProgressBarVariant.LUMO_SUCCESS : ProgressBarVariant.LUMO_ERROR));
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));
}
}

View File

@@ -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}
vaadin.urlMapping=/app/*
# In prod mode, never add the test data to the database
spring.sql.init.mode=never

View File

@@ -3,7 +3,6 @@ logging.level.org.atmosphere = warn
logging.level.org.springframework.security=DEBUG
spring.mustache.check-template-location = false
spring.datasource.url=jdbc:h2:file:./data
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

231
src/main/resources/data.sql Normal file
View 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');

View 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);