mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-18 11:49:09 +00:00
Merge branch 'master' into bug5440
This commit is contained in:
Generated
+7
-7
@@ -11,7 +11,7 @@
|
||||
"@sap/hana-client": "2.7.26",
|
||||
"@sap/hdi": "4.0.1",
|
||||
"@sap/xsenv": "3.1.0",
|
||||
"async": "3.1.0",
|
||||
"async": "3.2.2",
|
||||
"handlebars": "4.7.7"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -43,7 +43,7 @@
|
||||
"resolved": "http://nginx-redirector.repo-cache.svc.cluster.local/repository/appstudio-npm-group/@sap/hdi/-/hdi-4.0.1.tgz",
|
||||
"integrity": "sha512-f7QdRgFc/amQTgTepgMFpwNMmCvvo7o/sElaZhY/2/j/wYbEukpxvplzWpgAUyPJXe9+WL8BemrEwws0TOTNWQ==",
|
||||
"requires": {
|
||||
"async": "3.1.0"
|
||||
"async": "3.2.2"
|
||||
}
|
||||
},
|
||||
"@sap/xsenv": {
|
||||
@@ -76,8 +76,8 @@
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||
},
|
||||
"async": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "http://nginx-redirector.repo-cache.svc.cluster.local/repository/appstudio-npm-group/async/-/async-3.1.0.tgz",
|
||||
"version": "3.2.2",
|
||||
"resolved": "http://nginx-redirector.repo-cache.svc.cluster.local/repository/appstudio-npm-group/async/-/async-3.2.2.tgz",
|
||||
"integrity": "sha512-4vx/aaY6j/j3Lw3fbCHNWP0pPaTCew3F6F3hYyl/tHs/ndmV1q7NW9T5yuJ2XAGwdQrP+6Wu20x06U4APo/iQQ=="
|
||||
},
|
||||
"core-util-is": {
|
||||
@@ -95,7 +95,7 @@
|
||||
"resolved": "http://nginx-redirector.repo-cache.svc.cluster.local/repository/appstudio-npm-group/handlebars/-/handlebars-4.7.7.tgz",
|
||||
"integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.5",
|
||||
"minimist": "^1.2.6",
|
||||
"neo-async": "^2.6.0",
|
||||
"source-map": "^0.6.1",
|
||||
"uglify-js": "^3.1.4",
|
||||
@@ -103,8 +103,8 @@
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "http://nginx-redirector.repo-cache.svc.cluster.local/repository/appstudio-npm-group/minimist/-/minimist-1.2.5.tgz",
|
||||
"version": "1.2.6",
|
||||
"resolved": "http://nginx-redirector.repo-cache.svc.cluster.local/repository/appstudio-npm-group/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
},
|
||||
"neo-async": {
|
||||
|
||||
Binary file not shown.
@@ -2,21 +2,21 @@
|
||||
<feature
|
||||
id="com.amazon.aws.aws-java-api"
|
||||
label="AWS API"
|
||||
version="2.17.124"
|
||||
version="2.18.8"
|
||||
provider-name="Amazon">
|
||||
|
||||
<plugin
|
||||
id="com.amazon.aws.aws-java-api"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="2.17.124"
|
||||
version="2.18.8"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.amazon.aws.aws-java-api.source"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="2.17.124"
|
||||
version="2.18.8"
|
||||
unpack="false"/>
|
||||
|
||||
</feature>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site>
|
||||
<feature url="features/aws-sdk/com.amazon.aws.aws-java-api_2.17.124.jar" id="com.amazon.aws.aws-java-api" version="2.17.124">
|
||||
<feature url="features/aws-sdk/com.amazon.aws.aws-java-api_2.18.8.jar" id="com.amazon.aws.aws-java-api" version="2.18.8">
|
||||
<category name="aws-java-api"/>
|
||||
</feature>
|
||||
<category-def name="com.amazon.aws.aws-java-api" label="aws-java-api"/>
|
||||
|
||||
@@ -5,7 +5,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation platform('software.amazon.awssdk:bom:2.17.124')
|
||||
implementation platform('software.amazon.awssdk:bom:2.18.8')
|
||||
implementation 'software.amazon.awssdk:s3'
|
||||
implementation 'software.amazon.awssdk:lambda'
|
||||
implementation 'software.amazon.awssdk:ec2'
|
||||
|
||||
@@ -88,7 +88,7 @@ echo "Building the wrapper bundle..."
|
||||
cd ..
|
||||
echo "NOT USING ${extra} arguments for Maven to avoid using local target definition already for building local repo"
|
||||
echo "In folder $(pwd) using: mvn clean install"
|
||||
mvn clean install
|
||||
mvn --batch-mode clean install
|
||||
mkdir -p ${UPDATE_SITE_PROJECT}/plugins/aws-sdk
|
||||
rm -rf ${UPDATE_SITE_PROJECT}/plugins/aws-sdk/*
|
||||
# Note: the JAR ends up in target/ because the SDK project does not use any parent pom, so
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</properties>
|
||||
<groupId>com.amazon.aws</groupId>
|
||||
<artifactId>com.amazon.aws.aws-java-api</artifactId>
|
||||
<version>2.17.124</version>
|
||||
<version>2.18.8</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
+4
-2
@@ -30,6 +30,8 @@ public enum BoatClassMasterdata {
|
||||
BAVARIA_CRUISER_45 ("Bavaria Cruiser 45", true, 14.27, 4.35, BoatHullType.MONOHULL, true, "B45", "B 45", "BAVARIACRUISER45"),
|
||||
BAVARIA_CRUISER_46 ("Bavaria Cruiser 46", true, 14.27, 4.35, BoatHullType.MONOHULL, true, "B46", "B 46", "BAVARIACRUISER46"),
|
||||
BB10M ("BB 10m", true, 10.00, 2.30, BoatHullType.MONOHULL, true, "Dansk BB10M klub"),
|
||||
BENETEAU_FIRST_35 ("Benetau First 35", true, 10.66, 3.636, BoatHullType.MONOHULL, true, "First 35"),
|
||||
BENETEAU_FIRST_45 ("Benetau First 45", true, 13.68, 4.202, BoatHullType.MONOHULL, true, "First 45"),
|
||||
BRASSFAHRT_I ("Brassfahrt I", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 1"),
|
||||
BRASSFAHRT_II ("Brassfahrt II", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 2"),
|
||||
BRASSFAHRT_III ("Brassfahrt III", true, 12.00, 3.50, BoatHullType.MONOHULL, true, "Brassfahrt 3"),
|
||||
@@ -70,7 +72,7 @@ public enum BoatClassMasterdata {
|
||||
HOBIE_TIGER ("Hobie Tiger", true, 5.51, 2.60, BoatHullType.CATAMARAN, true),
|
||||
HOBIE_WILD_CAT ("Hobie Wild Cat", true, 5.49, 2.59, BoatHullType.CATAMARAN, true, "Hobie Wild Cat F18"),
|
||||
INTERNATIONAL_14("International 14", true, 4.27, 1.83, BoatHullType.MONOHULL, true, "I14", "Int.14", "Int14"),
|
||||
IQFOIL_MEN("iQFOil Men", true, 2.20, 0.95, BoatHullType.SURFERBOARD, false, "iQFOil 95 Men"),
|
||||
IQFOIL_MEN("iQFOil Men", true, 2.20, 0.95, BoatHullType.SURFERBOARD, false, "iQFOil 95 Men", "iQF95"),
|
||||
IQFOIL_WOMEN("iQFOil Women", true, 2.20, 0.95, BoatHullType.SURFERBOARD, false, "iQFOil 95 Women"),
|
||||
IQFOIL_YOUTH("iQFOil Youth", true, 2.15, 0.85, BoatHullType.SURFERBOARD, false, "iQFOil 85 Youth"),
|
||||
IRC("IRC", true, 15.00, 4.00, BoatHullType.MONOHULL, false),
|
||||
@@ -85,7 +87,7 @@ public enum BoatClassMasterdata {
|
||||
J111 ("J/111", true, 11.1, 3.29, BoatHullType.MONOHULL, true, "J111", "J-111"),
|
||||
JK_20 ("20qm Jollenkreuzer", true, 7.75, 2.50, BoatHullType.MONOHULL, true, "JK 20", "JK20", "20er"),
|
||||
KIELZUGVOGEL ("Kielzugvogel", true, 5.80, 1.88, BoatHullType.MONOHULL, false, "KZV"),
|
||||
FORMULA_KITE ("Kite", true, 3.35, 1.52, BoatHullType.MONOHULL, false, "Formula Kite"),
|
||||
FORMULA_KITE ("Kite", true, 3.35, 1.52, BoatHullType.MONOHULL, false, "Formula Kite", "FK"),
|
||||
LASER_2 ("Laser 2", true, 4.39, 1.42, BoatHullType.MONOHULL, false, "Laser II", "Laser2", "Laser-2", "Laser-II"),
|
||||
LASER_4_7 ("Laser 4.7", true, 4.20, 1.39, BoatHullType.MONOHULL, false, "L4.7", "ILCA 4", "ILCA4"),
|
||||
LASER_RADIAL ("Laser Radial", true, 4.19, 1.41, BoatHullType.MONOHULL, false, "LAR", "Laser RAD", "RAD", "Radial", "ILCA 6", "ILCA6"),
|
||||
|
||||
+5
-2
@@ -1,9 +1,12 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
public enum LeaderboardType {
|
||||
RegattaLeaderboard(false, true), RegattaMetaLeaderboard(true, true),
|
||||
RegattaLeaderboard(false, true),
|
||||
RegattaMetaLeaderboard(true, true),
|
||||
RegattaLeaderboardWithEliminations(false, true),
|
||||
FlexibleLeaderboard(false, false), FlexibleMetaLeaderboard(true, false);
|
||||
RegattaLeaderboardWithOtherTieBreakingLeaderboard(false, true),
|
||||
FlexibleLeaderboard(false, false),
|
||||
FlexibleMetaLeaderboard(true, false);
|
||||
|
||||
boolean isRegattaLeaderboard;
|
||||
boolean isMetaLeaderboard;
|
||||
|
||||
+10
-1
@@ -5,5 +5,14 @@ public enum ScoringSchemeType {
|
||||
LOW_POINT, LOW_POINT_WITH_AUTOMATIC_RDG, HIGH_POINT, HIGH_POINT_ESS_OVERALL,HIGH_POINT_ESS_OVERALL_12, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN,
|
||||
HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE, HIGH_POINT_WINNER_GETS_EIGHT,
|
||||
HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT, HIGH_POINT_FIRST_GETS_TWELVE_OR_EIGHT_2017, LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07,
|
||||
LOW_POINT_LEAGUE_OVERALL, HIGH_POINT_MATCH_RACING, LOW_POINT_TIE_BREAK_BASED_ON_LAST_SERIES_ONLY, LOW_POINT_FIRST_TO_WIN_TWO_RACES, LOW_POINT_FIRST_TO_WIN_THREE_RACES
|
||||
LOW_POINT_LEAGUE_OVERALL, HIGH_POINT_MATCH_RACING, LOW_POINT_TIE_BREAK_BASED_ON_LAST_SERIES_ONLY, LOW_POINT_FIRST_TO_WIN_TWO_RACES, LOW_POINT_FIRST_TO_WIN_THREE_RACES,
|
||||
HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD;
|
||||
|
||||
public static double getScaledScore(double columnFactor, double unscaledScore, boolean oneAlwaysStaysOne) {
|
||||
return unscaledScore * columnFactor - (oneAlwaysStaysOne ? columnFactor-1 : 0);
|
||||
}
|
||||
|
||||
public static double getUnscaledScore(double columnFactor, double scaledScore, boolean oneAlwaysStaysOne) {
|
||||
return (scaledScore + (oneAlwaysStaysOne ? columnFactor - 1 : 0)) / columnFactor;
|
||||
}
|
||||
}
|
||||
|
||||
+22
-5
@@ -36,6 +36,10 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured
|
||||
* Set to the non-<code>null</code> regatta name if this DTO represents a <code>RegattaLeaderboard</code>.
|
||||
*/
|
||||
public String regattaName;
|
||||
/**
|
||||
* Set if this DTO represents a {@code RegattaLeaderboardWithOtherTieBreakingLeaderboard}
|
||||
*/
|
||||
private String otherTieBreakingLeaderboardName;
|
||||
public String displayName;
|
||||
public List<CourseAreaDTO> courseAreas;
|
||||
public ScoringSchemeType scoringScheme;
|
||||
@@ -111,6 +115,14 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured
|
||||
return competitorDisplayNames.get(competitor);
|
||||
}
|
||||
}
|
||||
|
||||
public String getOtherTieBreakingLeaderboardName() {
|
||||
return otherTieBreakingLeaderboardName;
|
||||
}
|
||||
|
||||
public void setOtherTieBreakingLeaderboardName(String otherTieBreakingLeaderboardName) {
|
||||
this.otherTieBreakingLeaderboardName = otherTieBreakingLeaderboardName;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the race column whose name is specified in <code>raceColumnName</code> has at least one competitor who has valid
|
||||
@@ -196,7 +208,6 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured
|
||||
* ensures that a fleet named <code>fleetName</code> is present. If it's not present yet, it's added to the race
|
||||
* column's fleet name list. The <code>trackedRaceIdentifier</code> and <code>race</code> are associated with the
|
||||
* column for the fleet identified by <code>fleetName</code>.
|
||||
*
|
||||
* @param explicitFactor
|
||||
* factor by which to multiply the race column's points for the overall score; if <code>null</code>, the
|
||||
* default will be determined by whether or not the column is marked as medal race
|
||||
@@ -212,15 +223,16 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured
|
||||
* {@link RaceColumnInSeriesDTO}
|
||||
* @param fleetDTO
|
||||
* must not be null
|
||||
* @param oneAlwaysStaysOne TODO
|
||||
*/
|
||||
public RaceColumnDTO addRace(String raceColumnName, Double explicitFactor, double effectiveFactor,
|
||||
String regattaName, String seriesName, FleetDTO fleetDTO, boolean medalRace,
|
||||
RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race, boolean isMetaLeaderboardColumn) {
|
||||
RegattaAndRaceIdentifier trackedRaceIdentifier, RaceDTO race, boolean isMetaLeaderboardColumn, boolean oneAlwaysStaysOne) {
|
||||
assert fleetDTO != null;
|
||||
RaceColumnDTO raceColumnDTO = getRaceColumnByName(raceColumnName);
|
||||
if (raceColumnDTO == null) {
|
||||
raceColumnDTO = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName, medalRace,
|
||||
explicitFactor, regattaName, seriesName, isMetaLeaderboardColumn);
|
||||
explicitFactor, regattaName, seriesName, isMetaLeaderboardColumn, oneAlwaysStaysOne);
|
||||
races.add(raceColumnDTO);
|
||||
}
|
||||
raceColumnDTO.setEffectiveFactor(effectiveFactor);
|
||||
@@ -240,9 +252,9 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured
|
||||
}
|
||||
|
||||
public RaceColumnDTO createEmptyRaceColumn(String raceColumnName, boolean medalRace, String regattaName,
|
||||
String seriesName, boolean isMetaLeaderboardColumn) {
|
||||
String seriesName, boolean isMetaLeaderboardColumn, boolean oneAlwaysStaysOne) {
|
||||
final RaceColumnDTO raceColumn = RaceColumnDTOFactory.INSTANCE.createRaceColumnDTO(raceColumnName,
|
||||
medalRace, /* explicit factor */ null, regattaName, seriesName, isMetaLeaderboardColumn);
|
||||
medalRace, /* explicit factor */ null, regattaName, seriesName, isMetaLeaderboardColumn, oneAlwaysStaysOne);
|
||||
races.add(raceColumn);
|
||||
return raceColumn;
|
||||
}
|
||||
@@ -437,6 +449,11 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Secured
|
||||
return false;
|
||||
} else if (!getName().equals(other.getName()))
|
||||
return false;
|
||||
if (getOtherTieBreakingLeaderboardName() == null) {
|
||||
if (other.getOtherTieBreakingLeaderboardName() != null)
|
||||
return false;
|
||||
} else if (!getOtherTieBreakingLeaderboardName().equals(other.getOtherTieBreakingLeaderboardName()))
|
||||
return false;
|
||||
if (races == null) {
|
||||
if (other.races != null)
|
||||
return false;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ public class MetaLeaderboardRaceColumnDTO extends RaceColumnDTO {
|
||||
MetaLeaderboardRaceColumnDTO() {} // for GWT RPC serialization only
|
||||
|
||||
public MetaLeaderboardRaceColumnDTO(String name) {
|
||||
super(name);
|
||||
super(name, /* oneAlwaysStaysOne */ false);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 6152752963316150432L;
|
||||
|
||||
+12
-1
@@ -23,6 +23,7 @@ public class RaceColumnDTO extends NamedDTO implements Serializable {
|
||||
private Map<FleetDTO, RaceDTO> racesPerFleet;
|
||||
private Double explicitFactor;
|
||||
private double effectiveFactor;
|
||||
private boolean oneAlwaysStaysOne;
|
||||
private Map<FleetDTO, RaceLogTrackingInfoDTO> raceLogTrackingInfos;
|
||||
|
||||
public enum RaceColumnLiveState { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE };
|
||||
@@ -30,12 +31,18 @@ public class RaceColumnDTO extends NamedDTO implements Serializable {
|
||||
@Deprecated
|
||||
RaceColumnDTO() {} // for GWT RPC serialization only
|
||||
|
||||
public RaceColumnDTO(String name) {
|
||||
/**
|
||||
* @param oneAlwaysStaysOne
|
||||
* tells whether when scaling results with the {@link #getEffectiveFactor() effective column factor}, the
|
||||
* 1.0 score shall always remain 1.0.
|
||||
*/
|
||||
public RaceColumnDTO(String name, boolean oneAlwaysStaysOne) {
|
||||
super(name);
|
||||
trackedRaceIdentifiersPerFleet = new HashMap<FleetDTO, RegattaAndRaceIdentifier>();
|
||||
raceLogTrackingInfos = new HashMap<FleetDTO, RaceLogTrackingInfoDTO>();
|
||||
racesPerFleet = new HashMap<FleetDTO, RaceDTO>();
|
||||
fleets = new ArrayList<FleetDTO>();
|
||||
this.oneAlwaysStaysOne = oneAlwaysStaysOne;
|
||||
}
|
||||
|
||||
public RaceColumnLiveState getLiveState(long serverTimePointAsMillis) {
|
||||
@@ -59,6 +66,10 @@ public class RaceColumnDTO extends NamedDTO implements Serializable {
|
||||
public void setExplicitFactor(Double explicitFactor) {
|
||||
this.explicitFactor = explicitFactor;
|
||||
}
|
||||
|
||||
public boolean isOneAlwaysStaysOne() {
|
||||
return oneAlwaysStaysOne;
|
||||
}
|
||||
|
||||
public boolean hasTrackedRace(RaceIdentifier raceIdentifier) {
|
||||
return trackedRaceIdentifiersPerFleet.values().contains(raceIdentifier);
|
||||
|
||||
+10
-3
@@ -10,14 +10,21 @@ public class RaceColumnDTOFactory {
|
||||
* when <code>null</code>, a regular {@link RaceColumnDTO} or {@link MetaLeaderboardRaceColumnDTO} will
|
||||
* be produced (depending on <code>isMetaLeaderboardColumn</code>) and initialized from the properties
|
||||
* passed; otherwise, a {@link RaceColumnInSeriesDTO} will be created.
|
||||
* @param oneAlwaysStaysOne
|
||||
* When scores in this column are scaled by some factor, either based on the {@link #getExplicitFactor()
|
||||
* explicit factor} set for this column, or implicitly, e.g., because the {@link ScoringScheme} mandates
|
||||
* the doubling of medal race scores and this column {@link #isMedalRace() represents a medal race}, then
|
||||
* some configurations still want the 1.0 score still to be 1.0. For example, with a column factor of 2.0
|
||||
* scores 1, 2, 3 would end up as 1, 3, 5; or with a column factor of 3.0 scores 1, 2, 3 would end up as
|
||||
* 1, 4, 7. This method tells whether this column shall apply such a scheme.
|
||||
*/
|
||||
public RaceColumnDTO createRaceColumnDTO(String columnName, boolean isMedal, Double explicitFactor,
|
||||
String regattaName, String seriesName, boolean isMetaLeaderboardColumn) {
|
||||
String regattaName, String seriesName, boolean isMetaLeaderboardColumn, boolean oneAlwaysStaysOne) {
|
||||
final RaceColumnDTO raceColumnDTO;
|
||||
if (seriesName != null) {
|
||||
raceColumnDTO = new RaceColumnInSeriesDTO(columnName, seriesName, regattaName);
|
||||
raceColumnDTO = new RaceColumnInSeriesDTO(columnName, seriesName, regattaName, oneAlwaysStaysOne);
|
||||
} else {
|
||||
raceColumnDTO = isMetaLeaderboardColumn ? new MetaLeaderboardRaceColumnDTO(columnName) : new RaceColumnDTO(columnName);
|
||||
raceColumnDTO = isMetaLeaderboardColumn ? new MetaLeaderboardRaceColumnDTO(columnName) : new RaceColumnDTO(columnName, oneAlwaysStaysOne);
|
||||
}
|
||||
fillRaceColumnDTO(raceColumnDTO, isMedal, explicitFactor);
|
||||
return raceColumnDTO;
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@ public class RaceColumnInSeriesDTO extends RaceColumnDTO {
|
||||
@Deprecated
|
||||
RaceColumnInSeriesDTO() {} // for GWT serialization
|
||||
|
||||
public RaceColumnInSeriesDTO(String name, String seriesName, String regattaName) {
|
||||
super(name);
|
||||
public RaceColumnInSeriesDTO(String name, String seriesName, String regattaName, boolean oneAlwaysStaysOne) {
|
||||
super(name, oneAlwaysStaysOne);
|
||||
this.seriesName = seriesName;
|
||||
this.regattaName = regattaName;
|
||||
}
|
||||
|
||||
+8
-1
@@ -22,10 +22,12 @@ public class SeriesCreationParametersDTO implements Serializable {
|
||||
|
||||
private Integer maximumNumberOfDiscards;
|
||||
|
||||
private boolean oneAlwaysStaysOne;
|
||||
|
||||
SeriesCreationParametersDTO() {}
|
||||
|
||||
public SeriesCreationParametersDTO(List<FleetDTO> fleets, boolean isMedal, boolean isFleetsCanRunInParallel, boolean isStartsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward,
|
||||
int[] discardingThresholds, boolean hasSplitFleetContiguousScoring, Integer maximumNumberOfDiscards) {
|
||||
int[] discardingThresholds, boolean hasSplitFleetContiguousScoring, Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne) {
|
||||
super();
|
||||
this.fleets = fleets;
|
||||
this.isMedal = isMedal;
|
||||
@@ -35,6 +37,7 @@ public class SeriesCreationParametersDTO implements Serializable {
|
||||
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
|
||||
this.discardingThresholds = discardingThresholds;
|
||||
this.maximumNumberOfDiscards = maximumNumberOfDiscards;
|
||||
this.oneAlwaysStaysOne = oneAlwaysStaysOne;
|
||||
}
|
||||
|
||||
public List<FleetDTO> getFleets() {
|
||||
@@ -68,4 +71,8 @@ public class SeriesCreationParametersDTO implements Serializable {
|
||||
public Integer getMaximumNumberOfDiscards() {
|
||||
return maximumNumberOfDiscards;
|
||||
}
|
||||
|
||||
public boolean isOneAlwaysStaysOne() {
|
||||
return oneAlwaysStaysOne;
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.RegattaRegistry;
|
||||
import com.sap.sailing.domain.base.RemoteSailingServerReference;
|
||||
import com.sap.sailing.domain.base.SailingServerConfiguration;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.base.impl.DynamicBoat;
|
||||
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
|
||||
@@ -35,8 +36,10 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardRegistry;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
@@ -223,4 +226,12 @@ public interface DomainObjectFactory {
|
||||
TypeBasedServiceFinder<RaceTrackingConnectivityParametersHandler> getRaceTrackingConnectivityParamsServiceFinder();
|
||||
|
||||
Wind loadWind(Document windDocument);
|
||||
|
||||
Map<RaceIdentifier, MarkPassingRaceFingerprint> loadFingerprintsForMarkPassingHashes();
|
||||
|
||||
/**
|
||||
* For races that have a {@link MarkPassingRaceFingerprint} stored in the database (see {@link #loadFingerprintsForMarkPassingHashes()})
|
||||
* a caller can load the corresponding mark passings with this method.
|
||||
*/
|
||||
Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier);
|
||||
}
|
||||
|
||||
+6
-2
@@ -49,6 +49,7 @@ public enum FieldNames {
|
||||
SERIES_STARTS_WITH_ZERO_SCORE,
|
||||
SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING,
|
||||
SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD,
|
||||
SERIES_ONE_ALWAYS_STAYS_ONE,
|
||||
SERIES_FLEETS,
|
||||
SERIES_RACE_COLUMNS,
|
||||
SERIES_DISCARDING_THRESHOLDS,
|
||||
@@ -87,7 +88,7 @@ public enum FieldNames {
|
||||
LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS,
|
||||
LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON, LEADERBOARD_CORRECTED_SCORE, LEADERBOARD_SCORE_CORRECTION_TIMESTAMP,
|
||||
LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_SCORE_CORRECTION_MERGE_STATE,
|
||||
LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME,
|
||||
LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME, OTHER_TIEBREAKING_LEADERBOARD_NAME,
|
||||
ELMINATED_COMPETITORS,
|
||||
|
||||
// leaderboard group
|
||||
@@ -187,5 +188,8 @@ public enum FieldNames {
|
||||
ORC_CERTIFICATE_TWS_14KT, ORC_CERTIFICATE_TWS_16KT, ORC_CERTIFICATE_TWS_20KT, ORC_CERTIFICATE_R52_PREDICTION, ORC_CERTIFICATE_R60_PREDICTION,
|
||||
ORC_CERTIFICATE_R75_PREDICTION, ORC_CERTIFICATE_R90_PREDICTION, ORC_CERTIFICATE_R110_PREDICTION, ORC_CERTIFICATE_R120_PREDICTION,
|
||||
ORC_CERTIFICATE_R135_PREDICTION, ORC_CERTIFICATE_R150_PREDICTION, ORC_CERTIFICATE_BOATCLASS, ORC_CERTIFICATE_LENGTH, ORC_CERTIFICATE_TWA_SPEED_PREDICTIONS,
|
||||
ORC_CERTIFICATE_CIRCULAR_RANDOM_SPEED_PREDICTIONS, ORC_CERTIFICATE_NON_SPINNAKER_SPEED_PREDICTIONS, ORC_OTHER_RACE_IDENTIFIER, ORC_IMPLIED_WIND_SOURCE;
|
||||
ORC_CERTIFICATE_CIRCULAR_RANDOM_SPEED_PREDICTIONS, ORC_CERTIFICATE_NON_SPINNAKER_SPEED_PREDICTIONS, ORC_OTHER_RACE_IDENTIFIER, ORC_IMPLIED_WIND_SOURCE,
|
||||
|
||||
// MARKPASSINGS collection
|
||||
MARK_PASSINGS_FINGERPRINT, MARK_PASSINGS;
|
||||
}
|
||||
|
||||
+15
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.persistence;
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -20,14 +21,18 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.RemoteSailingServerReference;
|
||||
import com.sap.sailing.domain.base.SailingServerConfiguration;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.dto.AnniversaryType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
@@ -228,4 +233,14 @@ public interface MongoObjectFactory {
|
||||
void storeAnniversaryData(ConcurrentHashMap<Integer, Pair<DetailedRaceInfo, AnniversaryType>> knownAnniversaries);
|
||||
|
||||
Document storeWind(Wind wind);
|
||||
|
||||
/**
|
||||
* Stores a race's mark passings persistently.
|
||||
*
|
||||
* @param raceIdentifier
|
||||
* identifies the race to which the mark passings belong
|
||||
* @param fingerprint
|
||||
* a composite fingerprint of the race in the state at which the mark passings were captured
|
||||
*/
|
||||
void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings);
|
||||
}
|
||||
|
||||
+6
-1
@@ -120,6 +120,11 @@ public enum CollectionNames {
|
||||
/**
|
||||
* Contains the known anniversaries
|
||||
*/
|
||||
ANNIVERSARIES;
|
||||
ANNIVERSARIES,
|
||||
|
||||
/**
|
||||
* Contains the Hashes for the {@link MarkPassingHashFingerprint}
|
||||
*/
|
||||
MARKPASSINGS;
|
||||
|
||||
}
|
||||
|
||||
+97
-8
@@ -233,8 +233,11 @@ import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithC
|
||||
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.markpassinghash.impl.MarkPassingRaceFingerprintImpl;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.FieldNames;
|
||||
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
|
||||
@@ -246,10 +249,12 @@ import com.sap.sailing.domain.ranking.RankingMetricConstructor;
|
||||
import com.sap.sailing.domain.ranking.RankingMetricsFactory;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.regattalog.RegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.BoatJsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.CompetitorWithBoatRefJsonDeserializer;
|
||||
@@ -394,6 +399,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #loadRaceIdentifier(Document)
|
||||
*/
|
||||
private void addRaceIdentifierToQuery(Document query, RaceIdentifier raceIdentifier) {
|
||||
query.put(FieldNames.EVENT_NAME.name(), raceIdentifier.getRegattaName());
|
||||
query.put(FieldNames.RACE_NAME.name(), raceIdentifier.getRaceName());
|
||||
}
|
||||
|
||||
static void ensureIndicesOnWindTracks(MongoCollection<Document> windTracks) {
|
||||
windTracks.createIndex(new Document(FieldNames.RACE_ID.name(), 1), new IndexOptions().name("windbyrace").background(false)); // for new programmatic access
|
||||
@@ -491,7 +504,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
result = leaderboardRegistry.getLeaderboardByName(leaderboardName);
|
||||
}
|
||||
if (result == null) {
|
||||
String wrappedRegattaLeaderboardName = (String) dbLeaderboard
|
||||
final String wrappedRegattaLeaderboardName = (String) dbLeaderboard
|
||||
.get(FieldNames.WRAPPED_REGATTA_LEADERBOARD_NAME.name());
|
||||
if (wrappedRegattaLeaderboardName != null) {
|
||||
result = loadRegattaLeaderboardWithEliminations(dbLeaderboard, leaderboardName,
|
||||
@@ -510,7 +523,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
result = loadFlexibleLeaderboard(dbLeaderboard, resultDiscardingRule);
|
||||
} else {
|
||||
result = loadRegattaLeaderboard(leaderboardName, regattaName, dbLeaderboard,
|
||||
resultDiscardingRule, regattaRegistry);
|
||||
resultDiscardingRule, regattaRegistry, leaderboardRegistry);
|
||||
}
|
||||
}
|
||||
if (result != null) {
|
||||
@@ -590,14 +603,21 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
*/
|
||||
private RegattaLeaderboard loadRegattaLeaderboard(String leaderboardName, String regattaName,
|
||||
Document dbLeaderboard, ThresholdBasedResultDiscardingRule resultDiscardingRule,
|
||||
RegattaRegistry regattaRegistry) {
|
||||
RegattaLeaderboard result = null;
|
||||
RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) {
|
||||
final RegattaLeaderboard result;
|
||||
Regatta regatta = regattaRegistry.getRegatta(new RegattaName(regattaName));
|
||||
if (regatta == null) {
|
||||
logger.info("Couldn't find regatta " + regattaName
|
||||
+ " for corresponding regatta leaderboard. Not loading regatta leaderboard.");
|
||||
result = null;
|
||||
} else {
|
||||
result = new RegattaLeaderboardImpl(regatta, resultDiscardingRule);
|
||||
final String otherTieBreakingLeaderboardName = (String) dbLeaderboard.get(FieldNames.OTHER_TIEBREAKING_LEADERBOARD_NAME.name());
|
||||
if (otherTieBreakingLeaderboardName == null) {
|
||||
result = new RegattaLeaderboardImpl(regatta, resultDiscardingRule);
|
||||
} else {
|
||||
result = new RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl(regatta, resultDiscardingRule,
|
||||
() -> (RegattaLeaderboard) leaderboardRegistry.getLeaderboardByName(otherTieBreakingLeaderboardName));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1426,11 +1446,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
final Number maximumNumberOfDiscardsAsObject = (Number) dbSeries
|
||||
.get(FieldNames.SERIES_MAXIMUM_NUMBER_OF_DISCARDS.name());
|
||||
final Integer maximumNumberOfDiscards = maximumNumberOfDiscardsAsObject == null ? null : maximumNumberOfDiscardsAsObject.intValue();
|
||||
Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name());
|
||||
Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries
|
||||
final Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name());
|
||||
final Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries
|
||||
.get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name());
|
||||
Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries
|
||||
final Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries
|
||||
.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name());
|
||||
final Boolean oneAlwaysStaysOne = (Boolean) dbSeries.get(FieldNames.SERIES_ONE_ALWAYS_STAYS_ONE.name());
|
||||
@SuppressWarnings("unchecked")
|
||||
final Iterable<Document> dbFleets = (Iterable<Document>) dbSeries.get(FieldNames.SERIES_FLEETS.name());
|
||||
List<Fleet> fleets = loadFleets(dbFleets);
|
||||
@@ -1454,6 +1475,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
if (firstColumnIsNonDiscardableCarryForward != null) {
|
||||
series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForward);
|
||||
}
|
||||
if (oneAlwaysStaysOne != null) {
|
||||
series.setOneAlwaysStaysOne(oneAlwaysStaysOne);
|
||||
}
|
||||
loadRaceColumnRaceLinks(dbRaceColumns, series);
|
||||
return series;
|
||||
}
|
||||
@@ -3146,5 +3170,70 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
public TypeBasedServiceFinder<RaceTrackingConnectivityParametersHandler> getRaceTrackingConnectivityParamsServiceFinder() {
|
||||
return raceTrackingConnectivityParamsServiceFinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<RaceIdentifier, MarkPassingRaceFingerprint> loadFingerprintsForMarkPassingHashes() {
|
||||
final MongoCollection<Document> markPassingsCollection = database.getCollection(CollectionNames.MARKPASSINGS.name());
|
||||
markPassingsCollection.createIndex(new Document()
|
||||
.append(FieldNames.EVENT_NAME.name(), 1)
|
||||
.append(FieldNames.RACE_NAME.name(), 1),
|
||||
new IndexOptions()
|
||||
.unique(true)
|
||||
.name("markpassingsbyeventandrace")
|
||||
.background(false));
|
||||
final Map<RaceIdentifier, MarkPassingRaceFingerprint> fingerprintHashMap = new HashMap<>();
|
||||
for (final Document currentDocument : markPassingsCollection.find()) {
|
||||
final Pair<RaceIdentifier, MarkPassingRaceFingerprint> fingerprint = loadMarkPassingsFingerprint(currentDocument);
|
||||
if (fingerprint != null && fingerprint.getB() != null) {
|
||||
fingerprintHashMap.put(fingerprint.getA(), fingerprint.getB());
|
||||
}
|
||||
}
|
||||
return fingerprintHashMap;
|
||||
}
|
||||
|
||||
private Pair<RaceIdentifier, MarkPassingRaceFingerprint> loadMarkPassingsFingerprint(final Document currentDocument) {
|
||||
final RaceIdentifier raceIdentifier = loadRaceIdentifier(currentDocument);
|
||||
MarkPassingRaceFingerprint fingerprint;
|
||||
try {
|
||||
final JSONObject json = Helpers.toJSONObjectSafe(
|
||||
new JSONParser().parse(((Document) currentDocument.get(FieldNames.MARK_PASSINGS_FINGERPRINT.name())).toJson()));
|
||||
fingerprint = new MarkPassingRaceFingerprintImpl(json);
|
||||
} catch (JsonDeserializationException | ParseException e) {
|
||||
logger.log(Level.WARNING, "Problem de-serializing mark passings from document; ignoring", e);
|
||||
fingerprint = null;
|
||||
}
|
||||
return new Pair<>(raceIdentifier, fingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier) {
|
||||
final Map<Competitor, Map<Waypoint, MarkPassing>> result;
|
||||
final Document query = new Document();
|
||||
addRaceIdentifierToQuery(query, raceIdentifier);
|
||||
final MongoCollection<Document> markPassingsCollection = database.getCollection(CollectionNames.MARKPASSINGS.name());
|
||||
final Document doc = markPassingsCollection.find(query).first();
|
||||
if (doc != null) {
|
||||
result = new HashMap<>();
|
||||
final List<Document> markPassingsDoc = doc.getList(FieldNames.MARK_PASSINGS.name(), Document.class);
|
||||
for (final Document markPassingsForOneCompetitorDoc : markPassingsDoc) {
|
||||
final Serializable competitorId = markPassingsForOneCompetitorDoc.get(FieldNames.COMPETITOR_ID.name(), Serializable.class);
|
||||
final Competitor competitor = baseDomainFactory.getExistingCompetitorById(competitorId);
|
||||
for (final Document markPassingForWaypoint : markPassingsForOneCompetitorDoc.getList(FieldNames.MARK_PASSINGS.name(), Document.class)) {
|
||||
final Pair<Waypoint, MarkPassing> waypointAndMarkPassing = loadWaypointAndMarkPassing(competitor, markPassingForWaypoint);
|
||||
result.computeIfAbsent(competitor, c->new HashMap<>()).put(waypointAndMarkPassing.getA(), waypointAndMarkPassing.getB());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Pair<Waypoint, MarkPassing> loadWaypointAndMarkPassing(Competitor competitor, Document markPassingForWaypoint) {
|
||||
final Serializable waypointId = markPassingForWaypoint.get(FieldNames.WAYPOINT_ID.name(), Serializable.class);
|
||||
final Waypoint waypoint = baseDomainFactory.getExistingWaypointById(waypointId);
|
||||
final TimePoint timePoint = TimePoint.of(markPassingForWaypoint.getLong(FieldNames.TIME_AS_MILLIS.name()));
|
||||
final MarkPassing markPassing = new MarkPassingImpl(timePoint, waypoint, competitor);
|
||||
return new Pair<>(waypoint, markPassing);
|
||||
}
|
||||
}
|
||||
|
||||
+37
@@ -122,13 +122,16 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.persistence.FieldNames;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -399,6 +402,10 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
|
||||
private void storeRegattaLeaderboard(RegattaLeaderboard leaderboard, Document dbLeaderboard) {
|
||||
dbLeaderboard.put(FieldNames.REGATTA_NAME.name(), leaderboard.getRegatta().getName());
|
||||
if (leaderboard instanceof RegattaLeaderboardWithOtherTieBreakingLeaderboard) {
|
||||
dbLeaderboard.put(FieldNames.OTHER_TIEBREAKING_LEADERBOARD_NAME.name(),
|
||||
((RegattaLeaderboardWithOtherTieBreakingLeaderboard) leaderboard).getOtherTieBreakingLeaderboard().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void storeFlexibleLeaderboard(FlexibleLeaderboard leaderboard, Document dbLeaderboard) {
|
||||
@@ -798,6 +805,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
dbSeries.put(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name(), s.hasSplitFleetContiguousScoring());
|
||||
dbSeries.put(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name(), s.isStartsWithZeroScore());
|
||||
dbSeries.put(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name(), s.isFirstColumnIsNonDiscardableCarryForward());
|
||||
dbSeries.put(FieldNames.SERIES_ONE_ALWAYS_STAYS_ONE.name(), s.isOneAlwaysStaysOne());
|
||||
BasicDBList dbFleets = new BasicDBList();
|
||||
for (Fleet fleet : s.getFleets()) {
|
||||
dbFleets.add(storeFleet(fleet));
|
||||
@@ -1958,5 +1966,34 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
document.append(FieldNames.ORC_CERTIFICATE.name(), createORCCertificateObject(event.getCertificate()));
|
||||
return storeRegattaLogEvent(regattaLikeIdentifier, document);
|
||||
}
|
||||
|
||||
private List<Document> storeMarkPassings(Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
|
||||
final List<Document> result = new ArrayList<>();
|
||||
for (final Entry<Competitor, Map<Waypoint, MarkPassing>> e : markPassings.entrySet()) {
|
||||
final Document competitorMarkPassings = new Document();
|
||||
competitorMarkPassings.put(FieldNames.COMPETITOR_ID.name(), e.getKey().getId());
|
||||
final List<Document> markPassingsList = new ArrayList<>();
|
||||
for (final Entry<Waypoint, MarkPassing> f : e.getValue().entrySet()) {
|
||||
final Document markPassingDoc = new Document();
|
||||
markPassingDoc.put(FieldNames.WAYPOINT_ID.name(), f.getKey().getId());
|
||||
markPassingDoc.put(FieldNames.TIME_AS_MILLIS.name(), f.getValue().getTimePoint().asMillis());
|
||||
markPassingsList.add(markPassingDoc);
|
||||
}
|
||||
competitorMarkPassings.put(FieldNames.MARK_PASSINGS.name(), markPassingsList);
|
||||
result.add(competitorMarkPassings);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void storeMarkPassings(RaceIdentifier raceIdentification, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
|
||||
MongoCollection<Document> markPassingCollection = database.getCollection(CollectionNames.MARKPASSINGS.name());
|
||||
JSONObject fingerprintjson = fingerprint.toJson();
|
||||
final Document result = new Document();
|
||||
final Document fingerprintDoc = Document.parse(fingerprintjson.toString());
|
||||
result.put(FieldNames.MARK_PASSINGS_FINGERPRINT.name(), fingerprintDoc);
|
||||
storeRaceIdentifier(result, raceIdentification);
|
||||
final List<Document> markPassingsDoc = storeMarkPassings(markPassings);
|
||||
result.put(FieldNames.MARK_PASSINGS.name(), markPassingsDoc);
|
||||
markPassingCollection.insertOne(result);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ public interface SharedDomainFactory<RLR extends RaceLogResolver> extends Compet
|
||||
*/
|
||||
Waypoint createWaypoint(ControlPoint controlPoint, PassingInstruction passingInstruction);
|
||||
|
||||
Waypoint getExistingWaypointById(Waypoint waypointPrototype);
|
||||
Waypoint getExistingWaypointById(Serializable waypointId);
|
||||
|
||||
/**
|
||||
* Atomically checks if a waypoint by an equal {@link Waypoint#getId()} as <code>waypoint</code> exists in this domain factory's
|
||||
|
||||
+3
-3
@@ -271,16 +271,16 @@ public class SharedDomainFactoryImpl<RLR extends RaceLogResolver> implements Sha
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getExistingWaypointById(Waypoint waypointPrototype) {
|
||||
public Waypoint getExistingWaypointById(Serializable waypointId) {
|
||||
synchronized (waypointCache) {
|
||||
expungeStaleWaypointCacheEntries();
|
||||
Waypoint result = null;
|
||||
Reference<Waypoint> ref = waypointCache.get(waypointPrototype.getId());
|
||||
Reference<Waypoint> ref = waypointCache.get(waypointId);
|
||||
if (ref != null) {
|
||||
result = ref.get();
|
||||
if (result == null) {
|
||||
// waypoint was finalized; remove entry from cache
|
||||
waypointCache.remove(waypointPrototype.getId());
|
||||
waypointCache.remove(waypointId);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
+1
-1
@@ -410,7 +410,7 @@ public class LeaderboardDTODiffingTest {
|
||||
public void testPartialRaceColumnDTOCompaction() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
|
||||
// create a modified R9 RaceDTO clone in newVersion to make sure that even changing a property in the RaceDTO will keep the RaceColumnDTO from being omitted
|
||||
RaceColumnDTO r9 = newVersion.getRaceColumnByName("R9");
|
||||
RaceColumnDTO clonedR9 = new RaceColumnDTO("R9");
|
||||
RaceColumnDTO clonedR9 = new RaceColumnDTO("R9", /* oneAlwaysStaysOne */ false);
|
||||
cloner.clone(r9, clonedR9);
|
||||
// also clone the racesPerFleet map, or else we'd be modifying the previous version's one too
|
||||
final Field racesPerFleetField = clonedR9.getClass().getDeclaredField("racesPerFleet");
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.sap.sailing.domain.common.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
|
||||
public class ScoringSchemeTypeScoreScalingTest {
|
||||
private static final double DELTA = 0.0000001;
|
||||
|
||||
@Test
|
||||
public void testScalingThreeWithThree() {
|
||||
testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 3, /* scaled */ 9, /* oneAlwaysStaysOne */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScalingOneWithThree() {
|
||||
testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 3, /* scaled */ 3, /* oneAlwaysStaysOne */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScalingThreeWithTwo() {
|
||||
testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 2, /* scaled */ 6, /* oneAlwaysStaysOne */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScalingOneWithTwo() {
|
||||
testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 2, /* scaled */ 2, /* oneAlwaysStaysOne */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScalingWithThreeWithOneStaysOne() {
|
||||
testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 3, /* scaled */ 7, /* oneAlwaysStaysOne */ true);
|
||||
testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 3, /* scaled */ 1, /* oneAlwaysStaysOne */ true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScalingWithTwoWithOneStaysOne() {
|
||||
testScalingAndUnscaling(/* unscaled */ 3, /* factor */ 2, /* scaled */ 5, /* oneAlwaysStaysOne */ true);
|
||||
testScalingAndUnscaling(/* unscaled */ 1, /* factor */ 2, /* scaled */ 1, /* oneAlwaysStaysOne */ true);
|
||||
}
|
||||
|
||||
private void testScalingAndUnscaling(final double unscaled, final double factor, final double scaled,
|
||||
final boolean oneAlwaysStaysOne) {
|
||||
assertEquals(scaled, ScoringSchemeType.getScaledScore(factor, unscaled, oneAlwaysStaysOne), DELTA);
|
||||
assertEquals(unscaled, ScoringSchemeType.getUnscaledScore(factor, scaled, oneAlwaysStaysOne), DELTA);
|
||||
}
|
||||
}
|
||||
+10
-5
@@ -29,6 +29,7 @@ import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
||||
import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.DynamicCompetitorWithBoat;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
@@ -83,10 +84,14 @@ public class TestFarthestAheadWithSkippedMarkPassing {
|
||||
private Waypoint windward;
|
||||
private Waypoint finish;
|
||||
|
||||
private void setUp(TimeOnTimeFactorMapping timeOnTimeFactors, Function<Competitor, Double> timeOnDistanceFactors) {
|
||||
CompetitorWithBoat fastCompetitor = TrackBasedTest.createCompetitorWithBoat("FastBoat");
|
||||
private void setUp(TimeOnTimeFactorMapping timeOnTimeFactors, Function<Competitor, Duration> timeOnDistanceAllowances) {
|
||||
DynamicCompetitorWithBoat fastCompetitor = (DynamicCompetitorWithBoat) TrackBasedTest.createCompetitorWithBoat("FastBoat");
|
||||
fastCompetitor.setTimeOnTimeFactor(timeOnTimeFactors.apply(fastCompetitor));
|
||||
fastCompetitor.setTimeOnDistanceAllowancePerNauticalMile(timeOnDistanceAllowances.apply(fastCompetitor));
|
||||
c1 = fastCompetitor;
|
||||
CompetitorWithBoat slowCompetitor = TrackBasedTest.createCompetitorWithBoat("SlowBoat");
|
||||
DynamicCompetitorWithBoat slowCompetitor = (DynamicCompetitorWithBoat) TrackBasedTest.createCompetitorWithBoat("SlowBoat");
|
||||
slowCompetitor.setTimeOnTimeFactor(timeOnTimeFactors.apply(slowCompetitor));
|
||||
slowCompetitor.setTimeOnDistanceAllowancePerNauticalMile(timeOnDistanceAllowances.apply(slowCompetitor));
|
||||
c2 = slowCompetitor;
|
||||
trackedRace = createTrackedRace(TrackBasedTest.createCompetitorAndBoatsMap(fastCompetitor, slowCompetitor));
|
||||
rankingMetric = trackedRace.getRankingMetric();
|
||||
@@ -141,7 +146,7 @@ public class TestFarthestAheadWithSkippedMarkPassing {
|
||||
*/
|
||||
@Test
|
||||
public void testWithOneMarkPassingOnly() {
|
||||
setUp(c -> c==c1 ? 2.0 : 1.0, c -> 0.0);
|
||||
setUp(c -> c==c1 ? 2.0 : 1.0, c -> Duration.NULL);
|
||||
final TimePoint startOfRace = MillisecondsTimePoint.now();
|
||||
final TimePoint middleOfFirstLeg = startOfRace.plus(Duration.ONE_HOUR.times(3));
|
||||
trackedRace.updateMarkPassings(
|
||||
@@ -174,7 +179,7 @@ public class TestFarthestAheadWithSkippedMarkPassing {
|
||||
*/
|
||||
@Test
|
||||
public void testWithTwoMarkPassingsForC1AndSkippedStartForC2() {
|
||||
setUp(c -> c==c1 ? 2.0 : 1.0, c -> 0.0);
|
||||
setUp(c -> c==c1 ? 2.0 : 1.0, c -> Duration.NULL);
|
||||
final TimePoint startOfRace = MillisecondsTimePoint.now();
|
||||
final TimePoint endOfFirstLeg = startOfRace.plus(Duration.ONE_HOUR.times(3));
|
||||
final TimePoint middleOfSecondLeg = endOfFirstLeg.plus(Duration.ONE_MINUTE.times(10));
|
||||
|
||||
-6
@@ -57,7 +57,6 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
|
||||
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
|
||||
@@ -85,11 +84,6 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.shared.util.impl.ArrayListNavigableSet;
|
||||
|
||||
public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRankingTestBase {
|
||||
protected DelegatingRegattaLeaderboardWithCompetitorElimination createDelegatingRegattaLeaderboardWithCompetitorElimination(
|
||||
Regatta regatta, String leaderboardName, int[] discardingThresholds) {
|
||||
return new DelegatingRegattaLeaderboardWithCompetitorElimination(()->createLeaderboard(regatta, discardingThresholds), leaderboardName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneStartedRaceWithDifferentScores() throws NoWindException {
|
||||
List<Competitor> competitors = createCompetitors(10);
|
||||
|
||||
-7
@@ -23,7 +23,6 @@ import com.sap.sailing.domain.common.CompetitorRegistrationType;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPointFirstToWinTwoRaces;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
|
||||
@@ -42,12 +41,6 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
public class LeaderboardScoringAndRankingTestForLowPoints extends LeaderboardScoringAndRankingTestBase {
|
||||
private static final double EPSILON = 0.000001;
|
||||
|
||||
protected DelegatingRegattaLeaderboardWithCompetitorElimination createDelegatingRegattaLeaderboardWithCompetitorElimination(
|
||||
Regatta regatta, String leaderboardName, int[] discardingThresholds) {
|
||||
return new DelegatingRegattaLeaderboardWithCompetitorElimination(
|
||||
() -> createLeaderboard(regatta, discardingThresholds), leaderboardName);
|
||||
}
|
||||
|
||||
private void executePreSeries(List<Competitor> yellow, List<Competitor> blue, TimePoint now) {
|
||||
RaceColumn qColumn = series.get(0).getRaceColumnByName("Q");
|
||||
TrackedRace qYellow = new MockedTrackedRaceWithStartTimeAndRanks(now, yellow);
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package com.sap.sailing.domain.test.markpassinghash;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory;
|
||||
import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class MarkPassingRaceFingerprintJsonSerializationTest extends OnlineTracTracBasedTest {
|
||||
DynamicTrackedRaceImpl trackedRace1;
|
||||
DynamicTrackedRaceImpl trackedRace2;
|
||||
MarkPassingCalculator calculator1;
|
||||
MarkPassingCalculator calculator2;
|
||||
|
||||
public MarkPassingRaceFingerprintJsonSerializationTest() throws MalformedURLException, URISyntaxException {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getExpectedEventName() {
|
||||
return "Academy Tracking 2011";
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp("event_20110505_SailingTea", // Semifinale
|
||||
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* liveUri */ null, /* storedUri */ null,
|
||||
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE,
|
||||
ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA });
|
||||
trackedRace1 = getTrackedRace();
|
||||
super.setUp();
|
||||
super.setUp("event_20110505_SailingTea", // Semifinale
|
||||
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* liveUri */ null, /* storedUri */ null,
|
||||
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE,
|
||||
ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA });
|
||||
trackedRace2 = getTrackedRace();
|
||||
calculator1 = new MarkPassingCalculator(trackedRace1, false, false);
|
||||
calculator2 = new MarkPassingCalculator(trackedRace2, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonSerialization() {
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
MarkPassingRaceFingerprint output1 = factory.fromJson(json1);
|
||||
assertTrue("Original and de-serialized copy are equal", output1.matches(trackedRace1));
|
||||
assertEquals(fingerprint1, output1);
|
||||
assertEquals(fingerprint1.hashCode(), output1.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonSerializationWithChangesInMarkFixes() {
|
||||
DynamicTrackedRaceImpl testRace = trackedRace2;
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
// Change of the race should result in a different hash
|
||||
TimePoint epoch = new MillisecondsTimePoint(0l);
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
Map<String, Position> markPositions = new HashMap<String, Position>();
|
||||
markPositions.put("CR Start (1)", new DegreePosition(53.562944999999985, 10.010104000000046));
|
||||
markPositions.put("CR Start (2)", new DegreePosition(53.562944999999985, 10.010104000000046));
|
||||
markPositions.put("Leeward mark", new DegreePosition(53.562145000000015, 10.009252));
|
||||
markPositions.put("Luvtonne", new DegreePosition(53.560581899999995, 10.005657));
|
||||
for (Waypoint w : trackedRace2.getRace().getCourse().getWaypoints()) {
|
||||
for (Mark mark : w.getMarks()) {
|
||||
assert markPositions.containsKey(mark.getName());
|
||||
testRace.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), epoch));
|
||||
testRace.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), now));
|
||||
}
|
||||
}
|
||||
assertFalse(fingerprint1.matches(trackedRace2));
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(testRace);
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWaypointChangePassingInstruction() {
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
Waypoint wp = trackedRace2.getRace().getCourse().getFirstWaypoint();
|
||||
ControlPoint cP = wp.getControlPoint();
|
||||
WaypointImpl wpNew = new WaypointImpl(cP, PassingInstruction.Gate);
|
||||
trackedRace2.getRace().getCourse().removeWaypoint(0);
|
||||
trackedRace2.getRace().getCourse().addWaypoint(0, wpNew);
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(trackedRace2);
|
||||
assertFalse(fingerprint1.matches(trackedRace2));
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlPointChange() {
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
Mark gate1 = new MarkImpl("Gate1");
|
||||
Mark gate2 = new MarkImpl("Gate2");
|
||||
ControlPointWithTwoMarks cp = new ControlPointWithTwoMarksImpl(gate1, gate2, "cp", "");
|
||||
Waypoint wpNew = new WaypointImpl(cp, PassingInstruction.None);
|
||||
trackedRace2.getRace().getCourse().removeWaypoint(0);
|
||||
trackedRace2.getRace().getCourse().addWaypoint(0, wpNew);
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(trackedRace2);
|
||||
assertFalse(fingerprint1.matches(trackedRace2));
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompetitorFixChange() {
|
||||
DynamicTrackedRaceImpl testRace = trackedRace2;
|
||||
final DynamicTrackedRaceImpl secureRace = trackedRace2;
|
||||
MarkPassingRaceFingerprintFactory factory = MarkPassingRaceFingerprintFactory.INSTANCE;
|
||||
MarkPassingRaceFingerprint fingerprint1 = factory.createFingerprint(trackedRace1);
|
||||
assertTrue(fingerprint1.matches(trackedRace2));
|
||||
Competitor firstCompetitor = trackedRace2.getRace().getCompetitors().iterator().next();
|
||||
secureRace.getTrack(firstCompetitor).lockForRead();
|
||||
GPSFixMoving firstFix;
|
||||
try {
|
||||
firstFix = testRace.getTrack(firstCompetitor).getRawFixes().iterator().next();
|
||||
} finally {
|
||||
secureRace.getTrack(firstCompetitor).unlockAfterRead();
|
||||
}
|
||||
SpeedWithBearing speed = firstFix.getSpeed();
|
||||
Position pos = firstFix.getPosition();
|
||||
TimePoint tp = firstFix.getTimePoint();
|
||||
DegreePosition degPos = new DegreePosition(pos.getLatDeg() + 0.05, pos.getLngDeg() + 0.05);
|
||||
GPSFixMoving gpsM = new GPSFixMovingImpl(degPos, tp, speed);
|
||||
testRace.getTrack(firstCompetitor).add(gpsM, true);
|
||||
MarkPassingRaceFingerprint fingerprint2 = factory.createFingerprint(testRace);
|
||||
assertFalse(fingerprint1.matches(testRace));
|
||||
assertFalse(fingerprint2.matches(trackedRace1));
|
||||
JSONObject json1 = fingerprint1.toJson();
|
||||
JSONObject json2 = fingerprint2.toJson();
|
||||
assertNotEquals("Json1 and Json2 are equal: " + json1 + " json2: " + json2, json1, json2);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ Export-Package: com.sap.sailing.domain.base,
|
||||
com.sap.sailing.domain.markpassingcalculation,
|
||||
com.sap.sailing.domain.markpassingcalculation.impl,
|
||||
com.sap.sailing.domain.markpassingcalculation.splining,
|
||||
com.sap.sailing.domain.markpassinghash,
|
||||
com.sap.sailing.domain.markpassinghash.impl;x-friends:="com.sap.sailing.domain.persistence",
|
||||
com.sap.sailing.domain.masterdataimport,
|
||||
com.sap.sailing.domain.orc,
|
||||
com.sap.sailing.domain.orc.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.test,com.sap.sailing.mongodb.test",
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.CompetitorRegistrationOnRaceLogDisabledException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStore;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
@@ -195,10 +196,20 @@ public interface RaceColumn extends Named {
|
||||
void setFactor(Double factor);
|
||||
|
||||
/**
|
||||
* If <code>null</code>, the {@link #getFactor() factor} defaults to 1 for non-medal and {@link #DEFAULT_MEDAL_RACE_FACTOR} for
|
||||
* medal races. Otherwise, the explicit factor is used.
|
||||
* {@link ScoringScheme}s shall prefer this explicit factor, if not <code>null</code>, and otherwise determine a default
|
||||
* based on the rules of the scoring scheme, e.g., defaulting to 1 for non-medal and {@link #DEFAULT_MEDAL_RACE_FACTOR} for
|
||||
* medal races, or similar.
|
||||
*/
|
||||
Double getExplicitFactor();
|
||||
|
||||
/**
|
||||
* When scores in this column are scaled by some factor, either based on the {@link #getExplicitFactor() explicit factor}
|
||||
* set for this column, or implicitly, e.g., because the {@link ScoringScheme} mandates the doubling of medal race scores
|
||||
* and this column {@link #isMedalRace() represents a medal race}, then some configurations still want the 1.0 score still
|
||||
* to be 1.0. For example, with a column factor of 2.0 scores 1, 2, 3 would end up as 1, 3, 5; or with a column factor of
|
||||
* 3.0 scores 1, 2, 3 would end up as 1, 4, 7. This method tells whether this column shall apply such a scheme.
|
||||
*/
|
||||
boolean isOneAlwaysStaysOne();
|
||||
|
||||
/**
|
||||
* Returns the race log identifier associated with this fleet and race log
|
||||
|
||||
@@ -23,6 +23,8 @@ public interface RaceColumnListener extends Serializable {
|
||||
|
||||
void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring);
|
||||
|
||||
void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne);
|
||||
|
||||
default boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.sap.sailing.domain.base;
|
||||
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
@@ -111,6 +112,9 @@ public interface Series extends SeriesBase {
|
||||
*/
|
||||
boolean isStartsWithZeroScore();
|
||||
|
||||
/**
|
||||
* @see #isStartsWithZeroScore()
|
||||
*/
|
||||
void setStartsWithZeroScore(boolean startsWithZeroScore);
|
||||
|
||||
boolean isFirstColumnIsNonDiscardableCarryForward();
|
||||
@@ -125,6 +129,23 @@ public interface Series extends SeriesBase {
|
||||
*/
|
||||
boolean hasSplitFleetContiguousScoring();
|
||||
|
||||
/**
|
||||
* @see #hasSplitFleetContiguousScoring()
|
||||
*/
|
||||
void setSplitFleetContiguousScoring(boolean hasSplitFleetScore);
|
||||
|
||||
/**
|
||||
* When scores in this series are scaled by some factor, either based on an {@link RaceColumn#getExplicitFactor()
|
||||
* explicit column factor}, or implicitly, e.g., because the {@link ScoringScheme} mandates the
|
||||
* doubling of medal race scores and this series {@link #isMedal() represents a medal series}, then some
|
||||
* configurations still want the 1.0 score still to be 1.0. For example, with a column factor of 2.0 scores 1, 2, 3
|
||||
* would end up as 1, 3, 5; or with a column factor of 3.0 scores 1, 2, 3 would end up as 1, 4, 7. This method tells
|
||||
* whether this column shall apply such a scheme.
|
||||
*/
|
||||
boolean isOneAlwaysStaysOne();
|
||||
|
||||
/**
|
||||
* @see #isOneAlwaysStaysOne()
|
||||
*/
|
||||
void setOneAlwaysStaysOne(boolean oneAlwaysStaysOne);
|
||||
}
|
||||
|
||||
+3
@@ -42,6 +42,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointByWinsTiesLastlyBrokenByOtherLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall12PointsMax;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie;
|
||||
@@ -155,6 +156,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl<RaceLogAndTracked
|
||||
return new LowPointFirstToWinTwoRaces();
|
||||
case LOW_POINT_FIRST_TO_WIN_THREE_RACES:
|
||||
return new LowPointFirstToWinThreeRaces();
|
||||
case HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD:
|
||||
return new HighPointByWinsTiesLastlyBrokenByOtherLeaderboard();
|
||||
}
|
||||
throw new RuntimeException("Unknown scoring scheme type "+scoringSchemeType.name());
|
||||
}
|
||||
|
||||
+5
@@ -71,6 +71,11 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo
|
||||
return series.isMedal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOneAlwaysStaysOne() {
|
||||
return getSeries().isOneAlwaysStaysOne();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Series getSeries() {
|
||||
return series;
|
||||
|
||||
+5
@@ -89,6 +89,11 @@ public interface RaceColumnListenerWithDefaultAction extends RaceColumnListener
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) {
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName) {
|
||||
defaultAction();
|
||||
|
||||
@@ -576,6 +576,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
hasSplitFleetContiguousScoring);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) {
|
||||
raceColumnListeners.notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
|
||||
return raceColumnListeners.canAddRaceColumnToContainer(raceColumn);
|
||||
|
||||
@@ -40,6 +40,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
|
||||
private Regatta regatta;
|
||||
private final RaceColumnListeners raceColumnListeners;
|
||||
private ThresholdBasedResultDiscardingRule resultDiscardingRule;
|
||||
private boolean oneAlwaysStaysOne;
|
||||
|
||||
/**
|
||||
* If not {@code null}, defines an upper inclusive limit for the number of races that may be discarded from
|
||||
@@ -320,6 +321,11 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
|
||||
raceColumnListeners.notifyListenersAboutFactorChanged(raceColumn, oldFactor, newFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) {
|
||||
raceColumnListeners.notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName) {
|
||||
raceColumnListeners.notifyListenersAboutCompetitorDisplayNameChanged(competitor, oldDisplayName, displayName);
|
||||
@@ -401,7 +407,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
|
||||
boolean oldStartsWithZeroScore = this.startsWithZeroScore;
|
||||
if (oldStartsWithZeroScore != startsWithZeroScore) {
|
||||
this.startsWithZeroScore = startsWithZeroScore;
|
||||
RaceColumn firstRaceColumnInSeries = getFirstRaceColumn();
|
||||
final RaceColumn firstRaceColumnInSeries = getFirstRaceColumn();
|
||||
if (firstRaceColumnInSeries != null) {
|
||||
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(firstRaceColumnInSeries, startsWithZeroScore);
|
||||
}
|
||||
@@ -428,7 +434,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
|
||||
boolean oldFirstColumnIsNonDiscardableCarryForward = this.firstColumnIsNonDiscardableCarryForward;
|
||||
if (oldFirstColumnIsNonDiscardableCarryForward != firstColumnIsNonDiscardableCarryForward) {
|
||||
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
|
||||
RaceColumn firstRaceColumnInSeries = getFirstRaceColumn();
|
||||
final RaceColumn firstRaceColumnInSeries = getFirstRaceColumn();
|
||||
if (firstRaceColumnInSeries != null) {
|
||||
raceColumnListeners.notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(firstRaceColumnInSeries, firstColumnIsNonDiscardableCarryForward);
|
||||
}
|
||||
@@ -440,4 +446,20 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
|
||||
public boolean hasSplitFleetContiguousScoring() {
|
||||
return hasSplitFleetContiguousScoring;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOneAlwaysStaysOne() {
|
||||
return oneAlwaysStaysOne;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOneAlwaysStaysOne(boolean oneAlwaysStaysOne) {
|
||||
boolean oldOneAlwaysStaysOne = this.oneAlwaysStaysOne;
|
||||
if (oldOneAlwaysStaysOne != oneAlwaysStaysOne) {
|
||||
this.oneAlwaysStaysOne = oneAlwaysStaysOne;
|
||||
for (RaceColumn raceColumn : getRaceColumns()) {
|
||||
raceColumnListeners.notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.sap.sailing.domain.leaderboard;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public abstract class AbstractScoreCorrectionListenerWithDefaultAction implements ScoreCorrectionListener {
|
||||
|
||||
@Override
|
||||
public void correctedScoreChanged(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore,
|
||||
Double newCorrectedScore) {
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maxPointsReasonChanged(Competitor competitor, RaceColumn raceColumn, MaxPointsReason oldMaxPointsReason,
|
||||
MaxPointsReason newMaxPointsReason) {
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void carriedPointsChanged(Competitor competitor, Double oldCarriedPoints, Double newCarriedPoints) {
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isSuppressedChanged(Competitor competitor, boolean newIsSuppressed) {
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timePointOfLastCorrectionsValidityChanged(TimePoint oldTimePointOfLastCorrectionsValidity,
|
||||
TimePoint newTimePointOfLastCorrectionsValidity) {
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commentChanged(String oldComment, String newComment) {
|
||||
defaultAction();
|
||||
}
|
||||
|
||||
abstract protected void defaultAction();
|
||||
}
|
||||
+9
@@ -6,5 +6,14 @@ import com.sap.sse.common.Renamable;
|
||||
|
||||
public interface FlexibleRaceColumn extends RaceColumn, Renamable {
|
||||
void setIsMedalRace(boolean isMedalRace);
|
||||
|
||||
void setRegattaLikeHelper(IsRegattaLike regattaLikeHelper);
|
||||
|
||||
/**
|
||||
* Flexible race columns always apply scoring factors linearly and regularly.
|
||||
*/
|
||||
@Override
|
||||
default boolean isOneAlwaysStaysOne() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
* Returns the total rank of the given competitor or {@code 0} if no rank can be determined for
|
||||
* the {@code competitor} in this leaderboard.
|
||||
*/
|
||||
int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) throws NoWindException;
|
||||
int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Fetches all entries for all competitors of all races tracked by this leaderboard in one sweep. This saves some
|
||||
|
||||
+5
-34
@@ -20,21 +20,19 @@ import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnListener;
|
||||
import com.sap.sailing.domain.base.WithNationality;
|
||||
import com.sap.sailing.domain.base.impl.RaceColumnListenerWithDefaultAction;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardCache;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.concurrent.ConcurrentWeakHashMap;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* Manages a {@link LeaderboardCache}. When a {@link Leaderboard} is {@link LeaderboardCache#add added} to the cache, it
|
||||
* start observing the leaderboard for changes through the linked {@link TrackedRace}s as a {@link RaceChangeListener}
|
||||
* starts observing the leaderboard for changes through the linked {@link TrackedRace}s as a {@link RaceChangeListener}
|
||||
* and through the race columns as a {@link RaceColumnListener}. When changes affecting a leaderboard occur, the
|
||||
* {@link LeaderboardCache#removeFromCache(Leaderboard)} method is called on the leaderboard cache.
|
||||
*
|
||||
@@ -200,7 +198,7 @@ public class LeaderboardCacheManager {
|
||||
}
|
||||
}
|
||||
|
||||
private class CacheInvalidationUponScoreCorrectionListener implements ScoreCorrectionListener {
|
||||
private class CacheInvalidationUponScoreCorrectionListener extends AbstractScoreCorrectionListenerWithDefaultAction {
|
||||
private final Leaderboard leaderboard;
|
||||
|
||||
public CacheInvalidationUponScoreCorrectionListener(Leaderboard leaderboard) {
|
||||
@@ -208,34 +206,7 @@ public class LeaderboardCacheManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void correctedScoreChanged(Competitor competitor, RaceColumn raceColumn, Double oldCorrectedScore, Double newCorrectedScore) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maxPointsReasonChanged(Competitor competitor, RaceColumn raceColumn,
|
||||
MaxPointsReason oldMaxPointsReason, MaxPointsReason newMaxPointsReason) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void carriedPointsChanged(Competitor competitor, Double oldCarriedPoints, Double newCarriedPoints) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isSuppressedChanged(Competitor competitor, boolean newIsSuppressed) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timePointOfLastCorrectionsValidityChanged(TimePoint oldTimePointOfLastCorrectionsValidity,
|
||||
TimePoint newTimePointOfLastCorrectionsValidity) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commentChanged(String oldComment, String newComment) {
|
||||
public void defaultAction() {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
}
|
||||
@@ -374,12 +345,12 @@ public class LeaderboardCacheManager {
|
||||
trackedRace.addListener(listener);
|
||||
invalidationListeners = invalidationListenersPerLeaderboard.get(leaderboard);
|
||||
if (invalidationListeners == null) {
|
||||
invalidationListeners = new ConcurrentHashMap<TrackedRace, Set<CacheInvalidationListener>>();
|
||||
invalidationListeners = new ConcurrentHashMap<>();
|
||||
invalidationListenersPerLeaderboard.put(leaderboard, invalidationListeners);
|
||||
}
|
||||
Set<CacheInvalidationListener> listeners = invalidationListeners.get(trackedRace);
|
||||
if (listeners == null) {
|
||||
listeners = Collections.synchronizedSet(new HashSet<CacheInvalidationListener>());
|
||||
listeners = Collections.synchronizedSet(new HashSet<>());
|
||||
invalidationListeners.put(trackedRace, listeners);
|
||||
}
|
||||
listeners.add(listener);
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.domain.leaderboard;
|
||||
|
||||
/**
|
||||
* A regatta leaderboard that may have to resort to the competitor standings in another leaderboard
|
||||
* for breaking specific ties. This {@link #getOtherTieBreakingLeaderboard() other leaderboard} can,
|
||||
* e.g., be obtained by a {@link ScoringScheme} which then looks up scores or ranks in that leaderboard
|
||||
* for tie-breaking purposes.<p>
|
||||
*
|
||||
* Changes in the tie-breaking leaderboard may lead to tie-breaking changes in this leaderboard. However,
|
||||
* this leaderboard assumes that by and large the tie-breaking leaderboard is complete and has no running
|
||||
* races anymore. An exception to this assumption are score corrections applied to the tie-breaking
|
||||
* leaderboard, e.g., by importing official results after the races in this leaderboard have started
|
||||
* already. Therefore, this leaderboard acts as a {@link ScoreCorrectionListener} on the
|
||||
* tie-breaking leaderboard and will forward those changes.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface RegattaLeaderboardWithOtherTieBreakingLeaderboard extends RegattaLeaderboard {
|
||||
RegattaLeaderboard getOtherTieBreakingLeaderboard();
|
||||
}
|
||||
+30
-2
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
@@ -117,10 +118,12 @@ public interface ScoringScheme extends Serializable {
|
||||
* <p>
|
||||
*
|
||||
* <em>"A8.2 If a tie remains between two or more boats, they shall be ranked in order of their scores in the last
|
||||
* race. Any remaining ties shall be broken by using the tied boats� scores in the next-to-last race and so on until
|
||||
* race. Any remaining ties shall be broken by using the tied boats' scores in the next-to-last race and so on until
|
||||
* all ties are broken. These scores shall be used even if some of them are excluded scores."</em>
|
||||
* @param timePoint TODO
|
||||
* @param cache TODO
|
||||
*/
|
||||
int compareByLastRace(List<Util.Pair<RaceColumn, Double>> o1Scores, List<Util.Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, Competitor o1, Competitor o2);
|
||||
int compareByLastRace(List<Util.Pair<RaceColumn, Double>> o1Scores, List<Util.Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, Competitor o1, Competitor o2, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
|
||||
|
||||
/**
|
||||
* Under certain circumstances, a scoring scheme may decide that the scores of a column are not (yet) to be used
|
||||
@@ -140,6 +143,8 @@ public interface ScoringScheme extends Serializable {
|
||||
* @throws NoWindException
|
||||
*/
|
||||
int compareByLatestRegattaInMetaLeaderboard(Leaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint);
|
||||
|
||||
int compareByOtherTieBreakingLeaderboard(RegattaLeaderboardWithOtherTieBreakingLeaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Returning {@code true} makes the number of wins in a medal series the primary ranking criteria.
|
||||
@@ -187,4 +192,27 @@ public interface ScoringScheme extends Serializable {
|
||||
}
|
||||
return factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a score corrected by a {@link #getScoreFactor(RaceColumn) column factor} and potentially other
|
||||
* column-specific rules, such as that despite multiplying, the original score 1 is to map to 1 again.
|
||||
* Respects {@link RaceColumn#isOneAlwaysStaysOne()} on the {@code raceColumn}.
|
||||
*
|
||||
* @see #getOriginalScoreFromScoreScaledByFactor(RaceColumn, double)
|
||||
* @see ScoringSchemeType#getScaledScore(double, double, boolean)
|
||||
*/
|
||||
default double getScoreScaledByFactor(RaceColumn raceColumn, double originalScore) {
|
||||
return ScoringSchemeType.getScaledScore(getScoreFactor(raceColumn), originalScore, raceColumn.isOneAlwaysStaysOne());
|
||||
}
|
||||
|
||||
/**
|
||||
* "Un-scales" a score; the inverse of {@link #getScoreScaledByFactor(RaceColumn, double)}. Respects
|
||||
* {@link RaceColumn#isOneAlwaysStaysOne()} on the {@code raceColumn}.
|
||||
*
|
||||
* @see #getScoreScaledByFactor(RaceColumn, double)
|
||||
* @see ScoringSchemeType#getUnscaledScore(double, double, boolean)
|
||||
*/
|
||||
default double getOriginalScoreFromScoreScaledByFactor(RaceColumn raceColumn, double scaledScore) {
|
||||
return ScoringSchemeType.getUnscaledScore(getScoreFactor(raceColumn), scaledScore, raceColumn.isOneAlwaysStaysOne());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -9,7 +9,6 @@ import java.util.concurrent.Callable;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
@@ -74,7 +73,7 @@ public abstract class AbstractHighPointExtremeSailingSeriesOverall extends HighP
|
||||
private int getWins(List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> scores) {
|
||||
int wins = 0;
|
||||
for (com.sap.sse.common.Util.Pair<RaceColumn, Double> score : scores) {
|
||||
if (Math.abs(score.getB() - maxPoints * getScoreFactor(score.getA())) < 0.0000001) {
|
||||
if (Math.abs(score.getB() - getScoreScaledByFactor(score.getA(), maxPoints)) < 0.0000001) {
|
||||
wins++;
|
||||
}
|
||||
}
|
||||
@@ -109,7 +108,6 @@ public abstract class AbstractHighPointExtremeSailingSeriesOverall extends HighP
|
||||
/**
|
||||
* Notice of Race (NOR) section 13.5 specifies for the series score: "If a tie still remains, it shall be broken in
|
||||
* favor of the boat that had the better place at the last Regatta sailed."
|
||||
* @throws NoWindException
|
||||
*/
|
||||
@Override
|
||||
public int compareByLatestRegattaInMetaLeaderboard(Leaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint) {
|
||||
|
||||
+3
-2
@@ -48,8 +48,9 @@ public abstract class AbstractHighPointFirstGetsFixedOr8AndLastBreaksTie extends
|
||||
private double getHighestScore(List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> scores) {
|
||||
double highestScore = 0;
|
||||
for (com.sap.sse.common.Util.Pair<RaceColumn, Double> score : scores) {
|
||||
if ((score.getB() * getScoreFactor(score.getA())) > highestScore) {
|
||||
highestScore = score.getB() * getScoreFactor(score.getA());
|
||||
final double scoreScaledByFactor = getScoreScaledByFactor(score.getA(), score.getB());
|
||||
if (scoreScaledByFactor > highestScore) {
|
||||
highestScore = scoreScaledByFactor;
|
||||
}
|
||||
}
|
||||
return highestScore;
|
||||
|
||||
+4
-4
@@ -419,7 +419,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
|
||||
RaceColumnDTO raceColumnDTO = result.createEmptyRaceColumn(raceColumn.getName(), raceColumn.isMedalRace(),
|
||||
raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getRegatta().getName() : null,
|
||||
raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getSeries().getName() : null,
|
||||
isMetaLeaderboardColumn);
|
||||
isMetaLeaderboardColumn, raceColumn.isOneAlwaysStaysOne());
|
||||
if (isMetaLeaderboardColumn && raceColumnDTO instanceof MetaLeaderboardRaceColumnDTO) {
|
||||
calculateRacesMetadata((MetaLeaderboardColumn) raceColumn, (MetaLeaderboardRaceColumnDTO) raceColumnDTO, baseDomainFactory);
|
||||
}
|
||||
@@ -438,7 +438,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
|
||||
result.addRace(raceColumn.getName(), raceColumn.getExplicitFactor(), getScoringScheme().getScoreFactor(raceColumn),
|
||||
raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getRegatta().getName() : null,
|
||||
raceColumn instanceof RaceColumnInSeries ? ((RaceColumnInSeries) raceColumn).getSeries().getName() : null,
|
||||
fleetDTO, raceColumn.isMedalRace(), raceIdentifier, race, isMetaLeaderboardColumn);
|
||||
fleetDTO, raceColumn.isMedalRace(), raceIdentifier, race, isMetaLeaderboardColumn, raceColumn.isOneAlwaysStaysOne());
|
||||
}
|
||||
Future<List<CompetitorDTO>> task = executor.submit(
|
||||
() -> baseDomainFactory.getCompetitorDTOList(AbstractLeaderboardWithCache.this.getCompetitorsFromBestToWorst(
|
||||
@@ -845,7 +845,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
|
||||
row.totalDurationFoiledInSeconds = totalDurationFoiled==null?null:totalDurationFoiled.asSeconds();
|
||||
}
|
||||
|
||||
private LeaderboardDTOCache getLeaderboardDTOCache() {
|
||||
protected LeaderboardDTOCache getLeaderboardDTOCache() {
|
||||
LeaderboardDTOCache result = this.leaderboardDTOCache;
|
||||
if (result == null) {
|
||||
synchronized (this) {
|
||||
@@ -1183,7 +1183,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) throws NoWindException {
|
||||
public int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) {
|
||||
return getCompetitorsFromBestToWorst(timePoint).indexOf(competitor) + 1;
|
||||
}
|
||||
|
||||
|
||||
+12
-6
@@ -15,8 +15,10 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
@@ -151,7 +153,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
* Assuming both competitors scored in the same number of races, compares the sorted scores based on World Sailing's
|
||||
* Racing Rules of Sailing (RRS) addendum A8.1:<p>
|
||||
*
|
||||
* <em>"A8.1 If there is a series-score tie between two or more boats, each boat’s race scores shall be listed in
|
||||
* <em>"A8.1 If there is a series-score tie between two or more boats, each boat's race scores shall be listed in
|
||||
* order of best to worst, and at the first point(s) where there is a difference the tie shall be broken in favour
|
||||
* of the boat(s) with the best score(s). No excluded scores shall be used."</em>
|
||||
*/
|
||||
@@ -166,14 +168,14 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
final Set<RaceColumn> o1Discards = discardedRaceColumnsPerCompetitor.get(o1);
|
||||
for (com.sap.sse.common.Util.Pair<RaceColumn, Double> o1ColumnAndScore : o1Scores) {
|
||||
if (includeDiscardedResults || !o1Discards.contains(o1ColumnAndScore.getA())) {
|
||||
o1NetScores.add(new Pair<>(o1ColumnAndScore.getA(), o1ColumnAndScore.getB() / getScoreFactor(o1ColumnAndScore.getA())));
|
||||
o1NetScores.add(new Pair<>(o1ColumnAndScore.getA(), getOriginalScoreFromScoreScaledByFactor(o1ColumnAndScore.getA(), o1ColumnAndScore.getB())));
|
||||
}
|
||||
}
|
||||
List<Pair<RaceColumn, Double>> o2NetScores = new ArrayList<>();
|
||||
final Set<RaceColumn> o2Discards = discardedRaceColumnsPerCompetitor.get(o2);
|
||||
for (com.sap.sse.common.Util.Pair<RaceColumn, Double> o2ColumnAndScore : o2Scores) {
|
||||
if (includeDiscardedResults || !o2Discards.contains(o2ColumnAndScore.getA())) {
|
||||
o2NetScores.add(new Pair<>(o2ColumnAndScore.getA(), o2ColumnAndScore.getB() / getScoreFactor(o2ColumnAndScore.getA())));
|
||||
o2NetScores.add(new Pair<>(o2ColumnAndScore.getA(), getOriginalScoreFromScoreScaledByFactor(o2ColumnAndScore.getA(), o2ColumnAndScore.getB())));
|
||||
}
|
||||
}
|
||||
Collections.sort(o1NetScores, ruleA8_1ScoreComparator);
|
||||
@@ -228,9 +230,8 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
* <p>
|
||||
*
|
||||
* <em>"A8.2 If a tie remains between two or more boats, they shall be ranked in order of their scores in the last race.
|
||||
* Any remaining ties shall be broken by using the tied boats’ scores in the next-to-last race and so on until all
|
||||
* Any remaining ties shall be broken by using the tied boats' scores in the next-to-last race and so on until all
|
||||
* ties are broken. These scores shall be used even if some of them are excluded scores."</em>
|
||||
*
|
||||
* @param o1ScoresIncludingDiscarded
|
||||
* scores that include the points for those races that have been discarded (total points)
|
||||
* @param o2ScoresIncludingDiscarded
|
||||
@@ -238,7 +239,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
*/
|
||||
@Override
|
||||
public int compareByLastRace(List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> o1ScoresIncludingDiscarded,
|
||||
List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> o2ScoresIncludingDiscarded, boolean nullScoresAreBetter, Competitor o1, Competitor o2) {
|
||||
List<com.sap.sse.common.Util.Pair<RaceColumn, Double>> o2ScoresIncludingDiscarded, boolean nullScoresAreBetter, Competitor o1, Competitor o2, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
|
||||
int result = 0;
|
||||
final Comparator<Double> pureScoreComparator = getScoreComparator(nullScoresAreBetter);
|
||||
ListIterator<Pair<RaceColumn, Double>> o1Iter = o1ScoresIncludingDiscarded.listIterator(o1ScoresIncludingDiscarded.size());
|
||||
@@ -270,4 +271,9 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
public int compareByLatestRegattaInMetaLeaderboard(Leaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareByOtherTieBreakingLeaderboard(RegattaLeaderboardWithOtherTieBreakingLeaderboard leaderboard, Competitor o1, Competitor o2, TimePoint timePoint) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -271,6 +271,11 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW
|
||||
hasSplitFleetContiguousScoring);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) {
|
||||
getRaceColumnListeners().notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
|
||||
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
|
||||
@@ -490,7 +495,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW
|
||||
if (totalPoints == null) {
|
||||
result = null;
|
||||
} else {
|
||||
result = getScoringScheme().getScoreFactor(raceColumn) * totalPoints;
|
||||
result = getScoringScheme().getScoreScaledByFactor(raceColumn, totalPoints);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -676,11 +681,10 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW
|
||||
boolean discarded = isDiscarded(competitor, race, timePoint, discardedRaceColumns);
|
||||
final Double correctedScore = correctedResults.getCorrectedScore();
|
||||
final Double correctedScoreScaledByColumnFactor = correctedScore == null ? null
|
||||
: Double.valueOf((correctedScore * getScoringScheme().getScoreFactor(race)));
|
||||
: Double.valueOf(getScoringScheme().getScoreScaledByFactor(race, correctedScore));
|
||||
return new EntryImpl(trackedRankProvider, correctedScoreScaledByColumnFactor, () -> correctedResults.getUncorrectedScore(),
|
||||
correctedResults.isCorrected(),
|
||||
discarded ? DOUBLE_0
|
||||
: correctedScore == null ? null : Double.valueOf(correctedScore * getScoringScheme().getScoreFactor(race)),
|
||||
discarded ? DOUBLE_0 : correctedScoreScaledByColumnFactor,
|
||||
correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor));
|
||||
}
|
||||
|
||||
@@ -752,7 +756,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW
|
||||
boolean discarded = discardedRacesForCompetitor.contains(raceColumn);
|
||||
final Double correctedScore = correctedResults.getCorrectedScore();
|
||||
final Double correctedScoreScaledByColumnFactor = correctedScore == null ? null
|
||||
: Double.valueOf((correctedScore * getScoringScheme().getScoreFactor(raceColumn)));
|
||||
: Double.valueOf(getScoringScheme().getScoreScaledByFactor(raceColumn, correctedScore));
|
||||
Entry entry = new EntryImpl(trackedRankProvider, correctedScoreScaledByColumnFactor,
|
||||
() -> correctedResults.getUncorrectedScore(), correctedResults.isCorrected(),
|
||||
discarded ? DOUBLE_0 : correctedScoreScaledByColumnFactor,
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
|
||||
public class DelegateLeaderboard implements Serializable {
|
||||
private static final long serialVersionUID = -1764036951368356044L;
|
||||
|
||||
private RegattaLeaderboard delegateLeaderboard;
|
||||
|
||||
private transient final Supplier<RegattaLeaderboard> delegateLeaderboardSupplier;
|
||||
|
||||
/**
|
||||
* The particular use case for which this field is introduced is registering score correction
|
||||
* listeners at a point in time when the full leaderboard hasn't been resolved yet. Instead of
|
||||
* letting this listener registration attempt the resolution without success the request can
|
||||
* be queued here, and each time the {@link #getDelegateLeaderboard()} successfully resolves a
|
||||
* leaderboard, all consumers in this set will be triggered.
|
||||
*/
|
||||
private final ConcurrentHashMap<Consumer<RegattaLeaderboard>, Boolean> triggerWhenDelegateLeaderboardIsResolved;
|
||||
|
||||
public DelegateLeaderboard(Supplier<RegattaLeaderboard> delegateLeaderboardSupplier) {
|
||||
this.delegateLeaderboardSupplier = delegateLeaderboardSupplier;
|
||||
this.triggerWhenDelegateLeaderboardIsResolved = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public RegattaLeaderboard getDelegateLeaderboard() {
|
||||
// TODO make thread-safe; if multiple threads call concurrently, triggers from triggerWhenFullLeaderboardIsResolved may fire several times
|
||||
if (delegateLeaderboard == null) {
|
||||
if (delegateLeaderboardSupplier == null) {
|
||||
throw new NullPointerException("Internal error: Regatta leaderboard supplier is null; this can only happen upon premature serialization");
|
||||
}
|
||||
delegateLeaderboard = delegateLeaderboardSupplier.get();
|
||||
if (delegateLeaderboard != null) {
|
||||
for (Iterator<Consumer<RegattaLeaderboard>> i=triggerWhenDelegateLeaderboardIsResolved.keySet().iterator(); i.hasNext(); ) {
|
||||
final Consumer<RegattaLeaderboard> toTrigger = i.next();
|
||||
toTrigger.accept(delegateLeaderboard);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return delegateLeaderboard;
|
||||
}
|
||||
|
||||
public void scheduleLeaderboardConsumer(final Consumer<RegattaLeaderboard> consumer) {
|
||||
triggerWhenDelegateLeaderboardIsResolved.put(consumer, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the {@link #getDelegateLeaderboard() delegate leaderboard} is already available, the {@code action} is
|
||||
* run with it right away; otherwise it is scheduled and run the next time that {@link #getDelegateLeaderboard()}
|
||||
* is invoked such that it returns a valid leaderboard.
|
||||
*/
|
||||
public void runOrSchedule(final Consumer<RegattaLeaderboard> action) {
|
||||
if (getDelegateLeaderboard() != null) {
|
||||
action.accept(getDelegateLeaderboard());
|
||||
} else {
|
||||
scheduleLeaderboardConsumer(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
-120
@@ -6,12 +6,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
@@ -66,18 +64,7 @@ import com.sap.sse.common.Util.Pair;
|
||||
public class DelegatingRegattaLeaderboardWithCompetitorElimination extends AbstractLeaderboardWithCache implements RegattaLeaderboardWithEliminations {
|
||||
private static final long serialVersionUID = 8331154893189722924L;
|
||||
private final String name;
|
||||
private RegattaLeaderboard fullLeaderboard;
|
||||
|
||||
private transient final Supplier<RegattaLeaderboard> fullLeaderboardSupplier;
|
||||
|
||||
/**
|
||||
* The particular use case for which this field is introduced is registering score correction
|
||||
* listeners at a point in time when the full leaderboard hasn't been resolved yet. Instead of
|
||||
* letting this listener registration attempt the resolution without success the request can
|
||||
* be queued here, and each time the {@link #getFullLeaderboard()} successfully resolves a
|
||||
* leaderboard, all consumers in this set will be triggered.
|
||||
*/
|
||||
private final ConcurrentHashMap<Consumer<RegattaLeaderboard>, Boolean> triggerWhenFullLeaderboardIsResolved;
|
||||
private final DelegateLeaderboard fullLeaderboard;
|
||||
|
||||
/**
|
||||
* Competitors eliminated from this leaderboard for regatta ranking; those competitors are not part of
|
||||
@@ -93,22 +80,24 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
/**
|
||||
* The leaderboard wrapper starts out with an empty set of eliminated competitors
|
||||
*/
|
||||
public DelegatingRegattaLeaderboardWithCompetitorElimination(Supplier<RegattaLeaderboard> fullLeaderboardSupplier,
|
||||
String name) {
|
||||
public DelegatingRegattaLeaderboardWithCompetitorElimination(Supplier<RegattaLeaderboard> fullLeaderboardSupplier, String name) {
|
||||
this.name = name;
|
||||
this.fullLeaderboardSupplier = fullLeaderboardSupplier;
|
||||
this.eliminatedCompetitors = new ConcurrentHashMap<>();
|
||||
this.triggerWhenFullLeaderboardIsResolved = new ConcurrentHashMap<>();
|
||||
fullLeaderboard = new DelegateLeaderboard(fullLeaderboardSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private RegattaLeaderboard getDelegateLeaderboard() {
|
||||
return fullLeaderboard.getDelegateLeaderboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getCompetitors() {
|
||||
return new ObscuringIterable<>(getFullLeaderboard().getCompetitors(), eliminatedCompetitors.keySet());
|
||||
return new ObscuringIterable<>(getDelegateLeaderboard().getCompetitors(), eliminatedCompetitors.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,6 +107,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
} else {
|
||||
eliminatedCompetitors.remove(competitor);
|
||||
}
|
||||
getLeaderboardDTOCache().invalidate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,7 +123,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
@Override
|
||||
public Map<RaceColumn, List<Competitor>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint,
|
||||
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException {
|
||||
Map<RaceColumn, List<Competitor>> preResult = getFullLeaderboard().getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(timePoint, cache);
|
||||
Map<RaceColumn, List<Competitor>> preResult = getDelegateLeaderboard().getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(timePoint, cache);
|
||||
for (final List<Competitor> e : preResult.values()) {
|
||||
e.removeAll(eliminatedCompetitors.keySet());
|
||||
}
|
||||
@@ -143,7 +133,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
@Override
|
||||
public Map<Competitor, Double> getCompetitorsForWhichThereAreCarriedPoints() {
|
||||
final Map<Competitor, Double> result = new HashMap<>();
|
||||
for (final java.util.Map.Entry<Competitor, Double> e : getFullLeaderboard().getCompetitorsForWhichThereAreCarriedPoints().entrySet()) {
|
||||
for (final java.util.Map.Entry<Competitor, Double> e : getDelegateLeaderboard().getCompetitorsForWhichThereAreCarriedPoints().entrySet()) {
|
||||
if (!isEliminated(e.getKey())) {
|
||||
result.put(e.getKey(), e.getValue());
|
||||
}
|
||||
@@ -154,13 +144,13 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
@Override
|
||||
public Iterable<Competitor> getCompetitorsFromBestToWorst(RaceColumn raceColumn, TimePoint timePoint,
|
||||
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException {
|
||||
return new ObscuringIterable<>(getFullLeaderboard().getCompetitorsFromBestToWorst(raceColumn, timePoint, cache), eliminatedCompetitors.keySet());
|
||||
return new ObscuringIterable<>(getDelegateLeaderboard().getCompetitorsFromBestToWorst(raceColumn, timePoint, cache), eliminatedCompetitors.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
|
||||
final List<Competitor> result = new ArrayList<>();
|
||||
for (final Competitor c : getFullLeaderboard().getCompetitorsFromBestToWorst(timePoint, cache)) {
|
||||
for (final Competitor c : getDelegateLeaderboard().getCompetitorsFromBestToWorst(timePoint, cache)) {
|
||||
if (!isEliminated(c)) {
|
||||
result.add(c);
|
||||
}
|
||||
@@ -171,7 +161,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
@Override
|
||||
public Map<Pair<Competitor, RaceColumn>, Entry> getContent(TimePoint timePoint) throws NoWindException {
|
||||
final Map<Pair<Competitor, RaceColumn>, Entry> result = new HashMap<>();
|
||||
for (final java.util.Map.Entry<Pair<Competitor, RaceColumn>, Entry> e : getFullLeaderboard().getContent(timePoint).entrySet()) {
|
||||
for (final java.util.Map.Entry<Pair<Competitor, RaceColumn>, Entry> e : getDelegateLeaderboard().getContent(timePoint).entrySet()) {
|
||||
if (!isEliminated(e.getKey().getA())) {
|
||||
result.put(e.getKey(), e.getValue());
|
||||
}
|
||||
@@ -187,383 +177,357 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
// --------------------- Delegate Pattern Implementation ----------------------
|
||||
@Override
|
||||
public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
|
||||
return getFullLeaderboard().getOrCreateCompetitorsProvider();
|
||||
return getDelegateLeaderboard().getOrCreateCompetitorsProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Regatta getRegatta() {
|
||||
return getFullLeaderboard().getRegatta();
|
||||
return getDelegateLeaderboard().getRegatta();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getCompetitorsRegisteredInRegattaLog() {
|
||||
return getFullLeaderboard().getCompetitorsRegisteredInRegattaLog();
|
||||
return getDelegateLeaderboard().getCompetitorsRegisteredInRegattaLog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IsRegattaLike getRegattaLike() {
|
||||
return getFullLeaderboard().getRegattaLike();
|
||||
return getDelegateLeaderboard().getRegattaLike();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceLog getRacelog(String raceColumnName, String fleetName) {
|
||||
return getFullLeaderboard().getRacelog(raceColumnName, fleetName);
|
||||
return getDelegateLeaderboard().getRacelog(raceColumnName, fleetName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCompetitor(Competitor competitor) {
|
||||
getFullLeaderboard().registerCompetitor(competitor);
|
||||
getDelegateLeaderboard().registerCompetitor(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCompetitors(Iterable<Competitor> competitors) {
|
||||
getFullLeaderboard().registerCompetitors(competitors);
|
||||
getDelegateLeaderboard().registerCompetitors(competitors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregisterCompetitor(Competitor competitor) {
|
||||
getFullLeaderboard().deregisterCompetitor(competitor);
|
||||
getDelegateLeaderboard().deregisterCompetitor(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregisterCompetitors(Iterable<Competitor> competitor) {
|
||||
getFullLeaderboard().deregisterCompetitors(competitor);
|
||||
getDelegateLeaderboard().deregisterCompetitors(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getAllCompetitors() {
|
||||
return getFullLeaderboard().getAllCompetitors();
|
||||
return getDelegateLeaderboard().getAllCompetitors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Iterable<RaceDefinition>, Iterable<Competitor>> getAllCompetitorsWithRaceDefinitionsConsidered() {
|
||||
return getFullLeaderboard().getAllCompetitorsWithRaceDefinitionsConsidered();
|
||||
return getDelegateLeaderboard().getAllCompetitorsWithRaceDefinitionsConsidered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getAllCompetitors(RaceColumn raceColumn, Fleet fleet) {
|
||||
return getFullLeaderboard().getAllCompetitors(raceColumn, fleet);
|
||||
return getDelegateLeaderboard().getAllCompetitors(raceColumn, fleet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getCompetitors(RaceColumn raceColumn, Fleet fleet) {
|
||||
return getFullLeaderboard().getCompetitors(raceColumn, fleet);
|
||||
return getDelegateLeaderboard().getCompetitors(raceColumn, fleet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getSuppressedCompetitors() {
|
||||
return getFullLeaderboard().getSuppressedCompetitors();
|
||||
return getDelegateLeaderboard().getSuppressedCompetitors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuppressed(Competitor competitor) {
|
||||
return getFullLeaderboard().isSuppressed(competitor);
|
||||
return getDelegateLeaderboard().isSuppressed(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSuppressed(Competitor competitor, boolean suppressed) {
|
||||
getFullLeaderboard().setSuppressed(competitor, suppressed);
|
||||
getDelegateLeaderboard().setSuppressed(competitor, suppressed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fleet getFleet(String fleetName) {
|
||||
return getFullLeaderboard().getFleet(fleetName);
|
||||
return getDelegateLeaderboard().getFleet(fleetName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry getEntry(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException {
|
||||
return getFullLeaderboard().getEntry(competitor, race, timePoint);
|
||||
return getDelegateLeaderboard().getEntry(competitor, race, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entry getEntry(Competitor competitor, RaceColumn race, TimePoint timePoint,
|
||||
Set<RaceColumn> discardedRaceColumns) throws NoWindException {
|
||||
return getFullLeaderboard().getEntry(competitor, race, timePoint, discardedRaceColumns);
|
||||
return getDelegateLeaderboard().getEntry(competitor, race, timePoint, discardedRaceColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<RaceColumn, Map<Competitor, Double>> getNetPointsSumAfterRaceColumn(TimePoint timePoint)
|
||||
throws NoWindException {
|
||||
return getFullLeaderboard().getNetPointsSumAfterRaceColumn(timePoint);
|
||||
return getDelegateLeaderboard().getNetPointsSumAfterRaceColumn(timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCarriedPoints(Competitor competitor) {
|
||||
return getFullLeaderboard().getCarriedPoints(competitor);
|
||||
return getDelegateLeaderboard().getCarriedPoints(competitor);
|
||||
}
|
||||
|
||||
public int getTrackedRank(Competitor competitor, RaceColumn race, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
|
||||
return getFullLeaderboard().getTrackedRank(competitor, race, timePoint, cache);
|
||||
return getDelegateLeaderboard().getTrackedRank(competitor, race, timePoint, cache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
|
||||
return getFullLeaderboard().getTotalPoints(competitor, raceColumn, timePoint, cache);
|
||||
return getDelegateLeaderboard().getTotalPoints(competitor, raceColumn, timePoint, cache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn race, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getMaxPointsReason(competitor, race, timePoint);
|
||||
return getDelegateLeaderboard().getMaxPointsReason(competitor, race, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getNetPoints(competitor, race, timePoint);
|
||||
return getDelegateLeaderboard().getNetPoints(competitor, race, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
|
||||
return getFullLeaderboard().isDiscarded(competitor, raceColumn, timePoint);
|
||||
return getDelegateLeaderboard().isDiscarded(competitor, raceColumn, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getNetPoints(competitor, timePoint);
|
||||
return getDelegateLeaderboard().getNetPoints(competitor, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint)
|
||||
throws NoWindException {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumnsToConsider, timePoint);
|
||||
return getDelegateLeaderboard().getNetPoints(competitor, raceColumnsToConsider, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<RaceColumn> getRaceColumns() {
|
||||
final RegattaLeaderboard theFullLeaderboard = getFullLeaderboard();
|
||||
final RegattaLeaderboard theFullLeaderboard = getDelegateLeaderboard();
|
||||
return theFullLeaderboard == null ? Collections.emptySet() : theFullLeaderboard.getRaceColumns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceColumn getRaceColumnByName(String name) {
|
||||
return getFullLeaderboard().getRaceColumnByName(name);
|
||||
return getDelegateLeaderboard().getRaceColumnByName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCarriedPoints(Competitor competitor, double carriedPoints) {
|
||||
getFullLeaderboard().setCarriedPoints(competitor, carriedPoints);
|
||||
getDelegateLeaderboard().setCarriedPoints(competitor, carriedPoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsetCarriedPoints(Competitor competitor) {
|
||||
getFullLeaderboard().unsetCarriedPoints(competitor);
|
||||
getDelegateLeaderboard().unsetCarriedPoints(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCarriedPoints() {
|
||||
return getFullLeaderboard().hasCarriedPoints();
|
||||
return getDelegateLeaderboard().hasCarriedPoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCarriedPoints(Competitor competitor) {
|
||||
return getFullLeaderboard().hasCarriedPoints(competitor);
|
||||
return getDelegateLeaderboard().hasCarriedPoints(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettableScoreCorrection getScoreCorrection() {
|
||||
return getFullLeaderboard().getScoreCorrection();
|
||||
return getDelegateLeaderboard().getScoreCorrection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScoreCorrectionListener(ScoreCorrectionListener listener) {
|
||||
if (getFullLeaderboard() != null) {
|
||||
getFullLeaderboard().addScoreCorrectionListener(listener);
|
||||
} else {
|
||||
triggerWhenFullLeaderboardIsResolved.put(leaderboard->leaderboard.addScoreCorrectionListener(listener), true);
|
||||
}
|
||||
fullLeaderboard.runOrSchedule(leaderboard->leaderboard.addScoreCorrectionListener(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScoreCorrectionListener(ScoreCorrectionListener listener) {
|
||||
if (getFullLeaderboard() != null) {
|
||||
getFullLeaderboard().removeScoreCorrectionListener(listener);
|
||||
} else {
|
||||
triggerWhenFullLeaderboardIsResolved.put(leaderboard->leaderboard.removeScoreCorrectionListener(listener), true);
|
||||
}
|
||||
fullLeaderboard.runOrSchedule(leaderboard->leaderboard.removeScoreCorrectionListener(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Competitor getCompetitorByName(String competitorName) {
|
||||
return getFullLeaderboard().getCompetitorByName(competitorName);
|
||||
return getDelegateLeaderboard().getCompetitorByName(competitorName);
|
||||
}
|
||||
|
||||
public void setDisplayName(Competitor competitor, String displayName) {
|
||||
getFullLeaderboard().setDisplayName(competitor, displayName);
|
||||
getDelegateLeaderboard().setDisplayName(competitor, displayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName(Competitor competitor) {
|
||||
return getFullLeaderboard().getDisplayName(competitor);
|
||||
return getDelegateLeaderboard().getDisplayName(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn,
|
||||
TimePoint timePoint) {
|
||||
return getFullLeaderboard().countRaceForComparisonWithDiscardingThresholds(competitor, raceColumn, timePoint);
|
||||
return getDelegateLeaderboard().countRaceForComparisonWithDiscardingThresholds(competitor, raceColumn, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultDiscardingRule getResultDiscardingRule() {
|
||||
return getFullLeaderboard().getResultDiscardingRule();
|
||||
return getDelegateLeaderboard().getResultDiscardingRule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) {
|
||||
getFullLeaderboard().setCrossLeaderboardResultDiscardingRule(discardingRule);
|
||||
getDelegateLeaderboard().setCrossLeaderboardResultDiscardingRule(discardingRule);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Competitor getCompetitorByIdAsString(String idAsString) {
|
||||
return getFullLeaderboard().getCompetitorByIdAsString(idAsString);
|
||||
return getDelegateLeaderboard().getCompetitorByIdAsString(idAsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceColumnListener(RaceColumnListener listener) {
|
||||
getFullLeaderboard().addRaceColumnListener(listener);
|
||||
getDelegateLeaderboard().addRaceColumnListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRaceColumnListener(RaceColumnListener listener) {
|
||||
getFullLeaderboard().removeRaceColumnListener(listener);
|
||||
getDelegateLeaderboard().removeRaceColumnListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDelayToLiveInMillis() {
|
||||
return getFullLeaderboard().getDelayToLiveInMillis();
|
||||
return getDelegateLeaderboard().getDelayToLiveInMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedRace> getTrackedRaces() {
|
||||
return getFullLeaderboard().getTrackedRaces();
|
||||
return getDelegateLeaderboard().getTrackedRaces();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScoringScheme getScoringScheme() {
|
||||
return getFullLeaderboard().getScoringScheme();
|
||||
return getDelegateLeaderboard().getScoringScheme();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getTimePointOfLatestModification() {
|
||||
return getFullLeaderboard().getTimePointOfLatestModification();
|
||||
return getDelegateLeaderboard().getTimePointOfLatestModification();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<GPSFixMoving, Speed> getMaximumSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getMaximumSpeedOverGround(competitor, timePoint);
|
||||
return getDelegateLeaderboard().getMaximumSpeedOverGround(competitor, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getAverageSpeedOverGround(competitor, timePoint);
|
||||
return getDelegateLeaderboard().getAverageSpeedOverGround(competitor, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, Iterable<RaceColumn> raceColumnsToConsider,
|
||||
TimePoint timePoint) throws NoWindException {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumn, raceColumnsToConsider, timePoint);
|
||||
return getDelegateLeaderboard().getNetPoints(competitor, raceColumn, raceColumnsToConsider, timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
|
||||
Set<RaceColumn> discardedRaceColumns) {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
return getDelegateLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getNowMinusDelay() {
|
||||
return getFullLeaderboard().getNowMinusDelay();
|
||||
return getDelegateLeaderboard().getNowMinusDelay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<CourseArea> getCourseAreas() {
|
||||
return getFullLeaderboard().getCourseAreas();
|
||||
return getDelegateLeaderboard().getCourseAreas();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher() {
|
||||
return getFullLeaderboard().getNumberOfCompetitorsInLeaderboardFetcher();
|
||||
return getDelegateLeaderboard().getNumberOfCompetitorsInLeaderboardFetcher();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<RaceColumn, Fleet> getRaceColumnAndFleet(TrackedRace trackedRace) {
|
||||
return getFullLeaderboard().getRaceColumnAndFleet(trackedRace);
|
||||
return getDelegateLeaderboard().getRaceColumnAndFleet(trackedRace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoatClass getBoatClass() {
|
||||
return getFullLeaderboard().getBoatClass();
|
||||
return getDelegateLeaderboard().getBoatClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boat getBoatOfCompetitor(Competitor competitor, RaceColumn raceColumn, Fleet fleet) {
|
||||
return getFullLeaderboard().getBoatOfCompetitor(competitor, raceColumn, fleet);
|
||||
return getDelegateLeaderboard().getBoatOfCompetitor(competitor, raceColumn, fleet);
|
||||
}
|
||||
|
||||
private RegattaLeaderboard getFullLeaderboard() {
|
||||
if (fullLeaderboard == null) {
|
||||
if (fullLeaderboardSupplier == null) {
|
||||
throw new NullPointerException("Internal error: Regatta leaderboard supplier is null; this can only happen upon premature serialization");
|
||||
}
|
||||
fullLeaderboard = fullLeaderboardSupplier.get();
|
||||
if (fullLeaderboard != null) {
|
||||
for (Iterator<Consumer<RegattaLeaderboard>> i=triggerWhenFullLeaderboardIsResolved.keySet().iterator(); i.hasNext(); ) {
|
||||
final Consumer<RegattaLeaderboard> toTrigger = i.next();
|
||||
toTrigger.accept(fullLeaderboard);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fullLeaderboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Before being serialized, ensure that the leaderboard supplier has been used to tru
|
||||
* Before being serialized, ensure that the leaderboard supplier has been used
|
||||
* to resolve the leaderboard.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream oos) throws IOException {
|
||||
getFullLeaderboard();
|
||||
getDelegateLeaderboard();
|
||||
oos.defaultWriteObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Boat> getBoatsRegisteredInRegattaLog() {
|
||||
return getFullLeaderboard().getBoatsRegisteredInRegattaLog();
|
||||
return getDelegateLeaderboard().getBoatsRegisteredInRegattaLog();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Boat> getAllBoats() {
|
||||
return getFullLeaderboard().getAllBoats();
|
||||
return getDelegateLeaderboard().getAllBoats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBoat(Boat boat) {
|
||||
getFullLeaderboard().registerBoat(boat);
|
||||
getDelegateLeaderboard().registerBoat(boat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBoats(Iterable<Boat> boats) {
|
||||
getFullLeaderboard().registerBoats(boats);
|
||||
getDelegateLeaderboard().registerBoats(boats);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregisterBoat(Boat boat) {
|
||||
getFullLeaderboard().deregisterBoat(boat);
|
||||
getDelegateLeaderboard().deregisterBoat(boat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregisterBoats(Iterable<Boat> boats) {
|
||||
getFullLeaderboard().deregisterBoats(boats);
|
||||
getDelegateLeaderboard().deregisterBoats(boats);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
|
||||
Set<RaceColumn> discardedRaceColumns, Supplier<Double> totalPointsProvider) {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns,
|
||||
return getDelegateLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns,
|
||||
totalPointsProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResultsAreOfficial(RaceColumn raceColumn, Fleet fleet) {
|
||||
return getFullLeaderboard().isResultsAreOfficial(raceColumn, fleet);
|
||||
return getDelegateLeaderboard().isResultsAreOfficial(raceColumn, fleet);
|
||||
}
|
||||
}
|
||||
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
/**
|
||||
* Race winners get scored one point, all others zero points, just like in {@link HighPointMatchRacing}. Additionally,
|
||||
* when the number of wins compares equal, the competitors are compared by their "finishing order" in their last race.
|
||||
* As this scoring scheme disregards numbers of races scored as a ranking criterion, two competitors tied on the number
|
||||
* of wins may have different numbers of races scored; therefore, comparing their "last" race may compare races from
|
||||
* different race columns.
|
||||
* <p>
|
||||
*
|
||||
* The "finishing order" is determined by first checking for a penalty code ({@link MaxPointsReason}). If a
|
||||
* non-{@code null}. non-{@link MaxPointsReason#NONE NONE} penalty code is found, the competitor is considered to not
|
||||
* have finished. Otherwise, the finishing rank is determined by looking at the competitor's {@link TrackedRace} for the
|
||||
* last race. If no tracked race is found for a competitor's last race, {@code null} is assumed for the finishing order,
|
||||
* representing "not finished." The finishing ranks are then compared. If they compare equal, this scoring scheme
|
||||
* assumes that the ranking comparator will eventually break the tie by calling
|
||||
* {@link #compareByOtherTieBreakingLeaderboard(RegattaLeaderboardWithOtherTieBreakingLeaderboard, Competitor, Competitor, TimePoint)}.<p>
|
||||
*
|
||||
* Columns marked as "medal race" are <em>not</em> doubled automatically by this scoring scheme.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class HighPointByWinsTiesLastlyBrokenByOtherLeaderboard extends HighPointMatchRacing {
|
||||
private static final long serialVersionUID = -2930982687072741643L;
|
||||
|
||||
@Override
|
||||
public ScoringSchemeType getType() {
|
||||
return ScoringSchemeType.HIGH_POINT_BY_WINS_TIES_LASTLY_BROKEN_BY_OTHER_LEADERBOARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* No A8.1 comparison based on sorted scores happens for this scoring scheme
|
||||
*/
|
||||
@Override
|
||||
public int compareByBetterScore(Competitor o1, List<Pair<RaceColumn, Double>> o1Scores, Competitor o2,
|
||||
List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter, TimePoint timePoint,
|
||||
Leaderboard leaderboard, Map<Competitor, Set<RaceColumn>> discardedRaceColumnsPerCompetitor) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparing by "last race" here assumes explicitly that the competitors may have sailed a different number of
|
||||
* races, e.g., in the 2024 Foiling Kite Olympic Semifinal format. The comparison by last race then may compare
|
||||
* results from different races. Furthermore, the rules specify that not the score / points but the <em>finishing order</em>
|
||||
* is to be compared. Should two boats not have finished their respective last race (which usually leads to a
|
||||
* penalty such as {@link MaxPointsReason#DNF DNF}) or should they not have started ({@link MaxPointsReason#DNS
|
||||
* DNS}) or received some other penalty, that penalty leads to a score of {@code 0.0} by default and will lead to
|
||||
* such competitors to be considered "tied" based on their last race. In this, a penalty code that exists for a
|
||||
* competitor in the last race takes precedence over a finish mark passing and the corresponding order implied
|
||||
* by the tracked race's finishing ranks.
|
||||
* <p>
|
||||
*
|
||||
* Should by some definition one penalty code be considered "better" than another, manual intervention based on
|
||||
* 1/1000th of a point may be required, or the implementation of
|
||||
* {@link ScoringScheme#getPenaltyScore(RaceColumn, Competitor, MaxPointsReason, Integer, com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher, TimePoint, com.sap.sailing.domain.leaderboard.Leaderboard)}
|
||||
* must be overridden accordingly here to provide those 1/1000th of a point automatically.
|
||||
*/
|
||||
@Override
|
||||
public int compareByLastRace(List<Pair<RaceColumn, Double>> o1ScoresIncludingDiscarded,
|
||||
List<Pair<RaceColumn, Double>> o2ScoresIncludingDiscarded, boolean nullScoresAreBetter, Competitor o1,
|
||||
Competitor o2, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
|
||||
int result = 0;
|
||||
if (!o1ScoresIncludingDiscarded.isEmpty() && !o2ScoresIncludingDiscarded.isEmpty()) {
|
||||
final Comparator<Double> doubleComparatorNullsLast = Comparator.nullsLast(Comparator.naturalOrder());
|
||||
final Double o1LastRaceScore = o1ScoresIncludingDiscarded.get(o1ScoresIncludingDiscarded.size() - 1).getB();
|
||||
final Double o2LastRaceScore = o2ScoresIncludingDiscarded.get(o2ScoresIncludingDiscarded.size() - 1).getB();
|
||||
result = doubleComparatorNullsLast.compare(o1LastRaceScore, o2LastRaceScore);
|
||||
// the scores would differ only if one competitor won its last race and the other didn't
|
||||
if (result == 0 && o1LastRaceScore != null && o1LastRaceScore != 1.0) {
|
||||
final TrackedRace tr1 = o1ScoresIncludingDiscarded.get(o1ScoresIncludingDiscarded.size()-1).getA().getTrackedRace(o1);
|
||||
final int rank1;
|
||||
final Integer finishingRank1 = tr1 == null ? null : (rank1=tr1.getRank(o1, timePoint, cache))==0?null:rank1;
|
||||
final TrackedRace tr2 = o2ScoresIncludingDiscarded.get(o2ScoresIncludingDiscarded.size()-1).getA().getTrackedRace(o2);
|
||||
final int rank2;
|
||||
final Integer finishingRank2 = tr2 == null ? null : (rank2=tr2.getRank(o2, timePoint, cache))==0?null:rank2;
|
||||
final Comparator<Integer> intComparatorNullsLast = Comparator.nullsLast(Comparator.naturalOrder());
|
||||
result = intComparatorNullsLast.compare(finishingRank1, finishingRank2);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getScoreFactor(RaceColumn raceColumn) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareByOtherTieBreakingLeaderboard(RegattaLeaderboardWithOtherTieBreakingLeaderboard leaderboard,
|
||||
Competitor o1, Competitor o2, TimePoint timePoint) {
|
||||
final int result;
|
||||
final int o1RankInOtherTieBreakingLeaderboard = leaderboard.getOtherTieBreakingLeaderboard().getTotalRankOfCompetitor(o1, timePoint);
|
||||
final int o2RankInOtherTieBreakingLeaderboard = leaderboard.getOtherTieBreakingLeaderboard().getTotalRankOfCompetitor(o2, timePoint);
|
||||
if (o1RankInOtherTieBreakingLeaderboard == o2RankInOtherTieBreakingLeaderboard) {
|
||||
result = 0;
|
||||
} else {
|
||||
if (o1RankInOtherTieBreakingLeaderboard == 0) {
|
||||
result = 1; // o1 has no rank; this is worse ("greater") than any valid rank
|
||||
} else if (o2RankInOtherTieBreakingLeaderboard == 0) {
|
||||
result = -1;
|
||||
} else {
|
||||
result = Integer.compare(o1RankInOtherTieBreakingLeaderboard, o2RankInOtherTieBreakingLeaderboard);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
@@ -146,7 +147,7 @@ public class HighPointFirstGets1LastBreaksTie extends HighPointFirstGetsFixedSco
|
||||
@Override
|
||||
public int compareByLastRace(List<Pair<RaceColumn, Double>> o1ScoresIncludingDiscarded,
|
||||
List<Pair<RaceColumn, Double>> o2ScoresIncludingDiscarded, boolean nullScoresAreBetter, Competitor o1,
|
||||
Competitor o2) {
|
||||
Competitor o2, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -32,5 +32,4 @@ public class HighPointMatchRacing extends HighPoint {
|
||||
Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
return rank == 0 ? null : rank == 1 ? 1.0 : 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-2
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingAndORCPerformanceCurveCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
@@ -59,6 +60,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
private final Map<Competitor, Set<RaceColumn>> discardedRaceColumnsPerCompetitor;
|
||||
private final boolean nullScoresAreBetter;
|
||||
private final TimePoint timePoint;
|
||||
private final WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache;
|
||||
|
||||
/**
|
||||
* Considers all of the leaderboard's columns in their state at <code>timePoint</code> for calculating the score and rank.
|
||||
@@ -88,6 +90,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
this.timePoint = timePoint;
|
||||
this.scoringScheme = scoringScheme;
|
||||
this.nullScoresAreBetter = nullScoresAreBetter;
|
||||
this.cache = cache;
|
||||
netPointsCache = new HashMap<>();
|
||||
totalPointsCache = new HashMap<>();
|
||||
discardedRaceColumnsPerCompetitor = new HashMap<>();
|
||||
@@ -251,11 +254,17 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
result = compareByBetterScore(o1, Collections.unmodifiableList(o1TotalPoints), o2, Collections.unmodifiableList(o2TotalPoints), timePoint);
|
||||
if (result == 0) {
|
||||
// compare by last race:
|
||||
result = scoringScheme.compareByLastRace(o1TotalPoints, o2TotalPoints, nullScoresAreBetter, o1, o2);
|
||||
result = scoringScheme.compareByLastRace(o1TotalPoints, o2TotalPoints, nullScoresAreBetter, o1, o2, timePoint, cache);
|
||||
if (result == 0) {
|
||||
result = scoringScheme.compareByLatestRegattaInMetaLeaderboard(getLeaderboard(), o1, o2, timePoint);
|
||||
if (result == 0) {
|
||||
result = compareByArbitraryButStableCriteria(o1, o2);
|
||||
if (getLeaderboard() instanceof RegattaLeaderboardWithOtherTieBreakingLeaderboard) {
|
||||
result = scoringScheme.compareByOtherTieBreakingLeaderboard(
|
||||
(RegattaLeaderboardWithOtherTieBreakingLeaderboard) getLeaderboard(), o1, o2, timePoint);
|
||||
}
|
||||
if (result == 0) {
|
||||
result = compareByArbitraryButStableCriteria(o1, o2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.LeaderboardType;
|
||||
import com.sap.sailing.domain.leaderboard.AbstractScoreCorrectionListenerWithDefaultAction;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithOtherTieBreakingLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
|
||||
/**
|
||||
* Upon construction accepts a supplier for another {@link RegattaLeaderboard} and schedules the registration
|
||||
* of a {@link ScoreCorrectionListener} on that leaderboard. When score corrections change on that other
|
||||
* leaderboard, this leaderboard's cache contents are invalidated.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl extends RegattaLeaderboardImpl
|
||||
implements RegattaLeaderboardWithOtherTieBreakingLeaderboard {
|
||||
private static final long serialVersionUID = -4859656155952136210L;
|
||||
private final DelegateLeaderboard otherTieBreakingLeaderboardProvider;
|
||||
|
||||
public RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl(Regatta regatta,
|
||||
ThresholdBasedResultDiscardingRule resultDiscardingRule, Supplier<RegattaLeaderboard> otherTieBreakingLeaderboardProvider) {
|
||||
super(regatta, resultDiscardingRule);
|
||||
this.otherTieBreakingLeaderboardProvider = new DelegateLeaderboard(otherTieBreakingLeaderboardProvider);
|
||||
this.otherTieBreakingLeaderboardProvider.runOrSchedule(otherTieBreakingLeaderboard->otherTieBreakingLeaderboard.addScoreCorrectionListener(
|
||||
new AbstractScoreCorrectionListenerWithDefaultAction() {
|
||||
@Override
|
||||
protected void defaultAction() {
|
||||
getLeaderboardDTOCache().invalidate(RegattaLeaderboardWithOtherTieBreakingLeaderboardImpl.this);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegattaLeaderboard getOtherTieBreakingLeaderboard() {
|
||||
return otherTieBreakingLeaderboardProvider.getDelegateLeaderboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LeaderboardType getLeaderboardType() {
|
||||
return LeaderboardType.RegattaLeaderboardWithOtherTieBreakingLeaderboard;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -258,9 +258,9 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
|
||||
private boolean isCertainlyBeforeRaceFinish(TimePoint timePoint, RaceColumn raceColumn, Competitor competitor) {
|
||||
final boolean result;
|
||||
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
|
||||
final TimePoint endOfRace;
|
||||
final TimePoint endOfRace = trackedRace == null ? null : trackedRace.getFinishedTime() == null ? trackedRace.getEndOfRace() : trackedRace.getFinishedTime();
|
||||
// endOfRace != null means we at least will have a fallback result even if there are no mark passings for the competitor
|
||||
if (trackedRace != null && (endOfRace = trackedRace.getEndOfRace()) != null) {
|
||||
if (endOfRace != null) {
|
||||
NavigableSet<MarkPassing> markPassings = trackedRace.getMarkPassings(competitor);
|
||||
final MarkPassing lastMarkPassing;
|
||||
// count race as finished for the competitor if the finish mark passing exists or the time is after the end of race
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ public class ThresholdBasedResultDiscardingRuleImpl implements ThresholdBasedRes
|
||||
sortedRaces.add(raceColumn);
|
||||
final Double totalPoints = totalPointsSupplier.apply(raceColumn);
|
||||
totalPointsForCompetitorPerColumn.put(raceColumn,
|
||||
totalPoints == null ? null : (totalPoints * scoringScheme.getScoreFactor(raceColumn)));
|
||||
totalPoints == null ? null : scoringScheme.getScoreScaledByFactor(raceColumn, totalPoints));
|
||||
}
|
||||
}
|
||||
result = new HashSet<RaceColumn>();
|
||||
|
||||
+10
@@ -109,6 +109,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOneAlwaysStaysOne() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseTrackedRace(Fleet fleet) {
|
||||
}
|
||||
@@ -148,6 +153,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
|
||||
getRaceColumnListeners().notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void oneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) {
|
||||
getRaceColumnListeners().notifyListenersAboutOneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
|
||||
return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn);
|
||||
|
||||
+12
@@ -58,6 +58,11 @@ public class MarkPassingCalculator {
|
||||
private final MarkPassingUpdateListener listener;
|
||||
private final static ExecutorService executor = ThreadPoolUtil.INSTANCE.getDefaultBackgroundTaskThreadPoolExecutor();
|
||||
private final LinkedBlockingQueue<StorePositionUpdateStrategy> queue;
|
||||
/**
|
||||
* This CALCULATOR_VERSION variable indicates the Version of the {@link MarkPassingCalculator} and must be changed manually when changing the calculator.
|
||||
* It should be changed by adding +1;
|
||||
*/
|
||||
private final int CALCULATOR_VERSION = 1;
|
||||
|
||||
/**
|
||||
* An "end marker" that can be {@link #enqueueUpdate(StorePositionUpdateStrategy) enqueued} in order to tell the {@link Listen}
|
||||
@@ -96,6 +101,9 @@ public class MarkPassingCalculator {
|
||||
*/
|
||||
private boolean listenerThreadStarted;
|
||||
|
||||
/**
|
||||
* Remember to change the {@link CALCULATOR_VERSION} when changing the MarkPassingCalculator
|
||||
*/
|
||||
public MarkPassingCalculator(DynamicTrackedRace race, boolean doListen, boolean waitForInitialMarkPassingCalculation) {
|
||||
if (doListen) {
|
||||
queue = new LinkedBlockingQueue<>();
|
||||
@@ -542,4 +550,8 @@ public class MarkPassingCalculator {
|
||||
public String toString() {
|
||||
return getClass().getName()+" for "+race+" with chooser "+chooser;
|
||||
}
|
||||
|
||||
public int getCalculatorVersion() {
|
||||
return CALCULATOR_VERSION;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.sap.sailing.domain.markpassinghash;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
/**
|
||||
* An instance of this class represents a composite fingerprint of those components of a {@link TrackedRace} that are
|
||||
* relevant for computing the
|
||||
* {@link TrackedRace#getMarkPassing(com.sap.sailing.domain.base.Competitor, com.sap.sailing.domain.base.Waypoint) mark
|
||||
* passings} in the {@link MarkPassingCalculator}. It can be {@link #matches(TrackedRace) matched} against a {@link TrackedRace}
|
||||
* instance to see whether the {@link TrackedRace} will produce a set of {@link MarkPassing}s equal to that of the {@link TrackedRace}
|
||||
* from which this fingerprint was produced.<p>
|
||||
*
|
||||
* To produce a fingerprint from a {@link TrackedRace} or from a JSON representation use {@link MarkPassingRaceFingerprintFactory}.<p>
|
||||
*
|
||||
* The {@link #equals(Object)} and {@link #hashCode()} methods are defined based on the contents of this fingerprint.
|
||||
*
|
||||
* @author Fabian Kallenbach (i550803)
|
||||
* @author Axel Uhl (d043530)
|
||||
*/
|
||||
public interface MarkPassingRaceFingerprint {
|
||||
/**
|
||||
* Returns a {@link JSONObject} of the hash values.
|
||||
*/
|
||||
JSONObject toJson();
|
||||
|
||||
/**
|
||||
* Incrementally computes the composite fingerprint of the {@code trackedRace} and compares to this fingerprint
|
||||
* component by component. The fingerprint components are computed in ascending order of computational complexity,
|
||||
* trying to fail early / fast.
|
||||
*
|
||||
* @return {@code true} if the {@code trackedRace} produces a fingerprint equal to this one if passed to
|
||||
* {@link MarkPassingRaceFingerprintFactory#createFingerprint(TrackedRace)}
|
||||
*/
|
||||
boolean matches(TrackedRace trackedRace);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.sap.sailing.domain.markpassinghash;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.markpassinghash.impl.MarkPassingRaceFingerprintFactoryImpl;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
/**
|
||||
* Factory for the creation of a {@link MarkPassingRaceFingerprint}.
|
||||
*
|
||||
* @author Fabian Kallenbach (i550803)
|
||||
*/
|
||||
public interface MarkPassingRaceFingerprintFactory {
|
||||
MarkPassingRaceFingerprintFactory INSTANCE = new MarkPassingRaceFingerprintFactoryImpl();
|
||||
|
||||
/**
|
||||
* Creates a {@link MarkPassingRaceFingerprint} out of a given {@link TrackedRace}.
|
||||
*/
|
||||
MarkPassingRaceFingerprint createFingerprint(TrackedRace trackedRace);
|
||||
|
||||
/**
|
||||
* Creates a {@link MarkPassingRaceFingerprint} out of a given {@link JSONObject}.
|
||||
*/
|
||||
MarkPassingRaceFingerprint fromJson(JSONObject json);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sailing.domain.markpassinghash.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprintFactory;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
|
||||
public class MarkPassingRaceFingerprintFactoryImpl implements MarkPassingRaceFingerprintFactory {
|
||||
@Override
|
||||
public MarkPassingRaceFingerprint createFingerprint(TrackedRace trackedRace) {
|
||||
return new MarkPassingRaceFingerprintImpl(trackedRace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkPassingRaceFingerprint fromJson(JSONObject json) {
|
||||
return new MarkPassingRaceFingerprintImpl(json);
|
||||
}
|
||||
}
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
package com.sap.sailing.domain.markpassinghash.impl;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.markpassinghash.MarkPassingRaceFingerprint;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
public class MarkPassingRaceFingerprintImpl implements MarkPassingRaceFingerprint {
|
||||
private final int calculatorVersion;
|
||||
private final int competitorHash;
|
||||
private final TimePoint startOfTracking;
|
||||
private final TimePoint startTimeReceived;
|
||||
private final TimePoint endOfTracking;
|
||||
private final TimePoint startTimeFromRaceLog;
|
||||
private final TimePoint finishTimeFromRaceLog;
|
||||
private final int waypointsHash;
|
||||
private final int numberOfGPSFixes;
|
||||
private final int gpsFixesHash;
|
||||
|
||||
private static enum JSON_FIELDS {
|
||||
COMPETITOR_HASH, START_OF_TRACKING_AS_MILLIS, END_OF_TRACKING_AS_MILLIS, START_TIME_RECEIVED_AS_MILLIS, START_TIME_FROM_RACE_LOG_AS_MILLIS, FINISH_TIME_FROM_RACE_LOG_AS_MILLIS, WAYPOINTS_HASH, NUMBEROFGPSFIXES, GPSFIXES_HASH, RACE_ID, CALCULATOR_VERSION
|
||||
};
|
||||
|
||||
public MarkPassingRaceFingerprintImpl(TrackedRace trackedRace) {
|
||||
this.calculatorVersion = getCalculatorVersion(trackedRace);
|
||||
this.competitorHash = calculateHashForCompetitors(trackedRace);
|
||||
this.startOfTracking = trackedRace.getStartOfTracking();
|
||||
this.endOfTracking = trackedRace.getEndOfTracking();
|
||||
this.startTimeReceived = trackedRace.getStartTimeReceived();
|
||||
final Pair<TimePoint, TimePoint> startAndFinishedTimeFromRaceLogs = trackedRace
|
||||
.getStartAndFinishedTimeFromRaceLogs();
|
||||
this.startTimeFromRaceLog = startAndFinishedTimeFromRaceLogs == null ? null
|
||||
: startAndFinishedTimeFromRaceLogs.getA();
|
||||
this.finishTimeFromRaceLog = startAndFinishedTimeFromRaceLogs == null ? null
|
||||
: startAndFinishedTimeFromRaceLogs.getB();
|
||||
this.waypointsHash = calculateHashForWaypoints(trackedRace);
|
||||
this.numberOfGPSFixes = calculateHashForNumberOfGPSFixes(trackedRace);
|
||||
this.gpsFixesHash = calculateHashForGPSFixes(trackedRace);
|
||||
}
|
||||
|
||||
public MarkPassingRaceFingerprintImpl(JSONObject json) {
|
||||
this.calculatorVersion = ((Number) json.get(JSON_FIELDS.CALCULATOR_VERSION.name())).intValue();
|
||||
this.competitorHash = ((Number) json.get(JSON_FIELDS.COMPETITOR_HASH.name())).intValue();
|
||||
this.startOfTracking = TimePoint.of((Long) json.get(JSON_FIELDS.START_OF_TRACKING_AS_MILLIS.name()));
|
||||
this.endOfTracking = TimePoint.of((Long) json.get(JSON_FIELDS.END_OF_TRACKING_AS_MILLIS.name()));
|
||||
this.startTimeReceived = TimePoint.of((Long) json.get(JSON_FIELDS.START_TIME_RECEIVED_AS_MILLIS.name()));
|
||||
this.startTimeFromRaceLog = TimePoint
|
||||
.of((Long) json.get(JSON_FIELDS.START_TIME_FROM_RACE_LOG_AS_MILLIS.name()));
|
||||
this.finishTimeFromRaceLog = TimePoint
|
||||
.of((Long) json.get(JSON_FIELDS.FINISH_TIME_FROM_RACE_LOG_AS_MILLIS.name()));
|
||||
this.waypointsHash = ((Number) json.get(JSON_FIELDS.WAYPOINTS_HASH.name())).intValue();
|
||||
this.numberOfGPSFixes = ((Number) json.get(JSON_FIELDS.NUMBEROFGPSFIXES.name())).intValue();
|
||||
this.gpsFixesHash = ((Number) json.get(JSON_FIELDS.GPSFIXES_HASH.name())).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject toJson() {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put(JSON_FIELDS.CALCULATOR_VERSION.name(), calculatorVersion);
|
||||
result.put(JSON_FIELDS.COMPETITOR_HASH.name(), competitorHash);
|
||||
result.put(JSON_FIELDS.START_OF_TRACKING_AS_MILLIS.name(),
|
||||
startOfTracking == null ? null : startOfTracking.asMillis());
|
||||
result.put(JSON_FIELDS.END_OF_TRACKING_AS_MILLIS.name(),
|
||||
endOfTracking == null ? null : endOfTracking.asMillis());
|
||||
result.put(JSON_FIELDS.START_TIME_RECEIVED_AS_MILLIS.name(),
|
||||
startTimeReceived == null ? null : startTimeReceived.asMillis());
|
||||
result.put(JSON_FIELDS.START_TIME_FROM_RACE_LOG_AS_MILLIS.name(),
|
||||
startTimeFromRaceLog == null ? null : startTimeFromRaceLog.asMillis());
|
||||
result.put(JSON_FIELDS.FINISH_TIME_FROM_RACE_LOG_AS_MILLIS.name(),
|
||||
finishTimeFromRaceLog == null ? null : finishTimeFromRaceLog.asMillis());
|
||||
result.put(JSON_FIELDS.WAYPOINTS_HASH.name(), waypointsHash);
|
||||
result.put(JSON_FIELDS.NUMBEROFGPSFIXES.name(), numberOfGPSFixes);
|
||||
result.put(JSON_FIELDS.GPSFIXES_HASH.name(), gpsFixesHash);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(TrackedRace trackedRace) {
|
||||
final boolean result;
|
||||
if (!Util.equalsWithNull(calculatorVersion, getCalculatorVersion(trackedRace))) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(startOfTracking, trackedRace.getStartOfTracking())) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(endOfTracking, trackedRace.getEndOfTracking())) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(startTimeReceived, trackedRace.getStartTimeReceived())) {
|
||||
result = false;
|
||||
} else {
|
||||
final Pair<TimePoint, TimePoint> startAndFinishedTimeFromRaceLogs = trackedRace
|
||||
.getStartAndFinishedTimeFromRaceLogs();
|
||||
if (!Util.equalsWithNull(startTimeFromRaceLog,
|
||||
startAndFinishedTimeFromRaceLogs == null ? null : startAndFinishedTimeFromRaceLogs.getA())) {
|
||||
result = false;
|
||||
} else if (!Util.equalsWithNull(finishTimeFromRaceLog,
|
||||
startAndFinishedTimeFromRaceLogs == null ? null : startAndFinishedTimeFromRaceLogs.getB())) {
|
||||
result = false;
|
||||
} else if (waypointsHash != calculateHashForWaypoints(trackedRace)) {
|
||||
result = false;
|
||||
} else if (competitorHash != calculateHashForCompetitors(trackedRace)) {
|
||||
result = false;
|
||||
} else if (numberOfGPSFixes != calculateHashForNumberOfGPSFixes(trackedRace)) {
|
||||
result = false;
|
||||
} else if (gpsFixesHash != calculateHashForGPSFixes(trackedRace)) {
|
||||
result = false;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getCalculatorVersion(TrackedRace trackedRace) {
|
||||
MarkPassingCalculator calculator = new MarkPassingCalculator((DynamicTrackedRace) trackedRace, false, false);
|
||||
int result = calculator.getCalculatorVersion();
|
||||
return result;
|
||||
}
|
||||
|
||||
private int calculateHashForCompetitors(TrackedRace trackedRace) {
|
||||
int hashForCompetitors = 1023;
|
||||
for (Competitor c : trackedRace.getRace().getCompetitors()) {
|
||||
hashForCompetitors = hashForCompetitors ^ c.getId().hashCode();
|
||||
}
|
||||
return hashForCompetitors;
|
||||
}
|
||||
|
||||
private int calculateHashForNumberOfGPSFixes(TrackedRace trackedRace) {
|
||||
int count = 0;
|
||||
for (Mark m : trackedRace.getMarks()) {
|
||||
count += trackedRace.getTrack(m).size();
|
||||
}
|
||||
for (final Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||
count += trackedRace.getTrack(competitor).size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int calculateHashForGPSFixes(TrackedRace trackedRace) {
|
||||
int res = 511;
|
||||
for (Mark m : trackedRace.getMarks()) {
|
||||
final GPSFixTrack<Mark, GPSFix> markTrack = trackedRace.getTrack(m);
|
||||
markTrack.lockForRead();
|
||||
try {
|
||||
for (GPSFix gf : markTrack.getFixes()) {
|
||||
res = res ^ gf.getTimePoint().hashCode();
|
||||
res = res ^ gf.getPosition().hashCode();
|
||||
}
|
||||
} finally {
|
||||
markTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
for (final Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||
final GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = trackedRace.getTrack(competitor);
|
||||
competitorTrack.lockForRead();
|
||||
try {
|
||||
for (GPSFixMoving gfm : competitorTrack.getFixes()) {
|
||||
res = res ^ gfm.getTimePoint().hashCode();
|
||||
res = res ^ gfm.getPosition().hashCode();
|
||||
res = res ^ gfm.getSpeed().getBearing().hashCode();
|
||||
res = res ^ Double.hashCode(gfm.getSpeed().getKnots());
|
||||
}
|
||||
} finally {
|
||||
competitorTrack.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private int calculateHashForWaypoints(TrackedRace trackedRace) {
|
||||
Iterable<Waypoint> waypoints = trackedRace.getRace().getCourse().getWaypoints();
|
||||
int res = 0;
|
||||
for (Waypoint p : waypoints) {
|
||||
Iterable<Mark> marks = p.getMarks();
|
||||
for (Mark m : marks) {
|
||||
res = res ^ m.getId().hashCode();
|
||||
}
|
||||
res = res ^ p.getPassingInstructions().hashCode();
|
||||
res = (res << 5) - res; // we want to detect changes in order
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + calculatorVersion;
|
||||
result = prime * result + competitorHash;
|
||||
result = prime * result + ((endOfTracking == null) ? 0 : endOfTracking.hashCode());
|
||||
result = prime * result + ((finishTimeFromRaceLog == null) ? 0 : finishTimeFromRaceLog.hashCode());
|
||||
result = prime * result + gpsFixesHash;
|
||||
result = prime * result + numberOfGPSFixes;
|
||||
result = prime * result + ((startOfTracking == null) ? 0 : startOfTracking.hashCode());
|
||||
result = prime * result + ((startTimeFromRaceLog == null) ? 0 : startTimeFromRaceLog.hashCode());
|
||||
result = prime * result + ((startTimeReceived == null) ? 0 : startTimeReceived.hashCode());
|
||||
result = prime * result + waypointsHash;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MarkPassingRaceFingerprintImpl other = (MarkPassingRaceFingerprintImpl) obj;
|
||||
if (calculatorVersion != other.calculatorVersion)
|
||||
return false;
|
||||
if (competitorHash != other.competitorHash)
|
||||
return false;
|
||||
if (endOfTracking == null) {
|
||||
if (other.endOfTracking != null)
|
||||
return false;
|
||||
} else if (!endOfTracking.equals(other.endOfTracking))
|
||||
return false;
|
||||
if (finishTimeFromRaceLog == null) {
|
||||
if (other.finishTimeFromRaceLog != null)
|
||||
return false;
|
||||
} else if (!finishTimeFromRaceLog.equals(other.finishTimeFromRaceLog))
|
||||
return false;
|
||||
if (gpsFixesHash != other.gpsFixesHash)
|
||||
return false;
|
||||
if (numberOfGPSFixes != other.numberOfGPSFixes)
|
||||
return false;
|
||||
if (startOfTracking == null) {
|
||||
if (other.startOfTracking != null)
|
||||
return false;
|
||||
} else if (!startOfTracking.equals(other.startOfTracking))
|
||||
return false;
|
||||
if (startTimeFromRaceLog == null) {
|
||||
if (other.startTimeFromRaceLog != null)
|
||||
return false;
|
||||
} else if (!startTimeFromRaceLog.equals(other.startTimeFromRaceLog))
|
||||
return false;
|
||||
if (startTimeReceived == null) {
|
||||
if (other.startTimeReceived != null)
|
||||
return false;
|
||||
} else if (!startTimeReceived.equals(other.startTimeReceived))
|
||||
return false;
|
||||
if (waypointsHash != other.waypointsHash)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,13 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
/**
|
||||
* Mark passings have an {@link #equals(Object)} and {@link #hashCode()} implementation based
|
||||
* on their content, not their identity.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public interface MarkPassing extends Timed {
|
||||
Waypoint getWaypoint();
|
||||
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
|
||||
/**
|
||||
* The Waypoint before an Offset Mark (see {@link PassingInstruction}) contains a MarkPassing for this mark, which does
|
||||
* not start an own leg but can be interesting for analysis.
|
||||
*
|
||||
* @author Nicolas Klose
|
||||
*
|
||||
*/
|
||||
public interface MarkPassingForOffsetWaypoint extends MarkPassing {
|
||||
|
||||
MarkPassing getOffsetPassing();
|
||||
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
@@ -484,6 +487,21 @@ public interface TrackedRace
|
||||
* {@link MarkPassingCalculator }to block the thread until all calculations will be finished.
|
||||
*/
|
||||
NavigableSet<MarkPassing> getMarkPassings(Competitor competitor, boolean waitForLatestUpdates);
|
||||
|
||||
/**
|
||||
* Fetches all mark passings for all competitors from this tracked race.
|
||||
*
|
||||
* @see #getMarkPassings(Competitor, boolean)
|
||||
*/
|
||||
default Map<Competitor, Map<Waypoint, MarkPassing>> getMarkPassings(boolean waitForLatestUpdates) {
|
||||
final Map<Competitor, Map<Waypoint, MarkPassing>> result = new HashMap<>();
|
||||
for (final Competitor competitor : getRace().getCompetitors()) {
|
||||
for (final MarkPassing markPassing : getMarkPassings(competitor, waitForLatestUpdates)) {
|
||||
result.computeIfAbsent(competitor, k->new HashMap<>()).put(markPassing.getWaypoint(), markPassing);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This obtains the course's read lock before asking for the read lock for the <code>markPassings</code> structure.
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingForOffsetWaypoint;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public class MarkPassingForOffsetImpl extends MarkPassingImpl implements MarkPassingForOffsetWaypoint {
|
||||
|
||||
private static final long serialVersionUID = -2168312586388285697L;
|
||||
private final MarkPassing markpassingforOffset;
|
||||
|
||||
|
||||
public MarkPassingForOffsetImpl(TimePoint timePoint, Waypoint waypoint, Competitor competitor, MarkPassing markPassingforOffset) {
|
||||
super(timePoint, waypoint, competitor);
|
||||
this.markpassingforOffset = markPassingforOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarkPassing getOffsetPassing() {
|
||||
return markpassingforOffset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+37
@@ -43,4 +43,41 @@ public class MarkPassingImpl implements MarkPassing {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((competitor == null) ? 0 : competitor.hashCode());
|
||||
result = prime * result + ((timePoint == null) ? 0 : timePoint.hashCode());
|
||||
result = prime * result + ((waypoint == null) ? 0 : waypoint.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MarkPassingImpl other = (MarkPassingImpl) obj;
|
||||
if (competitor == null) {
|
||||
if (other.competitor != null)
|
||||
return false;
|
||||
} else if (!competitor.equals(other.competitor))
|
||||
return false;
|
||||
if (timePoint == null) {
|
||||
if (other.timePoint != null)
|
||||
return false;
|
||||
} else if (!timePoint.equals(other.timePoint))
|
||||
return false;
|
||||
if (waypoint == null) {
|
||||
if (other.waypoint != null)
|
||||
return false;
|
||||
} else if (!waypoint.equals(other.waypoint))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -1134,6 +1134,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
protected void setFinishingTime(final TimePoint newFinishingTime) {
|
||||
finishingTime = newFinishingTime;
|
||||
updated(newFinishingTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1143,6 +1144,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
protected void setFinishedTime(final TimePoint newFinishedTime) {
|
||||
finishedTime = newFinishedTime;
|
||||
updated(newFinishedTime);
|
||||
}
|
||||
|
||||
private TimePoint getLastPassingOfFinishLine() {
|
||||
|
||||
@@ -181,4 +181,10 @@ public class RaceColumnListeners implements Serializable {
|
||||
listener.regattaLogEventAdded(event);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyListenersAboutOneAlwaysStaysOneChanged(RaceColumn raceColumn, boolean oneAlwaysStaysOne) {
|
||||
for (RaceColumnListener listener : getRaceColumnListeners()) {
|
||||
listener.oneAlwaysStaysOneChanged(raceColumn, oneAlwaysStaysOne);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+50
-33
@@ -14,6 +14,8 @@ import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
@@ -44,10 +46,12 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
private final int XKM_RADIUS = 5;
|
||||
private final int ROWS_PER_XKM_RADIUS = 15;
|
||||
|
||||
private final int MAX_ROW_NUMBER = 1000;
|
||||
private final int MAX_ROW_NUMBER = 500;
|
||||
private final int MAX_RADIUS = 300;
|
||||
|
||||
private QuadTree<Util.Triple<Position, Double, List<Placemark>>> cache = new QuadTree<Util.Triple<Position,Double,List<Placemark>>>();;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ReverseGeocoderImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public Placemark getPlacemarkNearest(Position position) throws IOException, ParseException {
|
||||
@@ -71,14 +75,13 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
|
||||
@Override
|
||||
public List<Placemark> getPlacemarksNear(Position position, double radius) throws IOException, ParseException {
|
||||
List<Placemark> placemarks = null;
|
||||
Util.Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
final List<Placemark> placemarks;
|
||||
final Util.Triple<Position, Double, List<Placemark>> cachedPlacemarks = checkCache(position);
|
||||
// Calculating the search radius and the maximum number of returning Placemarks
|
||||
double limitedRadius = Math.min(radius, MAX_RADIUS);
|
||||
int radiusInt = (int) limitedRadius;
|
||||
int xKmRadius = radiusInt / XKM_RADIUS;
|
||||
int maxRows = (int) (ROWS_PER_XKM_RADIUS * Math.pow(2, xKmRadius));
|
||||
maxRows = Math.min(maxRows, MAX_ROW_NUMBER);
|
||||
final int radiusInt = (int) limitedRadius;
|
||||
final int xKmRadius = radiusInt / XKM_RADIUS;
|
||||
final int maxRows = (int) Math.min(ROWS_PER_XKM_RADIUS * Math.pow(2, xKmRadius), MAX_ROW_NUMBER);
|
||||
if (cachedPlacemarks != null && cachedPlacemarks.getB() >= limitedRadius) {
|
||||
if (cachedPlacemarks.getC().size() > maxRows) {
|
||||
placemarks = cachedPlacemarks.getC().subList(0, maxRows);
|
||||
@@ -95,23 +98,29 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
} else {
|
||||
searchPosition = position;
|
||||
}
|
||||
JSONArray geonames = callNearbyService(searchPosition, limitedRadius, maxRows);
|
||||
final JSONArray geonames = callNearbyService(searchPosition, limitedRadius, maxRows);
|
||||
if (geonames != null) {
|
||||
Iterator<Object> iterator = geonames.iterator();
|
||||
placemarks = iterator.hasNext() ? new ArrayList<Placemark>() : null;
|
||||
while (iterator.hasNext()) {
|
||||
JSONObject object = (JSONObject) iterator.next();
|
||||
Placemark place = jsonToPlacemark(object);
|
||||
if (place != null) {
|
||||
placemarks.add(jsonToPlacemark(object));
|
||||
if (iterator.hasNext()) {
|
||||
placemarks = new ArrayList<Placemark>();
|
||||
while (iterator.hasNext()) {
|
||||
JSONObject object = (JSONObject) iterator.next();
|
||||
Placemark place = jsonToPlacemark(object);
|
||||
if (place != null) {
|
||||
placemarks.add(jsonToPlacemark(object));
|
||||
}
|
||||
}
|
||||
// If there are no cached placemarks for the requested Position just cache them, otherwise update the cache
|
||||
if (cachedPlacemarks == null) {
|
||||
cachePlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
} else {
|
||||
updateCachedPlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
}
|
||||
} else {
|
||||
placemarks = null;
|
||||
}
|
||||
}
|
||||
// If there are no cached placemarks for the requested Position just cache them, otherwise update the cache
|
||||
if (cachedPlacemarks == null && placemarks != null) {
|
||||
cachePlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
} else if (placemarks != null) {
|
||||
updateCachedPlacemarks(searchPosition, limitedRadius, placemarks);
|
||||
} else {
|
||||
placemarks = null;
|
||||
}
|
||||
}
|
||||
return placemarks;
|
||||
@@ -135,11 +144,7 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
public Placemark getPlacemark(String name, Comparator<Placemark> comp) throws IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(SEARCH_BY_NAME_SERVICE);
|
||||
url.append("name=" + URLEncoder.encode(name, "UTF-8"));
|
||||
URLConnection connection = addUsernameParameterAndConnect(url);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
final JSONParser parser = new JSONParser();
|
||||
final JSONObject obj = (JSONObject) parser.parse(in);
|
||||
final JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
final JSONArray geonames = submitGeonamesRequestForJSONArrayResult(url);
|
||||
return geonames.stream().map(o->jsonToPlacemark((JSONObject) o)).sorted(comp).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@@ -242,14 +247,8 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
}
|
||||
|
||||
private JSONArray callNearestService(Position position) throws MalformedURLException, IOException, ParseException {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
URLConnection connection = addUsernameParameterAndConnect(url);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
StringBuilder url = generateRequestUrlNearbyPlaceService(position);
|
||||
JSONArray geonames = submitGeonamesRequestForJSONArrayResult(url);
|
||||
return geonames;
|
||||
}
|
||||
|
||||
@@ -261,16 +260,34 @@ public class ReverseGeocoderImpl implements ReverseGeocoder {
|
||||
|
||||
private JSONArray callNearbyService(Position position, double radius, int maxRows) throws MalformedURLException,
|
||||
IOException, ParseException {
|
||||
StringBuilder url = generateRequestUrlNearbyPlaceService(position, radius, maxRows);
|
||||
JSONArray geonames = submitGeonamesRequestForJSONArrayResult(url);
|
||||
return geonames;
|
||||
}
|
||||
|
||||
private StringBuilder generateRequestUrlNearbyPlaceService(Position position) {
|
||||
StringBuilder url = new StringBuilder(NEARBY_PLACE_SERVICE);
|
||||
url.append("lat=" + Double.toString(position.getLatDeg()));
|
||||
url.append("&lng=" + Double.toString(position.getLngDeg()));
|
||||
return url;
|
||||
}
|
||||
|
||||
private StringBuilder generateRequestUrlNearbyPlaceService(Position position, double radius, int maxRows) {
|
||||
StringBuilder url = generateRequestUrlNearbyPlaceService(position);
|
||||
url.append("&radius=" + Double.toString(radius));
|
||||
url.append("&maxRows=" + Integer.toString(maxRows));
|
||||
return url;
|
||||
}
|
||||
|
||||
private JSONArray submitGeonamesRequestForJSONArrayResult(StringBuilder url) throws MalformedURLException, IOException, ParseException {
|
||||
URLConnection connection = addUsernameParameterAndConnect(url);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONObject obj = (JSONObject) parser.parse(in);
|
||||
JSONArray geonames = (JSONArray) obj.get("geonames");
|
||||
if (geonames == null) {
|
||||
logger.log(Level.WARNING, "Returning null value for geonames object: " + obj.toJSONString());
|
||||
}
|
||||
return geonames;
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -40,10 +40,12 @@ public class BoatClassImageResolver {
|
||||
boatClassIconsMap.put(BoatClassMasterdata.ALBIN_EXPRESS.getDisplayName(), imageResources.AlbinExpressIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.ALBIN_BALLAD.getDisplayName(), imageResources.AlbinBalladIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.B_ONE.getDisplayName(), imageResources.BOneIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_41S.getDisplayName(), imageResources.BavariaCruiser41S());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_45.getDisplayName(), imageResources.BavariaCruiser45());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_46.getDisplayName(), imageResources.BavariaCruiser46());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_41S.getDisplayName(), imageResources.BavariaCruiser41SIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_45.getDisplayName(), imageResources.BavariaCruiser45Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BAVARIA_CRUISER_46.getDisplayName(), imageResources.BavariaCruiser46Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BB10M.getDisplayName(), imageResources.BB10MIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_35.getDisplayName(), imageResources.BeneteauFirst35Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BENETEAU_FIRST_45.getDisplayName(), imageResources.BeneteauFirst45Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_I.getDisplayName(), imageResources.Brassfahrt1Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_II.getDisplayName(), imageResources.Brassfahrt2Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.BRASSFAHRT_III.getDisplayName(), imageResources.Brassfahrt3Icon());
|
||||
|
||||
+11
-3
@@ -78,20 +78,28 @@ public interface BoatClassImageResources extends ClientBundle {
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/BAVARIA_CRUISER_41S.png")
|
||||
@ImageOptions(preventInlining = true)
|
||||
ImageResource BavariaCruiser41S();
|
||||
ImageResource BavariaCruiser41SIcon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/BAVARIA_CRUISER_45.png")
|
||||
@ImageOptions(preventInlining = true)
|
||||
ImageResource BavariaCruiser45();
|
||||
ImageResource BavariaCruiser45Icon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/BAVARIA_CRUISER_46.png")
|
||||
@ImageOptions(preventInlining = true)
|
||||
ImageResource BavariaCruiser46();
|
||||
ImageResource BavariaCruiser46Icon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/BB10M.png")
|
||||
@ImageOptions(preventInlining = true)
|
||||
ImageResource BB10MIcon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_35.png")
|
||||
@ImageOptions(preventInlining = true)
|
||||
ImageResource BeneteauFirst35Icon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/BENETEAU_FIRST_45.png")
|
||||
@ImageOptions(preventInlining = true)
|
||||
ImageResource BeneteauFirst45Icon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/Brassfahrt1.png")
|
||||
@ImageOptions(preventInlining = true)
|
||||
ImageResource Brassfahrt1Icon();
|
||||
|
||||
+8
-29
@@ -9,7 +9,6 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CompetitorAndBoatStore;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.gwt.home.communication.SailingAction;
|
||||
@@ -43,13 +42,10 @@ public class GetEventsForSailorProfileAction implements SailingAction<SailorProf
|
||||
@Override
|
||||
@GwtIncompatible
|
||||
public SailorProfileEventsDTO execute(SailingDispatchContext ctx) throws DispatchException {
|
||||
|
||||
CompetitorAndBoatStore store = ctx.getRacingEventService().getCompetitorAndBoatStore();
|
||||
|
||||
SailorProfilePreferences prefs = ctx.getPreferenceForCurrentUser(SailorProfilePreferences.PREF_NAME);
|
||||
SailorProfilePreference pref = findSailorProfile(store, prefs);
|
||||
Collection<ParticipatedEventDTO> participatedEvents = new ArrayList<>();
|
||||
|
||||
final CompetitorAndBoatStore store = ctx.getRacingEventService().getCompetitorAndBoatStore();
|
||||
final SailorProfilePreferences prefs = ctx.getPreferenceForCurrentUser(SailorProfilePreferences.PREF_NAME);
|
||||
final SailorProfilePreference pref = findSailorProfile(store, prefs);
|
||||
final Collection<ParticipatedEventDTO> participatedEvents = new ArrayList<>();
|
||||
// iterate over all existing events
|
||||
for (Event event : ctx.getRacingEventService().getAllEvents()) {
|
||||
Collection<ParticipatedRegattaDTO> participatedRegattas = new ArrayList<>();
|
||||
@@ -57,57 +53,41 @@ public class GetEventsForSailorProfileAction implements SailingAction<SailorProf
|
||||
for (LeaderboardGroup leaderboardGroup : event.getLeaderboardGroups()) {
|
||||
if (leaderboardGroup.hasOverallLeaderboard()) {
|
||||
leaderboardGroup.getOverallLeaderboard();
|
||||
|
||||
}
|
||||
|
||||
// iterate over the leaderboards in each leaderboard group
|
||||
for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) {
|
||||
|
||||
// check if this leaderboard contains at least one of the selected competitors
|
||||
Collection<Competitor> containedCompetitors = new ArrayList<>();
|
||||
final Collection<Competitor> containedCompetitors = new ArrayList<>();
|
||||
for (Competitor competitor : pref.getCompetitors()) {
|
||||
if (leaderboard.getCompetitors() != null
|
||||
&& Util.contains(leaderboard.getCompetitors(), competitor)) {
|
||||
containedCompetitors.add(competitor);
|
||||
}
|
||||
}
|
||||
|
||||
// skip if none of the selected competitors is in this leaderboard
|
||||
if (containedCompetitors.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// create and add ParticipatedRegattaDTO for each of the selected competitors who was in this
|
||||
// leaderboard
|
||||
for (Competitor competitor : containedCompetitors) {
|
||||
int rank = 0;
|
||||
try {
|
||||
rank = leaderboard.getTotalRankOfCompetitor(competitor, MillisecondsTimePoint.now());
|
||||
} catch (NoWindException e1) {
|
||||
// ignore
|
||||
}
|
||||
int rank = leaderboard.getTotalRankOfCompetitor(competitor, MillisecondsTimePoint.now());
|
||||
// final String leaderboardName = leaderboard.getDisplayName();
|
||||
final double points = leaderboard.getNetPoints(competitor, MillisecondsTimePoint.now());
|
||||
|
||||
// regatta name is equal to the leaderboard name
|
||||
String regattaName = leaderboard.getName();
|
||||
|
||||
Regatta regatta = ctx.getRacingEventService().getRegattaByName(regattaName);
|
||||
|
||||
final String regattaName = leaderboard.getName();
|
||||
final Regatta regatta = ctx.getRacingEventService().getRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip, if the regatta is not part of this event (e.g. shared leaderboard group)
|
||||
if (!leaderboard.isPartOfEvent(event)) {
|
||||
|
||||
// skip, if overall leaderboard is not part of this event, don't skip if this is a regatta
|
||||
// during an event which is not part of an overall leaderboard
|
||||
if (leaderboardGroup.hasOverallLeaderboard()
|
||||
&& !leaderboardGroup.getOverallLeaderboard().isPartOfEvent(event))
|
||||
continue;
|
||||
}
|
||||
|
||||
participatedRegattas.add(
|
||||
new ParticipatedRegattaDTO(regattaName, rank, new SimpleCompetitorWithIdDTO(competitor),
|
||||
"" + regatta.getId(), "" + event.getId(), points));
|
||||
@@ -119,7 +99,6 @@ public class GetEventsForSailorProfileAction implements SailingAction<SailorProf
|
||||
.add(new ParticipatedEventDTO(event.getName(), event.getId().toString(), participatedRegattas));
|
||||
}
|
||||
}
|
||||
|
||||
return new SailorProfileEventsDTO(participatedEvents);
|
||||
}
|
||||
|
||||
|
||||
+29
-1
@@ -1,11 +1,17 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public abstract class AbstractLeaderboardDialog<LD extends LeaderboardDescriptor> extends DataEntryDialog<LD> {
|
||||
@@ -17,7 +23,7 @@ public abstract class AbstractLeaderboardDialog<LD extends LeaderboardDescriptor
|
||||
protected DiscardThresholdBoxes discardThresholdBoxes;
|
||||
|
||||
public AbstractLeaderboardDialog(String title, LD leaderboardDescriptor, StringMessages stringMessages,
|
||||
Validator<LD> validator, DialogCallback<LD> callback) {
|
||||
Validator<LD> validator, DialogCallback<LD> callback) {
|
||||
super(title, null, stringMessages.ok(), stringMessages.cancel(), validator, callback);
|
||||
nameTextBox = createTextBox(leaderboardDescriptor.getName());
|
||||
nameTextBox.setVisibleLength(50);
|
||||
@@ -63,4 +69,26 @@ public abstract class AbstractLeaderboardDialog<LD extends LeaderboardDescriptor
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected ListBox createSortedRegattaLeaderboardsListBox(Collection<StrippedLeaderboardDTO> existingLeaderboards, String preSelectedRegattaName) {
|
||||
ListBox result = createListBox(false);
|
||||
// sort the regatta names
|
||||
List<StrippedLeaderboardDTO> sortedRegattaLeaderboards = new ArrayList<>();
|
||||
for (StrippedLeaderboardDTO leaderboard : existingLeaderboards) {
|
||||
sortedRegattaLeaderboards.add(leaderboard);
|
||||
}
|
||||
Collections.sort(sortedRegattaLeaderboards, (rl1, rl2) -> rl1.getName().compareTo(rl2.getName()));
|
||||
result.addItem(stringMessages.pleaseSelectARegatta());
|
||||
int i=1;
|
||||
for (StrippedLeaderboardDTO leaderboard : sortedRegattaLeaderboards) {
|
||||
if (leaderboard.type.isRegattaLeaderboard()) {
|
||||
result.addItem(leaderboard.getName(), leaderboard.getName());
|
||||
if (preSelectedRegattaName != null && leaderboard.getName().equals(preSelectedRegattaName)) {
|
||||
result.setSelectedIndex(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -159,8 +159,7 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl
|
||||
stringMessages, errorReporter, /* multiSelection */ true, /* enablePager */ true,
|
||||
/* filterCompetitorWithBoat */ false, /* filterCompetitorsWithoutBoat */ !canBoatsOfCompetitorsChangePerRace);
|
||||
registeredCompetitorsTable = new CompetitorTableWrapper<>(sailingServiceWrite, userService,
|
||||
/* competitorsRefresher not needed; filled based on registrations */ null,
|
||||
/* boatsRefresher not needed */ null, stringMessages,
|
||||
competitorsRefresher, boatsRefresher, stringMessages,
|
||||
errorReporter, /* multiSelection */ true, /* enablePager */ false, /* filterCompetitorWithBoat */ false,
|
||||
/* filterCompetitorsWithoutBoat */ false);
|
||||
registeredCompetitorsTable.getSelectionModel().addSelectionChangeHandler(event -> validateAndUpdate());
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ public class CreateRegattaCallback implements DialogCallback<RegattaDTO>{
|
||||
SeriesCreationParametersDTO seriesPair = new SeriesCreationParametersDTO(seriesDTO.getFleets(),
|
||||
seriesDTO.isMedal(), seriesDTO.isFleetsCanRunInParallel(), seriesDTO.isStartsWithZeroScore(),
|
||||
seriesDTO.isFirstColumnIsNonDiscardableCarryForward(), seriesDTO.getDiscardThresholds(),
|
||||
seriesDTO.hasSplitFleetContiguousScoring(), seriesDTO.getMaximumNumberOfDiscards());
|
||||
seriesDTO.hasSplitFleetContiguousScoring(), seriesDTO.getMaximumNumberOfDiscards(), seriesDTO.isOneAlwaysStaysOne());
|
||||
seriesStructure.put(seriesDTO.getName(), seriesPair);
|
||||
}
|
||||
sailingServiceWrite.createRegatta(newRegatta.getName(),
|
||||
|
||||
+60
-4
@@ -163,6 +163,10 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
|
||||
stringMessages.createRegattaLeaderboardWithEliminations() + " ...",
|
||||
leaderboardCreateAndRegattaReadPermission, this::createRegattaLeaderboardWithEliminations);
|
||||
createRegattaLeaderboardWithEliminationsBtn.ensureDebugId("CreateRegattaLeaderboardWithEliminationsButton");
|
||||
final Button createRegattaLeaderboardWithOtherTieBreakingLeaderboardBtn = buttonPanel.addAction(
|
||||
stringMessages.createRegattaLeaderboardWithOtherTieBreakingLeaderboard() + " ...",
|
||||
leaderboardCreateAndRegattaReadPermission, this::createRegattaLeaderboardWithOtherTieBreakingLeaderboard);
|
||||
createRegattaLeaderboardWithOtherTieBreakingLeaderboardBtn.ensureDebugId("CreateRegattaLeaderboardWithOtherTieBreakingLeaderboardButton");
|
||||
|
||||
leaderboardRemoveButton = buttonPanel.addRemoveAction(stringMessages.remove(), leaderboardSelectionModel, true,
|
||||
() -> removeLeaderboards(leaderboardSelectionModel.getSelectedSet()));
|
||||
@@ -402,7 +406,7 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
|
||||
AbstractLeaderboardDialog<?> dialog;
|
||||
switch (leaderboardDTO.type) {
|
||||
case RegattaLeaderboard:
|
||||
dialog = new RegattaLeaderboardEditDialog(Collections.unmodifiableCollection(otherExistingLeaderboard),
|
||||
dialog = new RegattaLeaderboardEditDialog<>(Collections.unmodifiableCollection(otherExistingLeaderboard),
|
||||
Collections.unmodifiableCollection(allRegattas),
|
||||
createLeaderboardDescriptor(leaderboardDTO,
|
||||
/* scoring scheme is provided by regatta, not leaderboard */ null),
|
||||
@@ -418,6 +422,25 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
|
||||
});
|
||||
dialog.show();
|
||||
break;
|
||||
case RegattaLeaderboardWithOtherTieBreakingLeaderboard:
|
||||
dialog = new RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog(Collections.unmodifiableCollection(otherExistingLeaderboard),
|
||||
Collections.unmodifiableCollection(allRegattas),
|
||||
new LeaderboardDescriptorWithOtherTieBreakingLeaderboard(
|
||||
leaderboardDTO.getName(), leaderboardDTO.displayName, /* scoringScheme is provided by regatta, not leaderboard */ null,
|
||||
leaderboardDTO.discardThresholds, leaderboardDTO.regattaName, Util.mapToArrayList(leaderboardDTO.courseAreas, CourseAreaDTO::getId),
|
||||
leaderboardDTO.getOtherTieBreakingLeaderboardName()),
|
||||
stringMessages, errorReporter, new DialogCallback<LeaderboardDescriptorWithOtherTieBreakingLeaderboard>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(LeaderboardDescriptorWithOtherTieBreakingLeaderboard result) {
|
||||
updateLeaderboard(oldLeaderboardName, result);
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
break;
|
||||
case RegattaLeaderboardWithEliminations:
|
||||
dialog = new RegattaLeaderboardWithEliminationsEditDialog(sailingServiceWrite, userService,
|
||||
Collections.unmodifiableCollection(otherExistingLeaderboard),
|
||||
@@ -930,10 +953,10 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
|
||||
}
|
||||
|
||||
private void createRegattaLeaderboard() {
|
||||
RegattaLeaderboardCreateDialog dialog = new RegattaLeaderboardCreateDialog(
|
||||
RegattaLeaderboardCreateDialog<LeaderboardDescriptor> dialog = new RegattaLeaderboardCreateDialog<LeaderboardDescriptor>(
|
||||
Collections.unmodifiableCollection(availableLeaderboardList),
|
||||
Collections.unmodifiableCollection(allRegattas), stringMessages, errorReporter,
|
||||
new DialogCallback<LeaderboardDescriptor>() {
|
||||
Collections.unmodifiableCollection(allRegattas), new LeaderboardDescriptor(), stringMessages,
|
||||
errorReporter, new DialogCallback<LeaderboardDescriptor>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
@@ -1008,6 +1031,39 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void createRegattaLeaderboardWithOtherTieBreakingLeaderboard() {
|
||||
RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog dialog = new RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog(
|
||||
Collections.unmodifiableCollection(availableLeaderboardList),
|
||||
Collections.unmodifiableCollection(allRegattas), stringMessages, errorReporter,
|
||||
new DialogCallback<LeaderboardDescriptorWithOtherTieBreakingLeaderboard>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(final LeaderboardDescriptorWithOtherTieBreakingLeaderboard newLeaderboard) {
|
||||
final RegattaName regattaIdentifier = new RegattaName(newLeaderboard.getRegattaName());
|
||||
sailingServiceWrite.createRegattaLeaderboardWithOtherTieBreakingLeaderboard(
|
||||
regattaIdentifier, newLeaderboard.getDisplayName(),
|
||||
newLeaderboard.getDiscardThresholds(), newLeaderboard.getOtherTieBreakingLeaderboardName(),
|
||||
new AsyncCallback<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
errorReporter.reportError("Error trying to create the new regatta leaderboard "
|
||||
+ newLeaderboard.getName() + ": " + t.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(StrippedLeaderboardDTO result) {
|
||||
addLeaderboard(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
dialog.ensureDebugId("RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog");
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void addLeaderboard(StrippedLeaderboardDTO result) {
|
||||
filteredLeaderboardList.getList().add(result);
|
||||
availableLeaderboardList.add(result);
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
|
||||
public class LeaderboardDescriptorWithOtherTieBreakingLeaderboard extends LeaderboardDescriptor {
|
||||
private String otherTieBreakingLeaderboardName;
|
||||
|
||||
public LeaderboardDescriptorWithOtherTieBreakingLeaderboard() {
|
||||
super();
|
||||
}
|
||||
|
||||
public LeaderboardDescriptorWithOtherTieBreakingLeaderboard(String name, String displayName, ScoringSchemeType scoringScheme,
|
||||
int[] discardThresholds, String regattaName, Iterable<UUID> courseAreaIds, String otherTieBreakingLeaderboardName) {
|
||||
super(name, displayName, scoringScheme, discardThresholds, regattaName, courseAreaIds);
|
||||
this.otherTieBreakingLeaderboardName = otherTieBreakingLeaderboardName;
|
||||
}
|
||||
|
||||
public String getOtherTieBreakingLeaderboardName() {
|
||||
return otherTieBreakingLeaderboardName;
|
||||
}
|
||||
}
|
||||
+27
-3
@@ -6,8 +6,10 @@ import static com.sap.sse.security.shared.HasPermissions.DefaultActions.CHANGE_O
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.google.gwt.safehtml.shared.UriUtils;
|
||||
import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
@@ -17,9 +19,12 @@ import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.ui.adminconsole.places.AdminConsoleView.Presenter;
|
||||
import com.sap.sailing.gwt.ui.adminconsole.places.advanced.UserGroupManagementPlace;
|
||||
import com.sap.sailing.gwt.ui.adminconsole.places.advanced.UserManagementPlace;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceWriteAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.ServerConfigurationDTO;
|
||||
import com.sap.sse.gwt.adminconsole.AbstractFilterablePlace;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.Notification;
|
||||
import com.sap.sse.gwt.client.Notification.NotificationType;
|
||||
@@ -41,7 +46,8 @@ public class LocalServerManagementPanel extends SimplePanel {
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
private final AccessControlledButtonPanel buttonPanel;
|
||||
private Label serverNameInfo, buildVersionInfo, groupOwnerInfo, userOwnerInfo;
|
||||
private Label serverNameInfo, buildVersionInfo;
|
||||
private Anchor groupOwnerInfo, userOwnerInfo;
|
||||
private CheckBox isStandaloneServerCheckbox, isPublicServerCheckbox, isSelfServiceServerCheckbox;
|
||||
|
||||
private ServerInfoDTO currentServerInfo;
|
||||
@@ -101,8 +107,8 @@ public class LocalServerManagementPanel extends SimplePanel {
|
||||
final ServerDataCaptionPanel captionPanel = new ServerDataCaptionPanel(stringMessages.serverInformation(), 4);
|
||||
serverNameInfo = captionPanel.addInformation(stringMessages.name() + ":");
|
||||
buildVersionInfo = captionPanel.addInformation(stringMessages.buildVersion() + ":");
|
||||
groupOwnerInfo = captionPanel.addInformation(stringMessages.ownership() + " - " + stringMessages.group() + ":");
|
||||
userOwnerInfo = captionPanel.addInformation(stringMessages.ownership() + " - " + stringMessages.user() + ":");
|
||||
groupOwnerInfo = captionPanel.addAnchor(stringMessages.ownership() + " - " + stringMessages.group() + ":");
|
||||
userOwnerInfo = captionPanel.addAnchor(stringMessages.ownership() + " - " + stringMessages.user() + ":");
|
||||
return captionPanel;
|
||||
}
|
||||
|
||||
@@ -157,7 +163,21 @@ public class LocalServerManagementPanel extends SimplePanel {
|
||||
final boolean hasGroupOwner = ownership != null && ownership.getTenantOwner() != null;
|
||||
final boolean hasUserOwner = ownership != null && ownership.getUserOwner() != null;
|
||||
groupOwnerInfo.setText(hasGroupOwner ? ownership.getTenantOwner().getName() : "---");
|
||||
if (hasGroupOwner) {
|
||||
groupOwnerInfo.setHref(
|
||||
UriUtils.fromString("#"+UserGroupManagementPlace.class.getSimpleName()+":"+AbstractFilterablePlace.FILTER_KEY+"="+ownership.getTenantOwner().getName()+
|
||||
"&"+AbstractFilterablePlace.SELECT_EXACT_KEY+"="+ownership.getTenantOwner().getId().toString()));
|
||||
} else {
|
||||
groupOwnerInfo.setHref("javascript:;");
|
||||
}
|
||||
userOwnerInfo.setText(hasUserOwner ? ownership.getUserOwner().getName() : "---");
|
||||
if (hasUserOwner) {
|
||||
userOwnerInfo.setHref(
|
||||
UriUtils.fromString("#"+UserManagementPlace.class.getSimpleName()+":"+AbstractFilterablePlace.FILTER_KEY+"="+ownership.getUserOwner().getName()+
|
||||
"&"+AbstractFilterablePlace.SELECT_EXACT_KEY+"="+ownership.getUserOwner().getName()));
|
||||
} else {
|
||||
userOwnerInfo.setHref("javascript:;");
|
||||
}
|
||||
// Update changeability
|
||||
isSelfServiceServerCheckbox.setEnabled(userService.hasServerPermission(DefaultActions.CHANGE_ACL));
|
||||
// TODO update isPublicServerCheckbox -> default server tenant is currently not available in the UI
|
||||
@@ -205,6 +225,10 @@ public class LocalServerManagementPanel extends SimplePanel {
|
||||
private Label addInformation(final String labelText) {
|
||||
return addWidget(labelText, new Label());
|
||||
}
|
||||
|
||||
private Anchor addAnchor(final String labelText) {
|
||||
return addWidget(labelText, new Anchor());
|
||||
}
|
||||
|
||||
private CheckBox addChekBox(final String labelText, final Command callback) {
|
||||
final CheckBox checkBox = new CheckBox();
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ public class RaceColumnsInLeaderboardDialog extends DataEntryDialog<List<RaceCol
|
||||
int racesCount = raceNameEntryFields.size();
|
||||
for(int i = 0; i < racesCount; i++) {
|
||||
String raceColumnName = raceNameEntryFields.get(i).getValue();
|
||||
RaceColumnDTO raceColumnDTO = new RaceColumnDTO(raceColumnName);
|
||||
RaceColumnDTO raceColumnDTO = new RaceColumnDTO(raceColumnName, /* oneAlwaysStaysOne */ false);
|
||||
raceColumnDTO.setMedalRace(isMedalRaceCheckboxes.get(i).getValue());
|
||||
racesWithFleet.add(raceColumnDTO);
|
||||
}
|
||||
|
||||
+10
-2
@@ -214,6 +214,12 @@ public class RegattaDetailsComposite extends Composite {
|
||||
return series.isFleetsCanRunInParallel() ? stringMessages.yes() : stringMessages.no();
|
||||
}
|
||||
};
|
||||
TextColumn<SeriesDTO> isOneAlwaysStaysOneColumn = new TextColumn<SeriesDTO>() {
|
||||
@Override
|
||||
public String getValue(SeriesDTO series) {
|
||||
return series.isOneAlwaysStaysOne() ? stringMessages.yes() : stringMessages.no();
|
||||
}
|
||||
};
|
||||
TextColumn<SeriesDTO> maximumNumberOfDiscardsColumn = new TextColumn<SeriesDTO>() {
|
||||
@Override
|
||||
public String getValue(SeriesDTO series) {
|
||||
@@ -311,6 +317,7 @@ public class RegattaDetailsComposite extends Composite {
|
||||
table.addColumn(startsWithZeroScoreColumn, stringMessages.startsWithZeroScore());
|
||||
table.addColumn(hasSplitFleetContiguousScoringColumn, stringMessages.hasSplitFleetContiguousScoring());
|
||||
table.addColumn(isFleetsCanRunInParallelColumn, stringMessages.canFleetsRunInParallel());
|
||||
table.addColumn(isOneAlwaysStaysOneColumn, stringMessages.oneAlwaysStaysOne());
|
||||
table.addColumn(maximumNumberOfDiscardsColumn, stringMessages.maximumNumberOfDiscards());
|
||||
table.addColumn(actionsColumn, stringMessages.actions());
|
||||
return table;
|
||||
@@ -352,6 +359,7 @@ public class RegattaDetailsComposite extends Composite {
|
||||
final boolean seriesResultDiscardingThresholdsChanged = !Arrays.equals(series.getDiscardThresholds(),
|
||||
seriesDescriptor.getResultDiscardingThresholds());
|
||||
final boolean maximumNumberOfDiscardsChanged = series.getMaximumNumberOfDiscards() != seriesDescriptor.getMaximumNumberOfDiscards();
|
||||
final boolean oneAlwaysStaysOneChanged = series.isOneAlwaysStaysOne() != seriesDescriptor.isOneAlwaysStaysOne();
|
||||
final boolean seriesNameChanged = !series.getName().equals(seriesDescriptor.getSeriesName());
|
||||
final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.getName());
|
||||
List<RaceColumnDTO> existingRaceColumns = series.getRaceColumns();
|
||||
@@ -405,13 +413,13 @@ public class RegattaDetailsComposite extends Composite {
|
||||
public void onSuccess(List<RaceColumnInSeriesDTO> raceColumns) {
|
||||
if (isMedalChanged || isFleetsCanRunInParallelChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged
|
||||
|| isFirstColumnIsNonDiscardableCarryForwardChanged || hasSplitFleetContiguousScoringChanged
|
||||
|| seriesNameChanged || maximumNumberOfDiscardsChanged) {
|
||||
|| seriesNameChanged || maximumNumberOfDiscardsChanged || oneAlwaysStaysOneChanged) {
|
||||
sailingServiceWrite.updateSeries(regattaIdentifier, series.getName(), seriesDescriptor.getSeriesName(),
|
||||
seriesDescriptor.isMedal(), seriesDescriptor.isFleetsCanRunInParallel(), seriesDescriptor.getResultDiscardingThresholds(),
|
||||
seriesDescriptor.isStartsWithZeroScore(),
|
||||
seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(),
|
||||
seriesDescriptor.hasSplitFleetContiguousScoring(), seriesDescriptor.getMaximumNumberOfDiscards(),
|
||||
series.getFleets(), new AsyncCallback<Void>() {
|
||||
seriesDescriptor.isOneAlwaysStaysOne(), series.getFleets(), new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error trying to update series " + series.getName() + ": "
|
||||
|
||||
+5
-5
@@ -9,13 +9,13 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
|
||||
public class RegattaLeaderboardCreateDialog<LD extends LeaderboardDescriptor> extends RegattaLeaderboardDialog<LD> {
|
||||
|
||||
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards,
|
||||
Collection<RegattaDTO> existingRegattas, StringMessages stringMessages, ErrorReporter errorReporter,
|
||||
DialogCallback<LeaderboardDescriptor> callback) {
|
||||
super(stringMessages.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringMessages,
|
||||
errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards),
|
||||
Collection<RegattaDTO> existingRegattas, LD initialLeaderboardDescriptor, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter, DialogCallback<LD> callback) {
|
||||
super(stringMessages.createRegattaLeaderboard(), initialLeaderboardDescriptor, existingRegattas, stringMessages,
|
||||
errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator<LD>(stringMessages, existingLeaderboards),
|
||||
callback);
|
||||
displayNameTextBox = createTextBox(null);
|
||||
displayNameTextBox.ensureDebugId("DisplayNameTextBox");
|
||||
|
||||
+12
-12
@@ -16,12 +16,12 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
|
||||
public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog<LeaderboardDescriptor> {
|
||||
public abstract class RegattaLeaderboardDialog<LD extends LeaderboardDescriptor> extends AbstractLeaderboardDialog<LD> {
|
||||
protected ListBox regattaListBox;
|
||||
protected Collection<RegattaDTO> existingRegattas;
|
||||
private Label regattaDefinesDiscardsLabel;
|
||||
protected Label regattaDefinesDiscardsLabel;
|
||||
|
||||
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
|
||||
protected static class LeaderboardParameterValidator<LD extends LeaderboardDescriptor> implements Validator<LD> {
|
||||
protected final StringMessages stringMessages;
|
||||
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
|
||||
|
||||
@@ -32,7 +32,7 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
|
||||
public String getErrorMessage(LD leaderboardToValidate) {
|
||||
String errorMessage;
|
||||
boolean unique = true;
|
||||
for (StrippedLeaderboardDTO dao : existingLeaderboards) {
|
||||
@@ -58,9 +58,9 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
|
||||
}
|
||||
}
|
||||
|
||||
public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection<RegattaDTO> existingRegattas, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback<LeaderboardDescriptor> callback) {
|
||||
super(title, leaderboardDTO, stringMessages, validator, callback);
|
||||
public RegattaLeaderboardDialog(String title, LD leaderboardDescriptor, Collection<RegattaDTO> existingRegattas, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter, LeaderboardParameterValidator<LD> validator, DialogCallback<LD> callback) {
|
||||
super(title, leaderboardDescriptor, stringMessages, validator, callback);
|
||||
this.existingRegattas = existingRegattas;
|
||||
regattaDefinesDiscardsLabel = new Label(stringMessages.regattaDefinesResultDiscardingRules());
|
||||
}
|
||||
@@ -100,8 +100,8 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardDescriptor getResult() {
|
||||
LeaderboardDescriptor leaderboard = super.getResult();
|
||||
protected LD getResult() {
|
||||
LD leaderboard = super.getResult();
|
||||
leaderboard.setRegattaName(getSelectedRegatta() != null ? getSelectedRegatta().getName() : null);
|
||||
return leaderboard;
|
||||
}
|
||||
@@ -109,13 +109,13 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
VerticalPanel mainPanel = new VerticalPanel();
|
||||
Grid formGrid = new Grid(3,3);
|
||||
Grid formGrid = new Grid(3, 3);
|
||||
formGrid.setCellSpacing(3);
|
||||
formGrid.setWidget(0, 0, createLabel(stringMessages.regatta()));
|
||||
formGrid.setWidget(0, 1, regattaListBox);
|
||||
formGrid.setWidget(1, 0, createLabel(stringMessages.name()));
|
||||
formGrid.setWidget(1, 0, createLabel(stringMessages.name()));
|
||||
formGrid.setWidget(1, 1, nameTextBox);
|
||||
formGrid.setWidget(2, 0, createLabel(stringMessages.displayName()));
|
||||
formGrid.setWidget(2, 0, createLabel(stringMessages.displayName()));
|
||||
formGrid.setWidget(2, 1, displayNameTextBox);
|
||||
mainPanel.add(formGrid);
|
||||
mainPanel.add(regattaDefinesDiscardsLabel);
|
||||
|
||||
+4
-4
@@ -7,12 +7,12 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public class RegattaLeaderboardEditDialog extends RegattaLeaderboardDialog {
|
||||
public class RegattaLeaderboardEditDialog<LD extends LeaderboardDescriptor> extends RegattaLeaderboardDialog<LD> {
|
||||
public RegattaLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards, Collection<RegattaDTO> existingRegattas,
|
||||
LeaderboardDescriptor leaderboardDescriptor, StringMessages stringConstants, ErrorReporter errorReporter,
|
||||
DialogCallback<LeaderboardDescriptor> callback) {
|
||||
LD leaderboardDescriptor, StringMessages stringConstants, ErrorReporter errorReporter,
|
||||
DialogCallback<LD> callback) {
|
||||
super(stringConstants.editRegattaLeaderboard(), leaderboardDescriptor, existingRegattas, stringConstants, errorReporter,
|
||||
new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringConstants, otherExistingLeaderboards), callback);
|
||||
new RegattaLeaderboardDialog.LeaderboardParameterValidator<>(stringConstants, otherExistingLeaderboards), callback);
|
||||
displayNameTextBox = createTextBox(leaderboardDescriptor.getDisplayName());
|
||||
displayNameTextBox.setVisibleLength(50);
|
||||
regattaListBox = createSortedRegattaListBox(existingRegattas, leaderboardDescriptor.getRegattaName());
|
||||
|
||||
-25
@@ -1,10 +1,7 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
@@ -159,28 +156,6 @@ public abstract class RegattaLeaderboardWithEliminationsDialog extends AbstractL
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ListBox createSortedRegattaLeaderboardsListBox(Collection<StrippedLeaderboardDTO> existingLeaderboards, String preSelectedRegattaName) {
|
||||
ListBox result = createListBox(false);
|
||||
// sort the regatta names
|
||||
List<StrippedLeaderboardDTO> sortedRegattaLeaderboards = new ArrayList<>();
|
||||
for (StrippedLeaderboardDTO leaderboard : existingLeaderboards) {
|
||||
sortedRegattaLeaderboards.add(leaderboard);
|
||||
}
|
||||
Collections.sort(sortedRegattaLeaderboards, (rl1, rl2) -> rl1.getName().compareTo(rl2.getName()));
|
||||
result.addItem(stringMessages.pleaseSelectARegatta());
|
||||
int i=1;
|
||||
for (StrippedLeaderboardDTO leaderboard : sortedRegattaLeaderboards) {
|
||||
if (leaderboard.type.isRegattaLeaderboard()) {
|
||||
result.addItem(leaderboard.getName(), leaderboard.getName());
|
||||
if (preSelectedRegattaName != null && leaderboard.getName().equals(preSelectedRegattaName)) {
|
||||
result.setSelectedIndex(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardDescriptorWithEliminations getResult() {
|
||||
LeaderboardDescriptor leaderboard = super.getResult();
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ public class RegattaLeaderboardWithEliminationsEditDialog extends RegattaLeaderb
|
||||
Collection<StrippedLeaderboardDTO> otherExistingLeaderboards, Collection<RegattaDTO> existingRegattas,
|
||||
LeaderboardDescriptorWithEliminations leaderboardDescriptor, StringMessages stringMessages, ErrorReporter errorReporter,
|
||||
DialogCallback<LeaderboardDescriptorWithEliminations> callback) {
|
||||
super(sailingServiceWrite, userService, stringMessages.editRegattaLeaderboard(), leaderboardDescriptor,
|
||||
super(sailingServiceWrite, userService, stringMessages.editRegattaLeaderboardWithEliminations(), leaderboardDescriptor,
|
||||
existingRegattas, otherExistingLeaderboards, stringMessages,
|
||||
errorReporter,
|
||||
new RegattaLeaderboardWithEliminationsDialog.LeaderboardParameterValidator(stringMessages,
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
import com.google.gwt.event.dom.client.ChangeHandler;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public class RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog extends RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog {
|
||||
public RegattaLeaderboardWithOtherTieBreakingLeaderboardCreateDialog(
|
||||
Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas,
|
||||
StringMessages stringMessages, ErrorReporter errorReporter,
|
||||
DialogCallback<LeaderboardDescriptorWithOtherTieBreakingLeaderboard> callback) {
|
||||
super(stringMessages.createRegattaLeaderboardWithOtherTieBreakingLeaderboard(), new LeaderboardDescriptorWithOtherTieBreakingLeaderboard(),
|
||||
existingLeaderboards, existingRegattas, stringMessages, errorReporter, callback);
|
||||
displayNameTextBox = createTextBox(null);
|
||||
displayNameTextBox.ensureDebugId("DisplayNameTextBox");
|
||||
displayNameTextBox.setVisibleLength(50);
|
||||
regattaListBox = createSortedRegattaListBox(existingRegattas, null);
|
||||
regattaListBox.ensureDebugId("RegattaListBox");
|
||||
regattaListBox.addChangeHandler(new ChangeHandler() {
|
||||
public void onChange(ChangeEvent event) {
|
||||
int selectedIndex = regattaListBox.getSelectedIndex();
|
||||
if (selectedIndex > 0) {
|
||||
nameTextBox.setText(regattaListBox.getValue(selectedIndex));
|
||||
validateAndUpdate();
|
||||
}
|
||||
adjustVisibilityOfResultDiscardingRuleComponent();
|
||||
}
|
||||
});
|
||||
otherTieBreakingLeaderboardsListBox = createSortedRegattaLeaderboardsListBox(existingLeaderboards, null);
|
||||
otherTieBreakingLeaderboardsListBox.ensureDebugId("OtherTieBreakingLeaderboardsListBox");
|
||||
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public class RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog extends RegattaLeaderboardDialog<LeaderboardDescriptorWithOtherTieBreakingLeaderboard> {
|
||||
protected ListBox otherTieBreakingLeaderboardsListBox;
|
||||
|
||||
public RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog(
|
||||
String title, LeaderboardDescriptorWithOtherTieBreakingLeaderboard leaderboardDescriptor,
|
||||
Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas,
|
||||
StringMessages stringMessages, ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptorWithOtherTieBreakingLeaderboard> callback) {
|
||||
super(title, leaderboardDescriptor,
|
||||
existingRegattas, stringMessages, errorReporter,
|
||||
new RegattaLeaderboardDialog.LeaderboardParameterValidator<LeaderboardDescriptorWithOtherTieBreakingLeaderboard>(
|
||||
stringMessages, existingLeaderboards),
|
||||
callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
VerticalPanel mainPanel = new VerticalPanel();
|
||||
Grid formGrid = new Grid(4, 3);
|
||||
formGrid.setCellSpacing(3);
|
||||
formGrid.setWidget(0, 0, createLabel(stringMessages.regatta()));
|
||||
formGrid.setWidget(0, 1, regattaListBox);
|
||||
formGrid.setWidget(1, 0, createLabel(stringMessages.otherTieBreakingLeaderboard()));
|
||||
formGrid.setWidget(1, 1, otherTieBreakingLeaderboardsListBox);
|
||||
formGrid.setWidget(2, 0, createLabel(stringMessages.name()));
|
||||
formGrid.setWidget(2, 1, nameTextBox);
|
||||
formGrid.setWidget(3, 0, createLabel(stringMessages.displayName()));
|
||||
formGrid.setWidget(3, 1, displayNameTextBox);
|
||||
mainPanel.add(formGrid);
|
||||
mainPanel.add(regattaDefinesDiscardsLabel);
|
||||
if (discardThresholdBoxes != null) {
|
||||
mainPanel.add(discardThresholdBoxes.getWidget());
|
||||
regattaDefinesDiscardsLabel.setVisible(false);
|
||||
}
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardDescriptorWithOtherTieBreakingLeaderboard getResult() {
|
||||
final LeaderboardDescriptor interimsResult = super.getResult();
|
||||
return new LeaderboardDescriptorWithOtherTieBreakingLeaderboard(interimsResult.getName(),
|
||||
interimsResult.getDisplayName(), interimsResult.getScoringScheme(),
|
||||
interimsResult.getDiscardThresholds(), interimsResult.getRegattaName(),
|
||||
interimsResult.getCourseAreaIds(), otherTieBreakingLeaderboardsListBox.getSelectedValue());
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public class RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog extends RegattaLeaderboardWithOtherTieBreakingLeaderboardDialog {
|
||||
public RegattaLeaderboardWithOtherTieBreakingLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards, Collection<RegattaDTO> existingRegattas,
|
||||
LeaderboardDescriptorWithOtherTieBreakingLeaderboard leaderboardDescriptor, StringMessages stringMessages, ErrorReporter errorReporter,
|
||||
DialogCallback<LeaderboardDescriptorWithOtherTieBreakingLeaderboard> callback) {
|
||||
super(stringMessages.editRegattaLeaderboardWithOtherTieBreakingLeaderboard(), leaderboardDescriptor, otherExistingLeaderboards, existingRegattas, stringMessages, errorReporter, callback);
|
||||
displayNameTextBox = createTextBox(leaderboardDescriptor.getDisplayName());
|
||||
displayNameTextBox.setVisibleLength(50);
|
||||
regattaListBox = createSortedRegattaListBox(existingRegattas, leaderboardDescriptor.getRegattaName());
|
||||
regattaListBox.setEnabled(false);
|
||||
otherTieBreakingLeaderboardsListBox = createSortedRegattaLeaderboardsListBox(otherExistingLeaderboards, leaderboardDescriptor.getOtherTieBreakingLeaderboardName());
|
||||
otherTieBreakingLeaderboardsListBox.ensureDebugId("OtherTieBreakingLeaderboardsListBox");
|
||||
otherTieBreakingLeaderboardsListBox.setEnabled(false);
|
||||
if (!getSelectedRegatta().definesSeriesDiscardThresholds()) {
|
||||
discardThresholdBoxes = new DiscardThresholdBoxes(this, leaderboardDescriptor.getDiscardThresholds(), stringMessages);
|
||||
} // else, the regatta leaderboard obtains its result discarding rule implicitly from the underlying regatta
|
||||
adjustVisibilityOfResultDiscardingRuleComponent();
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -381,7 +381,8 @@ public class RegattaListComposite extends Composite {
|
||||
sailingServiceWrite.updateSeries(regattaName, series.getName(), series.getName(), series.isMedal(),
|
||||
series.isFleetsCanRunInParallel(), series.getDiscardThresholds(), series.isStartsWithZeroScore(),
|
||||
series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring(),
|
||||
series.getMaximumNumberOfDiscards(), series.getFleets(), new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
|
||||
series.getMaximumNumberOfDiscards(), series.isOneAlwaysStaysOne(), series.getFleets(),
|
||||
new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error trying to update regatta " + editedRegatta.getName()
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd
|
||||
if (series.getName().equals(Series.DEFAULT_NAME)) {
|
||||
List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>();
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
RaceColumnDTO raceColumnDTO = new RaceColumnInSeriesDTO("R"+i, series.getName(), dto.getName());
|
||||
RaceColumnDTO raceColumnDTO = new RaceColumnInSeriesDTO("R"+i, series.getName(), dto.getName(), series.isOneAlwaysStaysOne());
|
||||
races.add(raceColumnDTO);
|
||||
}
|
||||
series.setRaceColumns(races);
|
||||
|
||||
+7
-1
@@ -23,10 +23,11 @@ public class SeriesDescriptor {
|
||||
private final boolean firstColumnIsNonDiscardableCarryForward;
|
||||
private final Integer maximumNumberOfDiscards;
|
||||
private final int[] resultDiscardingThresholds;
|
||||
private final boolean oneAlwaysStaysOne;
|
||||
|
||||
public SeriesDescriptor(SeriesDTO series, String seriesName, List<RaceColumnDTO> races, boolean isMedal, boolean isFleetsCanRunInParallel,
|
||||
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
|
||||
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, Integer maximumNumberOfDiscards) {
|
||||
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, Integer maximumNumberOfDiscards, boolean oneAlwaysStaysOne) {
|
||||
this.seriesName = seriesName;
|
||||
this.series = series;
|
||||
this.races = races;
|
||||
@@ -37,6 +38,7 @@ public class SeriesDescriptor {
|
||||
this.resultDiscardingThresholds = resultDiscardingThresholds;
|
||||
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
|
||||
this.maximumNumberOfDiscards = maximumNumberOfDiscards;
|
||||
this.oneAlwaysStaysOne = oneAlwaysStaysOne;
|
||||
}
|
||||
|
||||
public SeriesDTO getSeries() {
|
||||
@@ -78,4 +80,8 @@ public class SeriesDescriptor {
|
||||
public Integer getMaximumNumberOfDiscards() {
|
||||
return maximumNumberOfDiscards;
|
||||
}
|
||||
|
||||
public boolean isOneAlwaysStaysOne() {
|
||||
return oneAlwaysStaysOne;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -45,6 +45,7 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
|
||||
private CheckBox startWithZeroScoreCheckbox;
|
||||
private CheckBox hasSplitFleetContiguousScoringCheckbox;
|
||||
private CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox;
|
||||
private CheckBox oneAlwaysStaysOneCheckbox;
|
||||
private IntegerBox maximumNumberOfDiscardsBox;
|
||||
private CheckBox useSeriesResultDiscardingThresholdsCheckbox;
|
||||
private final StringMessages stringMessages;
|
||||
@@ -131,7 +132,7 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
|
||||
for (String name : raceNamesEditor.getValue()) {
|
||||
RaceColumnDTO raceColumnDTO = findRaceColumnInSeriesByName(selectedSeries, name);
|
||||
if (raceColumnDTO == null) {
|
||||
raceColumnDTO = new RaceColumnInSeriesDTO(name, selectedSeries.getName(), regatta.getName());
|
||||
raceColumnDTO = new RaceColumnInSeriesDTO(name, selectedSeries.getName(), regatta.getName(), selectedSeries.isOneAlwaysStaysOne());
|
||||
}
|
||||
races.add(raceColumnDTO);
|
||||
}
|
||||
@@ -140,7 +141,7 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
|
||||
useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds()
|
||||
: null, startWithZeroScoreCheckbox.getValue(),
|
||||
firstColumnIsNonDiscardableCarryForwardCheckbox.getValue(), hasSplitFleetContiguousScoringCheckbox.getValue(),
|
||||
maximumNumberOfDiscardsBox.getValue());
|
||||
maximumNumberOfDiscardsBox.getValue(), oneAlwaysStaysOneCheckbox.getValue());
|
||||
}
|
||||
|
||||
private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) {
|
||||
@@ -196,6 +197,11 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
|
||||
firstColumnIsNonDiscardableCarryForwardCheckbox.setValue(selectedSeries.isFirstColumnIsNonDiscardableCarryForward());
|
||||
additionalWidgetPanel.add(firstColumnIsNonDiscardableCarryForwardCheckbox);
|
||||
|
||||
oneAlwaysStaysOneCheckbox = createCheckbox(stringMessages.oneAlwaysStaysOne());
|
||||
oneAlwaysStaysOneCheckbox.ensureDebugId("OneAlwaysStaysOneCheckbox");
|
||||
oneAlwaysStaysOneCheckbox.setValue(selectedSeries.isOneAlwaysStaysOne());
|
||||
additionalWidgetPanel.add(oneAlwaysStaysOneCheckbox);
|
||||
|
||||
final HorizontalPanel maximumNumberOfDiscardsPanel = new HorizontalPanel();
|
||||
maximumNumberOfDiscardsPanel.add(new Label(stringMessages.maximumNumberOfDiscards()));
|
||||
maximumNumberOfDiscardsBox = createIntegerBox(selectedSeries.getMaximumNumberOfDiscards(), /* visibleLength */ 3);
|
||||
|
||||
+6
-3
@@ -40,6 +40,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
|
||||
protected CheckBox hasSplitFleetContiguousScoringCheckbox;
|
||||
protected CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox;
|
||||
protected CheckBox useSeriesResultDiscardingThresholdsCheckbox;
|
||||
protected CheckBox oneAlwaysStaysOneCheckbox;
|
||||
protected IntegerBox maximumNumberOfDiscardsBox;
|
||||
protected final DiscardThresholdBoxes discardThresholdBoxes;
|
||||
protected ListEditorComposite<FleetDTO> fleetListComposite;
|
||||
@@ -149,10 +150,11 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
|
||||
SeriesWithFleetsCreateDialog.this.discardThresholdBoxes.getWidget().setVisible(event.getValue());
|
||||
}
|
||||
});
|
||||
oneAlwaysStaysOneCheckbox = createCheckbox(stringMessages.oneAlwaysStaysOne());
|
||||
oneAlwaysStaysOneCheckbox.ensureDebugId("OneAlwaysStaysOneCheckbox");
|
||||
Widget widget = discardThresholdBoxes.getWidget();
|
||||
widget.ensureDebugId("DiscardThresholdBoxes");
|
||||
widget.setVisible(false);
|
||||
|
||||
initializeFleetListComposite(stringMessages);
|
||||
}
|
||||
|
||||
@@ -177,6 +179,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
|
||||
series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoringCheckbox.getValue());
|
||||
series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForwardCheckbox.getValue());
|
||||
series.setMaximumNumberOfDiscards(maximumNumberOfDiscardsBox.getValue());
|
||||
series.setOneAlwaysStaysOne(oneAlwaysStaysOneCheckbox.getValue());
|
||||
series.setFleets(fleetListComposite.getValue());
|
||||
series.setDiscardThresholds(useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds() : null);
|
||||
return series;
|
||||
@@ -189,7 +192,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
|
||||
if (additionalWidget != null) {
|
||||
panel.add(additionalWidget);
|
||||
}
|
||||
Grid formGrid = new Grid(9, 2);
|
||||
Grid formGrid = new Grid(10, 2);
|
||||
panel.add(formGrid);
|
||||
int row = 0;
|
||||
formGrid.setWidget(row, 0, new Label(stringMessages.name() + ":"));
|
||||
@@ -199,11 +202,11 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
|
||||
formGrid.setWidget(row++, 1, startsWithZeroScoreCheckbox);
|
||||
formGrid.setWidget(row++, 1, hasSplitFleetContiguousScoringCheckbox);
|
||||
formGrid.setWidget(row++, 1, firstColumnIsNonDiscardableCarryForwardCheckbox);
|
||||
formGrid.setWidget(row++, 1, oneAlwaysStaysOneCheckbox);
|
||||
formGrid.setWidget(row, 0, new Label(stringMessages.maximumNumberOfDiscards()));
|
||||
formGrid.setWidget(row++, 1, maximumNumberOfDiscardsBox);
|
||||
formGrid.setWidget(row++, 1, useSeriesResultDiscardingThresholdsCheckbox);
|
||||
formGrid.setWidget(row++, 1, discardThresholdBoxes.getWidget());
|
||||
|
||||
TabPanel tabPanel = new TabPanel();
|
||||
tabPanel.setWidth("100%");
|
||||
tabPanel.add(fleetListComposite, stringMessages.fleets());
|
||||
|
||||
+4
-1
@@ -271,7 +271,7 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa
|
||||
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal,
|
||||
boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore,
|
||||
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, Integer maximumNumberOfDiscards,
|
||||
List<FleetDTO> fleets) throws UnauthorizedException;
|
||||
boolean oneAlwaysStaysOne, List<FleetDTO> fleets) throws UnauthorizedException;
|
||||
|
||||
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, List<UUID> courseAreaUuids,
|
||||
RegattaConfigurationDTO regattaConfiguration, Double buoyZoneRadiusInHullLengths,
|
||||
@@ -378,6 +378,9 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa
|
||||
StrippedLeaderboardDTO createRegattaLeaderboardWithEliminations(String name, String displayName,
|
||||
String regattaName) throws UnauthorizedException;
|
||||
|
||||
StrippedLeaderboardDTO createRegattaLeaderboardWithOtherTieBreakingLeaderboard(RegattaName regattaIdentifier,
|
||||
String leaderboardDisplayName, int[] discardThresholds, String otherTieBreakingLeaderboardName);
|
||||
|
||||
StrippedLeaderboardDTO createRegattaLeaderboard(RegattaName regattaIdentifier,
|
||||
String leaderboardDisplayName, int[] discardThresholds) throws UnauthorizedException;
|
||||
|
||||
|
||||
+6
-3
@@ -356,8 +356,11 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic
|
||||
int[] discardThresholds, AsyncCallback<StrippedLeaderboardDTO> asyncCallback);
|
||||
|
||||
void createRegattaLeaderboardWithEliminations(String name, String displayName,
|
||||
String regattaName,
|
||||
AsyncCallback<StrippedLeaderboardDTO> asyncCallback);
|
||||
String regattaName, AsyncCallback<StrippedLeaderboardDTO> asyncCallback);
|
||||
|
||||
void createRegattaLeaderboardWithOtherTieBreakingLeaderboard(RegattaName regattaIdentifier,
|
||||
String leaderboardDisplayName, int[] discardThresholds, String otherTieBreakingLeaderboardName,
|
||||
AsyncCallback<StrippedLeaderboardDTO> callback);
|
||||
|
||||
void removeLeaderboard(String leaderboardName, AsyncCallback<Void> asyncCallback);
|
||||
|
||||
@@ -480,7 +483,7 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic
|
||||
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal,
|
||||
boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore,
|
||||
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, Integer maximumNumberOfDiscards,
|
||||
List<FleetDTO> fleets, AsyncCallback<Void> callback);
|
||||
boolean oneAlwaysStaysOne, List<FleetDTO> fleets, AsyncCallback<Void> callback);
|
||||
|
||||
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
|
||||
AsyncCallback<Void> callback);
|
||||
|
||||
+7
@@ -463,8 +463,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String createFlexibleLeaderboard();
|
||||
String createRegattaLeaderboard();
|
||||
String createRegattaLeaderboardWithEliminations();
|
||||
String createRegattaLeaderboardWithOtherTieBreakingLeaderboard();
|
||||
String editFlexibleLeaderboard();
|
||||
String editRegattaLeaderboard();
|
||||
String editRegattaLeaderboardWithEliminations();
|
||||
String editRegattaLeaderboardWithOtherTieBreakingLeaderboard();
|
||||
String defaultRegatta();
|
||||
String useOverallLeaderboard();
|
||||
String overallStandings();
|
||||
@@ -2410,4 +2413,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String goToSelfServicePortalDialogTitle();
|
||||
String goToSelfServicePortalDialogText();
|
||||
String failedFetchingSelfServicePortalSession();
|
||||
String oneAlwaysStaysOne();
|
||||
String otherTieBreakingLeaderboard();
|
||||
String scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboard();
|
||||
String scoringSchemeHighPointsByWindTiesLastlyBrokenByOtherLeaderboardDescription();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user