Browse Source

table

master
GAM 4 years ago
parent
commit
1e87b2e167
10 changed files with 726 additions and 208 deletions
  1. +1
    -1
      frontend/views/results/results-view.css
  2. +3
    -0
      frontend/views/table/table-view.css
  3. +8
    -8
      src/main/java/com/example/application/data/bean/CalculatedMatch.java
  4. +129
    -0
      src/main/java/com/example/application/data/bean/PlayerForTable.java
  5. +9
    -9
      src/main/java/com/example/application/data/service/MatchService.java
  6. +149
    -1
      src/main/java/com/example/application/data/service/PlayerService.java
  7. +199
    -0
      src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigationView.java
  8. +3
    -0
      src/main/java/com/example/application/views/main/MainView.java
  9. +79
    -189
      src/main/java/com/example/application/views/results/ResultsView.java
  10. +146
    -0
      src/main/java/com/example/application/views/table/TableView.java

+ 1
- 1
frontend/views/results/results-view.css View File

@ -12,7 +12,7 @@
font-size: x-large;
}
.matchday_grid {
.my_grid {
--_lumo-grid-border-width: 0;
}


+ 3
- 0
frontend/views/table/table-view.css View File

@ -0,0 +1,3 @@
.about-view {
display: block;
}

src/main/java/com/example/application/data/bean/MatchForMatchdayView.java → src/main/java/com/example/application/data/bean/CalculatedMatch.java View File

@ -3,14 +3,14 @@ package com.example.application.data.bean;
import com.example.application.data.entity.Match;
import com.example.application.data.entity.Player;
public class MatchForMatchdayView {
public class CalculatedMatch {
private Match match;
private Player player1;
private Player player2;
private Integer score1;
private Integer score2;
private Double score1;
private Double score2;
public MatchForMatchdayView(Match match, Player player1, Player player2, Integer score1, Integer score2) {
public CalculatedMatch(Match match, Player player1, Player player2, Double score1, Double score2) {
this.match = match;
this.player1 = player1;
this.player2 = player2;
@ -42,19 +42,19 @@ public class MatchForMatchdayView {
this.player2 = player2;
}
public Integer getScore1() {
public Double getScore1() {
return score1;
}
public void setScore1(Integer score1) {
public void setScore1(Double score1) {
this.score1 = score1;
}
public Integer getScore2() {
public Double getScore2() {
return score2;
}
public void setScore2(Integer score2) {
public void setScore2(Double score2) {
this.score2 = score2;
}
}

+ 129
- 0
src/main/java/com/example/application/data/bean/PlayerForTable.java View File

@ -0,0 +1,129 @@
package com.example.application.data.bean;
import com.example.application.data.entity.Player;
public class PlayerForTable {
private Player player;
private Integer matchPoints;
private Integer amountOfMatches;
private Integer amountOfMatchesWon;
private Integer amountOfMatchesDrawn;
private Integer amountOfMatchesLost;
private Double gamePointsForSelf;
private Double gamePointsForOpponents;
private Double gamePointDiff;
private Integer place;
private Integer placeDiffToLastMatchday;
public PlayerForTable(Player player,
Integer matchPoints,
Integer amountOfMatches,
Integer amountOfMatchesWon,
Integer amountOfMatchesDrawn,
Integer amountOfMatchesLost,
Double gamePointsForSelf,
Double gamePointsForOpponents,
Double gamePointDiff) {
this.player = player;
this.matchPoints = matchPoints;
this.amountOfMatches = amountOfMatches;
this.amountOfMatchesWon = amountOfMatchesWon;
this.amountOfMatchesDrawn = amountOfMatchesDrawn;
this.amountOfMatchesLost = amountOfMatchesLost;
this.gamePointsForSelf = gamePointsForSelf;
this.gamePointsForOpponents = gamePointsForOpponents;
this.gamePointDiff = gamePointDiff;
}
public Player getPlayer() {
return player;
}
public void setPlayer(Player player) {
this.player = player;
}
public Integer getMatchPoints() {
return matchPoints;
}
public void setMatchPoints(Integer matchPoints) {
this.matchPoints = matchPoints;
}
public Integer getAmountOfMatches() {
return amountOfMatches;
}
public void setAmountOfMatches(Integer amountOfMatches) {
this.amountOfMatches = amountOfMatches;
}
public Integer getAmountOfMatchesWon() {
return amountOfMatchesWon;
}
public void setAmountOfMatchesWon(Integer amountOfMatchesWon) {
this.amountOfMatchesWon = amountOfMatchesWon;
}
public Integer getAmountOfMatchesDrawn() {
return amountOfMatchesDrawn;
}
public void setAmountOfMatchesDrawn(Integer amountOfMatchesDrawn) {
this.amountOfMatchesDrawn = amountOfMatchesDrawn;
}
public Integer getAmountOfMatchesLost() {
return amountOfMatchesLost;
}
public void setAmountOfMatchesLost(Integer amountOfMatchesLost) {
this.amountOfMatchesLost = amountOfMatchesLost;
}
public Double getGamePointsForSelf() {
return gamePointsForSelf;
}
public void setGamePointsForSelf(Double gamePointsForSelf) {
this.gamePointsForSelf = gamePointsForSelf;
}
public Double getGamePointsForOpponents() {
return gamePointsForOpponents;
}
public void setGamePointsForOpponents(Double gamePointsForOpponents) {
this.gamePointsForOpponents = gamePointsForOpponents;
}
public Double getGamePointDiff() {
return gamePointDiff;
}
public void setGamePointDiff(Double gamePointDiff) {
this.gamePointDiff = gamePointDiff;
}
public Integer getPlace() {
return place;
}
public void setPlace(Integer place) {
this.place = place;
}
public Integer getPlaceDiffToLastMatchday() {
return placeDiffToLastMatchday;
}
public void setPlaceDiffToLastMatchday(Integer placeDiffToLastMatchday) {
this.placeDiffToLastMatchday = placeDiffToLastMatchday;
}
}

+ 9
- 9
src/main/java/com/example/application/data/service/MatchService.java View File

@ -1,6 +1,6 @@
package com.example.application.data.service;
import com.example.application.data.bean.MatchForMatchdayView;
import com.example.application.data.bean.CalculatedMatch;
import com.example.application.data.entity.Game;
import com.example.application.data.entity.Match;
import com.example.application.data.entity.Matchday;
@ -25,25 +25,25 @@ public class MatchService extends CrudService<Match, Integer> {
return repository;
}
public List<MatchForMatchdayView> getMatchesForMatchdayView(Matchday matchday) {
public List<CalculatedMatch> getCalculatedMatches(Matchday matchday) {
return repository.findAll().stream()
.filter(match -> match.getMatchday().equals(matchday))
.map(match -> new MatchForMatchdayView(match, match.getPlayer1(), match.getPlayer2(), getScore1(match), getScore2(match)))
.map(match -> new CalculatedMatch(match, match.getPlayer1(), match.getPlayer2(), getScore1(match), getScore2(match)))
.collect(Collectors.toList());
}
private static Integer getScore1(Match match) {
int score = 0;
private static double getScore1(Match match) {
double score = 0;
for (Game game : match.getGames()) {
score += ((game.getPlayer1IsWhite() ? game.getResult() : -game.getResult()) + 1) / 2;
score += (double) ((game.getPlayer1IsWhite() ? game.getResult() : -game.getResult()) + 1) / 2;
}
return score;
}
private static Integer getScore2(Match match) {
int score = 0;
private static double getScore2(Match match) {
double score = 0;
for (Game game : match.getGames()) {
score += ((game.getPlayer1IsWhite() ? -game.getResult() : game.getResult()) + 1) / 2;
score += (double) ((game.getPlayer1IsWhite() ? -game.getResult() : game.getResult()) + 1) / 2;
}
return score;
}


+ 149
- 1
src/main/java/com/example/application/data/service/PlayerService.java View File

@ -1,17 +1,30 @@
package com.example.application.data.service;
import com.example.application.data.bean.CalculatedMatch;
import com.example.application.data.bean.PlayerForTable;
import com.example.application.data.entity.Matchday;
import com.example.application.data.entity.Player;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.vaadin.artur.helpers.CrudService;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Service
public class PlayerService extends CrudService<Player, Integer> {
private final PlayerRepository repository;
public PlayerService(@Autowired PlayerRepository repository) {
private final MatchdayService matchdayService;
private final MatchService matchService;
public PlayerService(@Autowired PlayerRepository repository, @Autowired MatchdayService matchdayService, @Autowired MatchService matchService) {
this.repository = repository;
this.matchdayService = matchdayService;
this.matchService = matchService;
}
@Override
@ -19,4 +32,139 @@ public class PlayerService extends CrudService<Player, Integer> {
return repository;
}
public List<PlayerForTable> getPlayersForTable(Matchday matchday) {
return new PlayerForTableProvider(matchday).getPlayersForTableSorted();
}
private class PlayerForTableProvider {
List<Matchday> matchdays = new ArrayList<>();
List<CalculatedMatch> calculatedMatches = new ArrayList<>();
List<Player> players = repository.findAll();
public PlayerForTableProvider(Matchday matchday) {
matchdays.addAll(matchdayService.getMatchdaysForSeason(matchday.getSeason()).stream()
.filter(matchdayToFilter -> matchdayToFilter.getNumber() <= matchday.getNumber())
.collect(Collectors.toList()));
calculatedMatches.addAll(matchdays.stream()
.flatMap((Function<Matchday, Stream<CalculatedMatch>>) matchdayToMap -> matchService.getCalculatedMatches(matchdayToMap).stream())
.collect(Collectors.toList()));
}
private List<PlayerForTable> getPlayersForTableSorted() {
List<PlayerForTable> playerForTableList = players.stream()
.map(this::getPlayerForTable)
.sorted(Comparator.comparingDouble(PlayerForTable::getGamePointsForSelf).reversed())
.sorted(Comparator.comparingDouble(PlayerForTable::getGamePointDiff).reversed())
.sorted(Comparator.comparingInt(PlayerForTable::getMatchPoints).reversed())
.collect(Collectors.toList());
int offset = 0;
PlayerForTable lastPlayer;
PlayerForTable currentPlayer;
for (int i = 0; i < playerForTableList.size(); i++) {
currentPlayer = playerForTableList.get(i);
if (i>0) {
lastPlayer = playerForTableList.get(i-1);
if (Objects.equals(currentPlayer.getMatchPoints(), lastPlayer.getMatchPoints())
&& Objects.equals(currentPlayer.getGamePointsForSelf(), lastPlayer.getGamePointsForSelf())
&& Objects.equals(currentPlayer.getGamePointsForOpponents(), lastPlayer.getGamePointsForOpponents())) {
offset +=1;
}
else {
offset = 0;
}
}
currentPlayer.setPlace(i + 1 - offset);
}
// TODO: add diff to last matchday
return playerForTableList;
}
private PlayerForTable getPlayerForTable(Player player) {
List<CalculatedMatch> matchesAsPlayer1 = getMatchesAsPlayer1(player);
List<CalculatedMatch> matchesAsPlayer2 = getMatchesAsPlayer2(player);
Map<WinState, List<CalculatedMatch>> map = getWinStateLists(matchesAsPlayer1, matchesAsPlayer2);
int amountOfMatchesWon = map.get(WinState.WON).size();
int amountOfMatchesDrawn = map.get(WinState.DRAWN).size();
int amountOfMatchesLost = map.get(WinState.LOST).size();
int amountOfMatches = amountOfMatchesWon + amountOfMatchesDrawn + amountOfMatchesLost;
int matchPoints = amountOfMatchesWon * 2 + amountOfMatchesDrawn;
double gamePointsForSelf = 0;
gamePointsForSelf += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore1).sum();
gamePointsForSelf += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore2).sum();
double gamePointsForOpponents = 0;
gamePointsForOpponents += matchesAsPlayer1.stream().mapToDouble(CalculatedMatch::getScore2).sum();
gamePointsForOpponents += matchesAsPlayer2.stream().mapToDouble(CalculatedMatch::getScore1).sum();
double gamePointDiff = gamePointsForSelf - gamePointsForOpponents;
return new PlayerForTable(player,
matchPoints,
amountOfMatches,
amountOfMatchesWon,
amountOfMatchesDrawn,
amountOfMatchesLost,
gamePointsForSelf,
gamePointsForOpponents,
gamePointDiff);
}
private List<CalculatedMatch> getMatchesAsPlayer1(Player player) {
return calculatedMatches.stream()
.filter(match -> match.getPlayer1().equals(player))
.collect(Collectors.toList());
}
private List<CalculatedMatch> getMatchesAsPlayer2(Player player) {
return calculatedMatches.stream()
.filter(match -> match.getPlayer2().equals(player))
.collect(Collectors.toList());
}
private Map<WinState, List<CalculatedMatch>> getWinStateLists(List<CalculatedMatch> matchesAsPlayer1, List<CalculatedMatch> matchesAsPlayer2) {
Map<WinState, List<CalculatedMatch>> map = new HashMap<>();
map.put(WinState.WON, new ArrayList<>());
map.put(WinState.DRAWN, new ArrayList<>());
map.put(WinState.LOST, new ArrayList<>());
for (CalculatedMatch match : matchesAsPlayer1) {
if (match.getScore1() > match.getScore2()) {
map.get(WinState.WON).add(match);
continue;
}
if (match.getScore1() < match.getScore2()) {
map.get(WinState.LOST).add(match);
continue;
}
if (match.getScore1() > 0) {
map.get(WinState.DRAWN).add(match);
}
}
for (CalculatedMatch match : matchesAsPlayer2) {
if (match.getScore2() > match.getScore1()) {
map.get(WinState.WON).add(match);
continue;
}
if (match.getScore2() < match.getScore1()) {
map.get(WinState.LOST).add(match);
continue;
}
if (match.getScore2() > 0) {
map.get(WinState.DRAWN).add(match);
}
}
return map;
}
}
private enum WinState {
WON, DRAWN, LOST
}
}

+ 199
- 0
src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigationView.java View File

@ -0,0 +1,199 @@
package com.example.application.views.abstractnavigation;
import com.example.application.data.entity.Matchday;
import com.example.application.data.entity.Season;
import com.example.application.data.service.MatchdayService;
import com.example.application.data.service.SeasonService;
import com.vaadin.flow.component.AbstractField;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasValue;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.select.Select;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.WildcardParameter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.util.ArrayList;
import java.util.List;
public abstract class SeasonAndMatchdayNavigationView extends Div implements HasUrlParameter<String> {
// TODO: show dropdown menus also for invalid URLs (with content that fits the situation)
protected final SeasonService seasonService;
protected final MatchdayService matchdayService;
protected String seasonParam;
protected String matchdayParam;
private final Label invalidUrlLabel = new Label();
protected final VerticalLayout outer = new VerticalLayout();
protected final HorizontalLayout selectionLayout = new HorizontalLayout();
protected final List<Season> seasonList = new ArrayList<>();
protected final Select<Season> seasonSelect = new Select<>();
protected final List<Matchday> matchdayList = new ArrayList<>();
protected final Select<Matchday> matchdaySelect = new Select<>();
protected Component contentLayout;
public SeasonAndMatchdayNavigationView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService) {
this.seasonService = seasonService;
this.matchdayService = matchdayService;
addClassName("results-view");
configureOuterLayout();
configureSelectionLayout();
configureContentLayout();
}
protected abstract String route();
protected abstract void configureContentLayout();
protected abstract void configureContent();
private void configureOuterLayout() {
add(outer);
outer.setAlignItems(FlexComponent.Alignment.CENTER);
}
private void configureSelectionLayout() {
selectionLayout.setWidthFull();
selectionLayout.setAlignItems(FlexComponent.Alignment.CENTER);
selectionLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
selectionLayout.add(new Label("Season:"), seasonSelect, new Label("Matchday:"), matchdaySelect);
seasonSelect.addValueChangeListener(seasonSelectValueChangeListener());
matchdaySelect.addValueChangeListener(matchdaySelectValueChangeListener());
// provide data
fillSeasonSelectWithData();
}
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Matchday>, Matchday>> matchdaySelectValueChangeListener() {
return matchdayChangeEvent -> getUI().ifPresent(ui -> {
Season season = seasonSelect.getValue();
Matchday matchday = matchdayChangeEvent.getValue();
if (season != null && matchday != null) {
String seasonParam = season.toString();
String matchdayParam = matchday.toString();
navigate(ui, seasonParam, matchdayParam);
}
});
}
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Season>, Season>> seasonSelectValueChangeListener() {
return seasonChangeEvent -> getUI().ifPresent(ui -> {
Season newSeason = seasonChangeEvent.getValue();
if (newSeason != null) {
String seasonParam = newSeason.toString();
String matchdayParam = null;
Matchday matchdayInNewSeason = null;
Matchday matchdayInOldSeason = matchdaySelect.getValue();
if (matchdayInOldSeason != null) {
matchdayParam = matchdayInOldSeason.toString();
matchdayInNewSeason = getMatchdayFromParam(matchdayParam, newSeason);
}
matchdayParam = matchdayInNewSeason == null ? "1" : matchdayParam;
navigate(ui, seasonParam, matchdayParam);
}
});
}
private void fillSeasonSelectWithData() {
seasonList.clear();
seasonList.addAll(seasonService.getAllSeasonsSorted());
seasonSelect.setItems(seasonList);
}
private void fillMatchdaySelectWithData() {
matchdayList.clear();
matchdayList.addAll(matchdayService.getMatchdaysForSeason(seasonSelect.getValue()));
matchdaySelect.setItems(matchdayList);
}
protected boolean isMatchDayParamValid(@NonNull String matchdayParam) {
return matchdayList.stream().anyMatch(matchday -> matchdayParam.equals(matchday.toString()));
}
protected void navigate(UI ui, String seasonParam, String matchdayParam) {
ui.navigate(String.format("%s/%s/%s/", route(), seasonParam, matchdayParam));
}
@Override
public void setParameter(BeforeEvent beforeEvent, @WildcardParameter String param) {
outer.removeAll();
if (!param.matches("^[0-9]*/[0-9]*/?$")) {
invalidUrlLabel.setText("Invalid URL! Please provide params in the form season/matchday/");
outer.add(invalidUrlLabel);
return;
}
String[] params = param.split("/");
seasonParam = params[0];
matchdayParam = params[1];
Season season = getSeasonFromParam(seasonParam);
if (season == null) {
invalidUrlLabel.setText(String.format("Invalid URL! Season \"%s\" does not exist in the database!", seasonParam));
outer.add(invalidUrlLabel);
return;
}
seasonSelect.setValue(season);
fillMatchdaySelectWithData();
Matchday matchday = getMatchdayFromParam(matchdayParam);
if (matchday == null) {
invalidUrlLabel.setText(String.format("Invalid URL! Matchday \"%s\" in Season \"%s\" does not exist in the database!", matchdayParam, seasonParam));
outer.add(invalidUrlLabel);
return;
}
matchdaySelect.setValue(matchday);
outer.add(selectionLayout);
outer.add(contentLayout);
configureContent();
}
@Nullable
private Season getSeasonFromParam(@NonNull String seasonParam) {
for (Season season : seasonList) {
if (seasonParam.equals(season.toString())) {
return season;
}
}
return null;
}
@Nullable
private Matchday getMatchdayFromParam(@NonNull String matchdayParam) {
return getMatchdayFromParam(matchdayParam, null);
}
@Nullable
private Matchday getMatchdayFromParam(@NonNull String matchdayParam, @Nullable Season season) {
List<Matchday> matchdayList = season == null ? this.matchdayList : matchdayService.getMatchdaysForSeason(season);
for (Matchday matchday : matchdayList) {
if (matchdayParam.equals(matchday.toString())) {
return matchday;
}
}
return null;
}
}

+ 3
- 0
src/main/java/com/example/application/views/main/MainView.java View File

@ -1,6 +1,7 @@
package com.example.application.views.main;
import com.example.application.views.results.ResultsView;
import com.example.application.views.table.TableView;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentUtil;
import com.vaadin.flow.component.applayout.AppLayout;
@ -35,6 +36,7 @@ import java.util.Optional;
@JsModule("./styles/shared-styles.js")
@Theme(value = Lumo.class, variant = Lumo.DARK)
public class MainView extends AppLayout {
// TODO: Add Localization
private final Tabs menu;
private H1 viewTitle;
@ -95,6 +97,7 @@ public class MainView extends AppLayout {
// createTab("Address Form", AddressFormView.class),
// createTab("Credit Card Form", CreditCardFormView.class),
// createTab("Map", MapView.class),
createTab("Table", TableView.class),
createTab("Results", ResultsView.class)};
}


+ 79
- 189
src/main/java/com/example/application/views/results/ResultsView.java View File

@ -1,151 +1,106 @@
package com.example.application.views.results;
import com.example.application.data.bean.MatchForMatchdayView;
import com.example.application.data.entity.Matchday;
import com.example.application.data.bean.CalculatedMatch;
import com.example.application.data.entity.Player;
import com.example.application.data.entity.Season;
import com.example.application.data.service.MatchService;
import com.example.application.data.service.MatchdayService;
import com.example.application.data.service.SeasonService;
import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigationView;
import com.example.application.views.main.MainView;
import com.vaadin.flow.component.*;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.grid.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.select.Select;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.NumberFormatter;
@CssImport("./views/results/results-view.css")
@Route(value = "results", layout = MainView.class)
@RouteAlias(value = "", layout = MainView.class)
@PageTitle("Results")
public class ResultsView extends Div implements HasUrlParameter<String> {
public class ResultsView extends SeasonAndMatchdayNavigationView {
private final SeasonService seasonService;
private final MatchdayService matchdayService;
private final MatchService matchService;
private String seasonParam;
private String matchdayParam;
private VerticalLayout matchdayLayout;
private Label matchdayHeader;
private Grid<CalculatedMatch> grid;
private final Label invalidUrlLabel = new Label();
private final Label matchdayViewHeader = new Label();
private final VerticalLayout outer = new VerticalLayout();
private final HorizontalLayout selectionLayout = new HorizontalLayout();
private final HorizontalLayout outerMatchdaylayout = new HorizontalLayout();
private final VerticalLayout innerMatchdayLayout = new VerticalLayout();
private final List<Season> seasonList = new ArrayList<>();
private final Select<Season> seasonSelect = new Select<>();
private final List<Matchday> matchdayList = new ArrayList<>();
private final Select<Matchday> matchdaySelect = new Select<>();
private final Button prevButton = new Button(new Icon(VaadinIcon.ARROW_LEFT));
private final Button nextButton = new Button(new Icon(VaadinIcon.ARROW_RIGHT));
private final Grid<MatchForMatchdayView> grid = new Grid<>();
private Button prevButton;
private Button nextButton;
public ResultsView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired MatchService matchService) {
this.seasonService = seasonService;
this.matchdayService = matchdayService;
super(seasonService, matchdayService);
this.matchService = matchService;
addClassName("results-view");
configureOuterLayout();
configureSelectionLayout();
configureMatchdayViewLayout();
}
private void configureOuterLayout() {
add(outer);
outer.setAlignItems(FlexComponent.Alignment.CENTER);
@Override
protected String route() {
return "results";
}
private void configureSelectionLayout() {
// pretty looks
selectionLayout.setWidthFull();
selectionLayout.setAlignItems(FlexComponent.Alignment.CENTER);
selectionLayout.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
selectionLayout.add(new Label("Season:"), seasonSelect, new Label("Matchday:"), matchdaySelect);
seasonSelect.addValueChangeListener(seasonSelectValueChangeListener());
matchdaySelect.addValueChangeListener(matchdaySelectValueChangeListener());
////////////
// LAYOUT //
////////////
// provide data
fillSeasonSelectWithData();
private VerticalLayout getMatchdayLayout() {
if (matchdayLayout == null) {
matchdayLayout = new VerticalLayout();
}
return matchdayLayout;
}
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Matchday>, Matchday>> matchdaySelectValueChangeListener() {
return matchdayChangeEvent -> getUI().ifPresent(ui -> {
Season season = seasonSelect.getValue();
Matchday matchday = matchdayChangeEvent.getValue();
if (season != null && matchday != null) {
String seasonParam = season.toString();
String matchdayParam = matchday.toString();
navigate(ui, seasonParam, matchdayParam);
}
});
private Label getMatchdayHeader() {
if (matchdayHeader == null) {
matchdayHeader = new Label();
}
return matchdayHeader;
}
private HasValue.ValueChangeListener<? super AbstractField.ComponentValueChangeEvent<Select<Season>, Season>> seasonSelectValueChangeListener() {
return seasonChangeEvent -> getUI().ifPresent(ui -> {
Season newSeason = seasonChangeEvent.getValue();
if (newSeason != null) {
String seasonParam = newSeason.toString();
String matchdayParam = null;
Matchday matchdayInNewSeason = null;
Matchday matchdayInOldSeason = matchdaySelect.getValue();
if (matchdayInOldSeason != null) {
matchdayParam = matchdayInOldSeason.toString();
matchdayInNewSeason = getMatchdayFromParam(matchdayParam, newSeason);
}
matchdayParam = matchdayInNewSeason == null ? "1" : matchdayParam;
navigate(ui, seasonParam, matchdayParam);
}
});
private Grid<CalculatedMatch> getGrid() {
if (grid == null) {
grid = new Grid<>();
}
return grid;
}
private void fillSeasonSelectWithData() {
seasonList.clear();
seasonList.addAll(seasonService.getAllSeasonsSorted());
seasonSelect.setItems(seasonList);
private Button getPrevButton() {
if (prevButton == null) {
prevButton = new Button(new Icon(VaadinIcon.ARROW_LEFT));
}
return prevButton;
}
private void fillMatchdaySelectWithData() {
matchdayList.clear();
matchdayList.addAll(matchdayService.getMatchdaysForSeason(seasonSelect.getValue()));
matchdaySelect.setItems(matchdayList);
private Button getNextButton() {
if (nextButton == null) {
nextButton = new Button(new Icon(VaadinIcon.ARROW_RIGHT));
}
return nextButton;
}
private void configureMatchdayViewLayout() {
outerMatchdaylayout.add(prevButton, innerMatchdayLayout, nextButton);
@Override
protected void configureContentLayout() {
contentLayout = new HorizontalLayout(getPrevButton(), getMatchdayLayout(), getNextButton());
configureMatchdayLayout();
}
matchdayViewHeader.addClassName("big_header");
private void configureMatchdayLayout() {
getMatchdayHeader().addClassName("big_header");
innerMatchdayLayout.setPadding(false);
innerMatchdayLayout.add(matchdayViewHeader, grid);
innerMatchdayLayout.setAlignItems(FlexComponent.Alignment.CENTER);
innerMatchdayLayout.addClassName("inner_matchday_layout");
getMatchdayLayout().setPadding(false);
getMatchdayLayout().add(getMatchdayHeader(), getGrid());
getMatchdayLayout().setAlignItems(FlexComponent.Alignment.CENTER);
getMatchdayLayout().addClassName("inner_matchday_layout");
Label headerPlayer1 = new Label("Player 1");
headerPlayer1.addClassName("column_header");
@ -154,129 +109,64 @@ public class ResultsView extends Div implements HasUrlParameter<String> {
Label headerResult = new Label("Result");
headerResult.addClassName("column_header");
grid.addColumn((ValueProvider<MatchForMatchdayView, Player>) MatchForMatchdayView::getPlayer1)
getGrid().addColumn((ValueProvider<CalculatedMatch, Player>) CalculatedMatch::getPlayer1)
.setHeader(headerPlayer1)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("13em");
grid.addColumn((ValueProvider<MatchForMatchdayView, String>) match -> "vs.")
getGrid().addColumn((ValueProvider<CalculatedMatch, String>) match -> "vs.")
.setHeader("vs.")
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em");
grid.addColumn((ValueProvider<MatchForMatchdayView, Player>) MatchForMatchdayView::getPlayer2)
getGrid().addColumn((ValueProvider<CalculatedMatch, Player>) CalculatedMatch::getPlayer2)
.setHeader(headerPlayer2)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("13em");
grid.addColumn((ValueProvider<MatchForMatchdayView, String>) match -> {
String result = match.getScore1().toString() + " : " + match.getScore2().toString();
getGrid().addColumn((ValueProvider<CalculatedMatch, String>) match -> {
String result = match.getScore1().toString().replace(".0", "")
+ " : "
+ match.getScore2().toString().replace(".0", "");
return result.equals("0 : 0") ? "" : result;
})
.setHeader(headerResult)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("6em");
grid.setWidth("36em");
grid.setHeightByRows(true);
getGrid().setWidth("36em");
getGrid().setHeightByRows(true);
grid.addClassName("matchday_grid");
getGrid().addClassName("my_grid");
}
private String getPrevMatchdayParam() {
return String.valueOf(Integer.parseInt(matchdayParam) - 1);
}
private String getNextMatchdayParam() {
return String.valueOf(Integer.parseInt(matchdayParam) + 1);
}
private boolean isMatchDayParamValid(@NonNull String matchdayParam) {
return matchdayList.stream().anyMatch(matchday -> matchdayParam.equals(matchday.toString()));
}
private ComponentEventListener<ClickEvent<Button>> getButtonClickListener(Button button, String matchdayParam) {
return buttonClickEvent -> getUI().ifPresent(ui -> navigate(ui, seasonParam, matchdayParam));
}
private void navigate(UI ui, String seasonParam, String matchdayParam) {
ui.navigate(String.format("results/%s/%s/", seasonParam, matchdayParam));
}
private void configureMatchdayView() {
matchdayViewHeader.setText(String.format("Matchday %s", matchdaySelect.getValue().toString()));
grid.setItems(matchService.getMatchesForMatchdayView(matchdaySelect.getValue()));
}
/////////////
// CONTENT //
/////////////
@Override
public void setParameter(BeforeEvent beforeEvent, @WildcardParameter String param) {
outer.removeAll();
if (!param.matches("^[0-9]*/[0-9]*/?$")) {
invalidUrlLabel.setText("Invalid URL! Please provide params in the form season/matchday/");
outer.add(invalidUrlLabel);
return;
}
String[] params = param.split("/");
seasonParam = params[0];
matchdayParam = params[1];
Season season = getSeasonFromParam(seasonParam);
if (season == null) {
invalidUrlLabel.setText(String.format("Invalid URL! Season \"%s\" does not exist in the database!", seasonParam));
outer.add(invalidUrlLabel);
return;
}
seasonSelect.setValue(season);
fillMatchdaySelectWithData();
Matchday matchday = getMatchdayFromParam(matchdayParam);
if (matchday == null) {
invalidUrlLabel.setText(String.format("Invalid URL! Matchday \"%s\" in Season \"%s\" does not exist in the database!", matchdayParam, seasonParam));
outer.add(invalidUrlLabel);
return;
}
matchdaySelect.setValue(matchday);
configureMatchdayView();
outer.add(selectionLayout);
outer.add(outerMatchdaylayout);
protected void configureContent() {
getMatchdayHeader().setText(String.format("Matchday %s", matchdaySelect.getValue().toString()));
getGrid().setItems(matchService.getCalculatedMatches(matchdaySelect.getValue()));
configureButtons();
}
private void configureButtons() {
prevButton.setEnabled(isMatchDayParamValid(getPrevMatchdayParam()));
prevButton.addClickListener(getButtonClickListener(prevButton, getPrevMatchdayParam()));
getPrevButton().setEnabled(isMatchDayParamValid(getPrevMatchdayParam()));
getPrevButton().addClickListener(getButtonClickListener(getPrevMatchdayParam()));
nextButton.setEnabled(isMatchDayParamValid(getNextMatchdayParam()));
nextButton.addClickListener(getButtonClickListener(nextButton, getNextMatchdayParam()));
getNextButton().setEnabled(isMatchDayParamValid(getNextMatchdayParam()));
getNextButton().addClickListener(getButtonClickListener(getNextMatchdayParam()));
}
@Nullable
private Season getSeasonFromParam(@NonNull String seasonParam) {
for (Season season : seasonList) {
if (seasonParam.equals(season.toString())) {
return season;
}
}
return null;
private ComponentEventListener<ClickEvent<Button>> getButtonClickListener(String matchdayParam) {
return buttonClickEvent -> getUI().ifPresent(ui -> navigate(ui, seasonParam, matchdayParam));
}
@Nullable
private Matchday getMatchdayFromParam(@NonNull String matchdayParam) {
return getMatchdayFromParam(matchdayParam, null);
private String getPrevMatchdayParam() {
return String.valueOf(Integer.parseInt(matchdayParam) - 1);
}
@Nullable
private Matchday getMatchdayFromParam(@NonNull String matchdayParam, @Nullable Season season) {
List<Matchday> matchdayList = season == null ? this.matchdayList : matchdayService.getMatchdaysForSeason(season);
for (Matchday matchday : matchdayList) {
if (matchdayParam.equals(matchday.toString())) {
return matchday;
}
}
return null;
private String getNextMatchdayParam() {
return String.valueOf(Integer.parseInt(matchdayParam) + 1);
}
}

+ 146
- 0
src/main/java/com/example/application/views/table/TableView.java View File

@ -0,0 +1,146 @@
package com.example.application.views.table;
import com.example.application.data.bean.PlayerForTable;
import com.example.application.data.entity.Player;
import com.example.application.data.service.MatchdayService;
import com.example.application.data.service.PlayerService;
import com.example.application.data.service.SeasonService;
import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigationView;
import com.example.application.views.main.MainView;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.grid.ColumnTextAlign;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouteAlias;
import org.springframework.beans.factory.annotation.Autowired;
@CssImport("./views/table/table-view.css")
@Route(value = "table", layout = MainView.class)
@RouteAlias(value = "", layout = MainView.class)
@PageTitle("Table")
public class TableView extends SeasonAndMatchdayNavigationView {
private final PlayerService playerService;
private Grid<PlayerForTable> grid;
public TableView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired PlayerService playerService) {
super(seasonService, matchdayService);
this.playerService = playerService;
addClassName("table-view");
}
@Override
protected String route() {
return "table";
}
////////////
// LAYOUT //
////////////
private Grid<PlayerForTable> getGrid() {
if (grid == null) {
grid = new Grid<>();
}
return grid;
}
@Override
protected void configureContentLayout() {
contentLayout = new HorizontalLayout(getGrid());
Label headerPlace = new Label("Place");
headerPlace.addClassName("column_header");
Label headerPlayer = new Label("Player");
headerPlayer.addClassName("column_header");
Label headerMatchesPlayed = new Label("Played");
Label headerMatchPoints = new Label("Points");
headerMatchPoints.addClassName("column_header");
Label headerWon = new Label("W");
Label headerDrawn = new Label("D");
Label headerLost = new Label("L");
Label headerGames = new Label("Games");
Label headerDiff = new Label("Diff");
headerDiff.addClassName("column_header");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getPlace)
.setHeader(headerPlace)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("5em");
getGrid().addColumn((ValueProvider<PlayerForTable, Player>) PlayerForTable::getPlayer)
.setHeader(headerPlayer)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("13em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatches)
.setHeader(headerMatchesPlayed)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("5em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getMatchPoints)
.setHeader(headerMatchPoints)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("6em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesWon)
.setHeader(headerWon)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesDrawn)
.setHeader(headerDrawn)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em");
getGrid().addColumn((ValueProvider<PlayerForTable, Integer>) PlayerForTable::getAmountOfMatchesLost)
.setHeader(headerLost)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em");
getGrid().addColumn((ValueProvider<PlayerForTable, String>) playerForTable -> {
//noinspection CodeBlock2Expr
return playerForTable.getGamePointsForSelf().toString().replace(".0", "") // TODO: make this reusable
+ " : "
+ playerForTable.getGamePointsForOpponents().toString().replace(".0", "");
})
.setHeader(headerGames)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("6em");
getGrid().addColumn((ValueProvider<PlayerForTable, String>) playerForTable -> {
Double diff = playerForTable.getGamePointDiff();
String diffString = playerForTable.getGamePointDiff().toString().replace(".0", "");
return diff > 0 ? "+" + diffString : diffString;
})
.setHeader(headerDiff)
.setTextAlign(ColumnTextAlign.CENTER)
.setWidth("3em");
getGrid().setWidth("49em");
getGrid().setHeightByRows(true);
getGrid().addClassName("my_grid");
}
/////////////
// CONTENT //
/////////////
@Override
protected void configureContent() {
getGrid().setItems(playerService.getPlayersForTable(matchdaySelect.getValue()));
}
}

Loading…
Cancel
Save