diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
index 711c93b3b06..c92e76930c8 100755
--- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
+++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java
@@ -302,9 +302,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
private void storeScoreCorrections(Leaderboard leaderboard, BasicDBObject dbScoreCorrections) {
TimePoint now = MillisecondsTimePoint.now();
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
- for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
+ for (RaceColumn raceColumn : scoreCorrection.getRaceColumnsThatHaveCorrections()) {
BasicDBList dbCorrectionForRace = new BasicDBList();
- for (Competitor competitor : leaderboard.getCompetitors()) {
+ for (Competitor competitor : scoreCorrection.getCompetitorsThatHaveCorrectionsIn(raceColumn)) {
// TODO bug 655: make score corrections time dependent
if (scoreCorrection.isScoreCorrected(competitor, raceColumn, now)) {
BasicDBObject dbCorrectionForCompetitor = new BasicDBObject();
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java
index 38c8ef1d754..d2267ca8781 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java
@@ -96,6 +96,24 @@ public interface ScoreCorrection extends Serializable {
* the race column specified by raceInLeaderboard.
*/
boolean hasCorrectionFor(RaceColumn raceInLeaderboard);
+
+ /**
+ * @return all race columns for which this score corrections object has at least one correction; note that this
+ * object may hold corrections also for competitors that are not currently returned by
+ * {@link Leaderboard#getCompetitors()}, e.g., because one or more tracked races are not currently attached
+ * to the leaderboard. It is still useful to retain these corrections as the race(s) may re-appear later on.
+ * In particular, those corrections should be stored persistently.
+ */
+ Iterable getRaceColumnsThatHaveCorrections();
+
+ /**
+ * @return all competitors for which this score corrections object has at least one correction in column
+ * raceColumn; note that this object may hold corrections also for competitors that are not
+ * currently returned by {@link Leaderboard#getCompetitors()}, e.g., because one or more tracked races are
+ * not currently attached to the leaderboard. It is still useful to retain these corrections as the race(s)
+ * may re-appear later on. In particular, those corrections should be stored persistently.
+ */
+ Iterable getCompetitorsThatHaveCorrectionsIn(RaceColumn raceColumn);
/**
* Tells when the score correction was last updated. This should usually be the "validity time" and not the
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/SettableScoreCorrection.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/SettableScoreCorrection.java
index 732bf75467d..171f26b02a5 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/SettableScoreCorrection.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/SettableScoreCorrection.java
@@ -40,4 +40,5 @@ public interface SettableScoreCorrection extends ScoreCorrection {
void notifyListenersAboutCarriedPointsChange(Competitor competitor, Double oldCarriedPoints, Double newCarriedPoints);
void notifyListenersAboutIsSuppressedChange(Competitor competitor, boolean suppressed);
+
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java
index 5d8aadb4a91..8835dc6c950 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java
@@ -473,4 +473,32 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
return leaderboard;
}
+ @Override
+ public Iterable getRaceColumnsThatHaveCorrections() {
+ Set result = new HashSet<>();
+ for (Pair correctedScoresKey : correctedScores.keySet()) {
+ result.add(correctedScoresKey.getB());
+ }
+ for (Pair maxPointsReasonsKey : maxPointsReasons.keySet()) {
+ result.add(maxPointsReasonsKey.getB());
+ }
+ return result;
+ }
+
+ @Override
+ public Iterable getCompetitorsThatHaveCorrectionsIn(RaceColumn raceColumn) {
+ Set result = new HashSet<>();
+ for (Pair correctedScoresKey : correctedScores.keySet()) {
+ if (raceColumn == correctedScoresKey.getB()) {
+ result.add(correctedScoresKey.getA());
+ }
+ }
+ for (Pair maxPointsReasonsKey : maxPointsReasons.keySet()) {
+ if (raceColumn == maxPointsReasonsKey.getB()) {
+ result.add(maxPointsReasonsKey.getA());
+ }
+ }
+ return result;
+ }
+
}
diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java
index ccd170120b0..4c49e56a79a 100755
--- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java
+++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java
@@ -290,6 +290,51 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitor, loadedColumn2, MillisecondsTimePoint.now()));
}
+ /**
+ * See bug 1707; the problem that this test case is trying to reproduce occurs when unlinking the last race from a leaderboard
+ * that was contributing one or more competitors that have a score correction. At commit 31074ccf2d3e2440d6a6f680504f150c264992ff
+ * the leaderboard is updated in the DB when a tracked race is removed (not explicitly unlinked). This is problematic for two reasons.
+ * First, the inverse operation (re-loading the race and automatically linking it again) does not lead to an update; second,
+ * the leaderboard loses all its score corrections in the DB because the score corrections, although still present in the
+ * ScoreCorrectionsImpl object, are not written to the DB because the leaderboard's competitor list is computed from its
+ * attached tracked races and therefore now is empty.
+ */
+ @Test
+ public void testRemovingAndReAddingLastRaceWithScoreCorrections() {
+ final String leaderboardName = "TestLeaderboard";
+ final String raceColumnName1 = "My First Race 1";
+ final String raceColumnName2 = "My First Race 2";
+ final double correctedPoints = 2.55;
+ final MaxPointsReason maxPointsReason = MaxPointsReason.DNF;
+ final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
+ FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces),
+ new LowPoint(), null);
+ Competitor competitor = createCompetitor();
+ TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
+ TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
+ RaceColumn r1 = leaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace */ false,
+ leaderboard.getFleet(null));
+ RaceColumn r2 = leaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace */ true,
+ leaderboard.getFleet(null));
+ leaderboard.getScoreCorrection().correctScore(competitor, leaderboard.getRaceColumnByName(raceColumnName1), correctedPoints);
+ leaderboard.getScoreCorrection().setMaxPointsReason(competitor, leaderboard.getRaceColumnByName(raceColumnName2), maxPointsReason);
+ // now release the tracked races; leaving no competitors in leaderboard.getCompetitors()
+ r1.releaseTrackedRace(leaderboard.getFleet(null));
+ r2.releaseTrackedRace(leaderboard.getFleet(null));
+ new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard);
+ FlexibleLeaderboard loadedLeaderboard = (FlexibleLeaderboard) new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard(leaderboardName, /* regattaRegistry */ null);
+ // attach tracked race to leaderboard to ensure that competitor object is assigned properly
+ RaceColumn loadedColumn1 = loadedLeaderboard.addRace(raceWithOneCompetitor1, raceColumnName1, /* medalRace, ignored */ false,
+ leaderboard.getFleet(null));
+ RaceColumn loadedColumn2 = loadedLeaderboard.addRace(raceWithOneCompetitor2, raceColumnName2, /* medalRace, ignored */ false,
+ leaderboard.getFleet(null));
+ assertEquals(leaderboardName, loadedLeaderboard.getName());
+ assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn1, MillisecondsTimePoint.now()));
+ assertEquals(correctedPoints, (double) loadedLeaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, loadedColumn1), 0.000000001);
+ assertTrue(loadedLeaderboard.getScoreCorrection().isScoreCorrected(competitor, loadedColumn2, MillisecondsTimePoint.now()));
+ assertEquals(maxPointsReason, loadedLeaderboard.getScoreCorrection().getMaxPointsReason(competitor, loadedColumn2, MillisecondsTimePoint.now()));
+ }
+
private Competitor createCompetitor() {
Competitor competitor = new CompetitorImpl(123, "$$$Dr. Wolfgang+Hunger$$$", Color.RED, new TeamImpl("STG", Collections.singleton(
new PersonImpl("$$$Dr. Wolfgang+Hunger$$$", new NationalityImpl("GER"),