From d5ccedb10256ee29f7c1d344e92e718efed48b06 Mon Sep 17 00:00:00 2001 From: GAM Date: Mon, 28 Jun 2021 18:11:25 +0200 Subject: [PATCH] match results without games --- db/db_increment_005.sql | 2 + src/main/java/app/data/entity/Match.java | 13 ++++ .../java/app/data/service/MatchService.java | 14 +++++ .../app/data/service/MatchdayService.java | 5 +- .../data/service/PlayerForTableProvider.java | 6 +- src/main/java/app/navigation/Navigation.java | 24 +++++++- .../java/app/navigation/NavigationUtils.java | 4 +- src/main/java/app/utils/StringUtils.java | 21 +++++++ .../views/match/components/EditMatchCard.java | 59 +++++++++++++++++++ .../match/components/MatchComponent.java | 39 +++++++++--- .../matchday/components/MatchdayCard.java | 6 +- .../views/player/components/PlayerCard.java | 9 +-- 12 files changed, 182 insertions(+), 20 deletions(-) create mode 100644 db/db_increment_005.sql diff --git a/db/db_increment_005.sql b/db/db_increment_005.sql new file mode 100644 index 0000000..42d4c4e --- /dev/null +++ b/db/db_increment_005.sql @@ -0,0 +1,2 @@ +ALTER TABLE "match" ADD COLUMN "state" varchar not null default 'NOT_YET_PLAYED'; +UPDATE match SET state='PLAYED' WHERE (id IN (SELECT match from game)); \ No newline at end of file diff --git a/src/main/java/app/data/entity/Match.java b/src/main/java/app/data/entity/Match.java index c99e29b..6779b09 100644 --- a/src/main/java/app/data/entity/Match.java +++ b/src/main/java/app/data/entity/Match.java @@ -15,12 +15,25 @@ import java.util.Collection; catalog = "chessleague") public class Match extends AbstractEntity implements Navigable { + public enum State { + NOT_YET_PLAYED, + PLAYED, + SIX_ZERO, + ZERO_SIX, + ZERO_ZERO + } + @ManyToOne(cascade = CascadeType.DETACH) @JoinColumn(name = "matchday", referencedColumnName = "id", nullable = false) private Matchday matchday; + @Basic + @Enumerated(EnumType.STRING) + @Column(name = "state", nullable = false) + private State state; + @ManyToOne(cascade = CascadeType.DETACH) @JoinColumn(name = "player1", referencedColumnName = "id", diff --git a/src/main/java/app/data/service/MatchService.java b/src/main/java/app/data/service/MatchService.java index 13a9213..ae659db 100644 --- a/src/main/java/app/data/service/MatchService.java +++ b/src/main/java/app/data/service/MatchService.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import static app.data.entity.Match.State.*; + @Service public class MatchService extends CrudService implements NavigableService { @@ -53,6 +55,12 @@ public class MatchService extends CrudService implements Navigab } private static double getScore1(Match match) { + if (match.getState().equals(SIX_ZERO)) { + return 6; + } + if (match.getState().equals(ZERO_SIX) || match.getState().equals(ZERO_ZERO)) { + return 0; + } double score = 0; for (Game game : match.getGames()) { score += (double) ((game.getPlayer1IsWhite() ? game.getResult() : -game.getResult()) + 1) / 2; @@ -61,6 +69,12 @@ public class MatchService extends CrudService implements Navigab } private static double getScore2(Match match) { + if (match.getState().equals(ZERO_SIX)) { + return 6; + } + if (match.getState().equals(SIX_ZERO) || match.getState().equals(ZERO_ZERO)) { + return 0; + } double score = 0; for (Game game : match.getGames()) { score += (double) ((game.getPlayer1IsWhite() ? -game.getResult() : game.getResult()) + 1) / 2; diff --git a/src/main/java/app/data/service/MatchdayService.java b/src/main/java/app/data/service/MatchdayService.java index dfb46c2..3e8eaaa 100644 --- a/src/main/java/app/data/service/MatchdayService.java +++ b/src/main/java/app/data/service/MatchdayService.java @@ -1,6 +1,7 @@ package app.data.service; import app.data.bean.CalculatedMatch; +import app.data.entity.Match; import app.data.entity.Matchday; import app.data.entity.Season; import app.data.repository.MatchdayRepository; @@ -15,6 +16,8 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +import static app.data.entity.Match.State.NOT_YET_PLAYED; + @Service public class MatchdayService extends CrudService implements NavigableService { @@ -63,7 +66,7 @@ public class MatchdayService extends CrudService implements N } public boolean hasActivity(@NonNull Matchday matchday) { - return matchday.getMatches().stream().mapToInt(match -> match.getGames().size()).sum() != 0; + return matchday.getMatches().stream().anyMatch(match -> !match.getState().equals(NOT_YET_PLAYED)); } @Override diff --git a/src/main/java/app/data/service/PlayerForTableProvider.java b/src/main/java/app/data/service/PlayerForTableProvider.java index d398fea..f3ae008 100644 --- a/src/main/java/app/data/service/PlayerForTableProvider.java +++ b/src/main/java/app/data/service/PlayerForTableProvider.java @@ -10,6 +10,8 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import static app.data.entity.Match.State.ZERO_ZERO; + class PlayerForTableProvider { private final PlayerService playerService; private final MatchdayService matchdayService; @@ -160,7 +162,7 @@ class PlayerForTableProvider { map.get(WinState.WON).add(match); continue; } - if (match.getScore1() < match.getScore2()) { + if (match.getScore1() < match.getScore2() || match.getMatch().getState().equals(ZERO_ZERO)) { map.get(WinState.LOST).add(match); continue; } @@ -174,7 +176,7 @@ class PlayerForTableProvider { map.get(WinState.WON).add(match); continue; } - if (match.getScore2() < match.getScore1()) { + if (match.getScore2() < match.getScore1() || match.getMatch().getState().equals(ZERO_ZERO)) { map.get(WinState.LOST).add(match); continue; } diff --git a/src/main/java/app/navigation/Navigation.java b/src/main/java/app/navigation/Navigation.java index 6d1d54b..e93b00a 100644 --- a/src/main/java/app/navigation/Navigation.java +++ b/src/main/java/app/navigation/Navigation.java @@ -14,6 +14,7 @@ import org.springframework.lang.Nullable; import java.util.*; +import static app.navigation.NavigationUtils.ADMIN; import static app.navigation.NavigationUtils.EDIT; public abstract class Navigation implements HasUrlParameter { @@ -33,6 +34,8 @@ public abstract class Navigation implements HasUrlParameter { protected boolean editFlag = false; + protected boolean adminFlag = false; + @SafeVarargs public Navigation(NavigableService... services) { for (NavigableService service : services) { @@ -91,7 +94,7 @@ public abstract class Navigation implements HasUrlParameter { } } - String wildcardParam = NavigationUtils.getWildcardParam(editFlag, params); + String wildcardParam = NavigationUtils.getWildcardParam(editFlag, adminFlag, params); UI.getCurrent().getPage().getHistory().pushState(null, String.format("%s/%s", getRoute(), wildcardParam)); } @@ -107,6 +110,18 @@ public abstract class Navigation implements HasUrlParameter { } } + public boolean adminFlag() { + return adminFlag; + } + + public void setAdminFlag(boolean adminFlag) { + if (adminFlag != this.adminFlag) { + this.adminFlag = adminFlag; + updateUrl(); + runnablesToBeRunAfterSelection.forEach(Runnable::run); + } + } + public void addRunnableToBeRunAfterSelection(Runnable runnable) { runnablesToBeRunAfterSelection.add(runnable); } @@ -158,7 +173,10 @@ public abstract class Navigation implements HasUrlParameter { @Override public void setParameter(BeforeEvent event, @WildcardParameter String param) { String[] params = param.split("/"); - editFlag = params[params.length - 1].equals(EDIT); + List paramsAsList = Arrays.asList(params); + + editFlag = paramsAsList.contains(EDIT); + adminFlag = paramsAsList.contains(ADMIN); for (int i = 0; i < Math.min(params.length, navigableClasses.size()); i++) { navigateClass(navigableClasses.get(i), params[i]); @@ -180,7 +198,7 @@ public abstract class Navigation implements HasUrlParameter { for (int i = 0; i < navigableClasses.size(); i++) { params[i] = getParam(navigableClasses.get(i)).orElse(null); } - return NavigationUtils.getWildcardParam(editFlag, params); + return NavigationUtils.getWildcardParam(editFlag, adminFlag, params); } @SuppressWarnings("unchecked") diff --git a/src/main/java/app/navigation/NavigationUtils.java b/src/main/java/app/navigation/NavigationUtils.java index fdebc70..759b161 100644 --- a/src/main/java/app/navigation/NavigationUtils.java +++ b/src/main/java/app/navigation/NavigationUtils.java @@ -8,12 +8,13 @@ import java.util.Optional; public class NavigationUtils { public static final String EDIT = "edit"; + public static final String ADMIN = "admin"; private NavigationUtils() { } @NonNull - public static String getWildcardParam(boolean editFlag, String... params) { + public static String getWildcardParam(boolean editFlag, boolean adminFlag, String... params) { StringBuilder stringBuilder = new StringBuilder(); for (String param : params) { if (param == null || param.equals("")) { @@ -22,6 +23,7 @@ public class NavigationUtils { stringBuilder.append(param).append("/"); } if (editFlag) stringBuilder.append(EDIT).append("/"); + if (adminFlag) stringBuilder.append(ADMIN).append("/"); return stringBuilder.toString(); } diff --git a/src/main/java/app/utils/StringUtils.java b/src/main/java/app/utils/StringUtils.java index 0f3b8ce..6bb194a 100644 --- a/src/main/java/app/utils/StringUtils.java +++ b/src/main/java/app/utils/StringUtils.java @@ -1,10 +1,13 @@ package app.utils; +import app.data.bean.CalculatedMatch; import org.apache.commons.lang3.RandomStringUtils; import java.util.HashSet; import java.util.Set; +import static app.data.entity.Match.State.*; + public class StringUtils { private StringUtils() { } @@ -13,6 +16,24 @@ public class StringUtils { // SPECIAL FORMATTING // //////////////////////// + public static String getResultString(String delimiter, CalculatedMatch match, boolean player1left) { + if (match.getMatch().getState().equals(NOT_YET_PLAYED)) { + return "- : -"; // TODO: care about delimiter + } + if (match.getMatch().getState().equals(SIX_ZERO)) { + return "6 : 0"; // TODO: care about delimiter + } + if (match.getMatch().getState().equals(ZERO_SIX)) { + return "0 : 6"; // TODO: care about delimiter + } + + String score1String = match.getScore1().toString().replace(".0", ""); + String score2String = match.getScore2().toString().replace(".0", ""); + return player1left ? + String.format("%s %s %s", score1String, delimiter, score2String) : + String.format("%s %s %s", score2String, delimiter, score1String); + } // TODO: refactor these 2 methods for less duplication + public static String getResultString(String delimiter, Double first, Double second) { String firstString = first.toString().replace(".0", ""); String secondString = second.toString().replace(".0", ""); diff --git a/src/main/java/app/views/match/components/EditMatchCard.java b/src/main/java/app/views/match/components/EditMatchCard.java index 940204b..1fa7e59 100644 --- a/src/main/java/app/views/match/components/EditMatchCard.java +++ b/src/main/java/app/views/match/components/EditMatchCard.java @@ -2,6 +2,7 @@ package app.views.match.components; import app.data.entity.Game; import app.data.entity.Match; +import app.data.entity.Match.State; import app.data.service.ChessComService; import app.data.service.MatchService; import app.gameimage.GameImageService; @@ -23,6 +24,7 @@ import com.vaadin.flow.shared.Registration; import java.util.*; +import static app.data.entity.Match.State.*; import static app.utils.TimeControl.*; import static com.vaadin.flow.component.button.ButtonVariant.LUMO_PRIMARY; @@ -46,6 +48,14 @@ public class EditMatchCard extends VerticalLayout { private final Button chessComButton = new Button("Autofill with the latest games between the players", new Icon(VaadinIcon.MAGIC)); private Registration chessComButtonButtonRegistration; + HorizontalLayout adminButtonLayout = new HorizontalLayout(); + private Registration sixZeroButtonRegistration; + private final Button sixZeroButton = new Button("6 - 0", new Icon(VaadinIcon.CHECK)); + private Registration zeroSixButtonRegistration; + private final Button zeroSixButton = new Button("0 - 6", new Icon(VaadinIcon.CHECK)); + private Registration zeroZeroButtonRegistration; + private final Button zeroZeroButton = new Button("0 - 0", new Icon(VaadinIcon.CHECK)); + private Match match; public EditMatchCard(MatchNavigation matchNavigation, ChessComService chessComService, GameImageService gameImageService) { @@ -70,6 +80,7 @@ public class EditMatchCard extends VerticalLayout { defineTextFields(); defineSubmitButton(); addSubmitAndCancelButtons(); + addAdminButtons(); } private void defineTextFields() { @@ -111,6 +122,16 @@ public class EditMatchCard extends VerticalLayout { add(buttonLayout); } + private void addAdminButtons() { + sixZeroButton.addThemeVariants(LUMO_PRIMARY); + zeroSixButton.addThemeVariants(LUMO_PRIMARY); + zeroZeroButton.addThemeVariants(LUMO_PRIMARY); + + adminButtonLayout.setAlignItems(Alignment.CENTER); + adminButtonLayout.setJustifyContentMode(JustifyContentMode.CENTER); + adminButtonLayout.add(sixZeroButton, zeroSixButton, zeroZeroButton); + } + ///////////// // CONTENT // ///////////// @@ -124,6 +145,8 @@ public class EditMatchCard extends VerticalLayout { configureChessComButton(); configureEditSubmitButton(); configureEditCancelButton(); + + configureAdminButtons(); } private void clearTextFields() { @@ -191,6 +214,42 @@ public class EditMatchCard extends VerticalLayout { editCancelButtonRegistration = editCancelButton.addClickListener(createEditCancelButtonListener()); } + private void configureAdminButtons() { + if (matchNavigation.adminFlag()) { + configureSixZeroButton(); + configureZeroSixButton(); + configureZeroZeroButton(); + add(adminButtonLayout); + } + else { + remove(adminButtonLayout); + } + } + + private void configureSixZeroButton() { + if (sixZeroButtonRegistration != null) sixZeroButtonRegistration.remove(); + sixZeroButtonRegistration = sixZeroButton.addClickListener(event -> submitAdminDecision(SIX_ZERO)); + } + + private void configureZeroSixButton() { + if (zeroSixButtonRegistration != null) zeroSixButtonRegistration.remove(); + zeroSixButtonRegistration = zeroSixButton.addClickListener(event -> submitAdminDecision(ZERO_SIX)); + } + + private void configureZeroZeroButton() { + if (zeroZeroButtonRegistration != null) zeroZeroButtonRegistration.remove(); + zeroZeroButtonRegistration = zeroZeroButton.addClickListener(event -> submitAdminDecision(ZERO_ZERO)); + } + + private void submitAdminDecision(State sixZero) { + match.getGames().clear(); + match.setState(sixZero); + matchService.update(match); + + matchNavigation.setEditFlag(false); + matchNavigation.setAdminFlag(false); + } + private ComponentEventListener> createEditCancelButtonListener() { return event -> UI.getCurrent().navigate(String.format("matchday/%s", matchNavigation.getWildcardParam().replace("edit/", ""))); } diff --git a/src/main/java/app/views/match/components/MatchComponent.java b/src/main/java/app/views/match/components/MatchComponent.java index 9716195..3834d44 100644 --- a/src/main/java/app/views/match/components/MatchComponent.java +++ b/src/main/java/app/views/match/components/MatchComponent.java @@ -36,7 +36,8 @@ public class MatchComponent extends Div implements ContentConfigurable { private final Label headerResultLabel = new Label(); private final FlexLayout gamesLayout = new FlexLayout(); - private final VerticalLayout noGamesLayout = new VerticalLayout(); + private final VerticalLayout notPlayedYetLayout = new VerticalLayout(); + private final Label resultWithoutGamesLabel = new Label(); private final FlexLayout editLayout = new FlexLayout(); private final EditMatchCard editMatchCard; @@ -84,8 +85,8 @@ public class MatchComponent extends Div implements ContentConfigurable { } private void defineNoGamesLayout() { - noGamesLayout.add(createNoGamesLabel(), createAddGamesButton()); - noGamesLayout.setAlignItems(FlexComponent.Alignment.CENTER); + notPlayedYetLayout.add(createNoGamesLabel(), createAddGamesButton()); + notPlayedYetLayout.setAlignItems(FlexComponent.Alignment.CENTER); } private Label createNoGamesLabel() { @@ -97,7 +98,7 @@ public class MatchComponent extends Div implements ContentConfigurable { button.addThemeVariants(ButtonVariant.LUMO_PRIMARY); button.addClickListener(event -> { matchNavigation.setEditFlag(true); - remove(noGamesLayout); + remove(notPlayedYetLayout); }); return button; } @@ -145,7 +146,7 @@ public class MatchComponent extends Div implements ContentConfigurable { } else { headerLayout.add(headerResultLabel); headerResultLabel.setText(String.format("%s", - StringUtils.getResultString("-", calculatedMatch.getScore1(), calculatedMatch.getScore2()))); + StringUtils.getResultString("-", calculatedMatch, true))); } } @@ -163,11 +164,19 @@ public class MatchComponent extends Div implements ContentConfigurable { private void configureGamesContent() { if (match.getGames().isEmpty()) { remove(gamesLayout); - add(noGamesLayout); + if (match.getState().equals(Match.State.NOT_YET_PLAYED)) { + remove(resultWithoutGamesLabel); + add(notPlayedYetLayout); + } else { + remove(notPlayedYetLayout); + configureResultWithoutGamesLabel(); + add(resultWithoutGamesLabel); + } return; } - remove(noGamesLayout); + remove(notPlayedYetLayout); + remove(resultWithoutGamesLabel); add(gamesLayout); gamesLayout.removeAll(); @@ -175,4 +184,20 @@ public class MatchComponent extends Div implements ContentConfigurable { gamesLayout.add(new GameCard(game, gameImageService)); } } + + private void configureResultWithoutGamesLabel() { + switch (match.getState()) { + case SIX_ZERO: + resultWithoutGamesLabel.setText("This match was set to be valued 6-0 by an administrator decision."); + break; + case ZERO_SIX: + resultWithoutGamesLabel.setText("This match was set to be valued 0-6 by an administrator decision."); + break; + case ZERO_ZERO: + resultWithoutGamesLabel.setText("An administrator decided to give both players 0 points for this match."); + break; + default: + resultWithoutGamesLabel.setText("This text should never appear!"); + } + } } diff --git a/src/main/java/app/views/matchday/components/MatchdayCard.java b/src/main/java/app/views/matchday/components/MatchdayCard.java index b106716..4637aaf 100644 --- a/src/main/java/app/views/matchday/components/MatchdayCard.java +++ b/src/main/java/app/views/matchday/components/MatchdayCard.java @@ -26,6 +26,8 @@ import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.NoSuchElementException; +import static app.data.entity.Match.State.NOT_YET_PLAYED; + public class MatchdayCard extends Div implements ContentConfigurable { private final MatchdayNavigation matchdayNavigation; @@ -110,7 +112,7 @@ public class MatchdayCard extends Div implements ContentConfigurable { String matchParam = EntityStringUtils.getMatchStringForURL(match.getMatch()); String targetWildcardParam = String.format("match/%s%s/", seasonAndMatchdayParam, matchParam); - if (match.getScore1() == 0 && match.getScore2() == 0) { + if (match.getMatch().getState().equals(NOT_YET_PLAYED)) { button.setIcon(VaadinIcon.PENCIL.create()); button.addClickListener(event -> UI.getCurrent().navigate(targetWildcardParam + "edit")); return button; @@ -126,7 +128,7 @@ public class MatchdayCard extends Div implements ContentConfigurable { } private String getResultString(CalculatedMatch match) { - return StringUtils.getResultString(":", match.getScore1(), match.getScore2()); + return StringUtils.getResultString(":", match, true); } @Override diff --git a/src/main/java/app/views/player/components/PlayerCard.java b/src/main/java/app/views/player/components/PlayerCard.java index 26540b1..2d0773b 100644 --- a/src/main/java/app/views/player/components/PlayerCard.java +++ b/src/main/java/app/views/player/components/PlayerCard.java @@ -1,6 +1,7 @@ package app.views.player.components; import app.data.bean.CalculatedMatch; +import app.data.entity.Match; import app.data.entity.Player; import app.data.entity.Season; import app.navigation.player.PlayerNavigation; @@ -24,6 +25,8 @@ import com.vaadin.flow.component.orderedlayout.VerticalLayout; import java.util.NoSuchElementException; +import static app.data.entity.Match.State.NOT_YET_PLAYED; + public class PlayerCard extends Div implements ContentConfigurable { private final PlayerNavigation playerNavigation; @@ -97,7 +100,7 @@ public class PlayerCard extends Div implements ContentConfigurable { String targetWildcardParam = String.format("match/%s/%s/%s/", seasonParam, matchdayParam, matchParam); - if (match.getScore1() == 0 && match.getScore2() == 0) { + if (match.getMatch().getState().equals(NOT_YET_PLAYED)) { button.setIcon(VaadinIcon.PENCIL.create()); button.addClickListener(event -> UI.getCurrent().navigate(targetWildcardParam + "edit")); return button; @@ -118,9 +121,7 @@ public class PlayerCard extends Div implements ContentConfigurable { private String getResultString(CalculatedMatch match) { boolean isPlayer1 = match.getPlayer1().equals(player); - double ownScore = isPlayer1 ? match.getScore1() : match.getScore2(); - double opponentScore = isPlayer1 ? match.getScore2() : match.getScore1(); - return StringUtils.getResultString(":", ownScore, opponentScore); + return StringUtils.getResultString(":", match, isPlayer1); } @Override