From 53ef4ea85c5d227d3e82dcf819f7dfb656f5a515 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 20 Jan 2014 22:04:01 +0100 Subject: [PATCH] fix for bug1707: storing score corrections also for competitors not currently attached to leaderboard --- .../impl/MongoObjectFactoryImpl.java | 4 +- .../domain/leaderboard/ScoreCorrection.java | 18 ++++++++ .../leaderboard/SettableScoreCorrection.java | 1 + .../leaderboard/impl/ScoreCorrectionImpl.java | 28 ++++++++++++ .../TestStoringAndRetrievingLeaderboards.java | 45 +++++++++++++++++++ 5 files changed, 94 insertions(+), 2 deletions(-) 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"),