mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-08 13:20:57 +00:00
a failing test case for bug3794, trying to push scores through the race log into the leaderboard's score corrections
Change-Id: I35bc58c25f032e254932b157790557c6f07d3621
This commit is contained in:
1 parent
912892eeff
commit
352c007a49
4 files changed
+349
-191
No files matched your search
+3
-189
@@ -55,7 +55,6 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
|
||||
@@ -70,7 +69,6 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndZeroRanks;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
@@ -82,10 +80,10 @@ import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||
|
||||
public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
private ArrayList<Series> series;
|
||||
public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRankingTestBase {
|
||||
protected ArrayList<Series> series;
|
||||
|
||||
private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||
protected Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
|
||||
return new RegattaLeaderboardImpl(regatta, discardingRules);
|
||||
}
|
||||
@@ -1250,19 +1248,6 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
checkScoresAfterSomeRaces(leaderboard, raceColumnsToConsider, scoresAfter6Races, later, c);
|
||||
}
|
||||
|
||||
private void checkScoresAfterSomeRaces(Leaderboard leaderboard, List<RaceColumn> raceColumnsToConsider,
|
||||
double[][] scoresAfterNRaces, TimePoint timePoint, Competitor[] competitors) throws NoWindException {
|
||||
for (int competitorIndex=0; competitorIndex<scoresAfterNRaces.length; competitorIndex++) {
|
||||
final Set<RaceColumn> discardedRaceColumns = leaderboard.getResultDiscardingRule()
|
||||
.getDiscardedRaceColumns(competitors[competitorIndex], leaderboard, raceColumnsToConsider, timePoint);
|
||||
for (int raceColumnIndex=0; raceColumnIndex<raceColumnsToConsider.size(); raceColumnIndex++) {
|
||||
assertEquals(scoresAfterNRaces[competitorIndex][raceColumnIndex],
|
||||
leaderboard.getNetPoints(competitors[competitorIndex], raceColumnsToConsider.get(raceColumnIndex),
|
||||
timePoint, discardedRaceColumns), 0.00000001);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTieBreakByMedalRaceScoreOnlyIfEqualNetScore() throws NoWindException {
|
||||
Competitor[] c = createCompetitors(2).toArray(new Competitor[0]);
|
||||
@@ -1863,177 +1848,6 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
assertEquals(0., leaderboard1.getNetPoints(c[3], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
|
||||
}
|
||||
|
||||
private TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, boolean withScores, Competitor[]... competitorLists) {
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
|
||||
for (Competitor[] competitorList : competitorLists) {
|
||||
RaceColumn raceColumn = columnIter.next();
|
||||
final TrackedRace trackedRace;
|
||||
if (withScores) {
|
||||
trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitorList));
|
||||
} else {
|
||||
trackedRace = new MockedTrackedRaceWithStartTimeAndZeroRanks(now, Arrays.asList(competitorList));
|
||||
}
|
||||
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
|
||||
}
|
||||
return later;
|
||||
}
|
||||
|
||||
private void createAndAttachTrackedRacesWithStartTimeAndLastMarkPassingTimes(
|
||||
Series theSeries, String fleetName, Competitor[][] competitorLists, TimePoint[] startTimes,
|
||||
Map<Competitor, TimePoint>[] lastMarkPassingTimesForCompetitors) {
|
||||
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
|
||||
int i=0;
|
||||
for (Competitor[] competitorList : competitorLists) {
|
||||
RaceColumn raceColumn = columnIter.next();
|
||||
final Map<Competitor, TimePoint> lastMarkPassingTimes = lastMarkPassingTimesForCompetitors[i];
|
||||
final Waypoint start = new WaypointImpl(new ControlPointWithTwoMarksImpl(new MarkImpl("Left StartBuoy"), new MarkImpl("Right StartBuoy"), "Start"));
|
||||
final Waypoint finish = new WaypointImpl(new MarkImpl("FinishBuoy"));
|
||||
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(startTimes[i], Arrays.asList(competitorList)) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override
|
||||
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
|
||||
ArrayListNavigableSet<MarkPassing> result = new ArrayListNavigableSet<>(new TimedComparator());
|
||||
result.add(new MarkPassingImpl(lastMarkPassingTimes.get(competitor), finish, competitor));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
trackedRace.getRace().getCourse().addWaypoint(0, start);
|
||||
trackedRace.getRace().getCourse().addWaypoint(1, finish);
|
||||
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Competitor> createCompetitors(int numberOfCompetitorsToCreate) {
|
||||
List<Competitor> result = new ArrayList<Competitor>();
|
||||
for (int i=1; i<=numberOfCompetitorsToCreate; i++) {
|
||||
result.add(createCompetitor("C"+i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Regatta createSimpleRegatta(final int numberOfRaces, final String regattaName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||
series = new ArrayList<Series>();
|
||||
List<Fleet> fleets = new ArrayList<Fleet>();
|
||||
fleets.add(new FleetImpl("Default"));
|
||||
|
||||
List<String> raceColumnNames = new ArrayList<String>();
|
||||
for (int i = 1; i <= numberOfRaces; i++) {
|
||||
raceColumnNames.add("R" + i);
|
||||
}
|
||||
Series defaultSeries = new SeriesImpl("Default", /* isMedal */false, /* isFleetsCanRunInParallel */true,
|
||||
fleets, raceColumnNames, /* trackedRegattaRegistry */null);
|
||||
series.add(defaultSeries);
|
||||
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass,
|
||||
/* startDate */null, /* endDate */null, series, /* persistent */false, scoringScheme, "123", null,
|
||||
OneDesignRankingMetric::new);
|
||||
return regatta;
|
||||
}
|
||||
|
||||
private Regatta createRegatta(final int numberOfQualifyingRaces, String[] qualifyingFleetNames, final int numberOfFinalRaces,
|
||||
String[] finalFleetNames, boolean medalRaceAndSeries, final int numberOfMedalRaces, final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||
series = new ArrayList<Series>();
|
||||
|
||||
// -------- qualifying series ------------
|
||||
if (qualifyingFleetNames != null && qualifyingFleetNames.length > 0) {
|
||||
List<Fleet> qualifyingFleets = new ArrayList<Fleet>();
|
||||
for (String qualifyingFleetName : qualifyingFleetNames) {
|
||||
qualifyingFleets.add(new FleetImpl(qualifyingFleetName));
|
||||
}
|
||||
List<String> qualifyingRaceColumnNames = new ArrayList<String>();
|
||||
for (int i = 1; i <= numberOfQualifyingRaces; i++) {
|
||||
qualifyingRaceColumnNames.add("Q" + i);
|
||||
}
|
||||
Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualifyingFleets,
|
||||
qualifyingRaceColumnNames, /* trackedRegattaRegistry */null);
|
||||
series.add(qualifyingSeries);
|
||||
}
|
||||
|
||||
// -------- final series ------------
|
||||
if (finalFleetNames != null && finalFleetNames.length > 0) {
|
||||
List<Fleet> finalFleets = new ArrayList<Fleet>();
|
||||
int fleetOrdering = 1;
|
||||
for (String finalFleetName : finalFleetNames) {
|
||||
finalFleets.add(new FleetImpl(finalFleetName, fleetOrdering++));
|
||||
}
|
||||
List<String> finalRaceColumnNames = new ArrayList<String>();
|
||||
for (int i = 1; i <= numberOfFinalRaces; i++) {
|
||||
finalRaceColumnNames.add("F" + i);
|
||||
}
|
||||
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, /* isFleetsCanRunInParallel */ true, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||
series.add(finalSeries);
|
||||
}
|
||||
|
||||
if (medalRaceAndSeries) {
|
||||
// ------------ medal --------------
|
||||
List<Fleet> medalFleets = new ArrayList<Fleet>();
|
||||
medalFleets.add(new FleetImpl("Medal"));
|
||||
List<String> medalRaceColumnNames = new ArrayList<String>();
|
||||
if(numberOfMedalRaces == 1) {
|
||||
medalRaceColumnNames.add("M");
|
||||
} else if(numberOfMedalRaces > 1) {
|
||||
for (int i = 1; i <= numberOfMedalRaces; i++) {
|
||||
medalRaceColumnNames.add("M" + i);
|
||||
}
|
||||
}
|
||||
Series medalSeries = new SeriesImpl("Medal", /* isMedal */true, /* isFleetsCanRunInParallel */ true, medalFleets, medalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||
series.add(medalSeries);
|
||||
}
|
||||
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
|
||||
/*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme, "123", null, OneDesignRankingMetric::new);
|
||||
return regatta;
|
||||
}
|
||||
|
||||
private Regatta createRegattaWithEliminations(final int numberOfEliminations, final int[] numbersOfHeatsPerRound,
|
||||
final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||
series = new ArrayList<Series>();
|
||||
// example for numbersOfHeatsPerRound: [8, 4, 2, 2]
|
||||
for (int elimination=1; elimination<=numberOfEliminations; elimination++) {
|
||||
int heatNumber = 1;
|
||||
// create one elimination consisting of a number of rounds, each consisting of a number of heats
|
||||
int roundNumber = 1;
|
||||
for (int numberOfHeatsPerRound : numbersOfHeatsPerRound) {
|
||||
final boolean isFinalRound = roundNumber == numbersOfHeatsPerRound.length;
|
||||
// create one round as a series that has one fleet per heat
|
||||
List<Fleet> fleetsInRound = new ArrayList<Fleet>();
|
||||
for (int heatInRound=1; heatInRound<=numberOfHeatsPerRound; heatInRound++) {
|
||||
final int ordering = numbersOfHeatsPerRound.length-roundNumber+1+
|
||||
// in final round distinguish Final and Losers Final
|
||||
(isFinalRound ? heatInRound-1 : 1);
|
||||
fleetsInRound.add(new FleetImpl("Heat "+(heatNumber++), ordering));
|
||||
}
|
||||
List<String> raceColumnNameForRound = new ArrayList<String>();
|
||||
raceColumnNameForRound.add("E"+elimination+"R"+roundNumber);
|
||||
final String roundName;
|
||||
if (numbersOfHeatsPerRound.length-roundNumber == 2) {
|
||||
roundName = "Quarter-Final";
|
||||
} else if (numbersOfHeatsPerRound.length-roundNumber == 1) {
|
||||
roundName = "Semi-Final";
|
||||
} else if (numbersOfHeatsPerRound.length-roundNumber == 0) {
|
||||
roundName = "Final";
|
||||
} else {
|
||||
roundName = "Round "+roundNumber;
|
||||
}
|
||||
Series seriesForRound = new SeriesImpl("E"+elimination+" "+roundName, /* isMedal */ false, /* isFleetsCanRunInParallel */ true, fleetsInRound,
|
||||
raceColumnNameForRound, /* trackedRegattaRegistry */null);
|
||||
if (isFinalRound) {
|
||||
// last "Final" round; here, the fleets are contiguously scored
|
||||
seriesForRound.setSplitFleetContiguousScoring(true);
|
||||
}
|
||||
series.add(seriesForRound);
|
||||
roundNumber++;
|
||||
}
|
||||
}
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
|
||||
/*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme,
|
||||
/* ID */ "123", /* course area */ null, OneDesignRankingMetric::new);
|
||||
return regatta;
|
||||
}
|
||||
|
||||
/**
|
||||
* See bug 1260. There must be a possibility to have ordered fleets that are scored such that the winner of the race of the best fleet
|
||||
* gets the best score in that column; and the winner of the second-best fleet gets the n-th best score in the column with n being the
|
||||
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.SeriesImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndZeroRanks;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TimedComparator;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||
|
||||
public class LeaderboardScoringAndRankingTestBase extends AbstractLeaderboardTest {
|
||||
protected ArrayList<Series> series;
|
||||
|
||||
protected Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
|
||||
return new RegattaLeaderboardImpl(regatta, discardingRules);
|
||||
}
|
||||
|
||||
protected void checkScoresAfterSomeRaces(Leaderboard leaderboard, List<RaceColumn> raceColumnsToConsider,
|
||||
double[][] scoresAfterNRaces, TimePoint timePoint, Competitor[] competitors) throws NoWindException {
|
||||
for (int competitorIndex=0; competitorIndex<scoresAfterNRaces.length; competitorIndex++) {
|
||||
final Set<RaceColumn> discardedRaceColumns = leaderboard.getResultDiscardingRule()
|
||||
.getDiscardedRaceColumns(competitors[competitorIndex], leaderboard, raceColumnsToConsider, timePoint);
|
||||
for (int raceColumnIndex=0; raceColumnIndex<raceColumnsToConsider.size(); raceColumnIndex++) {
|
||||
assertEquals(scoresAfterNRaces[competitorIndex][raceColumnIndex],
|
||||
leaderboard.getNetPoints(competitors[competitorIndex], raceColumnsToConsider.get(raceColumnIndex),
|
||||
timePoint, discardedRaceColumns), 0.00000001);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, boolean withScores, Competitor[]... competitorLists) {
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
|
||||
for (Competitor[] competitorList : competitorLists) {
|
||||
RaceColumn raceColumn = columnIter.next();
|
||||
final TrackedRace trackedRace;
|
||||
if (withScores) {
|
||||
trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitorList));
|
||||
} else {
|
||||
trackedRace = new MockedTrackedRaceWithStartTimeAndZeroRanks(now, Arrays.asList(competitorList));
|
||||
}
|
||||
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
|
||||
}
|
||||
return later;
|
||||
}
|
||||
|
||||
protected void createAndAttachTrackedRacesWithStartTimeAndLastMarkPassingTimes(
|
||||
Series theSeries, String fleetName, Competitor[][] competitorLists, TimePoint[] startTimes,
|
||||
Map<Competitor, TimePoint>[] lastMarkPassingTimesForCompetitors) {
|
||||
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
|
||||
int i=0;
|
||||
for (Competitor[] competitorList : competitorLists) {
|
||||
RaceColumn raceColumn = columnIter.next();
|
||||
final Map<Competitor, TimePoint> lastMarkPassingTimes = lastMarkPassingTimesForCompetitors[i];
|
||||
final Waypoint start = new WaypointImpl(new ControlPointWithTwoMarksImpl(new MarkImpl("Left StartBuoy"), new MarkImpl("Right StartBuoy"), "Start"));
|
||||
final Waypoint finish = new WaypointImpl(new MarkImpl("FinishBuoy"));
|
||||
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(startTimes[i], Arrays.asList(competitorList)) {
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override
|
||||
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
|
||||
ArrayListNavigableSet<MarkPassing> result = new ArrayListNavigableSet<>(new TimedComparator());
|
||||
result.add(new MarkPassingImpl(lastMarkPassingTimes.get(competitor), finish, competitor));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
trackedRace.getRace().getCourse().addWaypoint(0, start);
|
||||
trackedRace.getRace().getCourse().addWaypoint(1, finish);
|
||||
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Competitor> createCompetitors(int numberOfCompetitorsToCreate) {
|
||||
List<Competitor> result = new ArrayList<Competitor>();
|
||||
for (int i=1; i<=numberOfCompetitorsToCreate; i++) {
|
||||
result.add(createCompetitor("C"+i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Regatta createSimpleRegatta(final int numberOfRaces, final String regattaName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||
series = new ArrayList<Series>();
|
||||
List<Fleet> fleets = new ArrayList<Fleet>();
|
||||
fleets.add(new FleetImpl("Default"));
|
||||
|
||||
List<String> raceColumnNames = new ArrayList<String>();
|
||||
for (int i = 1; i <= numberOfRaces; i++) {
|
||||
raceColumnNames.add("R" + i);
|
||||
}
|
||||
Series defaultSeries = new SeriesImpl("Default", /* isMedal */false, /* isFleetsCanRunInParallel */true,
|
||||
fleets, raceColumnNames, /* trackedRegattaRegistry */null);
|
||||
series.add(defaultSeries);
|
||||
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass,
|
||||
/* startDate */null, /* endDate */null, series, /* persistent */false, scoringScheme, "123", null,
|
||||
OneDesignRankingMetric::new);
|
||||
return regatta;
|
||||
}
|
||||
|
||||
protected Regatta createRegatta(final int numberOfQualifyingRaces, String[] qualifyingFleetNames, final int numberOfFinalRaces,
|
||||
String[] finalFleetNames, boolean medalRaceAndSeries, final int numberOfMedalRaces, final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||
series = new ArrayList<Series>();
|
||||
|
||||
// -------- qualifying series ------------
|
||||
if (qualifyingFleetNames != null && qualifyingFleetNames.length > 0) {
|
||||
List<Fleet> qualifyingFleets = new ArrayList<Fleet>();
|
||||
for (String qualifyingFleetName : qualifyingFleetNames) {
|
||||
qualifyingFleets.add(new FleetImpl(qualifyingFleetName));
|
||||
}
|
||||
List<String> qualifyingRaceColumnNames = new ArrayList<String>();
|
||||
for (int i = 1; i <= numberOfQualifyingRaces; i++) {
|
||||
qualifyingRaceColumnNames.add("Q" + i);
|
||||
}
|
||||
Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualifyingFleets,
|
||||
qualifyingRaceColumnNames, /* trackedRegattaRegistry */null);
|
||||
series.add(qualifyingSeries);
|
||||
}
|
||||
|
||||
// -------- final series ------------
|
||||
if (finalFleetNames != null && finalFleetNames.length > 0) {
|
||||
List<Fleet> finalFleets = new ArrayList<Fleet>();
|
||||
int fleetOrdering = 1;
|
||||
for (String finalFleetName : finalFleetNames) {
|
||||
finalFleets.add(new FleetImpl(finalFleetName, fleetOrdering++));
|
||||
}
|
||||
List<String> finalRaceColumnNames = new ArrayList<String>();
|
||||
for (int i = 1; i <= numberOfFinalRaces; i++) {
|
||||
finalRaceColumnNames.add("F" + i);
|
||||
}
|
||||
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, /* isFleetsCanRunInParallel */ true, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||
series.add(finalSeries);
|
||||
}
|
||||
|
||||
if (medalRaceAndSeries) {
|
||||
// ------------ medal --------------
|
||||
List<Fleet> medalFleets = new ArrayList<Fleet>();
|
||||
medalFleets.add(new FleetImpl("Medal"));
|
||||
List<String> medalRaceColumnNames = new ArrayList<String>();
|
||||
if(numberOfMedalRaces == 1) {
|
||||
medalRaceColumnNames.add("M");
|
||||
} else if(numberOfMedalRaces > 1) {
|
||||
for (int i = 1; i <= numberOfMedalRaces; i++) {
|
||||
medalRaceColumnNames.add("M" + i);
|
||||
}
|
||||
}
|
||||
Series medalSeries = new SeriesImpl("Medal", /* isMedal */true, /* isFleetsCanRunInParallel */ true, medalFleets, medalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||
series.add(medalSeries);
|
||||
}
|
||||
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
|
||||
/*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme, "123", null, OneDesignRankingMetric::new);
|
||||
return regatta;
|
||||
}
|
||||
|
||||
protected Regatta createRegattaWithEliminations(final int numberOfEliminations, final int[] numbersOfHeatsPerRound,
|
||||
final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||
series = new ArrayList<Series>();
|
||||
// example for numbersOfHeatsPerRound: [8, 4, 2, 2]
|
||||
for (int elimination=1; elimination<=numberOfEliminations; elimination++) {
|
||||
int heatNumber = 1;
|
||||
// create one elimination consisting of a number of rounds, each consisting of a number of heats
|
||||
int roundNumber = 1;
|
||||
for (int numberOfHeatsPerRound : numbersOfHeatsPerRound) {
|
||||
final boolean isFinalRound = roundNumber == numbersOfHeatsPerRound.length;
|
||||
// create one round as a series that has one fleet per heat
|
||||
List<Fleet> fleetsInRound = new ArrayList<Fleet>();
|
||||
for (int heatInRound=1; heatInRound<=numberOfHeatsPerRound; heatInRound++) {
|
||||
final int ordering = numbersOfHeatsPerRound.length-roundNumber+1+
|
||||
// in final round distinguish Final and Losers Final
|
||||
(isFinalRound ? heatInRound-1 : 1);
|
||||
fleetsInRound.add(new FleetImpl("Heat "+(heatNumber++), ordering));
|
||||
}
|
||||
List<String> raceColumnNameForRound = new ArrayList<String>();
|
||||
raceColumnNameForRound.add("E"+elimination+"R"+roundNumber);
|
||||
final String roundName;
|
||||
if (numbersOfHeatsPerRound.length-roundNumber == 2) {
|
||||
roundName = "Quarter-Final";
|
||||
} else if (numbersOfHeatsPerRound.length-roundNumber == 1) {
|
||||
roundName = "Semi-Final";
|
||||
} else if (numbersOfHeatsPerRound.length-roundNumber == 0) {
|
||||
roundName = "Final";
|
||||
} else {
|
||||
roundName = "Round "+roundNumber;
|
||||
}
|
||||
Series seriesForRound = new SeriesImpl("E"+elimination+" "+roundName, /* isMedal */ false, /* isFleetsCanRunInParallel */ true, fleetsInRound,
|
||||
raceColumnNameForRound, /* trackedRegattaRegistry */null);
|
||||
if (isFinalRound) {
|
||||
// last "Final" round; here, the fleets are contiguously scored
|
||||
seriesForRound.setSplitFleetContiguousScoring(true);
|
||||
}
|
||||
series.add(seriesForRound);
|
||||
roundNumber++;
|
||||
}
|
||||
}
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
|
||||
/*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme,
|
||||
/* ID */ "123", /* course area */ null, OneDesignRankingMetric::new);
|
||||
return regatta;
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package com.sap.sailing.server.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultsImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.RacingProcedureFactoryImpl;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration;
|
||||
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||
import com.sap.sailing.domain.base.impl.BoatImpl;
|
||||
import com.sap.sailing.domain.base.impl.NationalityImpl;
|
||||
import com.sap.sailing.domain.base.impl.PersonImpl;
|
||||
import com.sap.sailing.domain.base.impl.TeamImpl;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.test.LeaderboardScoringAndRankingTestBase;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
import com.sap.sse.common.Color;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTestBase {
|
||||
/**
|
||||
* See also bug 3794: make sure that different variants of scores and max points reasons / penalties are applied to the
|
||||
* leaderboard.
|
||||
*/
|
||||
@Test
|
||||
public void testApplicationOfScoresFromRaceLog() throws NoWindException {
|
||||
final RacingEventService service = new RacingEventServiceImpl();
|
||||
final List<Competitor> competitors = new ArrayList<>();
|
||||
for (int i=0; i<20; i++) {
|
||||
final String competitorName = "C"+i;
|
||||
competitors.add(service.getBaseDomainFactory().getCompetitorStore().getOrCreateCompetitor(UUID.randomUUID(),
|
||||
competitorName, /* displayColor */ Color.RED, /* email */ null, /* flagImageURI */ null,
|
||||
new TeamImpl("STG", Collections.singleton(
|
||||
new PersonImpl(competitorName, new NationalityImpl("GER"),
|
||||
/* dateOfBirth */ null, "This is famous "+competitorName)),
|
||||
new PersonImpl("Rigo van Maas", new NationalityImpl("NED"),
|
||||
/* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat",
|
||||
new BoatClassImpl("505", /* typicallyStartsUpwind */ true), /* sailID */ null),
|
||||
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null));
|
||||
}
|
||||
final Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */1,
|
||||
new String[] { "Default" },
|
||||
/* medal */false, /* medal */ 0, "testOneStartedRaceWithDifferentScores",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||
TrackedRace f1 = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors);
|
||||
RaceColumn f1Column = series.get(1).getRaceColumnByName("F1");
|
||||
f1Column.setTrackedRace(f1Column.getFleets().iterator().next(), f1);
|
||||
final Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
|
||||
service.addLeaderboard(leaderboard); // should add a RaceLogScoringReplicator as listener to the leaderboard
|
||||
service.addRegattaWithoutReplication(regatta);
|
||||
final Map<Competitor, Double> scores = new HashMap<>();
|
||||
final Map<Competitor, MaxPointsReason> mprs = new HashMap<>();
|
||||
int oneBasedRank = 1;
|
||||
final CompetitorResults results = new CompetitorResultsImpl();
|
||||
for (final Competitor c : competitors) {
|
||||
final MaxPointsReason mpr = new MaxPointsReason[] { null, MaxPointsReason.NONE, MaxPointsReason.DNF, MaxPointsReason.OCS }[oneBasedRank%4];
|
||||
final Double score = oneBasedRank%5 == 0 ? null : 20*Math.random();
|
||||
scores.put(c, score);
|
||||
mprs.put(c, mpr);
|
||||
results.add(new CompetitorResultImpl(c.getId(), c.getName(),
|
||||
oneBasedRank++, mpr, score, /* finishingTime */ null, /* comment */ null));
|
||||
}
|
||||
final RaceLog f1RaceLog = f1Column.getRaceLog(f1Column.getFleets().iterator().next());
|
||||
final LogEventAuthorImpl author = new LogEventAuthorImpl("Axel", 0);
|
||||
final RaceState f1RaceState = new RaceStateImpl(service, f1RaceLog, author,
|
||||
new RacingProcedureFactoryImpl(author, new EmptyRegattaConfiguration()));
|
||||
f1RaceState.setFinishPositioningListChanged(now, results);
|
||||
final List<Competitor> rankedCompetitorsBeforeApplying = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertEquals(competitors, rankedCompetitorsBeforeApplying); // no effects of preliminary results list yet
|
||||
f1RaceState.setFinishPositioningConfirmed(now);
|
||||
for (final Competitor c : competitors) {
|
||||
assertEquals(scores.get(c)==null?competitors.indexOf(c)+1:scores.get(c), leaderboard.getTotalPoints(c, f1Column, now), 0.00000001);
|
||||
}
|
||||
final List<Competitor> expectedNewOrder = new ArrayList<>(competitors);
|
||||
expectedNewOrder.sort((c1, c2)->
|
||||
new Double(scores.get(c1)==null?competitors.indexOf(c1)+1:scores.get(c1)).compareTo(
|
||||
new Double(scores.get(c2)==null?competitors.indexOf(c2)+1:scores.get(c2))));
|
||||
final List<Competitor> rankedCompetitorsAfterApplying = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertEquals(expectedNewOrder, rankedCompetitorsAfterApplying);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+2
-2
@@ -148,7 +148,7 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
|
||||
if (positioningList != null) {
|
||||
for (CompetitorResult positionedCompetitor : positioningList) {
|
||||
Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(positionedCompetitor.getCompetitorId());
|
||||
if (positionedCompetitor.getMaxPointsReason().equals(MaxPointsReason.NONE)) {
|
||||
if (Util.equalsWithNull(positionedCompetitor.getMaxPointsReason(), MaxPointsReason.NONE)) {
|
||||
try {
|
||||
resetMaxPointsReasonIfNecessary(leaderboard, raceColumn, timePoint, competitor);
|
||||
int rankByRaceCommittee = getRankInPositioningListByRaceCommittee(positionedCompetitor);
|
||||
@@ -172,7 +172,7 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
|
||||
boolean scoreHasBeenCorrected = false;
|
||||
MaxPointsReason trackedMaxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumn, timePoint);
|
||||
MaxPointsReason maxPointsReasonByRaceCommittee = positionedCompetitor.getMaxPointsReason();
|
||||
if (!maxPointsReasonByRaceCommittee.equals(trackedMaxPointsReason)) {
|
||||
if (!Util.equalsWithNull(maxPointsReasonByRaceCommittee, trackedMaxPointsReason)) {
|
||||
applyMaxPointsReasonOperation(leaderboard, raceColumn, competitor, maxPointsReasonByRaceCommittee, timePoint);
|
||||
scoreHasBeenCorrected = true;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user