Merge branch 'non-integer-scores'

This commit is contained in:
Axel Uhl committed 2012-07-21 01:07:44 +02:00
commit 37e1271c71
36 files changed
+395 -217

No files matched your search

@@ -32,16 +32,16 @@ import com.sap.sailing.domain.tracking.TrackedRace;
*/
public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorrections {
private static final long serialVersionUID = 8824782847677232275L;
private final Map<String, Integer> carriedPointsByCompetitorName;
private final Map<String, Double> carriedPointsByCompetitorName;
private final Map<String, Map<RaceColumn, MaxPointsReason>> maxPointsReasonsByCompetitorName;
private final Map<String, Map<RaceColumn, Integer>> correctedScoresByCompetitorName;
private final Map<String, Map<RaceColumn, Double>> correctedScoresByCompetitorName;
private final Map<String, String> displayNamesByCompetitorName;
private final Leaderboard leaderboard;
public DelayedLeaderboardCorrectionsImpl(Leaderboard leaderboard) {
carriedPointsByCompetitorName = new HashMap<String, Integer>();
carriedPointsByCompetitorName = new HashMap<String, Double>();
maxPointsReasonsByCompetitorName = new HashMap<String, Map<RaceColumn,MaxPointsReason>>();
correctedScoresByCompetitorName = new HashMap<String, Map<RaceColumn,Integer>>();
correctedScoresByCompetitorName = new HashMap<String, Map<RaceColumn, Double>>();
displayNamesByCompetitorName = new HashMap<String, String>();
this.leaderboard = leaderboard;
leaderboard.addRaceColumnListener(this);
@@ -61,7 +61,7 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
}
@Override
public void setCarriedPoints(String competitorName, int carriedPoints) {
public void setCarriedPoints(String competitorName, double carriedPoints) {
assertNoTrackedRaceAssociatedYet();
carriedPointsByCompetitorName.put(competitorName, carriedPoints);
}
@@ -78,11 +78,11 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
}
@Override
public void correctScore(String competitorName, RaceColumn raceColumn, int correctedScore) {
public void correctScore(String competitorName, RaceColumn raceColumn, double correctedScore) {
assertNoTrackedRaceAssociatedYet();
Map<RaceColumn, Integer> map = correctedScoresByCompetitorName.get(competitorName);
Map<RaceColumn, Double> map = correctedScoresByCompetitorName.get(competitorName);
if (map == null) {
map = new HashMap<RaceColumn, Integer>();
map = new HashMap<RaceColumn, Double>();
correctedScoresByCompetitorName.put(competitorName, map);
}
map.put(raceColumn, correctedScore);
@@ -101,8 +101,8 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
for (Competitor competitor : race.getRace().getCompetitors()) {
competitorsByName.put(competitor.getName(), competitor);
}
for (Iterator<Map.Entry<String, Integer>> carryEntryIter = carriedPointsByCompetitorName.entrySet().iterator(); carryEntryIter.hasNext(); ) {
Map.Entry<String, Integer> carryEntry = carryEntryIter.next();
for (Iterator<Map.Entry<String, Double>> carryEntryIter = carriedPointsByCompetitorName.entrySet().iterator(); carryEntryIter.hasNext(); ) {
Map.Entry<String, Double> carryEntry = carryEntryIter.next();
if (competitorsByName.containsKey(carryEntry.getKey())) {
leaderboard.setCarriedPoints(competitorsByName.get(carryEntry.getKey()), carryEntry.getValue());
carryEntryIter.remove();
@@ -119,11 +119,11 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
maxPointsReasonsEntryIter.remove();
}
}
for (Iterator<java.util.Map.Entry<String, Map<RaceColumn, Integer>>> correctedScoresEntryIter =
for (Iterator<java.util.Map.Entry<String, Map<RaceColumn, Double>>> correctedScoresEntryIter =
correctedScoresByCompetitorName.entrySet().iterator(); correctedScoresEntryIter.hasNext();) {
java.util.Map.Entry<String, Map<RaceColumn, Integer>> correctedScoresEntries = correctedScoresEntryIter.next();
java.util.Map.Entry<String, Map<RaceColumn, Double>> correctedScoresEntries = correctedScoresEntryIter.next();
if (competitorsByName.containsKey(correctedScoresEntries.getKey())) {
for (java.util.Map.Entry<RaceColumn, Integer> correctedScoreEntry : correctedScoresEntries.getValue().entrySet()) {
for (java.util.Map.Entry<RaceColumn, Double> correctedScoreEntry : correctedScoresEntries.getValue().entrySet()) {
leaderboard.getScoreCorrection().correctScore(competitorsByName.get(correctedScoresEntries.getKey()),
correctedScoreEntry.getKey(), correctedScoreEntry.getValue());
}
@@ -199,7 +199,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
if (result != null) {
DelayedLeaderboardCorrections loadedLeaderboardCorrections = new DelayedLeaderboardCorrectionsImpl(result);
loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections);
loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, scoreCorrection);
}
return result;
}
@@ -262,17 +262,27 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
return result;
}
private void loadLeaderboardCorrections(DBObject dbLeaderboard, DelayedLeaderboardCorrections correctionsToUpdate) {
private void loadLeaderboardCorrections(DBObject dbLeaderboard, DelayedLeaderboardCorrections correctionsToUpdate,
SettableScoreCorrection scoreCorrectionToUpdate) {
DBObject carriedPoints = (DBObject) dbLeaderboard.get(FieldNames.LEADERBOARD_CARRIED_POINTS.name());
if (carriedPoints != null) {
for (String competitorName : carriedPoints.keySet()) {
Integer carriedPointsForCompetitor = (Integer) carriedPoints.get(competitorName);
Double carriedPointsForCompetitor = ((Number) carriedPoints.get(competitorName)).doubleValue();
if (carriedPointsForCompetitor != null) {
correctionsToUpdate.setCarriedPoints(MongoUtils.unescapeDollarAndDot(competitorName), carriedPointsForCompetitor);
}
}
}
DBObject dbScoreCorrection = (DBObject) dbLeaderboard.get(FieldNames.LEADERBOARD_SCORE_CORRECTIONS.name());
if (dbScoreCorrection.containsField(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name())) {
scoreCorrectionToUpdate.setTimePointOfLastCorrectionsValidity(
new MillisecondsTimePoint((Long) dbScoreCorrection.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name())));
dbScoreCorrection.removeField(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name());
}
if (dbScoreCorrection.containsField(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name())) {
scoreCorrectionToUpdate.setComment((String) dbScoreCorrection.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name()));
dbScoreCorrection.removeField(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name());
}
for (String raceName : dbScoreCorrection.keySet()) {
DBObject dbScoreCorrectionForRace = (DBObject) dbScoreCorrection.get(raceName);
for (String competitorName : dbScoreCorrectionForRace.keySet()) {
@@ -284,8 +294,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name())));
}
if (dbScoreCorrectionForCompetitorInRace.containsField(FieldNames.LEADERBOARD_CORRECTED_SCORE.name())) {
correctionsToUpdate.correctScore(MongoUtils.unescapeDollarAndDot(competitorName), raceColumn, (Integer) dbScoreCorrectionForCompetitorInRace
.get(FieldNames.LEADERBOARD_CORRECTED_SCORE.name()));
final Double leaderboardCorrectedScore = ((Number) dbScoreCorrectionForCompetitorInRace
.get(FieldNames.LEADERBOARD_CORRECTED_SCORE.name())).doubleValue();
correctionsToUpdate.correctScore(MongoUtils.unescapeDollarAndDot(competitorName), raceColumn, (Double) leaderboardCorrectedScore);
}
}
}
@@ -41,11 +41,12 @@ public enum FieldNames {
// leaderboard:
LEADERBOARD_NAME, LEADERBOARD_COLUMNS, LEADERBOARD_COLUMN_NAME, LEADERBOARD_COMPETITOR_DISPLAY_NAMES, LEADERBOARD_IS_MEDAL_RACE_COLUMN,
LEADERBOARD_CARRIED_POINTS, LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS, LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON, LEADERBOARD_CORRECTED_SCORE,
LEADERBOARD_CARRIED_POINTS, LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS, LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON,
LEADERBOARD_CORRECTED_SCORE, LEADERBOARD_SCORE_CORRECTION_TIMESTAMP, LEADERBOARD_SCORE_CORRECTION_COMMENT,
// leaderboard group
LEADERBOARD_GROUP_NAME, LEADERBOARD_GROUP_DESCRIPTION, LEADERBOARD_GROUP_LEADERBOARDS,
// race ID to regatta name
RACE_ID_AS_STRING
RACE_ID_AS_STRING;
}
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
@@ -205,7 +206,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbCorrectionForCompetitor.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON.name(),
maxPointsReason.name());
}
Integer explicitScoreCorrection = scoreCorrection
Double explicitScoreCorrection = scoreCorrection
.getExplicitScoreCorrection(competitor, raceColumn);
if (explicitScoreCorrection != null) {
dbCorrectionForCompetitor.put(FieldNames.LEADERBOARD_CORRECTED_SCORE.name(),
@@ -218,6 +219,13 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbScoreCorrections.put(raceColumn.getName(), dbCorrectionForRace);
}
}
final TimePoint timePointOfLastCorrectionsValidity = scoreCorrection.getTimePointOfLastCorrectionsValidity();
if (timePointOfLastCorrectionsValidity != null) {
dbScoreCorrections.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_TIMESTAMP.name(), timePointOfLastCorrectionsValidity.asMillis());
}
if (scoreCorrection.getComment() != null) {
dbScoreCorrections.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name(), scoreCorrection.getComment());
}
}
@Override
@@ -43,24 +43,24 @@ public class LeaderboardForKielWeekTest extends OnlineTracTracBasedTest {
loadRace("357c700a-9d9a-11e0-85be-406186cbf87c"); // 505 Race 2
Competitor hasso = getCompetitorByName("Dr.Plattner");
RaceColumn column = leaderboard.addRace(getTrackedRace(), "Test Race 1", /* medalRace */ false, defaultFleet);
assertEquals(21, leaderboard.getTotalPoints(hasso, now));
assertEquals(21, leaderboard.getTotalPoints(hasso, now), 0.000000001);
Pair<Competitor, RaceColumn> key = new Pair<Competitor, RaceColumn>(hasso, column);
assertEquals(21, leaderboard.getContent(now).get(key).getTotalPoints());
assertEquals(21, leaderboard.getEntry(hasso, column, now).getTotalPoints());
assertEquals(21, leaderboard.getContent(now).get(key).getTotalPoints(), 0.000000001);
assertEquals(21, leaderboard.getEntry(hasso, column, now).getTotalPoints(), 0.000000001);
loadRace("e876c3a0-9da8-11e0-85be-406186cbf87c"); // 505 Race 3
column = leaderboard.addRace(getTrackedRace(), "Test Race 2", /* medalRace */ false, defaultFleet);
key = new Pair<Competitor, RaceColumn>(hasso, column);
// In Race 3, Hasso ranked 31st
assertEquals(52, leaderboard.getTotalPoints(hasso, now));
assertEquals(31, leaderboard.getContent(now).get(key).getTotalPoints());
assertEquals(31, leaderboard.getEntry(hasso, column, now).getTotalPoints());
assertEquals(52, leaderboard.getTotalPoints(hasso, now), 0.000000001);
assertEquals(31, leaderboard.getContent(now).get(key).getTotalPoints(), 0.000000001);
assertEquals(31, leaderboard.getEntry(hasso, column, now).getTotalPoints(), 0.000000001);
loadRace("7c666e50-9dde-11e0-85be-406186cbf87c"); // 505 Race 4
column = leaderboard.addRace(getTrackedRace(), "Test Race 3", /* medalRace */ false, defaultFleet);
key = new Pair<Competitor, RaceColumn>(hasso, column);
// now the second race is discarded because Hasso ranked worst compared to the other two; in race 4 he ranked 11th
assertEquals(32, leaderboard.getTotalPoints(hasso, now));
assertEquals(11, leaderboard.getContent(now).get(key).getTotalPoints());
assertEquals(11, leaderboard.getEntry(hasso, column, now).getTotalPoints());
assertEquals(32, leaderboard.getTotalPoints(hasso, now), 0.000000001);
assertEquals(11, leaderboard.getContent(now).get(key).getTotalPoints(), 0.000000001);
assertEquals(11, leaderboard.getEntry(hasso, column, now).getTotalPoints(), 0.000000001);
}
private void loadRace(String raceId) throws MalformedURLException, IOException, InterruptedException,
@@ -126,9 +126,9 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
scoreCorrection.setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DSQ);
// assert that best scoring but now disqualified race gets max net points because of disqualification:
assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1,
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints());
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001);
// now assert that it gets discarded because due to disqualification it scores worse than all others:
assertEquals(0, leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints());
assertEquals(0, leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001);
}
@Test
@@ -162,10 +162,10 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
scoreCorrection.setMaxPointsReason(competitor, bestScoringRaceColumn, MaxPointsReason.DND);
// assert that best scoring but now disqualified race gets max net points because of disqualification:
assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1,
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints());
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getNetPoints(), 0.000000001);
// now assert that it does not get discarded because it's a DND
assertEquals(Util.size(bestScoringRaceColumn.getTrackedRace(defaultFleet).getRace().getCompetitors())+1,
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints());
leaderboard.getEntry(competitor, bestScoringRaceColumn, MillisecondsTimePoint.now()).getTotalPoints(), 0.000000001);
}
protected void testLeaderboard(int numberOfStartedRaces, int numberOfNotStartedRaces, int firstDiscardingThreshold,
@@ -208,30 +208,30 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
assertEquals(rank, leaderboard.getTrackedRank(competitor, raceColumn, now));
assertEquals(rank, leaderboard.getContent(now).get(key).getTrackedPoints());
assertEquals(rank, leaderboard.getEntry(competitor, raceColumn, now).getTrackedPoints());
assertEquals(rank, leaderboard.getNetPoints(competitor, raceColumn, now));
assertEquals(rank, leaderboard.getContent(now).get(key).getNetPoints());
assertEquals(rank, leaderboard.getEntry(competitor, raceColumn, now).getNetPoints());
assertEquals(rank, leaderboard.getNetPoints(competitor, raceColumn, now), 0.000000001);
assertEquals(rank, leaderboard.getContent(now).get(key).getNetPoints(), 0.000000001);
assertEquals(rank, leaderboard.getEntry(competitor, raceColumn, now).getNetPoints(), 0.000000001);
// One race is discarded because four races were started, and for [3-6) one race can be discarded.
// The discarded race is the worst of those started, so the one with rank 4.
int expectedNumberOfDiscardedRaces =
numberOfRacesFromWhichToDiscard < firstDiscardingThreshold ? 0 : numberOfRacesFromWhichToDiscard < secondDiscardingThreshold ? 1 : 2;
boolean discarded = ranksOfNonMedalStartedRaces.indexOf(rank) >= ranksOfNonMedalStartedRaces.size()-expectedNumberOfDiscardedRaces;
int expected = discarded ? 0 : rank==medalRacePoints?2*rank:rank;
assertEquals(expected, leaderboard.getTotalPoints(competitor, raceColumn, now));
assertEquals(expected, leaderboard.getContent(now).get(key).getTotalPoints());
assertEquals(expected, leaderboard.getEntry(competitor, raceColumn, now).getTotalPoints());
assertEquals(expected, leaderboard.getTotalPoints(competitor, raceColumn, now), 0.000000001);
assertEquals(expected, leaderboard.getContent(now).get(key).getTotalPoints(), 0.000000001);
assertEquals(expected, leaderboard.getEntry(competitor, raceColumn, now).getTotalPoints(), 0.000000001);
totalPoints += leaderboard.getContent(now).get(key).getTotalPoints();
} else {
assertEquals(0, leaderboard.getTrackedRank(competitor, raceColumn, now));
assertEquals(0, leaderboard.getNetPoints(competitor, raceColumn, now));
assertEquals(0, leaderboard.getNetPoints(competitor, raceColumn, now), 0.000000001);
assertEquals(0, leaderboard.getContent(now).get(key).getTrackedPoints());
assertEquals(0, leaderboard.getContent(now).get(key).getNetPoints());
assertEquals(0, leaderboard.getContent(now).get(key).getNetPoints(), 0.000000001);
assertEquals(0, leaderboard.getEntry(competitor, raceColumn, now).getTrackedPoints());
assertEquals(0, leaderboard.getEntry(competitor, raceColumn, now).getNetPoints());
assertEquals(0, leaderboard.getEntry(competitor, raceColumn, now).getNetPoints(), 0.000000001);
// no increment on totalPoints
}
}
assertEquals(totalPoints, leaderboard.getTotalPoints(competitor, now));
assertEquals(totalPoints, leaderboard.getTotalPoints(competitor, now), 0.000000001);
}
private int getMedalRacePoints(Competitor competitor, TimePoint at, Fleet fleet) throws NoWindException {
@@ -87,12 +87,12 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(competitors.get(5), rankedCompetitors.get(9));
// Now test the total points and make sure the other competitors advanced by one, too
assertEquals(11, leaderboard.getTotalPoints(competitors.get(5), f1Column, now));
assertEquals(11, leaderboard.getTotalPoints(competitors.get(5), f1Column, now), 0.000000001);
for (int i=0; i<5; i++) {
assertEquals(i+1, leaderboard.getTotalPoints(competitors.get(i), f1Column, now));
assertEquals(i+1, leaderboard.getTotalPoints(competitors.get(i), f1Column, now), 0.000000001);
}
for (int i=6; i<10; i++) {
assertEquals(i, leaderboard.getTotalPoints(competitors.get(i), f1Column, now));
assertEquals(i, leaderboard.getTotalPoints(competitors.get(i), f1Column, now), 0.000000001);
}
}
@@ -198,7 +198,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3);
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later));
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertEquals(Arrays.asList(new Competitor[] { c[0], c[1], c[2] }), rankedCompetitors);
}
@@ -217,7 +217,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later));
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertTrue(rankedCompetitors.indexOf(c[0]) == rankedCompetitors.indexOf(c[1])-1);
}
@@ -235,7 +235,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
createAndAttachTrackedRaces(series.get(2), "Medal", m1);
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
// assert that both have equal score
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later));
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
// assert that c[0] ranks better than c[1] (reason: c[0] ranked better in medal race)
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
}
@@ -255,7 +255,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later));
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
}
@@ -19,9 +19,9 @@ import com.sap.sailing.domain.tracking.TrackedRace;
*
*/
public interface DelayedLeaderboardCorrections extends RaceColumnListener, Serializable {
void correctScore(String competitorName, RaceColumn raceColumn, int correctedScore);
void correctScore(String competitorName, RaceColumn raceColumn, double correctedScore);
void setCarriedPoints(String competitorName, int carriedPoints);
void setCarriedPoints(String competitorName, double carriedPoints);
void setMaxPointsReason(String competitorName, RaceColumn raceColumn, MaxPointsReason maxPointsReason);
@@ -40,8 +40,8 @@ public interface Leaderboard extends Named {
*/
public interface Entry {
int getTrackedPoints();
int getNetPoints() throws NoWindException;
int getTotalPoints() throws NoWindException;
double getNetPoints() throws NoWindException;
double getTotalPoints() throws NoWindException;
MaxPointsReason getMaxPointsReason();
boolean isDiscarded() throws NoWindException;
/**
@@ -77,7 +77,7 @@ public interface Leaderboard extends Named {
* the <code>competitor</code>. Returns <code>0</code> if there is no carried points definition for
* <code>competitor</code>.
*/
int getCarriedPoints(Competitor competitor);
double getCarriedPoints(Competitor competitor);
/**
* Shorthand for {@link TrackedRace#getRank(Competitor, com.sap.sailing.domain.common.TimePoint)} with the
@@ -106,7 +106,7 @@ public interface Leaderboard extends Named {
* @param race
* a race that is contained in the {@link #getRaceColumns()} result
*/
int getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException;
double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException;
/**
* Tells if and why a competitor received "penalty" points for a race (however the scoring rules define the
@@ -125,7 +125,7 @@ public interface Leaderboard extends Named {
* @param race
* a race that is contained in the {@link #getRaceColumns()} result
*/
int getTotalPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException;
double getTotalPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException;
/**
* Tells whether the contribution of <code>raceColumn</code> is discarded in the current leaderboard's standings for
@@ -139,7 +139,7 @@ public interface Leaderboard extends Named {
* Sums up the {@link #getTotalPoints(Competitor, TrackedRace, TimePoint) total points} of <code>competitor</code>
* across all races tracked by this leaderboard.
*/
int getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException;
double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException;
/**
* Sorts the competitors according to their ranking in the race column specified. Only competitors who have a score
@@ -191,7 +191,7 @@ public interface Leaderboard extends Named {
* simply added to the scores tracked by this leaderboard in the {@link #getTotalPoints(Competitor, TimePoint)}
* method.
*/
void setCarriedPoints(Competitor competitor, int carriedPoints);
void setCarriedPoints(Competitor competitor, double carriedPoints);
/**
* Reverses the effect of {@link #setCarriedPoints(Competitor, int)}, i.e., afterwards, asking {@link #getCarriedPoints(Competitor)}
@@ -21,7 +21,7 @@ import com.sap.sailing.domain.tracking.TrackedRace;
*/
public interface ScoreCorrection extends Serializable {
public interface Result {
int getCorrectedScore();
double getCorrectedScore();
MaxPointsReason getMaxPointsReason();
boolean isCorrected();
}
@@ -51,4 +51,18 @@ public interface ScoreCorrection extends Serializable {
*/
boolean isScoreCorrected(Competitor competitor, RaceColumn raceColumn);
/**
* Tells when the score correction was last updated. This should usually be the "validity time" and not the
* "transaction time." In other words, if scores provided by the race committee are updated to this score correction
* at time X, and the race committee's scores are tagged with time Y, then this method should return Y, not X. If
* Y is not available for some reason, X may be used as a default.
*/
TimePoint getTimePointOfLastCorrectionsValidity();
/**
* A free-form comment to display to the viewers of the leaderboard that has these score corrections. It should make
* crystal clear if the scores are preliminary or not yet jury-finalized. If <code>null</code> is returned, this
* always has to be interpreted as "preliminary" because then no comment as to the correctness have been made.
*/
String getComment();
}
@@ -1,25 +0,0 @@
package com.sap.sailing.domain.leaderboard;
import java.math.BigDecimal;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.TrackedRace;
/**
* Tells how ranks and disqualifications of a {@link TrackedRace} are converted into points in the scope
* of a {@link Regatta}, also considering rules for discarding results. Different implementations may, e.g.,
* distinguish between low-point and high-point scoring schemes, schemes where a winner's score is further
* improved as well as different discarding thresholds.
*
* @author Axel Uhl (D043530)
*
*/
public interface ScoringRules {
/**
* Note that scores can be fractional numbers, e.g., if the scoring rule determines that the winner's rank
* is divided by two to obtain their score.
*/
BigDecimal getScore(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint);
}
@@ -3,6 +3,7 @@ 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.sailing.domain.common.TimePoint;
public interface SettableScoreCorrection extends ScoreCorrection {
@@ -17,7 +18,7 @@ public interface SettableScoreCorrection extends ScoreCorrection {
MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn raceColumn);
void correctScore(Competitor competitor, RaceColumn raceColumn, int points);
void correctScore(Competitor competitor, RaceColumn raceColumn, double points);
/**
* Removes a score correction which makes the competitor's score for <code>raceColumn</code> to fall back to the score
@@ -29,8 +30,11 @@ public interface SettableScoreCorrection extends ScoreCorrection {
* @return <code>null</code> if not set for the competitor, e.g., because no correction was made or an explicit
* {@link MaxPointsReason} was provided for the competitor.
*/
Integer getExplicitScoreCorrection(Competitor competitor, RaceColumn raceColumn);
Double getExplicitScoreCorrection(Competitor competitor, RaceColumn raceColumn);
boolean hasCorrectionFor(RaceColumn raceInLeaderboard);
void setTimePointOfLastCorrectionsValidity(TimePoint timePointOfLastCorrectionsValidity);
void setComment(String scoreCorrectionComment);
}
@@ -43,7 +43,7 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
/**
* The factor by which a medal race score is multiplied in the overall point scheme
*/
private static final int MEDAL_RACE_FACTOR = 2;
private static final double MEDAL_RACE_FACTOR = 2.0;
private final SettableScoreCorrection scoreCorrection;
private ThresholdBasedResultDiscardingRule resultDiscardingRule;
@@ -59,9 +59,9 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
* with aggregated results of races not tracked / displayed by this leaderboard in detail. The points
* provided by this map are considered by {@link #getTotalPoints(Competitor, TimePoint)}.
*/
private final Map<Competitor, Integer> carriedPoints;
private final Map<Competitor, Double> carriedPoints;
private final Comparator<Integer> scoreComparator;
private final Comparator<Double> scoreComparator;
private Set<RaceColumnListener> raceColumnListeners;
@@ -79,14 +79,14 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
*/
private class EntryImpl implements Entry {
private final Callable<Integer> trackedPoints;
private final int netPoints;
private final double netPoints;
private final boolean isNetPointsCorrected;
private final int totalPoints;
private final double totalPoints;
private final MaxPointsReason maxPointsReason;
private final boolean discarded;
private final Fleet fleet;
private EntryImpl(Callable<Integer> trackedPoints, int netPoints, boolean isNetPointsCorrected, int totalPoints,
private EntryImpl(Callable<Integer> trackedPoints, double netPoints, boolean isNetPointsCorrected, double totalPoints,
MaxPointsReason maxPointsReason, boolean discarded, Fleet fleet) {
super();
this.trackedPoints = trackedPoints;
@@ -106,7 +106,7 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
}
}
@Override
public int getNetPoints() {
public double getNetPoints() {
return netPoints;
}
@Override
@@ -114,7 +114,7 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
return isNetPointsCorrected;
}
@Override
public int getTotalPoints() {
public double getTotalPoints() {
return totalPoints;
}
@Override
@@ -132,12 +132,14 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
}
/**
* @param scoreCorrection must not be <code>null</code>
* @param scoreComparator the comparator to use to compare basic scores, such as net points
* @param name must not be <code>null</code>
*/
public AbstractLeaderboardImpl(SettableScoreCorrection scoreCorrection,
ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator<Integer> scoreComparator) {
this.carriedPoints = new HashMap<Competitor, Integer>();
ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator<Double> scoreComparator) {
assert scoreCorrection != null;
this.carriedPoints = new HashMap<Competitor, Double>();
this.scoreCorrection = scoreCorrection;
this.displayNames = new HashMap<Competitor, String>();
this.resultDiscardingRule = resultDiscardingRule;
@@ -264,7 +266,7 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
}
@Override
public int getNetPoints(final Competitor competitor, final RaceColumn raceColumn, final TimePoint timePoint) throws NoWindException {
public double getNetPoints(final Competitor competitor, final RaceColumn raceColumn, final TimePoint timePoint) throws NoWindException {
return getScoreCorrection().getCorrectedScore(
new Callable<Integer>() {
public Integer call() throws NoWindException {
@@ -288,15 +290,15 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
}
@Override
public int getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) throws NoWindException {
public double getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) throws NoWindException {
return isDiscarded(competitor, raceColumn, timePoint) ?
0 :
(raceColumn.isMedalRace() ? MEDAL_RACE_FACTOR : 1) * getNetPoints(competitor, raceColumn, timePoint);
0.0 :
(raceColumn.isMedalRace() ? MEDAL_RACE_FACTOR : 1.0) * getNetPoints(competitor, raceColumn, timePoint);
}
@Override
public int getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException {
int result = getCarriedPoints(competitor);
public double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException {
double result = getCarriedPoints(competitor);
for (RaceColumn r : getRaceColumns()) {
result += getTotalPoints(competitor, r, timePoint);
}
@@ -315,7 +317,7 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
boolean discarded = isDiscarded(competitor, race, timePoint);
return new EntryImpl(trackedPoints, correctedResults.getCorrectedScore(), correctedResults.isCorrected(),
discarded ? 0
: correctedResults.getCorrectedScore() * (race.isMedalRace() ? MEDAL_RACE_FACTOR : 1),
: correctedResults.getCorrectedScore() * (race.isMedalRace() ? MEDAL_RACE_FACTOR : 1.0),
correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor));
}
@@ -348,7 +350,7 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
boolean discarded = discardedRacesForCompetitor.contains(raceColumn);
Entry entry = new EntryImpl(trackedPoints, correctedResults.getCorrectedScore(),
correctedResults.isCorrected(), discarded ? 0 : correctedResults.getCorrectedScore()
* (raceColumn.isMedalRace() ? MEDAL_RACE_FACTOR : 1), correctedResults.getMaxPointsReason(), discarded,
* (raceColumn.isMedalRace() ? MEDAL_RACE_FACTOR : 1.0), correctedResults.getMaxPointsReason(), discarded,
raceColumn.getFleetOfCompetitor(competitor));
result.put(new Pair<Competitor, RaceColumn>(competitor, raceColumn), entry);
}
@@ -357,13 +359,13 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
}
@Override
public void setCarriedPoints(Competitor competitor, int carriedPoints) {
public void setCarriedPoints(Competitor competitor, double carriedPoints) {
this.carriedPoints.put(competitor, carriedPoints);
}
@Override
public int getCarriedPoints(Competitor competitor) {
Integer result = carriedPoints.get(competitor);
public double getCarriedPoints(Competitor competitor) {
Double result = carriedPoints.get(competitor);
return result == null ? 0 : result;
}
@@ -418,11 +420,11 @@ public abstract class AbstractLeaderboardImpl implements Leaderboard, RaceColumn
*/
@Override
public List<Competitor> getCompetitorsFromBestToWorst(final RaceColumn raceColumn, TimePoint timePoint) throws NoWindException {
final Map<Competitor, Pair<Integer, Fleet>> netPointsAndFleet = new HashMap<Competitor, Pair<Integer, Fleet>>();
final Map<Competitor, Pair<Double, Fleet>> netPointsAndFleet = new HashMap<Competitor, Pair<Double, Fleet>>();
for (Competitor competitor : getCompetitors()) {
int netPoints = getNetPoints(competitor, raceColumn, timePoint);
double netPoints = getNetPoints(competitor, raceColumn, timePoint);
if (netPoints != 0) {
netPointsAndFleet.put(competitor, new Pair<Integer, Fleet>(netPoints, raceColumn.getFleetOfCompetitor(competitor)));
netPointsAndFleet.put(competitor, new Pair<Double, Fleet>(netPoints, raceColumn.getFleetOfCompetitor(competitor)));
}
}
List<Competitor> result = new ArrayList<Competitor>(netPointsAndFleet.keySet());
@@ -36,7 +36,7 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
private String name;
public FlexibleLeaderboardImpl(String name, SettableScoreCorrection scoreCorrection,
ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator<Integer> scoreComparator) {
ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator<Double> scoreComparator) {
super(scoreCorrection, resultDiscardingRule, scoreComparator);
if (name == null) {
throw new IllegalArgumentException("A leaderboard's name must not be null");
@@ -47,14 +47,14 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
*/
public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
private final Leaderboard leaderboard;
private final Comparator<Integer> scoreComparator;
private final Map<Pair<Competitor, RaceColumn>, Integer> totalPointsCache;
private final Comparator<Double> scoreComparator;
private final Map<Pair<Competitor, RaceColumn>, Double> totalPointsCache;
public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint, Comparator<Integer> scoreComparator) throws NoWindException {
public LeaderboardTotalRankComparator(Leaderboard leaderboard, TimePoint timePoint, Comparator<Double> scoreComparator) throws NoWindException {
super();
this.leaderboard = leaderboard;
this.scoreComparator = scoreComparator;
totalPointsCache = new HashMap<Pair<Competitor, RaceColumn>, Integer>();
totalPointsCache = new HashMap<Pair<Competitor, RaceColumn>, Double>();
for (Competitor competitor : leaderboard.getCompetitors()) {
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
totalPointsCache.put(new Pair<Competitor, RaceColumn>(competitor, raceColumn),
@@ -69,20 +69,20 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
@Override
public int compare(Competitor o1, Competitor o2) {
List<Integer> o1Scores = new ArrayList<Integer>();
List<Integer> o2Scores = new ArrayList<Integer>();
List<Double> o1Scores = new ArrayList<Double>();
List<Double> o2Scores = new ArrayList<Double>();
int o1ScoreSum = 0;
int o2ScoreSum = 0;
int o1MedalRaceScore = 0;
int o2MedalRaceScore = 0;
double o1MedalRaceScore = 0;
double o2MedalRaceScore = 0;
for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) {
int preemptiveColumnResult = 0;
final int o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
final double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
if (o1Score != 0) {
o1Scores.add(o1Score);
o1ScoreSum += o1Score;
}
final int o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
final double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
if (o2Score != 0) {
o2Scores.add(o2Score);
o2ScoreSum += o2Score;
@@ -123,7 +123,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
* Precondition: either both scored in medal race or both didn't. If both scored, the better score wins.
* This is to be applied only if the total score of both competitors are equal to each other.
*/
private int compareByMedalRaceScore(int o1MedalRaceScore, int o2MedalRaceScore) {
private int compareByMedalRaceScore(double o1MedalRaceScore, double o2MedalRaceScore) {
assert o1MedalRaceScore != 0 || o2MedalRaceScore == 0;
if (o1MedalRaceScore != 0) {
return getScoreComparator().compare(o1MedalRaceScore, o2MedalRaceScore);
@@ -185,7 +185,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
return result;
}
private int compareByMedalRaceParticipation(int o1Score, int o2Score) {
private int compareByMedalRaceParticipation(double o1Score, double o2Score) {
// if only one scored in medal race, this decides the order and returns immediately
if (o1Score == 0) {
if (o2Score != 0) {
@@ -207,14 +207,14 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
* @param o2Scores
* @return
*/
protected int compareByBetterScore(List<Integer> o1Scores, List<Integer> o2Scores) {
protected int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores) {
assert o1Scores.size() == o2Scores.size();
Comparator<Integer> scoreComparator = getScoreComparator();
Comparator<Double> scoreComparator = getScoreComparator();
Collections.sort(o1Scores, scoreComparator);
Collections.sort(o2Scores, scoreComparator);
// now both lists are sorted from best to worst score
Iterator<Integer> o1Iter = o1Scores.iterator();
Iterator<Integer> o2Iter = o2Scores.iterator();
Iterator<Double> o1Iter = o1Scores.iterator();
Iterator<Double> o2Iter = o2Scores.iterator();
int result = 0;
while (result == 0 && o1Iter.hasNext() && o2Iter.hasNext()) {
result = scoreComparator.compare(o1Iter.next(), o2Iter.next());
@@ -227,14 +227,14 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
* scheme where lesser scores compare "better" which again means "lesser." Therefore, the comparator retunred compares
* the integer numbers by their natural ordering.
*/
protected Comparator<Integer> getScoreComparator() {
protected Comparator<Double> getScoreComparator() {
return scoreComparator;
}
/**
* This implementation ranks a competitor better (lesser) if it has the lower score sum
*/
protected int compareByScoreSum(int o1ScoreSum, int o2ScoreSum) {
protected int compareByScoreSum(double o1ScoreSum, double o2ScoreSum) {
return getScoreComparator().compare(o1ScoreSum, o2ScoreSum);
}
@@ -9,12 +9,12 @@ import java.util.Comparator;
* @author Axel Uhl (D043530)
*
*/
public class LowerScoreIsBetter implements Comparator<Integer>, Serializable {
public class LowerScoreIsBetter implements Comparator<Double>, Serializable {
private static final long serialVersionUID = -2767385186133743330L;
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
public int compare(Double o1, Double o2) {
return Double.valueOf(o1).compareTo(Double.valueOf(o2));
}
}
@@ -29,7 +29,7 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
private final Regatta regatta;
public RegattaLeaderboardImpl(Regatta regatta, SettableScoreCorrection scoreCorrection,
ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator<Integer> scoreComparator) {
ThresholdBasedResultDiscardingRule resultDiscardingRule, Comparator<Double> scoreComparator) {
super(scoreCorrection, resultDiscardingRule, scoreComparator);
this.regatta = regatta;
regatta.addRaceColumnListener(this);
@@ -57,7 +57,8 @@ public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingR
@Override
public int compare(RaceColumn o1, RaceColumn o2) {
try {
return leaderboard.getNetPoints(competitor, o1, timePoint) - leaderboard.getNetPoints(competitor, o2, timePoint);
return Double.valueOf(leaderboard.getNetPoints(competitor, o1, timePoint)).compareTo(
Double.valueOf(leaderboard.getNetPoints(competitor, o2, timePoint)));
} catch (NoWindException e) {
throw new NoWindError(e);
}
@@ -33,11 +33,25 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
/**
* If no score correction is provided here, the uncorrected points are the default.
*/
private final Map<Pair<Competitor, RaceColumn>, Integer> correctedScores;
private final Map<Pair<Competitor, RaceColumn>, Double> correctedScores;
/**
* If <code>null</code>, despite a non-<code>null</code> {@link #timePointOfLastCorrectionsValidity} value the result have to be assumed
* to be preliminary and need to be displayed with a corresponding hint.
*/
private String comment;
/**
* Tells when the score correction was last updated. This should usually be the "validity time" and not the
* "transaction time." In other words, if scores provided by the race committee are updated to this score correction
* at time X, and the race committee's scores are tagged with time Y, then this method should return Y, not X. If
* Y is not available for some reason, X may be used as a default.
*/
private TimePoint timePointOfLastCorrectionsValidity;
public ScoreCorrectionImpl() {
this.maxPointsReasons = new HashMap<Util.Pair<Competitor,RaceColumn>, MaxPointsReason>();
this.correctedScores = new HashMap<Util.Pair<Competitor,RaceColumn>, Integer>();
this.correctedScores = new HashMap<Util.Pair<Competitor,RaceColumn>, Double>();
}
@Override
@@ -51,7 +65,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
}
@Override
public void correctScore(Competitor competitor, RaceColumn raceColumn, int points) {
public void correctScore(Competitor competitor, RaceColumn raceColumn, double points) {
correctedScores.put(raceColumn.getKey(competitor), points);
}
@@ -85,21 +99,21 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
@Override
public Result getCorrectedScore(Callable<Integer> uncorrectedScore, final Competitor competitor, final RaceColumn raceColumn,
TimePoint timePoint, int numberOfCompetitorsInLeaderboard) {
int result;
double result;
final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn);
if (maxPointsReason == MaxPointsReason.NONE) {
result = getCorrectedNonMaxedScore(competitor, raceColumn, uncorrectedScore);
} else {
// allow explicit override even when max points reason is specified; calculation may be wrong,
// e.g., in case we have an untracked race and the number of competitors is estimated incorrectly
Integer correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor));
Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor));
if (correctedNonMaxedScore == null) {
result = getMaxPoints(raceColumn.getTrackedRace(competitor), numberOfCompetitorsInLeaderboard);
} else {
result = correctedNonMaxedScore;
}
}
final int correctedScore = result;
final double correctedScore = result;
return new Result() {
@Override
public MaxPointsReason getMaxPointsReason() {
@@ -107,7 +121,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
}
@Override
public int getCorrectedScore() {
public double getCorrectedScore() {
return correctedScore;
}
@@ -123,8 +137,8 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
* reasons, computes the corrected score. If {@link #correctedScores} contains an entry for the <code>competitor</code>'s key,
* it is used. Otherwise, the <code>uncorrectedScore</code> is returned.
*/
protected int getCorrectedNonMaxedScore(Competitor competitor, RaceColumn raceColumn, Callable<Integer> uncorrectedScore) {
Integer correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor));
protected double getCorrectedNonMaxedScore(Competitor competitor, RaceColumn raceColumn, Callable<Integer> uncorrectedScore) {
Double correctedNonMaxedScore = correctedScores.get(raceColumn.getKey(competitor));
if (correctedNonMaxedScore == null) {
try {
return uncorrectedScore.call();
@@ -141,7 +155,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
}
@Override
public Integer getExplicitScoreCorrection(Competitor competitor, RaceColumn raceColumn) {
public Double getExplicitScoreCorrection(Competitor competitor, RaceColumn raceColumn) {
return correctedScores.get(raceColumn.getKey(competitor));
}
@@ -160,4 +174,24 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
return false;
}
@Override
public TimePoint getTimePointOfLastCorrectionsValidity() {
return timePointOfLastCorrectionsValidity;
}
@Override
public String getComment() {
return comment;
}
@Override
public void setTimePointOfLastCorrectionsValidity(TimePoint timePointOfLastCorrectionsValidity) {
this.timePointOfLastCorrectionsValidity = timePointOfLastCorrectionsValidity;
}
@Override
public void setComment(String scoreCorrectionComment) {
this.comment = scoreCorrectionComment;
}
}
@@ -51,7 +51,7 @@ public class TestColumnSwapping {
leaderboardCreationDate = new Date();
try {
// get Leaderboard with name and current date
leaderboardOriginalDTO = new LeaderboardDTO();
leaderboardOriginalDTO = new LeaderboardDTO(null, null);
leaderboardOriginalDTO.addRace("Race1", DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race3", DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race2", DEFAULT_FLEET, true, null, null);
@@ -101,7 +101,7 @@ public class TestColumnSwapping {
@Test
public void testLeaderBoardDTOMethods() {
lb = new LeaderboardDTO();
lb = new LeaderboardDTO(null, null);
assertNotNull("Leaderboard != NULL", lb);
lb.addRace("1", DEFAULT_FLEET, false, null, null);
lb.addRace("2", DEFAULT_FLEET, false, null, null);
@@ -162,17 +162,17 @@ public interface SailingService extends RemoteService {
Map<String, RegattaAndRaceIdentifier> getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(String leaderboardName, String raceColumnName);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Integer carriedPoints);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double carriedPoints);
/**
* @return the new net points in {@link Pair#getA()} and the new total points in {@link Pair#getB()} for time point
* <code>date</code> after the max points reason has been updated to <code>maxPointsReasonAsString</code>.
*/
Triple<Integer, Integer, Boolean> updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString,
Triple<Double, Double, Boolean> updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString,
String raceColumnName, MaxPointsReason maxPointsReason, Date date) throws NoWindException;
Triple<Integer, Integer, Boolean> updateLeaderboardScoreCorrection(String leaderboardName, String competitorIdAsString,
String columnName, Integer correctedScore, Date date) throws NoWindException;
Triple<Double, Double, Boolean> updateLeaderboardScoreCorrection(String leaderboardName, String competitorIdAsString,
String columnName, Double correctedScore, Date date) throws NoWindException;
void updateCompetitorDisplayNameInLeaderboard(String leaderboardName, String competitorIdAsString, String displayName);
@@ -252,4 +252,7 @@ public interface SailingService extends RemoteService {
void removeFregURLs(Set<String> toRemove) throws Exception;
void addFragUrl(String result) throws Exception;
Void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity,
String comment);
}
@@ -226,13 +226,16 @@ public interface SailingServiceAsync {
void disconnectLeaderboardColumnFromTrackedRace(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Integer carriedPoints, AsyncCallback<Void> callback);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double carriedPoints, AsyncCallback<Void> callback);
void updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString, String raceColumnName,
MaxPointsReason maxPointsReason, Date date, AsyncCallback<Triple<Integer, Integer, Boolean>> asyncCallback);
MaxPointsReason maxPointsReason, Date date, AsyncCallback<Triple<Double, Double, Boolean>> asyncCallback);
void updateLeaderboardScoreCorrection(String leaderboardName, String competitorIdAsString, String columnName,
Integer correctedScore, Date date, AsyncCallback<Triple<Integer, Integer, Boolean>> asyncCallback);
Double correctedScore, Date date, AsyncCallback<Triple<Double, Double, Boolean>> asyncCallback);
void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity,
String comment, AsyncCallback<Void> callback);
void updateLeaderboardScoreCorrectionsAndMaxPointsReasons(BulkScoreCorrectionDTO updates,
AsyncCallback<Void> callback);
@@ -19,6 +19,7 @@ import com.google.gwt.cell.client.TextCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
@@ -92,6 +93,8 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
private static final int SAIL_ID_COLUMN_INDEX = 1;
private static final int CARRY_COLUMN_INDEX = 3;
protected static final NumberFormat scoreFormat = NumberFormat.getFormat("0.00");
private final SailingServiceAsync sailingService;
@@ -489,15 +492,16 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
if (entry.reasonForMaxPoints == null || entry.reasonForMaxPoints == MaxPointsReason.NONE) {
if (!entry.discarded) {
html.appendHtmlConstant("<span style=\"font-weight: bold;\">");
html.appendHtmlConstant(entry.totalPoints == 0 ? "" : ""+entry.totalPoints);
html.appendHtmlConstant(entry.totalPoints == 0 ? "" : scoreFormat.format(entry.totalPoints));
html.appendHtmlConstant("</span>");
} else {
html.appendHtmlConstant(" <span style=\"opacity: 0.5;\"><del>");
html.appendHtmlConstant(entry.netPoints == 0 ? "" : ""+entry.netPoints);
html.appendHtmlConstant(entry.netPoints == 0 ? "" : scoreFormat.format(entry.netPoints));
html.appendHtmlConstant("</del></span>");
}
} else {
html.appendHtmlConstant(" <span title=\""+entry.netPoints+"/"+entry.totalPoints+"\" style=\"opacity: 0.5;\">");
html.appendHtmlConstant(" <span title=\""+scoreFormat.format(entry.netPoints)+"/"+
scoreFormat.format(entry.totalPoints)+"\" style=\"opacity: 0.5;\">");
if (entry.discarded) {
html.appendHtmlConstant("<del>");
}
@@ -562,10 +566,11 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
legColumns = new ArrayList<LegColumn>();
}
@Override
public String getValue(LeaderboardRowDTO object) {
final int totalPoints = object.fieldsByRaceColumnName.get(getRaceColumnName()).totalPoints;
return "" + (totalPoints == 0 ? "" : totalPoints);
// The following code exists only for robustness. This method should never be called because
// RaceColumn implements its own render(...) method which doesn't make use of getValue(...)
final double totalPoints = object.fieldsByRaceColumnName.get(getRaceColumnName()).totalPoints;
return "" + (totalPoints == 0 ? "" : scoreFormat.format(totalPoints));
}
@Override
@@ -835,8 +840,8 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
@Override
public String getValue(LeaderboardRowDTO object) {
int totalPoints = getLeaderboard().getTotalPoints(object);
return "" + (totalPoints==0 ? "" : totalPoints);
double totalPoints = getLeaderboard().getTotalPoints(object);
return "" + (totalPoints==0 ? "" : scoreFormat.format(totalPoints));
}
@Override
@@ -880,7 +885,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
@Override
public String getValue(LeaderboardRowDTO object) {
return object.carriedPoints == null ? "" : "" + object.carriedPoints;
return object.carriedPoints == null ? "" : scoreFormat.format(object.carriedPoints);
}
@Override
@@ -888,8 +893,15 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
return new Comparator<LeaderboardRowDTO>() {
@Override
public int compare(LeaderboardRowDTO o1, LeaderboardRowDTO o2) {
return (o1.carriedPoints == null ? 0 : o1.carriedPoints)
- (o2.carriedPoints == null ? 0 : o2.carriedPoints);
Double o1CarriedPoints = o1.carriedPoints;
if (o1CarriedPoints == null) {
o1CarriedPoints = 0.0;
}
Double o2CarriedPoints = o2.carriedPoints;
if (o2CarriedPoints == null) {
o2CarriedPoints = 0.0;
}
return o1CarriedPoints.compareTo(o2CarriedPoints);
}
};
}
@@ -63,7 +63,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
@Override
public void update(final int rowIndex, final LeaderboardRowDTO row, final String value) {
getSailingService().updateLeaderboardCarryValue(getLeaderboardName(), row.competitor.id,
value == null || value.length() == 0 ? null : Integer.valueOf(value.trim()),
value == null || value.length() == 0 ? null : Double.valueOf(value.trim()),
new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable t) {
@@ -74,7 +74,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
@Override
public void onSuccess(Void v) {
row.carriedPoints = value==null||value.length()==0 ? null : Integer.valueOf(value.trim());
row.carriedPoints = value==null||value.length()==0 ? null : Double.valueOf(value.trim());
EditableLeaderboardPanel.this.getData().getList().set(rowIndex, row);
}
});
@@ -227,7 +227,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
getBusyIndicator().setBusy(true);
getSailingService().updateLeaderboardMaxPointsReason(getLeaderboardName(), row.competitor.id,
raceColumnName, value == null || value.trim().length() == 0 ? null : MaxPointsReason.valueOf(value.trim()),
getLeaderboardDisplayDate(), new AsyncCallback<Triple<Integer, Integer, Boolean>>() {
getLeaderboardDisplayDate(), new AsyncCallback<Triple<Double, Double, Boolean>>() {
@Override
public void onFailure(Throwable t) {
getBusyIndicator().setBusy(false);
@@ -238,7 +238,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
}
@Override
public void onSuccess(Triple<Integer, Integer, Boolean> newNetAndTotalPointsAndIsCorrected) {
public void onSuccess(Triple<Double, Double, Boolean> newNetAndTotalPointsAndIsCorrected) {
getBusyIndicator().setBusy(false);
row.fieldsByRaceColumnName.get(raceColumnName).reasonForMaxPoints = value == null
|| value.length() == 0 ? null : MaxPointsReason.valueOf(value.trim());
@@ -322,8 +322,8 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
getWhiteboardOwner().whiteboardProduced(whiteboard);
getBusyIndicator().setBusy(true);
getSailingService().updateLeaderboardScoreCorrection(getLeaderboardName(), row.competitor.id, raceName,
value == null || value.trim().length() == 0 ? null : Integer.valueOf(value.trim()), getLeaderboardDisplayDate(),
new AsyncCallback<Triple<Integer, Integer, Boolean>>() {
value == null || value.trim().length() == 0 ? null : Double.valueOf(value.trim()), getLeaderboardDisplayDate(),
new AsyncCallback<Triple<Double, Double, Boolean>>() {
@Override
public void onFailure(Throwable t) {
getBusyIndicator().setBusy(false);
@@ -334,11 +334,11 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
}
@Override
public void onSuccess(Triple<Integer, Integer, Boolean> newNetAndTotalPointsAndIsCorrected) {
public void onSuccess(Triple<Double, Double, Boolean> newNetAndTotalPointsAndIsCorrected) {
getBusyIndicator().setBusy(false);
final LeaderboardEntryDTO leaderboardEntryDTO = row.fieldsByRaceColumnName.get(raceName);
leaderboardEntryDTO.netPoints = value == null || value.length() == 0 ? newNetAndTotalPointsAndIsCorrected
.getA() : Integer.valueOf(value.trim());
.getA() : Double.valueOf(value.trim());
leaderboardEntryDTO.totalPoints = newNetAndTotalPointsAndIsCorrected.getB();
leaderboardEntryDTO.netPointsCorrected = newNetAndTotalPointsAndIsCorrected.getC();
getCell().setViewData(row, null); // ensure that getValue() is called again
@@ -354,7 +354,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
LeaderboardEntryDTO leaderboardEntryDTO = object.fieldsByRaceColumnName.get(raceName);
String result = "";
if (leaderboardEntryDTO != null) {
result = result+leaderboardEntryDTO.netPoints;
result = scoreFormat.format(leaderboardEntryDTO.netPoints);
}
return result;
}
@@ -258,7 +258,7 @@ public class MatchAndApplyScoreCorrectionsDialog extends DataEntryDialog<BulkSco
if (officialCorrectionEntry.getScore() != null) {
double officialTotalPoints = officialCorrectionEntry.getScore().doubleValue();
double officialNetPoints = raceColumn.isMedalRace() ? officialTotalPoints / MEDAL_RACE_FACTOR : officialTotalPoints;
result.addScoreUpdate(competitor, raceColumn, (int) Math.round(officialNetPoints));
result.addScoreUpdate(competitor, raceColumn, officialNetPoints);
}
}
}
@@ -206,6 +206,7 @@ import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardCarryVa
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardGroup;
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardMaxPointsReason;
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrection;
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrectionMetadata;
import com.sap.sailing.server.operationaltransformation.UpdateRaceDelayToLive;
import com.sap.sailing.server.replication.ReplicaDescriptor;
import com.sap.sailing.server.replication.ReplicationFactory;
@@ -418,7 +419,9 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
LeaderboardDTO result = null;
final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
if (leaderboard != null) {
result = new LeaderboardDTO();
result = new LeaderboardDTO(leaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity()==null ?
null : leaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity().asDate(),
leaderboard.getScoreCorrection()==null?null:leaderboard.getScoreCorrection().getComment());
final TimePoint timePoint = new MillisecondsTimePoint(date);
result.competitors = new ArrayList<CompetitorDTO>();
result.name = leaderboard.getName();
@@ -1945,12 +1948,12 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
}
@Override
public void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Integer carriedPoints) {
public void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double carriedPoints) {
getService().apply(new UpdateLeaderboardCarryValue(leaderboardName, competitorIdAsString, carriedPoints));
}
@Override
public Triple<Integer, Integer, Boolean> updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString, String raceColumnName,
public Triple<Double, Double, Boolean> updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString, String raceColumnName,
MaxPointsReason maxPointsReason, Date date) throws NoWindException {
return getService().apply(
new UpdateLeaderboardMaxPointsReason(leaderboardName, raceColumnName, competitorIdAsString,
@@ -1958,18 +1961,24 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
}
@Override
public Triple<Integer, Integer, Boolean> updateLeaderboardScoreCorrection(String leaderboardName,
String competitorIdAsString, String columnName, Integer correctedScore, Date date) throws NoWindException {
public Triple<Double, Double, Boolean> updateLeaderboardScoreCorrection(String leaderboardName,
String competitorIdAsString, String columnName, Double correctedScore, Date date) throws NoWindException {
return getService().apply(
new UpdateLeaderboardScoreCorrection(leaderboardName, columnName, competitorIdAsString, correctedScore,
new MillisecondsTimePoint(date)));
}
@Override
public Void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity, String comment) {
return getService().apply(
new UpdateLeaderboardScoreCorrectionMetadata(leaderboardName, new MillisecondsTimePoint(timePointOfLastCorrectionValidity), comment));
}
@Override
public void updateLeaderboardScoreCorrectionsAndMaxPointsReasons(BulkScoreCorrectionDTO updates) throws NoWindException {
Date dateForResults = new Date(); // we don't care about the result date/time here; use current date as default
for (Map.Entry<String, Map<String, Integer>> e : updates.getScoreUpdatesForRaceColumnByCompetitorIdAsString().entrySet()) {
for (Map.Entry<String, Integer> raceColumnNameAndCorrectedScore : e.getValue().entrySet()) {
for (Map.Entry<String, Map<String, Double>> e : updates.getScoreUpdatesForRaceColumnByCompetitorIdAsString().entrySet()) {
for (Map.Entry<String, Double> raceColumnNameAndCorrectedScore : e.getValue().entrySet()) {
updateLeaderboardScoreCorrection(updates.getLeaderboardName(), e.getKey(),
raceColumnNameAndCorrectedScore.getKey(), raceColumnNameAndCorrectedScore.getValue(), dateForResults);
}
@@ -69,16 +69,16 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
return false;
}
public int getTotalPoints(LeaderboardRowDTO object) {
int totalPoints = object.carriedPoints == null ? 0 : object.carriedPoints;
public double getTotalPoints(LeaderboardRowDTO object) {
double totalPoints = object.carriedPoints == null ? 0 : object.carriedPoints;
for (LeaderboardEntryDTO e : object.fieldsByRaceColumnName.values()) {
totalPoints += e.totalPoints;
}
return totalPoints;
}
public int getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
int result = 0;
public double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
double result = 0;
LeaderboardRowDTO row = rows.get(competitor);
if (row != null) {
LeaderboardEntryDTO field = row.fieldsByRaceColumnName.get(nameOfLastRaceSoFar);
@@ -16,20 +16,20 @@ import com.sap.sailing.domain.common.MaxPointsReason;
public class BulkScoreCorrectionDTO implements IsSerializable {
private String leaderboardName;
private Map<String, Map<String, MaxPointsReason>> maxPointsUpdatesForRaceColumnByCompetitorIdAsString;
private Map<String, Map<String, Integer>> scoreUpdatesForRaceColumnByCompetitorIdAsString;
private Map<String, Map<String, Double>> scoreUpdatesForRaceColumnByCompetitorIdAsString;
public BulkScoreCorrectionDTO() {}
public BulkScoreCorrectionDTO(String leaderboardName) {
this.leaderboardName = leaderboardName;
maxPointsUpdatesForRaceColumnByCompetitorIdAsString = new HashMap<String, Map<String,MaxPointsReason>>();
scoreUpdatesForRaceColumnByCompetitorIdAsString = new HashMap<String, Map<String,Integer>>();
scoreUpdatesForRaceColumnByCompetitorIdAsString = new HashMap<String, Map<String, Double>>();
}
public void addScoreUpdate(CompetitorDTO competitor, RaceColumnDTO raceColumn, int newScore) {
Map<String, Integer> map = scoreUpdatesForRaceColumnByCompetitorIdAsString.get(competitor.id);
public void addScoreUpdate(CompetitorDTO competitor, RaceColumnDTO raceColumn, double newScore) {
Map<String, Double> map = scoreUpdatesForRaceColumnByCompetitorIdAsString.get(competitor.id);
if (map == null) {
map = new HashMap<String, Integer>();
map = new HashMap<String, Double>();
scoreUpdatesForRaceColumnByCompetitorIdAsString.put(competitor.id, map);
}
map.put(raceColumn.name, newScore);
@@ -48,7 +48,7 @@ public class BulkScoreCorrectionDTO implements IsSerializable {
return maxPointsUpdatesForRaceColumnByCompetitorIdAsString;
}
public Map<String, Map<String, Integer>> getScoreUpdatesForRaceColumnByCompetitorIdAsString() {
public Map<String, Map<String, Double>> getScoreUpdatesForRaceColumnByCompetitorIdAsString() {
return scoreUpdatesForRaceColumnByCompetitorIdAsString;
}
@@ -1,5 +1,6 @@
package com.sap.sailing.gwt.ui.shared;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -21,8 +22,16 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements IsSerializ
public List<CompetitorDTO> competitors;
private Map<RaceColumnDTO, List<CompetitorDTO>> competitorOrderingPerRace;
private Date timePointOfLastCorrectionsValidity;
private String comment;
LeaderboardDTO() {} // for serialization
public LeaderboardDTO() {
public LeaderboardDTO(Date timePointOfLastCorrectionsValidity, String comment) {
this.timePointOfLastCorrectionsValidity = timePointOfLastCorrectionsValidity;
this.comment = comment;
competitorOrderingPerRace = new HashMap<RaceColumnDTO, List<CompetitorDTO>>();
}
@@ -38,6 +47,25 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements IsSerializ
return competitors.indexOf(competitor) + 1;
};
/**
* A free-form comment to display to the viewers of the leaderboard that has these score corrections. It should make
* crystal clear if the scores are preliminary or not yet jury-finalized. If <code>null</code> is returned, this
* always has to be interpreted as "preliminary" because then no comment as to the correctness have been made.
*/
public String getComment() {
return comment;
}
/**
* Tells when the score correction was last updated. This should usually be the "validity time" and not the
* "transaction time." In other words, if scores provided by the race committee are updated to this score correction
* at time X, and the race committee's scores are tagged with time Y, then this method should return Y, not X. If
* Y is not available for some reason, X may be used as a default.
*/
public Date getTimePointOfLastCorrectionsValidity() {
return timePointOfLastCorrectionsValidity;
}
@Override
public int hashCode() {
return super.hashCode() ^ ((competitors == null) ? 0 : competitors.hashCode());
@@ -26,14 +26,14 @@ public class LeaderboardEntryDTO implements IsSerializable {
*/
public MaxPointsReason reasonForMaxPoints;
public int netPoints;
public double netPoints;
/**
* Tells if the net points have been overridden by a score correction. Can be used to render differently in editing environment.
*/
public boolean netPointsCorrected;
public int totalPoints;
public double totalPoints;
public boolean discarded;
@@ -63,11 +63,11 @@ public class LeaderboardEntryDTO implements IsSerializable {
int result = 1;
result = prime * result + (discarded ? 1231 : 1237);
result = prime * result + ((legDetails == null) ? 0 : legDetails.hashCode());
result = prime * result + netPoints;
result = prime * result + (int) netPoints;
result = prime * result + ((averageCrossTrackErrorInMeters == null) ? 0 : averageCrossTrackErrorInMeters.hashCode());
result = prime * result + ((reasonForMaxPoints == null) ? 0 : reasonForMaxPoints.hashCode());
result = prime * result + ((windwardDistanceToOverallLeaderInMeters == null) ? 0 : windwardDistanceToOverallLeaderInMeters.hashCode());
result = prime * result + totalPoints;
result = prime * result + (int) totalPoints;
return result;
}
@@ -13,7 +13,7 @@ import com.google.gwt.user.client.rpc.IsSerializable;
public class LeaderboardRowDTO implements IsSerializable {
public CompetitorDTO competitor;
public Map<String, LeaderboardEntryDTO> fieldsByRaceColumnName;
public Integer carriedPoints;
public Double carriedPoints;
@Override
public int hashCode() {
@@ -14,6 +14,7 @@ import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.base.impl.NationalityImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
@@ -21,6 +22,7 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowerScoreIsBetter;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
@@ -31,6 +33,22 @@ import com.sap.sailing.domain.test.MockedTrackedRaceWithFixedRank;
import com.sap.sailing.domain.tracking.TrackedRace;
public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
@Test
public void testStoreAndRetrieveLeaderboardWithCommentedScoreCorrection() {
final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, scoreCorrection,
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter());
scoreCorrection.setComment("Humba");
MillisecondsTimePoint now = MillisecondsTimePoint.now();
scoreCorrection.setTimePointOfLastCorrectionsValidity(now);
new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard);
Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName, /* regattaRegistry */ null);
assertEquals("Humba", loadedLeaderboard.getScoreCorrection().getComment());
assertEquals(now, loadedLeaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity());
}
@Test
public void testStoreAndRetrieveSimpleLeaderboard() {
final String leaderboardName = "TestLeaderboard";
@@ -48,7 +66,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
public void testStoreAndRetrieveLeaderboardWithCarryColumn() {
final String leaderboardName = "TestLeaderboard";
final String raceColumnName = "My First Race";
final int carriedPointsForWolfgangHunger = 3;
final double carriedPointsForWolfgangHunger = 3.7;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter());
@@ -65,7 +83,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
.getDiscardIndexResultsStartingWithHowManyRaces()));
assertEquals(1, Util.size(loadedLeaderboard.getCompetitors()));
assertEquals(competitor, loadedLeaderboard.getCompetitors().iterator().next());
assertEquals(carriedPointsForWolfgangHunger, loadedLeaderboard.getCarriedPoints(competitor));
assertEquals(carriedPointsForWolfgangHunger, loadedLeaderboard.getCarriedPoints(competitor), 0.000000001);
}
@Test
@@ -73,7 +91,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard";
final String raceColumnName1 = "My First Race 1";
final String raceColumnName2 = "My First Race 2";
final int correctedPoints = 2;
final double correctedPoints = 2.75;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowerScoreIsBetter());
@@ -90,7 +108,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
RaceColumn loadedColumn2 = loadedLeaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace, ignored */ false, leaderboard.getFleet(null));
assertEquals(leaderboardName, loadedLeaderboard.getName());
assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn1));
assertEquals(correctedPoints, (int) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1));
assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1), 0.00000001);
assertFalse(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn2));
}
@@ -125,7 +143,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard";
final String raceColumnName1 = "My First Race 1";
final String raceColumnName2 = "My First Race 2";
final int correctedPoints = 2;
final double correctedPoints = 2.55;
final MaxPointsReason maxPointsReason = MaxPointsReason.DNF;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
@@ -144,7 +162,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
RaceColumn loadedColumn2 = loadedLeaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace, ignored */ false, leaderboard.getFleet(null));
assertEquals(leaderboardName, loadedLeaderboard.getName());
assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn1));
assertEquals(correctedPoints, (int) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1));
assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1), 0.000000001);
assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn2));
assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitor, loadedColumn2));
}
@@ -8,9 +8,9 @@ import com.sap.sailing.server.RacingEventServiceOperation;
public class UpdateLeaderboardCarryValue extends AbstractLeaderboardOperation<Void> {
private static final long serialVersionUID = 5467731814788887426L;
private final String competitorIdAsString;
private final Integer newCarriedValue;
private final Double newCarriedValue;
public UpdateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Integer newCarriedValue) {
public UpdateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double newCarriedValue) {
super(leaderboardName);
this.competitorIdAsString = competitorIdAsString;
this.newCarriedValue = newCarriedValue;
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard.Entry;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
public class UpdateLeaderboardMaxPointsReason extends AbstractLeaderboardColumnOperation<Triple<Integer, Integer, Boolean>> {
public class UpdateLeaderboardMaxPointsReason extends AbstractLeaderboardColumnOperation<Triple<Double, Double, Boolean>> {
private static final long serialVersionUID = -492130952256848047L;
private final String competitorIdAsString;
private final MaxPointsReason newMaxPointsReason;
@@ -38,7 +38,7 @@ public class UpdateLeaderboardMaxPointsReason extends AbstractLeaderboardColumnO
}
@Override
public Triple<Integer, Integer, Boolean> internalApplyTo(RacingEventService toState) throws NoWindException {
public Triple<Double, Double, Boolean> internalApplyTo(RacingEventService toState) throws NoWindException {
Leaderboard leaderboard = toState.getLeaderboardByName(getLeaderboardName());
if (leaderboard != null) {
Competitor competitor = leaderboard.getCompetitorByIdAsString(competitorIdAsString);
@@ -51,7 +51,7 @@ public class UpdateLeaderboardMaxPointsReason extends AbstractLeaderboardColumnO
updateStoredLeaderboard(toState, leaderboard);
Entry updatedEntry = leaderboard.getEntry(competitor, raceColumn, timePoint);
boolean isScoreCorrected = leaderboard.getScoreCorrection().isScoreCorrected(competitor, raceColumn);
return new Triple<Integer, Integer, Boolean>(updatedEntry.getNetPoints(), updatedEntry.getTotalPoints(), isScoreCorrected);
return new Triple<Double, Double, Boolean>(updatedEntry.getNetPoints(), updatedEntry.getTotalPoints(), isScoreCorrected);
} else {
throw new IllegalArgumentException("Didn't find competitor with ID "+competitorIdAsString+" in leaderboard "+getLeaderboardName());
}
@@ -9,17 +9,17 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
public class UpdateLeaderboardScoreCorrection extends AbstractLeaderboardColumnOperation<Triple<Integer, Integer, Boolean>> {
public class UpdateLeaderboardScoreCorrection extends AbstractLeaderboardColumnOperation<Triple<Double, Double, Boolean>> {
private static final long serialVersionUID = -977025759476022993L;
private final String competitorIdAsString;
private final Integer correctedScore;
private final Double correctedScore;
private final TimePoint timePoint;
/**
* @param timePoint the time point for which to deliver leaderboard results as the result of this operation
*/
public UpdateLeaderboardScoreCorrection(String leaderboardName, String columnName, String competitorIdAsString,
Integer correctedScore, TimePoint timePoint) {
Double correctedScore, TimePoint timePoint) {
super(leaderboardName, columnName);
this.competitorIdAsString = competitorIdAsString;
this.correctedScore = correctedScore;
@@ -39,10 +39,10 @@ public class UpdateLeaderboardScoreCorrection extends AbstractLeaderboardColumnO
}
@Override
public Triple<Integer, Integer, Boolean> internalApplyTo(RacingEventService toState) throws NoWindException {
public Triple<Double, Double, Boolean> internalApplyTo(RacingEventService toState) throws NoWindException {
Leaderboard leaderboard = toState.getLeaderboardByName(getLeaderboardName());
int newNetPoints;
int newTotalPoints;
double newNetPoints;
double newTotalPoints;
boolean isScoreCorrected;
if (leaderboard != null) {
Competitor competitor = leaderboard.getCompetitorByIdAsString(competitorIdAsString);
@@ -64,7 +64,7 @@ public class UpdateLeaderboardScoreCorrection extends AbstractLeaderboardColumnO
throw new IllegalArgumentException("Didn't find leaderboard "+getLeaderboardName());
}
updateStoredLeaderboard(toState, leaderboard);
return new Triple<Integer, Integer, Boolean>(newNetPoints, newTotalPoints, isScoreCorrected);
return new Triple<Double, Double, Boolean>(newNetPoints, newTotalPoints, isScoreCorrected);
}
}
@@ -0,0 +1,55 @@
package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
public class UpdateLeaderboardScoreCorrectionMetadata extends AbstractLeaderboardOperation<Void> {
private static final long serialVersionUID = -977025759476022993L;
private final TimePoint timePointOfLastCorrectionValidity;
private final String comment;
/**
* @param timePoint the time point for which to deliver leaderboard results as the result of this operation
*/
public UpdateLeaderboardScoreCorrectionMetadata(String leaderboardName, TimePoint timePointOfLastCorrectionValidity, String comment) {
super(leaderboardName);
this.timePointOfLastCorrectionValidity = timePointOfLastCorrectionValidity;
this.comment = comment;
}
@Override
public RacingEventServiceOperation<?> transformClientOp(RacingEventServiceOperation<?> serverOp) {
// TODO Auto-generated method stub
return null;
}
@Override
public RacingEventServiceOperation<?> transformServerOp(RacingEventServiceOperation<?> clientOp) {
// TODO Auto-generated method stub
return null;
}
@Override
public Void internalApplyTo(RacingEventService toState) throws NoWindException {
Leaderboard leaderboard = toState.getLeaderboardByName(getLeaderboardName());
if (leaderboard != null) {
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if (scoreCorrection != null) {
scoreCorrection.setComment(comment);
scoreCorrection.setTimePointOfLastCorrectionsValidity(timePointOfLastCorrectionValidity);
} else {
throw new IllegalArgumentException("Leaderboard "+getLeaderboardName()+" has no score corrections to which to apply metadata "+
timePointOfLastCorrectionValidity+"/"+comment);
}
} else {
throw new IllegalArgumentException("Didn't find leaderboard "+getLeaderboardName());
}
updateStoredLeaderboard(toState, leaderboard);
return null;
}
}