mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-17 11:19:15 +00:00
bug4881: added a few TODOs and added Leaderboard.mapRegattaScoreCorrections
This commit is contained in:
@@ -25,6 +25,9 @@ import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.RegattaName;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionForCompetitorInRace;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionsForRace;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
@@ -763,4 +766,28 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
* given as the key, and as the second pair component the number of computations in that time period.
|
||||
*/
|
||||
Map<Duration, Pair<Duration, Integer>> getComputationTimeStatistics();
|
||||
|
||||
/**
|
||||
* Matches the results in {@code regattaScoreCorrections} to the {@link RaceColumn}s and {@link Competitor}s in this
|
||||
* leaderboard. The race columns are identified in {@code regattaScoreCorrections} by the
|
||||
* {@link ScoreCorrectionsForRace#getRaceNameOrNumber()} result and the ordering of the
|
||||
* {@link ScoreCorrectionsForRace} objects as delivered by
|
||||
* {@link RegattaScoreCorrections#getScoreCorrectionsForRaces()} as compared to the {@link #getRaceColumns()}
|
||||
* ordering of this leaderboard, furthermore the explicit mappings specified in
|
||||
* {@code raceNumberOrNameToRaceColumnMap}. The competitor mapping happens based on the
|
||||
* {@link ScoreCorrectionForCompetitorInRace#getSailID()} result that is compared to the {@link Competitor}'s
|
||||
* {@link Competitor#getShortName() short name} if the boats can change in this leaderboard, or to the
|
||||
* {@link Boat#getSailID()} result of the competitor's boat, overruled by the explicit mappings in
|
||||
* {@code sailIdToCompetitorMap}.
|
||||
*
|
||||
* @param allowRaceDefaultsByOrder
|
||||
* if {@code true}, an attempt will be made to map the race names/numbers from the
|
||||
* {@link RegattaScoreCorrections} to the leaderboard's {@link RaceColumn}s by their ordering, one by
|
||||
* one, but only for those that are not mapped explicitly by {@code raceNumberOrNameToRaceColumnMap}.
|
||||
* If there are excess race names/numbers in the {@link RegattaScoreCorrections} objects beyond the
|
||||
* number of race columns in this leaderboard, no mapping will be inferred for the excess races.
|
||||
*/
|
||||
ScoreCorrectionMapping mapRegattaScoreCorrections(RegattaScoreCorrections regattaScoreCorrections,
|
||||
Map<String, RaceColumn> raceNumberOrNameToRaceColumnMap, Map<String, Competitor> sailIdToCompetitorMap,
|
||||
boolean allowRaceDefaultsByOrder);
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
package com.sap.sailing.domain.leaderboard;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.RegattaScoreCorrections;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionForCompetitorInRace;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionsForRace;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
/**
|
||||
* Describes the result of
|
||||
* {@link Leaderboard#mapRegattaScoreCorrections(com.sap.sailing.domain.common.RegattaScoreCorrections, java.util.Map, java.util.Map, boolean)
|
||||
* mapping} a {@link RegattaScoreCorrections} object to an existing {@link Leaderboard}. For each
|
||||
* {@link RegattaScoreCorrections#getScoreCorrectionsForRaces() race} for which there are score corrections, the mapping of the
|
||||
* {@link ScoreCorrectionsForRace#getRaceNameOrNumber() race name or number} to a {@link RaceColumn} in the leaderboard is captured.
|
||||
* If no matching race column was assigned, that race name/number is mapped to {@code null} instead. Likewise, for all sail IDs/numbers
|
||||
* (that may alternatively be interpreted as competitor short names if the boat assignments can change per race in the leaderboard)
|
||||
* the matching {@link Competitor} is recorded where a {@code null} value again means that for the key sail ID/number/shortname no
|
||||
* matching competitor was identified.<p>
|
||||
*
|
||||
* All {@link ScoreCorrectionForCompetitorInRace} from the original {@link RegattaScoreCorrections} object that were mapped
|
||||
* successfully to a {@link RaceColumn} and a {@link Competitor} can be obtained using the {@link #getScoreCorrections} method.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface ScoreCorrectionMapping {
|
||||
Map<String, RaceColumn> getRaceMappings();
|
||||
|
||||
Map<String, Competitor> getCompetitorMappings();
|
||||
|
||||
Map<RaceColumn, Map<Competitor, Pair<Double, MaxPointsReason>>> getScoreCorrections();
|
||||
}
|
||||
+28
@@ -47,6 +47,8 @@ import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionsForRace;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.dto.BasicRaceDTO;
|
||||
import com.sap.sailing.domain.common.dto.BoatClassDTO;
|
||||
@@ -64,6 +66,7 @@ import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoreCorrectionMapping;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCache;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
|
||||
@@ -1371,4 +1374,29 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScoreCorrectionMapping mapRegattaScoreCorrections(RegattaScoreCorrections regattaScoreCorrections,
|
||||
Map<String, RaceColumn> raceNumberOrNameToRaceColumnMap, Map<String, Competitor> sailIdToCompetitorMap,
|
||||
boolean allowRaceDefaultsByOrder) {
|
||||
final Map<String, RaceColumn> raceMappings = new HashMap<>(raceNumberOrNameToRaceColumnMap);
|
||||
final Map<String, Competitor> competitorMappings = new HashMap<>(sailIdToCompetitorMap);
|
||||
final Iterator<RaceColumn> raceColumnIterator = getRaceColumns().iterator();
|
||||
for (final ScoreCorrectionsForRace raceCorrection : regattaScoreCorrections.getScoreCorrectionsForRaces()) {
|
||||
final RaceColumn currentRaceColumn = raceColumnIterator.hasNext() ? raceColumnIterator.next() : null;
|
||||
raceMappings.computeIfAbsent(raceCorrection.getRaceNameOrNumber(), raceNameOrNumber->
|
||||
allowRaceDefaultsByOrder ? currentRaceColumn : null);
|
||||
for (final String sailIdOrShortName : raceCorrection.getSailIDs()) {
|
||||
competitorMappings.computeIfAbsent(sailIdOrShortName, sailIdsOrShortName->findBestMatchingCompetitorBySailNumberOrShortName(sailIdsOrShortName));
|
||||
}
|
||||
}
|
||||
return new ScoreCorrectionMappingImpl(raceMappings, competitorMappings, regattaScoreCorrections);
|
||||
}
|
||||
|
||||
private Competitor findBestMatchingCompetitorBySailNumberOrShortName(String sailIdsOrShortName) {
|
||||
// TODO move MatchAndApplyScoreCorrectionsDialog.canonicalizeSailID and getCompetitorIdentifyingText to a common package both sides can see and use
|
||||
// TODO then use it to match this leaderboard's competitors against the sailIdsOrShortName, passed through canonicalizeSailID
|
||||
// TODO Implement AbstractLeaderboardWithCache.findBestMatchingCompetitorBySailNumberOrShortName(...)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.RegattaScoreCorrections;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionForCompetitorInRace;
|
||||
import com.sap.sailing.domain.common.RegattaScoreCorrections.ScoreCorrectionsForRace;
|
||||
import com.sap.sailing.domain.leaderboard.ScoreCorrectionMapping;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
public class ScoreCorrectionMappingImpl implements ScoreCorrectionMapping {
|
||||
private final Map<String, RaceColumn> raceMappings;
|
||||
private final Map<String, Competitor> competitorMappings;
|
||||
private final RegattaScoreCorrections regattaScoreCorrections;
|
||||
|
||||
public ScoreCorrectionMappingImpl(Map<String, RaceColumn> raceMappings, Map<String, Competitor> competitorMappings,
|
||||
RegattaScoreCorrections regattaScoreCorrections) {
|
||||
super();
|
||||
this.raceMappings = raceMappings;
|
||||
this.competitorMappings = competitorMappings;
|
||||
this.regattaScoreCorrections = regattaScoreCorrections;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, RaceColumn> getRaceMappings() {
|
||||
return raceMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Competitor> getCompetitorMappings() {
|
||||
return competitorMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<RaceColumn, Map<Competitor, Pair<Double, MaxPointsReason>>> getScoreCorrections() {
|
||||
final Map<RaceColumn, Map<Competitor, Pair<Double, MaxPointsReason>>> result = new HashMap<>();
|
||||
for (final ScoreCorrectionsForRace raceCorrection : regattaScoreCorrections.getScoreCorrectionsForRaces()) {
|
||||
final RaceColumn raceColumn = getRaceMappings().get(raceCorrection.getRaceNameOrNumber());
|
||||
if (raceColumn != null) {
|
||||
final Map<Competitor, Pair<Double, MaxPointsReason>> competitorMapForRace = result.computeIfAbsent(raceColumn, k->new HashMap<>());
|
||||
for (final String sailIdOrShortName : raceCorrection.getSailIDs()) {
|
||||
final Competitor competitor = getCompetitorMappings().get(sailIdOrShortName);
|
||||
final ScoreCorrectionForCompetitorInRace competitorCorrection = raceCorrection.getScoreCorrectionForCompetitor(sailIdOrShortName);
|
||||
if (competitor != null && competitorCorrection != null) {
|
||||
competitorMapForRace.put(competitor, new Pair<>(competitorCorrection.getPoints(), competitorCorrection.getMaxPointsReason()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+6
@@ -230,11 +230,17 @@ public class LeaderboardsResourceTest extends AbstractJaxRsApiTest {
|
||||
@Test
|
||||
public void testXrrScoreImport() throws Exception {
|
||||
dropAndCreateRegatta();
|
||||
final TimePoint validityTimePoint = MillisecondsTimePoint.now();
|
||||
final String comment = "A Comment";
|
||||
Response leaderboardResponse = leaderboardsResource.uploadResults(regatta.getName(),
|
||||
/* scoreCorrectionProviderName */ "ISAF XML Regatta Result (XRR) Importer",
|
||||
/* validity time point */ validityTimePoint.asMillis(),
|
||||
/* comment */ comment,
|
||||
getClass().getClassLoader().getResourceAsStream("YES_29er_XRR.xml"));
|
||||
assertNotNull(leaderboardResponse);
|
||||
JSONObject jsonObject = (JSONObject) JSONValue.parse((String) leaderboardResponse.getEntity());
|
||||
assertNotNull(jsonObject);
|
||||
assertEquals(comment, regattaLeaderboard.getScoreCorrection().getComment());
|
||||
assertEquals(validityTimePoint, regattaLeaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity());
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -133,6 +133,7 @@ import com.sap.sailing.server.interfaces.RacingEventService;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveAndUntrackRace;
|
||||
import com.sap.sailing.server.operationaltransformation.StopTrackingRace;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboard;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrectionMetadata;
|
||||
import com.sap.sailing.server.security.PermissionAwareRaceTrackingHandler;
|
||||
import com.sap.sse.InvalidDateException;
|
||||
import com.sap.sse.common.Bearing;
|
||||
@@ -146,6 +147,7 @@ import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.security.SessionUtils;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.security.shared.OwnershipAnnotation;
|
||||
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
|
||||
@@ -1625,6 +1627,8 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response uploadResults(@PathParam("name") String leaderboardName,
|
||||
@QueryParam("scoreCorrectionProvider") String scoreCorrectionProviderName,
|
||||
@QueryParam("timePointOfLastCorrectionValidityMillis") Long timePointOfLastCorrectionValidityMillis,
|
||||
@QueryParam("comment") String comment,
|
||||
InputStream inputStream) throws Exception {
|
||||
final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
if (leaderboard == null) {
|
||||
@@ -1636,6 +1640,24 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
|
||||
throw new NotFoundException("score correction provider with name " + scoreCorrectionProviderName + " not found");
|
||||
}
|
||||
final RegattaScoreCorrections scoreCorrection = scoreCorrectionProvider.get().getScoreCorrections(inputStream);
|
||||
if (comment != null || timePointOfLastCorrectionValidityMillis != null) {
|
||||
final String finalComment;
|
||||
if (comment == null) {
|
||||
finalComment = leaderboard.getScoreCorrection().getComment();
|
||||
} else {
|
||||
finalComment = comment;
|
||||
}
|
||||
final TimePoint timePointOfLastCorrectionValidity;
|
||||
if (timePointOfLastCorrectionValidityMillis == null) {
|
||||
timePointOfLastCorrectionValidity = leaderboard.getScoreCorrection().getTimePointOfLastCorrectionsValidity();
|
||||
} else {
|
||||
timePointOfLastCorrectionValidity = new MillisecondsTimePoint(timePointOfLastCorrectionValidityMillis);
|
||||
}
|
||||
logger.info("Applying score correction comment \""+finalComment+"\" and validity time point "+timePointOfLastCorrectionValidity+
|
||||
" to leaderboard "+leaderboardName+" on behalf of "+SessionUtils.getPrincipal());
|
||||
getService().apply(
|
||||
new UpdateLeaderboardScoreCorrectionMetadata(leaderboardName, timePointOfLastCorrectionValidity, finalComment));
|
||||
}
|
||||
return applyScoreCorrectionToLeaderboard(leaderboard, scoreCorrection);
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -51,5 +51,4 @@ public class UpdateLeaderboardScoreCorrectionMetadata extends AbstractLeaderboar
|
||||
updateStoredLeaderboard(toState, leaderboard);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user