bug5217: represent waypoints in DB as index into waypoint list of course

This commit is contained in:
Axel Uhl
2022-10-12 20:42:24 +02:00
parent e1da94e399
commit 316191f684
8 changed files with 38 additions and 25 deletions
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.anniversary.DetailedRaceInfo;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
@@ -232,6 +233,7 @@ public interface DomainObjectFactory {
/**
* For races that have a {@link MarkPassingRaceFingerprint} stored in the database (see {@link #loadFingerprintsForMarkPassingHashes()})
* a caller can load the corresponding mark passings with this method.
* @param course TODO
*/
Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier);
Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier, Course course);
}
@@ -15,6 +15,7 @@ import com.mongodb.client.MongoDatabase;
import com.sap.sailing.domain.anniversary.DetailedRaceInfo;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
@@ -241,6 +242,7 @@ public interface MongoObjectFactory {
* identifies the race to which the mark passings belong
* @param fingerprint
* a composite fingerprint of the race in the state at which the mark passings were captured
* @param course TODO
*/
void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings);
void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings, Course course);
}
@@ -158,6 +158,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.DomainFactory;
@@ -3206,7 +3207,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
@Override
public Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier) {
public Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier, Course course) {
final Map<Competitor, Map<Waypoint, MarkPassing>> result;
final Document query = new Document();
addRaceIdentifierToQuery(query, raceIdentifier);
@@ -3219,7 +3220,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final Serializable competitorId = markPassingsForOneCompetitorDoc.get(FieldNames.COMPETITOR_ID.name(), Serializable.class);
final Competitor competitor = baseDomainFactory.getExistingCompetitorById(competitorId);
for (final Document markPassingForWaypoint : markPassingsForOneCompetitorDoc.getList(FieldNames.MARK_PASSINGS.name(), Document.class)) {
final Pair<Waypoint, MarkPassing> waypointAndMarkPassing = loadWaypointAndMarkPassing(competitor, markPassingForWaypoint);
final Pair<Waypoint, MarkPassing> waypointAndMarkPassing = loadWaypointAndMarkPassing(competitor, markPassingForWaypoint, course);
result.computeIfAbsent(competitor, c->new HashMap<>()).put(waypointAndMarkPassing.getA(), waypointAndMarkPassing.getB());
}
}
@@ -3229,9 +3230,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
return result;
}
private Pair<Waypoint, MarkPassing> loadWaypointAndMarkPassing(Competitor competitor, Document markPassingForWaypoint) {
final Serializable waypointId = markPassingForWaypoint.get(FieldNames.WAYPOINT_ID.name(), Serializable.class);
final Waypoint waypoint = baseDomainFactory.getExistingWaypointById(waypointId);
private Pair<Waypoint, MarkPassing> loadWaypointAndMarkPassing(Competitor competitor, Document markPassingForWaypoint, Course course) {
final int waypointIndex = markPassingForWaypoint.getInteger(FieldNames.INDEX_OF_PASSED_WAYPOINT.name());
final Waypoint waypoint = Util.get(course.getWaypoints(), waypointIndex);
final TimePoint timePoint = TimePoint.of(markPassingForWaypoint.getLong(FieldNames.TIME_AS_MILLIS.name()));
final MarkPassing markPassing = new MarkPassingImpl(timePoint, waypoint, competitor);
return new Pair<>(waypoint, markPassing);
@@ -91,6 +91,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.ControlPointWithTwoMarks;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.Event;
@@ -1967,7 +1968,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
return storeRegattaLogEvent(regattaLikeIdentifier, document);
}
private List<Document> storeMarkPassings(Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
private List<Document> storeMarkPassings(Map<Competitor, Map<Waypoint, MarkPassing>> markPassings, Course course) {
final List<Document> result = new ArrayList<>();
for (final Entry<Competitor, Map<Waypoint, MarkPassing>> e : markPassings.entrySet()) {
final Document competitorMarkPassings = new Document();
@@ -1975,7 +1976,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
final List<Document> markPassingsList = new ArrayList<>();
for (final Entry<Waypoint, MarkPassing> f : e.getValue().entrySet()) {
final Document markPassingDoc = new Document();
markPassingDoc.put(FieldNames.WAYPOINT_ID.name(), f.getKey().getId());
markPassingDoc.put(FieldNames.INDEX_OF_PASSED_WAYPOINT.name(), course.getIndexOfWaypoint(f.getKey()));
markPassingDoc.put(FieldNames.TIME_AS_MILLIS.name(), f.getValue().getTimePoint().asMillis());
markPassingsList.add(markPassingDoc);
}
@@ -1985,7 +1986,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
return result;
}
public void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
@Override
public void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint,
Map<Competitor, Map<Waypoint, MarkPassing>> markPassings, Course course) {
MongoCollection<Document> markPassingCollection = database.getCollection(CollectionNames.MARKPASSINGS.name());
JSONObject fingerprintjson = fingerprint.toJson();
final Document query = new Document();
@@ -1994,7 +1997,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
final Document fingerprintDoc = Document.parse(fingerprintjson.toString());
result.put(FieldNames.MARK_PASSINGS_FINGERPRINT.name(), fingerprintDoc);
storeRaceIdentifier(result, raceIdentifier);
final List<Document> markPassingsDoc = storeMarkPassings(markPassings);
final List<Document> markPassingsDoc = storeMarkPassings(markPassings, course);
result.put(FieldNames.MARK_PASSINGS.name(), markPassingsDoc);
markPassingCollection.replaceOne(query, result, new ReplaceOptions().upsert(true));
}
@@ -15,6 +15,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -115,7 +116,7 @@ public class MarkPassingCalculator {
* If not {@code null} then upon {@link #resume()} this mark passing calculator will look for a stored set of mark
* passings for the {@link #race} using this registry. When such a set is found, the race's fingerprint is tried to
* match against the {@link #race}. If it's a match then the mark passings are
* {@link MarkPassingRaceFingerprintRegistry#loadMarkPassings(com.sap.sailing.domain.common.RaceIdentifier) loaded}
* {@link MarkPassingRaceFingerprintRegistry#loadMarkPassings(com.sap.sailing.domain.common.RaceIdentifier, Course) loaded}
* from the registry and all update events queued in the {@link #queue} are cleared. Otherwise, or if this is
* {@code null}, the mark passings are calculated normally.
*/
@@ -594,7 +595,7 @@ public class MarkPassingCalculator {
final Map<Competitor, Map<Waypoint, MarkPassing>> markPassings = race.getMarkPassings(/* waitForLatestUpdates */ true);
markPassingRaceFingerprintRegistry.storeMarkPassings(race.getRaceIdentifier(),
MarkPassingRaceFingerprintFactory.INSTANCE.createFingerprint(race),
markPassings);
markPassings, race.getRace().getCourse());
}, "Waiting for mark passings for "+race.getName()+" after having resumed to store the results in registry")
.start();
}
@@ -603,8 +604,9 @@ public class MarkPassingCalculator {
}
private void updateMarkPassingsFromRegistry() {
for (final Entry<Competitor, Map<Waypoint, MarkPassing>> e : markPassingRaceFingerprintRegistry.loadMarkPassings(race.getRaceIdentifier()).entrySet()) {
race.updateMarkPassings(e.getKey(), e.getValue().values().stream().sorted(TimedComparator.INSTANCE)::iterator);
for (final Entry<Competitor, Map<Waypoint, MarkPassing>> e : markPassingRaceFingerprintRegistry.loadMarkPassings(
race.getRaceIdentifier(), race.getRace().getCourse()).entrySet()) {
race.updateMarkPassings(e.getKey(), e.getValue().values().stream().sorted(TimedComparator.INSTANCE).collect(Collectors.toList()));
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.markpassinghash;
import java.util.Map;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
@@ -18,20 +19,20 @@ import com.sap.sailing.domain.tracking.TrackedRace;
* that identifier; the result is a {@link MarkPassingRaceFingerprint} which can may or may not be
* {@link MarkPassingRaceFingerprint#matches(com.sap.sailing.domain.tracking.TrackedRace) matched} by a
* {@link TrackedRace}</li>
* <li>If a matching fingerprint is stored in this registry for a race then {@link #loadMarkPassings(RaceIdentifier)
* <li>If a matching fingerprint is stored in this registry for a race then {@link #loadMarkPassings(RaceIdentifier, Course)
* loading the mark passings} for that race and {@link DynamicTrackedRace#updateMarkPassings(Competitor, Iterable)
* updating it to the tracked race} is the logical next step.</li>
* <li>When a tracked race has reached a "stable" state, e.g., because it's {@link TrackedRace#getEndOfTracking()
* end-of-tracking} time point has passed or its {@link TrackedRace#getStatus() status} has changed to
* {@link TrackedRaceStatusEnum#FINISHED} then the latest mark passing calculation results can be
* {@link #storeMarkPassings(RaceIdentifier, MarkPassingRaceFingerprint, Map) stored} together with the fingerprint
* {@link #storeMarkPassings(RaceIdentifier, MarkPassingRaceFingerprint, Map, Course) stored} together with the fingerprint
* computed from the tracked race.</li>
* </ul>
*
* @author Fabian Kallenbach (i550803)
*/
public interface MarkPassingRaceFingerprintRegistry {
void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings);
void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint, Map<Competitor, Map<Waypoint, MarkPassing>> markPassings, Course course);
/**
* Looks for a fingerprint for which mark passings have been stored for the race identified by
@@ -48,6 +49,7 @@ public interface MarkPassingRaceFingerprintRegistry {
* {@code (}{@link #getMarkPassingRaceFingerprint(RaceIdentifier) getMarkPassingRaceFingerprint(raceIdentifier)}{@code )}<p>
*
* If a mark passing race fingerprint is known for {@code raceIdentifier} in this registry and the
* @param course TODO
*/
Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier);
Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier, Course course);
}
@@ -67,12 +67,12 @@ public class MarkPassingRaceFingerprintConversionTest extends OnlineTracTracBase
MongoDatabase firstDatabase = myFirstMongo.getDatabase(dbConfiguration.getDatabaseName());
RaceIdentifier raceIdentifier = trackedRace1.getRaceIdentifier();
final Map<Competitor, Map<Waypoint, MarkPassing>> markPassings = trackedRace1.getMarkPassings(/* waitForLatestUpdates */ true);
new MongoObjectFactoryImpl(firstDatabase).storeMarkPassings(raceIdentifier, fingerprint, markPassings);
new MongoObjectFactoryImpl(firstDatabase).storeMarkPassings(raceIdentifier, fingerprint, markPassings, trackedRace1.getRace().getCourse());
DomainObjectFactory dF = PersistenceFactory.INSTANCE.getDomainObjectFactory(dbConfiguration.getService(), getDomainFactory().getBaseDomainFactory());
Map<RaceIdentifier, MarkPassingRaceFingerprint> fingerprintHashMap = dF.loadFingerprintsForMarkPassingHashes();
MarkPassingRaceFingerprint fingerprintAfterDB = fingerprintHashMap.get(trackedRace1.getRaceIdentifier());
assertTrue("Original and de-serialized copy are equal", fingerprintAfterDB.matches(trackedRace1));
final Map<Competitor, Map<Waypoint, MarkPassing>> markPassingsLoaded = dF.loadMarkPassings(trackedRace1.getRaceIdentifier());
final Map<Competitor, Map<Waypoint, MarkPassing>> markPassingsLoaded = dF.loadMarkPassings(trackedRace1.getRaceIdentifier(), trackedRace1.getRace().getCourse());
assertEquals(markPassings, markPassingsLoaded);
}
}
@@ -100,6 +100,7 @@ import com.sap.sailing.domain.base.CompetitorAndBoatStore.BoatUpdateListener;
import com.sap.sailing.domain.base.CompetitorAndBoatStore.CompetitorUpdateListener;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.DomainFactory;
@@ -945,9 +946,9 @@ Replicator {
@Override
public void storeMarkPassings(RaceIdentifier raceIdentifier, MarkPassingRaceFingerprint fingerprint,
Map<Competitor, Map<Waypoint, MarkPassing>> markPassings) {
Map<Competitor, Map<Waypoint, MarkPassing>> markPassings, Course course) {
markPassingRaceFingerprints.put(raceIdentifier, fingerprint);
mongoObjectFactory.storeMarkPassings(raceIdentifier, fingerprint, markPassings);
mongoObjectFactory.storeMarkPassings(raceIdentifier, fingerprint, markPassings, course);
}
@Override
@@ -956,10 +957,10 @@ Replicator {
}
@Override
public Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier) {
public Map<Competitor, Map<Waypoint, MarkPassing>> loadMarkPassings(RaceIdentifier raceIdentifier, Course course) {
final Map<Competitor, Map<Waypoint, MarkPassing>> result;
if (markPassingRaceFingerprints.containsKey(raceIdentifier)) {
result = domainObjectFactory.loadMarkPassings(raceIdentifier);
result = domainObjectFactory.loadMarkPassings(raceIdentifier, course);
} else {
result = null;
}