Merge branch 'ubilabs--rcapp--bug_3011' into bug3622

Conflicts:
	mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/utils/RaceHelper.java

Change-Id: I39313f9a5e9b0c3d079d2f1ad2cf1eadbb0ef844
This commit is contained in:
Axel Uhl committed 2016-04-24 12:48:11 +02:00
commit d9ea36c516
115 files changed
+5793 -4547

No files matched your search

@@ -1,58 +1,65 @@
package com.sap.sailing.domain.common.dto; package com.sap.sailing.domain.common.dto;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
public class SeriesCreationParametersDTO implements Serializable { public class SeriesCreationParametersDTO implements Serializable {
private static final long serialVersionUID = -3205172230707607515L; private static final long serialVersionUID = -3205172230707607515L;
private List<FleetDTO> fleets; private List<FleetDTO> fleets;
private boolean medal; private boolean isMedal;
private boolean startsWithZero; private boolean isFleetsCanRunInParallel;
private boolean firstColumnIsNonDiscardableCarryForward; private boolean isStartsWithZeroScore;
private int[] discardingThresholds; private boolean firstColumnIsNonDiscardableCarryForward;
private boolean hasSplitFleetContiguousScoring; private int[] discardingThresholds;
SeriesCreationParametersDTO() {} private boolean hasSplitFleetContiguousScoring;
public SeriesCreationParametersDTO(List<FleetDTO> fleets, boolean medal, boolean startsWithZero, boolean firstColumnIsNonDiscardableCarryForward, SeriesCreationParametersDTO() {}
int[] discardingThresholds, boolean hasSplitFleetContiguousScoring) {
super(); public SeriesCreationParametersDTO(List<FleetDTO> fleets, boolean isMedal, boolean isFleetsCanRunInParallel, boolean isStartsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward,
this.fleets = fleets; int[] discardingThresholds, boolean hasSplitFleetContiguousScoring) {
this.medal = medal; super();
this.startsWithZero = startsWithZero; this.fleets = fleets;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring; this.isMedal = isMedal;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
this.discardingThresholds = discardingThresholds; this.isStartsWithZeroScore = isStartsWithZeroScore;
} this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
public List<FleetDTO> getFleets() { this.discardingThresholds = discardingThresholds;
return fleets; }
}
public List<FleetDTO> getFleets() {
public boolean isMedal() { return fleets;
return medal; }
}
public boolean isMedal() {
public boolean isStartsWithZero() { return isMedal;
return startsWithZero; }
}
public boolean isStartsWithZero() {
public boolean hasSplitFleetContiguousScoring() { return isStartsWithZeroScore;
return hasSplitFleetContiguousScoring; }
}
public boolean isFleetsCanRunInParallel() {
public int[] getDiscardingThresholds() { return isFleetsCanRunInParallel;
return discardingThresholds; }
}
public boolean hasSplitFleetContiguousScoring() {
public boolean isFirstColumnIsNonDiscardableCarryForward() { return hasSplitFleetContiguousScoring;
return firstColumnIsNonDiscardableCarryForward; }
}
public int[] getDiscardingThresholds() {
} return discardingThresholds;
}
public boolean isFirstColumnIsNonDiscardableCarryForward() {
return firstColumnIsNonDiscardableCarryForward;
}
}
@@ -14,6 +14,11 @@ public enum RaceLogRaceStatus {
RaceLogRaceStatus(int orderNumber) { RaceLogRaceStatus(int orderNumber) {
this.orderNumber = orderNumber; this.orderNumber = orderNumber;
} }
public static boolean isPreRunning(RaceLogRaceStatus status) {
return status != null &&
(status.equals(SCHEDULED) || status.equals(STARTPHASE));
}
public static boolean isActive(RaceLogRaceStatus status) { public static boolean isActive(RaceLogRaceStatus status) {
return status != null && return status != null &&
@@ -21,6 +26,11 @@ public enum RaceLogRaceStatus {
} }
public static boolean isRunningOrFinished(RaceLogRaceStatus status) {
return status != null &&
(status.equals(STARTPHASE) || status.equals(RUNNING) || status.equals(FINISHING) || status.equals(FINISHED));
}
public int getOrderNumber() { public int getOrderNumber() {
return orderNumber; return orderNumber;
} }
@@ -1189,6 +1189,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private Series loadSeries(DBObject dbSeries, TrackedRegattaRegistry trackedRegattaRegistry) { private Series loadSeries(DBObject dbSeries, TrackedRegattaRegistry trackedRegattaRegistry) {
String name = (String) dbSeries.get(FieldNames.SERIES_NAME.name()); String name = (String) dbSeries.get(FieldNames.SERIES_NAME.name());
boolean isMedal = (Boolean) dbSeries.get(FieldNames.SERIES_IS_MEDAL.name()); boolean isMedal = (Boolean) dbSeries.get(FieldNames.SERIES_IS_MEDAL.name());
// isFleetCanRunInParallel is a new field -> set to true when it does not exist in older db versions
boolean isFleetsCanRunInParallel = true;
Object isFleetCanRunInParallelObject = dbSeries.get(FieldNames.SERIES_IS_FLEETS_CAN_RUN_IN_PARALLEL.name());
if (isFleetCanRunInParallelObject != null) {
isFleetsCanRunInParallel = (Boolean) dbSeries.get(FieldNames.SERIES_IS_FLEETS_CAN_RUN_IN_PARALLEL.name());
}
Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name()); Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name());
Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries.get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name()); Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries.get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name());
Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name()); Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name());
@@ -1196,7 +1202,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
List<Fleet> fleets = loadFleets(dbFleets); List<Fleet> fleets = loadFleets(dbFleets);
BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name()); BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name());
Iterable<String> raceColumnNames = loadRaceColumnNames(dbRaceColumns); Iterable<String> raceColumnNames = loadRaceColumnNames(dbRaceColumns);
Series series = new SeriesImpl(name, isMedal, fleets, raceColumnNames, trackedRegattaRegistry); Series series = new SeriesImpl(name, isMedal, isFleetsCanRunInParallel, fleets, raceColumnNames, trackedRegattaRegistry);
if (dbSeries.get(FieldNames.SERIES_DISCARDING_THRESHOLDS.name()) != null) { if (dbSeries.get(FieldNames.SERIES_DISCARDING_THRESHOLDS.name()) != null) {
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, FieldNames.SERIES_DISCARDING_THRESHOLDS); ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, FieldNames.SERIES_DISCARDING_THRESHOLDS);
series.setResultDiscardingRule(resultDiscardingRule); series.setResultDiscardingRule(resultDiscardingRule);
@@ -36,6 +36,7 @@ public enum FieldNames {
REGATTA_USE_START_TIME_INFERENCE, REGATTA_USE_START_TIME_INFERENCE,
SERIES_NAME, SERIES_NAME,
SERIES_IS_MEDAL, SERIES_IS_MEDAL,
SERIES_IS_FLEETS_CAN_RUN_IN_PARALLEL,
SERIES_STARTS_WITH_ZERO_SCORE, SERIES_STARTS_WITH_ZERO_SCORE,
SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING, SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING,
SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD, SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD,
@@ -674,6 +674,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
DBObject dbSeries = new BasicDBObject(); DBObject dbSeries = new BasicDBObject();
dbSeries.put(FieldNames.SERIES_NAME.name(), s.getName()); dbSeries.put(FieldNames.SERIES_NAME.name(), s.getName());
dbSeries.put(FieldNames.SERIES_IS_MEDAL.name(), s.isMedal()); dbSeries.put(FieldNames.SERIES_IS_MEDAL.name(), s.isMedal());
dbSeries.put(FieldNames.SERIES_IS_FLEETS_CAN_RUN_IN_PARALLEL.name(), s.isFleetsCanRunInParallel());
dbSeries.put(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name(), s.hasSplitFleetContiguousScoring()); dbSeries.put(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name(), s.hasSplitFleetContiguousScoring());
dbSeries.put(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name(), s.isStartsWithZeroScore()); dbSeries.put(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name(), s.isStartsWithZeroScore());
dbSeries.put(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name(), s.isFirstColumnIsNonDiscardableCarryForward()); dbSeries.put(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name(), s.isFirstColumnIsNonDiscardableCarryForward());
@@ -76,7 +76,7 @@ public class CreateAndTrackWithRaceLogTest {
gpsFixStore = service.getGPSFixStore(); gpsFixStore = service.getGPSFixStore();
service.getMongoObjectFactory().getDatabase().dropDatabase(); service.getMongoObjectFactory().getDatabase().dropDatabase();
author = service.getServerAuthor(); author = service.getServerAuthor();
Series series = new SeriesImpl("series", false, Collections.singletonList(fleet), Collections.emptySet(), Series series = new SeriesImpl("series", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(fleet), Collections.emptySet(),
service); service);
regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser",
/* startDate */null, /* endDate */null, UUID.randomUUID(), Collections.<Series> singletonList(series), false, /* startDate */null, /* endDate */null, UUID.randomUUID(), Collections.<Series> singletonList(series), false,
@@ -0,0 +1,395 @@
package com.sap.sailing.domain.base.racegroup.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.racegroup.CurrentRaceFilter;
import com.sap.sailing.domain.base.racegroup.RaceCell;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleetRaceColumn;
import com.sap.sailing.domain.base.racegroup.RaceRow;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.domain.common.BoatClassMasterdata;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
public class CurrentRaceFilterImplTest {
private CurrentRaceFilter<SimpleFilterableRace> fixture;
private RaceGroup _505;
private RaceGroup yes;
private RaceGroup isaf;
private RaceGroup league;
private RaceGroup ess;
private Map<RaceGroupSeriesFleetRaceColumn, SimpleFilterableRace> races;
@Before
public void setUp() {
races = new HashMap<>();
// five regattas ("RaceGroup"s):
// - simple single-series, single fleet set-up (505 style)
// - single-series, two fleets (Yellow / Blue, as used by YES)
// - qualification with Yellow / Blue; final with Gold / Silver; Medal
// - league set-up with single series with three fleets Red/Green/Blue
_505 = create505Regatta();
races.putAll(getAllRaces(_505));
yes = createYesRegatta();
races.putAll(getAllRaces(yes));
isaf = createIsafRegatta();
races.putAll(getAllRaces(isaf));
league = createLeagueRegatta();
races.putAll(getAllRaces(league));
ess = createEssRegatta();
races.putAll(getAllRaces(ess));
fixture = new CurrentRaceFilterImpl<>(races.values());
}
private RaceGroup create505Regatta() {
final List<SeriesWithRows> _505Series = new ArrayList<>();
final List<RaceRow> _505RaceRows = new ArrayList<>();
final RaceRow _505RaceRow = createRaceRow(/* firstRaceColumnNumber */ 1, /* numberOfRaces */ 9, /* raceColumnNamePrefix */ "R", /* fleetName */ "Default");
_505RaceRows.add(_505RaceRow);
_505Series.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, _505RaceRows));
final RaceGroup _505 = new RaceGroupImpl("505", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._5O5.getDisplayName(), BoatClassMasterdata._5O5),
/* courseArea */ null, _505Series, /* regattaConfiguration */ null);
return _505;
}
private RaceGroup createYesRegatta() {
final List<SeriesWithRows> yesSeries = new ArrayList<>();
final List<RaceRow> yesRaceRows = Arrays.asList(createRaceRow(1, 9, "R", "Yellow"), createRaceRow(1, 9, "R", "Blue"));
yesSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, yesRaceRows));
final RaceGroup yes = new RaceGroupImpl("YES", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470),
/* courseArea */ null, yesSeries, /* regattaConfiguration */ null);
return yes;
}
private RaceGroup createIsafRegatta() {
final List<SeriesWithRows> isafSeries = new ArrayList<>();
final List<RaceRow> isafQualificationRaceRows = Arrays.asList(createRaceRow(1, 5, "Q", "Yellow"), createRaceRow(1, 5, "Q", "Blue"));
isafSeries.add(new SeriesWithRowsImpl("Qualification", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, isafQualificationRaceRows));
final List<RaceRow> isafFinalRaceRows = Arrays.asList(createRaceRow(6, 5, "F", "Gold"), createRaceRow(6, 5, "F", "Silver"));
isafSeries.add(new SeriesWithRowsImpl("Final", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, isafFinalRaceRows));
final List<RaceRow> isafMedalRaceRows = Arrays.asList(createRaceRow(1, 1, "M", "Medal"));
isafSeries.add(new SeriesWithRowsImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true, isafMedalRaceRows));
final RaceGroup isaf = new RaceGroupImpl("ISAF", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470),
/* courseArea */ null, isafSeries, /* regattaConfiguration */ null);
return isaf;
}
private RaceGroup createEssRegatta() {
final List<SeriesWithRows> essSeries = new ArrayList<>();
final List<RaceRow> essDay1RaceRows = Arrays.asList(createRaceRow(1, 5, "R", "Default"));
essSeries.add(new SeriesWithRowsImpl("Day 1", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essDay1RaceRows));
final List<RaceRow> essDay2RaceRows = Arrays.asList(createRaceRow(6, 5, "R", "Default"));
essSeries.add(new SeriesWithRowsImpl("Day 2", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essDay2RaceRows));
final List<RaceRow> essKnockoutQualificationRaceRows = Arrays.asList(createRaceRow(1, 1, "Q", "Yellow"), createRaceRow(1, 1, "Q", "Blue"));
essSeries.add(new SeriesWithRowsImpl("Knockout Qualification", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essKnockoutQualificationRaceRows));
final List<RaceRow> essKnockoutScoringRaceRows = Arrays.asList(createRaceRow(1, 1, "F", "Gold"), createRaceRow(1, 1, "F", "Silver"));
essSeries.add(new SeriesWithRowsImpl("Knockout Finals", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, essKnockoutScoringRaceRows));
final List<RaceRow> essDay3RaceRows = Arrays.asList(createRaceRow(11, 5, "R", "Default"));
essSeries.add(new SeriesWithRowsImpl("Day 3", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essDay3RaceRows));
final List<RaceRow> essDay4RaceRows = Arrays.asList(createRaceRow(16, 5, "R", "Default"));
essSeries.add(new SeriesWithRowsImpl("Day 4", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essDay4RaceRows));
final RaceGroup ess = new RaceGroupImpl("ESS", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata.EXTREME_40.getDisplayName(), BoatClassMasterdata.EXTREME_40),
/* courseArea */ null, essSeries, /* regattaConfiguration */ null);
return ess;
}
private RaceGroup createLeagueRegatta() {
final List<SeriesWithRows> leagueSeries = new ArrayList<>();
final List<RaceRow> leagueRaceRows = Arrays.asList(createRaceRow(1, 15, "F", "Red"), createRaceRow(1, 15, "F", "Green"), createRaceRow(1, 15, "F", "Blue"));
leagueSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, leagueRaceRows));
final RaceGroup league = new RaceGroupImpl("League", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata.J70.getDisplayName(), BoatClassMasterdata.J70),
/* courseArea */ null, leagueSeries, /* regattaConfiguration */ null);
return league;
}
private Map<RaceGroupSeriesFleetRaceColumn, SimpleFilterableRace> getAllRaces(RaceGroup raceGroup) {
final Map<RaceGroupSeriesFleetRaceColumn, SimpleFilterableRace> result = new HashMap<>();
int zeroBasedSeriesIndex = 0;
for (SeriesWithRows series : raceGroup.getSeries()) {
for (final RaceRow raceRow : series.getRaceRows()) {
int zeroBasedIndexInFleet = 0;
for (final RaceCell raceCell : raceRow.getCells()) {
final SimpleFilterableRace race = new SimpleFilterableRace(raceGroup, series, raceRow.getFleet(), zeroBasedIndexInFleet, zeroBasedSeriesIndex, raceCell.getName());
result.put(new RaceGroupSeriesFleetRaceColumn(race), race);
zeroBasedIndexInFleet++;
}
}
zeroBasedSeriesIndex++;
}
return result;
}
@Test
public void testRaceAccessAndStatusUpdate() {
SimpleFilterableRace isafQ4Yellow = get(isaf, "F7", "Gold");
assertNotNull(isafQ4Yellow);
assertEquals("F7", isafQ4Yellow.getRaceColumnName());
assertEquals("Gold", isafQ4Yellow.getFleet().getName());
assertEquals(1, isafQ4Yellow.getZeroBasedIndexInFleet());
assertEquals(1, isafQ4Yellow.getZeroBasedSeriesIndex());
assertEquals(RaceLogRaceStatus.UNSCHEDULED, isafQ4Yellow.getStatus());
isafQ4Yellow.setStatus(RaceLogRaceStatus.SCHEDULED);
assertEquals(RaceLogRaceStatus.SCHEDULED, isafQ4Yellow.getStatus());
}
@Test
public void testBasicFilteringForFirstUnscheduledRaces() {
// with all races unscheduled we can expect the first race of each regatta's first series to show
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
{
final Set<SimpleFilterableRace> current505Races = currentRaces.stream().filter(r->r.getRaceGroup() == _505).collect(Collectors.toSet());
assertEquals(1, current505Races.size());
final SimpleFilterableRace firstRace = current505Races.iterator().next();
assertEquals("R1", firstRace.getRaceColumnName());
}
{
final Set<SimpleFilterableRace> currentYesRaces = currentRaces.stream().filter(r->r.getRaceGroup() == yes).collect(Collectors.toSet());
assertEquals(2, currentYesRaces.size());
for (final SimpleFilterableRace currentYesRace : currentYesRaces) {
assertEquals("R1", currentYesRace.getRaceColumnName());
}
}
{
final Set<SimpleFilterableRace> currentIsafRaces = currentRaces.stream().filter(r->r.getRaceGroup() == isaf).collect(Collectors.toSet());
assertEquals(2, currentIsafRaces.size());
for (final SimpleFilterableRace currentYesRace : currentIsafRaces) {
assertEquals("Q1", currentYesRace.getRaceColumnName());
}
}
{
final Set<SimpleFilterableRace> currentLeagueRaces = currentRaces.stream().filter(r->r.getRaceGroup() == league).collect(Collectors.toSet());
assertEquals(1, currentLeagueRaces.size());
final SimpleFilterableRace firstRace = currentLeagueRaces.iterator().next();
assertEquals("Red", firstRace.getFleet().getName());
assertEquals("F1", firstRace.getRaceColumnName());
}
{
final Set<SimpleFilterableRace> currentEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == ess).collect(Collectors.toSet());
assertEquals(1, currentEssRaces.size());
final SimpleFilterableRace firstRace = currentEssRaces.iterator().next();
assertEquals("Default", firstRace.getFleet().getName());
assertEquals("R1", firstRace.getRaceColumnName());
}
}
@Test
public void testSchedulingFirst505Race() {
// with all races unscheduled we can expect the first race of each regatta's first series to show
get(_505, "R1", "Default").setStatus(RaceLogRaceStatus.SCHEDULED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> current505Races = currentRaces.stream().filter(r->r.getRaceGroup() == _505).collect(Collectors.toSet());
assertEquals(2, current505Races.size());
assertTrue(current505Races.contains(get(_505, "R1", "Default"))); // it's scheduled, so it shows
assertTrue(current505Races.contains(get(_505, "R2", "Default"))); // it's unscheduled and has a scheduled immediate predecessor
}
@Test
public void testSchedulingFirstIsafRace() {
// with all races unscheduled we can expect the first race of each regatta's first series to show
get(isaf, "Q1", "Yellow").setStatus(RaceLogRaceStatus.SCHEDULED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == isaf).collect(Collectors.toSet());
assertEquals(3, currentEssRaces.size());
assertTrue(currentEssRaces.contains(get(isaf, "Q1", "Yellow"))); // it's scheduled, so it shows
assertTrue(currentEssRaces.contains(get(isaf, "Q1", "Blue"))); // it's unscheduled, first in fleet
assertTrue(currentEssRaces.contains(get(isaf, "Q2", "Yellow"))); // predecessor Q1/Yellow is scheduled, so show this as next in fleet
}
@Test
public void testSchedulingFirstEssRace() {
// with all races unscheduled we can expect the first race of each regatta's first series to show
get(ess, "R1", "Default").setStatus(RaceLogRaceStatus.SCHEDULED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == ess).collect(Collectors.toSet());
assertEquals(2, currentEssRaces.size());
assertTrue(currentEssRaces.contains(get(ess, "R1", "Default"))); // it's scheduled, so it shows
assertTrue(currentEssRaces.contains(get(ess, "R2", "Default"))); // it's unscheduled, first after last scheduled
}
@Test
public void testFinishingAllEssRacesBeforeKnockoutQualification() {
for (int i=1; i<=10; i++) {
get(ess, "R"+i, "Default").setStatus(RaceLogRaceStatus.FINISHED);
}
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentIsaEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == ess).collect(Collectors.toSet());
assertEquals(2, currentIsaEssRaces.size());
assertTrue(currentIsaEssRaces.contains(get(ess, "Q1", "Yellow")));
assertTrue(currentIsaEssRaces.contains(get(ess, "Q1", "Blue")));
}
@Test
public void testFinishingAllEssRacesBeforeKnockoutQualificationAndSchedulingFirstKnockout() {
for (int i=1; i<=10; i++) {
get(ess, "R"+i, "Default").setStatus(RaceLogRaceStatus.FINISHED);
}
get(ess, "Q1", "Yellow").setStatus(RaceLogRaceStatus.SCHEDULED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentIsaEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == ess).collect(Collectors.toSet());
assertEquals(3, currentIsaEssRaces.size());
assertTrue(currentIsaEssRaces.contains(get(ess, "Q1", "Yellow"))); // scheduled
assertTrue(currentIsaEssRaces.contains(get(ess, "Q1", "Blue"))); // unscheduled
assertTrue(currentIsaEssRaces.contains(get(ess, "F1", "Gold"))); // first unscheduled in series; last column in preceding series has scheduled race
// F1/Silver remains invisible because the knockout finals runs its fleets in sequence
}
@Test
public void testFinishingAllEssRacesBeforeKnockoutFinals() {
for (int i=1; i<=10; i++) {
get(ess, "R"+i, "Default").setStatus(RaceLogRaceStatus.FINISHED);
}
get(ess, "Q1", "Yellow").setStatus(RaceLogRaceStatus.FINISHED);
get(ess, "Q1", "Blue").setStatus(RaceLogRaceStatus.FINISHED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentIsaEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == ess).collect(Collectors.toSet());
assertEquals(1, currentIsaEssRaces.size());
assertTrue(currentIsaEssRaces.contains(get(ess, "F1", "Gold"))); // first unscheduled in series; last column in preceding series has scheduled race
// F1/Silver remains invisible because the knockout finals runs its fleets in sequence
}
@Test
public void testFinishingAllEssRacesBeforeKnockoutFinalsAndGoldRaceStarted() {
for (int i=1; i<=10; i++) {
get(ess, "R"+i, "Default").setStatus(RaceLogRaceStatus.FINISHED);
}
get(ess, "Q1", "Yellow").setStatus(RaceLogRaceStatus.FINISHED);
get(ess, "Q1", "Blue").setStatus(RaceLogRaceStatus.FINISHED);
get(ess, "F1", "Gold").setStatus(RaceLogRaceStatus.RUNNING);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentIsaEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == ess).collect(Collectors.toSet());
assertEquals(2, currentIsaEssRaces.size());
assertTrue(currentIsaEssRaces.contains(get(ess, "F1", "Gold"))); // running
assertTrue(currentIsaEssRaces.contains(get(ess, "F1", "Silver"))); // first unscheduled with a predecessor that's running
}
@Test
public void testFinishingAllEssRacesBeforeKnockoutFinalsAndGoldRaceFinishedAndSilverRaceScheduled() {
for (int i=1; i<=10; i++) {
get(ess, "R"+i, "Default").setStatus(RaceLogRaceStatus.FINISHED);
}
get(ess, "Q1", "Yellow").setStatus(RaceLogRaceStatus.FINISHED);
get(ess, "Q1", "Blue").setStatus(RaceLogRaceStatus.FINISHED);
get(ess, "F1", "Gold").setStatus(RaceLogRaceStatus.FINISHED);
get(ess, "F1", "Silver").setStatus(RaceLogRaceStatus.SCHEDULED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentIsaEssRaces = currentRaces.stream().filter(r->r.getRaceGroup() == ess).collect(Collectors.toSet());
assertEquals(2, currentIsaEssRaces.size());
assertTrue(currentIsaEssRaces.contains(get(ess, "F1", "Silver"))); // scheduled
assertTrue(currentIsaEssRaces.contains(get(ess, "R11", "Default"))); // first unscheduled in series with the last race in the preceding series scheduled
}
@Test
public void testSchedulingAllIsafQualificationRacesButOne() {
for (int i=1; i<=4; i++) {
get(isaf, "Q"+i, "Yellow").setStatus(RaceLogRaceStatus.FINISHED);
get(isaf, "Q"+i, "Blue").setStatus(RaceLogRaceStatus.FINISHED);
}
get(isaf, "Q5", "Blue").setStatus(RaceLogRaceStatus.SCHEDULED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentIsafRaces = currentRaces.stream().filter(r->r.getRaceGroup() == isaf).collect(Collectors.toSet());
assertEquals(4, currentIsafRaces.size());
assertTrue(currentIsafRaces.contains(get(isaf, "Q5", "Yellow"))); // it's unscheduled; its predecessor Q4/Yellow is FINISHED; show it
assertTrue(currentIsafRaces.contains(get(isaf, "Q5", "Blue"))); // it's scheduled
assertTrue(currentIsafRaces.contains(get(isaf, "F6", "Gold"))); // Q5/Blue is a scheduled immediate predecessor
assertTrue(currentIsafRaces.contains(get(isaf, "F6", "Silver"))); // Q5/Blue is a scheduled immediate predecessor
}
@Test
public void testFinishingAllIsafQualificationAndFinalRaces() {
// the medal race shall be the only one remaining
for (int i=1; i<=5; i++) {
get(isaf, "Q"+i, "Yellow").setStatus(RaceLogRaceStatus.FINISHED);
get(isaf, "Q"+i, "Blue").setStatus(RaceLogRaceStatus.FINISHED);
get(isaf, "F"+(i+5), "Gold").setStatus(RaceLogRaceStatus.FINISHED);
get(isaf, "F"+(i+5), "Silver").setStatus(RaceLogRaceStatus.FINISHED);
}
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentIsafRaces = currentRaces.stream().filter(r->r.getRaceGroup() == isaf).collect(Collectors.toSet());
assertEquals(1, currentIsafRaces.size());
assertTrue(currentIsafRaces.contains(get(isaf, "M1", "Medal")));
}
@Test
public void testFinishingFirstLeagueRace() {
// with all races unscheduled we can expect the first race of each regatta's first series to show
get(league, "F1", "Red").setStatus(RaceLogRaceStatus.FINISHED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentLeagueRaces = currentRaces.stream().filter(r->r.getRaceGroup() == league).collect(Collectors.toSet());
assertEquals(1, currentLeagueRaces.size());
assertTrue(currentLeagueRaces.contains(get(league, "F1", "Green"))); // Red is finished, don't show it anymore
}
@Test
public void testSkippingSecondLeagueRace() {
// with all races unscheduled we can expect the first race of each regatta's first series to show
get(league, "F1", "Red").setStatus(RaceLogRaceStatus.FINISHED);
get(league, "F1", "Blue").setStatus(RaceLogRaceStatus.SCHEDULED);
final Set<SimpleFilterableRace> currentRaces = fixture.getCurrentRaces();
final Set<SimpleFilterableRace> currentLeagueRaces = currentRaces.stream().filter(r->r.getRaceGroup() == league).collect(Collectors.toSet());
assertEquals(3, currentLeagueRaces.size());
assertTrue(currentLeagueRaces.contains(get(league, "F1", "Green"))); // Red is finished, don't show it anymore; show the next one instead which is Green
assertTrue(currentLeagueRaces.contains(get(league, "F1", "Blue"))); // show a scheduled race
assertTrue(currentLeagueRaces.contains(get(league, "F2", "Red"))); // This is the next one after the scheduled F1/Blue
}
private SimpleFilterableRace get(RaceGroup raceGroup, String raceColumnName, String fleetName) {
for (final SeriesWithRows series : raceGroup.getSeries()) {
final Fleet fleet = getFleet(series, fleetName);
if (fleet != null) {
final RaceRow raceRow = series.getRaceRow(fleet);
if (raceRow != null) {
for (final RaceCell raceCell : raceRow.getCells()) {
if (raceCell.getName().equals(raceColumnName)) {
return races.get(new RaceGroupSeriesFleetRaceColumn(raceGroup, series, fleet, raceColumnName));
}
}
}
}
}
return null;
}
private Fleet getFleet(SeriesWithRows series, String fleetName) {
for (final Fleet fleet : series.getFleets()) {
if (fleet.getName().equals(fleetName)) {
return fleet;
}
}
return null;
}
private RaceRow createRaceRow(final int firstRaceColumnNumber, final int numberOfRaces,
final String raceColumnNamePrefix, final String fleetName) {
final List<RaceCell> _505RaceCells = new ArrayList<>();
for (int i=0; i < numberOfRaces; i++) {
_505RaceCells.add(new RaceCellImpl(raceColumnNamePrefix+(i+firstRaceColumnNumber),
/* race log */ null, /* factor */ firstRaceColumnNumber, /* explicitFactor */ null,
/* zeroBasedIndexInFleet */ i));
}
final RaceRowImpl _505RaceRow = new RaceRowImpl(new FleetImpl(fleetName), _505RaceCells);
return _505RaceRow;
}
}
@@ -0,0 +1,75 @@
package com.sap.sailing.domain.base.racegroup.impl;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.FilterableRace;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
public class SimpleFilterableRace implements FilterableRace {
private final RaceGroup raceGroup;
private final SeriesBase series;
private final Fleet fleet;
private RaceLogRaceStatus status;
private final int zeroBasedIndexInFleet;
private final int zeroBasedSeriesIndex;
private final String raceColumnName;
public SimpleFilterableRace(RaceGroup raceGroup, SeriesBase series, Fleet fleet, int zeroBasedIndexInFleet,
int zeroBasedSeriesIndex, String raceColumnName) {
super();
this.raceGroup = raceGroup;
this.series = series;
this.fleet = fleet;
this.zeroBasedIndexInFleet = zeroBasedIndexInFleet;
this.zeroBasedSeriesIndex = zeroBasedSeriesIndex;
this.raceColumnName = raceColumnName;
this.status = RaceLogRaceStatus.UNSCHEDULED;
}
@Override
public RaceGroup getRaceGroup() {
return raceGroup;
}
@Override
public SeriesBase getSeries() {
return series;
}
@Override
public Fleet getFleet() {
return fleet;
}
@Override
public RaceLogRaceStatus getStatus() {
return status;
}
public void setStatus(RaceLogRaceStatus status) {
this.status = status;
}
@Override
public int getZeroBasedIndexInFleet() {
return zeroBasedIndexInFleet;
}
@Override
public int getZeroBasedSeriesIndex() {
return zeroBasedSeriesIndex;
}
@Override
public String getRaceColumnName() {
return raceColumnName;
}
@Override
public String toString() {
return "SimpleFilterableRace ["+getRaceGroup().getName() + " - " + getSeries().getName() + " - " +
getFleet().getName() + " - " + getRaceColumnName() + "]";
}
}
@@ -44,9 +44,10 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
@Override @Override
protected RaceLogRaceStatus performAnalysis() { protected RaceLogRaceStatus performAnalysis() {
ArrayListNavigableSet<RaceLogRaceStatusEvent> statusEvents = new ArrayListNavigableSet<>(RaceLogRaceStatusEventComparator.INSTANCE); ArrayListNavigableSet<RaceLogRaceStatusEvent> statusEvents = new ArrayListNavigableSet<>(
for(RaceLogEvent event: getPassEvents()) { RaceLogRaceStatusEventComparator.INSTANCE);
if(event instanceof RaceLogRaceStatusEvent) { for (RaceLogEvent event : getPassEvents()) {
if (event instanceof RaceLogRaceStatusEvent) {
statusEvents.add((RaceLogRaceStatusEvent) event); statusEvents.add((RaceLogRaceStatusEvent) event);
} }
} }
@@ -54,7 +55,6 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
event.accept(eventDispatcher); event.accept(eventDispatcher);
return eventDispatcher.nextStatus; return eventDispatcher.nextStatus;
} }
return RaceLogRaceStatus.UNSCHEDULED; return RaceLogRaceStatus.UNSCHEDULED;
} }
@@ -79,15 +79,15 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
private void setRaceLogStatusBasedOnStartTime(TimePoint startTime) { private void setRaceLogStatusBasedOnStartTime(TimePoint startTime) {
if (startTime == null) { if (startTime == null) {
nextStatus = RaceLogRaceStatus.PRESCHEDULED; nextStatus = RaceLogRaceStatus.PRESCHEDULED;
return;
}
TimePoint now = clock.now();
if (racingProcedure.isStartphaseActive(startTime, now)) {
nextStatus = RaceLogRaceStatus.STARTPHASE;
} else if (now.before(startTime)) {
nextStatus = RaceLogRaceStatus.SCHEDULED;
} else { } else {
nextStatus = RaceLogRaceStatus.RUNNING; TimePoint now = clock.now();
if (racingProcedure.isStartphaseActive(startTime, now)) {
nextStatus = RaceLogRaceStatus.STARTPHASE;
} else if (now.before(startTime)) {
nextStatus = RaceLogRaceStatus.SCHEDULED;
} else {
nextStatus = RaceLogRaceStatus.RUNNING;
}
} }
} }
@@ -101,6 +101,6 @@ public class RaceStatusAnalyzer extends RaceLogAnalyzer<RaceLogRaceStatus> {
DependentStartTimeResolver startTimeResolver = new DependentStartTimeResolver(resolver); DependentStartTimeResolver startTimeResolver = new DependentStartTimeResolver(resolver);
TimePoint startTime = startTimeResolver.resolve(event).getStartTime(); TimePoint startTime = startTimeResolver.resolve(event).getStartTime();
setRaceLogStatusBasedOnStartTime(startTime); setRaceLogStatusBasedOnStartTime(startTime);
}; }
}; }
} }
@@ -33,5 +33,5 @@ public interface SeriesBase extends Renamable {
* made except that within a fleet the races are usually run in the order as defined by the {@link #getRaceColumns() * made except that within a fleet the races are usually run in the order as defined by the {@link #getRaceColumns()
* race columns}. * race columns}.
*/ */
boolean isFleetsRunInParallel(); boolean isFleetsCanRunInParallel();
} }
@@ -0,0 +1,50 @@
package com.sap.sailing.domain.base.racegroup;
import java.util.Comparator;
import com.sap.sse.common.Util;
import com.sap.sse.common.util.NaturalComparator;
/**
* When current races of a regatta are to be displayed to a user, a reasonable ordering is key to usability. This
* comparator sorts by their series index (day 1 before day 2; qualification before final, etc.), then by their index
* within the fleet (R1 before R2, etc.) and lastly by their fleet index (keeping the ordering as set on the server; not
* to be confused with the ranking ordering set for the fleet; so if on the server silver is placed before gold then
* that order is kept, regardless of the 1 and 2 values of these fleets). For a typical regatta with qualification,
* final and medal series, with race columns Q1..Q5, F6..F10, M and fleets Yellow/Blue and Silver/Gold, this would result
* in an ordering Q1-Yellow, Q1-Blue, ..., Q5-Yellow, Q5-Blue, F6-Silver, F6-Gold, ..., F10-Silver, F10-Gold, Medal.
*
* @author Axel Uhl (d043530)
*
*/
public class CurrentRaceComparator implements Comparator<FilterableRace> {
private final NaturalComparator naturalComparator = new NaturalComparator();
@Override
public int compare(FilterableRace o1, FilterableRace o2) {
int result = getRaceGroupDisplayName(o1).compareTo(getRaceGroupDisplayName(o2));
if (result == 0) {
result = o1.getZeroBasedSeriesIndex() - o2.getZeroBasedSeriesIndex();
if (result == 0) {
result = o1.getZeroBasedIndexInFleet() - o2.getZeroBasedIndexInFleet();
if (result == 0) {
result = naturalComparator.compare(o1.getRaceColumnName(), o2.getRaceColumnName());
if (result == 0) {
result = Util.indexOf(o1.getSeries().getFleets(), o1.getFleet()) - Util.indexOf(o2.getSeries().getFleets(), o2.getFleet());
}
}
}
}
return result;
}
private String getRaceGroupDisplayName(FilterableRace r) {
final String result;
if (r.getRaceGroup().getDisplayName() != null) {
result = r.getRaceGroup().getDisplayName();
} else {
result = r.getRaceGroup().getName();
}
return result;
}
}
@@ -0,0 +1,17 @@
package com.sap.sailing.domain.base.racegroup;
import java.util.Set;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
/**
* Given a set of races organized in fleets, series and regattas, this filter determines those races
* relevant for a race officer based on the races' {@link RaceState state} and their place in the
* regatta structure.
*
* @author Charlotte Proeller (D062347)
* @author Axel Uhl (d043530)
*/
public interface CurrentRaceFilter<T extends FilterableRace> {
Set<T> getCurrentRaces();
}
@@ -0,0 +1,45 @@
package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sse.common.Util;
/**
* Describes the properties by which races may be filtered. This encompasses, in particular, the race's place
* in the regatta structure
* @author Axel Uhl (d043530)
*
*/
public interface FilterableRace {
RaceGroup getRaceGroup();
SeriesBase getSeries();
Fleet getFleet();
/**
* @return the status of the race's state.
*/
RaceLogRaceStatus getStatus();
/**
* A Series offers a sequence of RaceColumns, each of them split according to the Fleets modeled for the Series. A
* {@link RaceCell} describes a "slot" in this grid, defined by the series, the fleet and the race column. While
* this object's {@link #getName() name} represents the race column's name, this doesn't tell anything about the
* "horizontal" position in the "grid" or in other words what the index is of the race column in which this cell
* lies.
* <p>
*
* Indices returned by this method start with zero, meaning the first race column in the series. This corresponds to
* what one would get by asking {@link Util#indexOf(Iterable, Object) Util.indexOf(series.getRaceColumns(),
* thisCellsRaceColumn)}, except in case the first race column is a "virtual" one that holds a non-discardable
* carry-forward result. In this case, the second Race Column, which is the first "non-virtual" one, receives
* index 0.
*/
int getZeroBasedIndexInFleet();
int getZeroBasedSeriesIndex();
String getRaceColumnName();
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.base.SeriesBase;
public interface IsFleetFragment extends RaceGroupFragment {
SeriesBase getSeries();
}
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
public interface IsRaceFragment extends RaceGroupFragment {
RaceLogRaceStatus getCurrentStatus();
}
@@ -2,9 +2,10 @@ package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sse.common.Named; import com.sap.sse.common.Named;
import com.sap.sse.common.Util;
/** /**
* A "race". * A "race". Its {@link #getName() name} represents the Race Column name coming from the leaderboard / {@link RaceGroup}.
* <p> * <p>
* *
* Because this and all other {@link RaceGroup} interfaces are used for communication with the Android applications a * Because this and all other {@link RaceGroup} interfaces are used for communication with the Android applications a
@@ -16,4 +17,20 @@ public interface RaceCell extends Named {
double getFactor(); double getFactor();
Double getExplicitFactor(); Double getExplicitFactor();
/**
* A Series offers a sequence of RaceColumns, each of them split according to the Fleets modeled for the Series. A
* {@link RaceCell} describes a "slot" in this grid, defined by the series, the fleet and the race column. While
* this object's {@link #getName() name} represents the race column's name, this doesn't tell anything about the
* "horizontal" position in the "grid" or in other words what the index is of the race column in which this cell
* lies.
* <p>
*
* Indices returned by this method start with zero, meaning the first race column in the series. This corresponds to
* what one would get by asking {@link Util#indexOf(Iterable, Object) Util.indexOf(series.getRaceColumns(),
* thisCellsRaceColumn)}, except in case the first race column is a "virtual" one that holds a non-discardable
* carry-forward result. In this case, the second Race Column, which is the first "non-virtual" one, receives
* index 0.
*/
int getZeroBasedIndexInFleet();
} }
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.base.racegroup; package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseArea;
@@ -17,7 +18,9 @@ import com.sap.sse.common.Named;
* *
* To align with the semantics of the Android application a {@link RaceGroup} consists of race rows rather than race * To align with the semantics of the Android application a {@link RaceGroup} consists of race rows rather than race
* columns. A {@link RaceRow} is a set of races for one {@link Fleet}. A specific "race" of a row is called * columns. A {@link RaceRow} is a set of races for one {@link Fleet}. A specific "race" of a row is called
* {@link RaceCell}. * {@link RaceCell}. (Note that as described by bug 3011 this view is by now obsolete. The grouping by fleet is
* no longer desired by users; instead, grouping is desired only by regatta / {@link RaceGroup}, whereas <em>ordering</em>
* and <em>filtering</em> is based on series and fleet pertinence and {@link RaceState race state}, respectively.)
* <p> * <p>
* *
* For example: if you have 2 fleets (blue and yellow) there will be 2 {@link RaceRow}s in your {@link RaceGroup} * For example: if you have 2 fleets (blue and yellow) there will be 2 {@link RaceRow}s in your {@link RaceGroup}
@@ -39,7 +42,8 @@ public interface RaceGroup extends Named {
public BoatClass getBoatClass(); public BoatClass getBoatClass();
/** /**
* Gets the collection of series. * Gets the collection of series, for a {@link RaceGroup} representing a regatta in the same order as
* the {@code Regatta.getSeries()} call returns them.
*/ */
public Iterable<SeriesWithRows> getSeries(); public Iterable<SeriesWithRows> getSeries();
@@ -0,0 +1,5 @@
package com.sap.sailing.domain.base.racegroup;
public interface RaceGroupFragment {
}
@@ -1,33 +1,36 @@
package com.sap.sailing.racecommittee.app.domain.impl; package com.sap.sailing.domain.base.racegroup;
import android.text.TextUtils;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase; import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.common.LeaderboardNameConstants; import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.racecommittee.app.domain.ManagedRace;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
public class RaceGroupSeriesFleet { /**
* Represents a pair of {@link RaceGroup} (representing a regatta or a flexible leaderboard) and {@link SeriesBase
* series}. An instance can be constructed for a {@link ManagedRace} which then extracts these two properties from the
* race. Note that equal objects of this type can result for different races as long as they are in the equal
* {@link RaceGroup} and {@link SeriesBase series}.
*
* @author Axel Uhl (d043530)
*
*/
public class RaceGroupSeries {
private RaceGroup raceGroup; private final RaceGroup raceGroup;
private Fleet fleet; private final SeriesBase series;
private SeriesBase series; private final int seriesOrder;
private int seriesOrder;
private int fleetOrder;
public RaceGroupSeriesFleet(ManagedRace race) { public RaceGroupSeries(FilterableRace race) {
raceGroup = race.getRaceGroup(); this(race.getRaceGroup(), race.getSeries());
series = race.getSeries();
fleet = race.getFleet();
seriesOrder = getSeriesIndex(race, series);
fleetOrder = getFleetIndex(series.getFleets(), race.getFleet());
} }
private static int getSeriesIndex(ManagedRace race, SeriesBase series) { public RaceGroupSeries(RaceGroup raceGroup, SeriesBase series) {
return Util.indexOf(race.getRaceGroup().getSeries(), series); this.raceGroup = raceGroup;
this.series = series;
seriesOrder = getSeriesIndex(raceGroup, series);
}
private static int getSeriesIndex(RaceGroup raceGroup, SeriesBase series) {
return Util.indexOf(raceGroup.getSeries(), series);
} }
public RaceGroup getRaceGroup() { public RaceGroup getRaceGroup() {
@@ -38,10 +41,6 @@ public class RaceGroupSeriesFleet {
return series; return series;
} }
public Fleet getFleet() {
return fleet;
}
public String getRaceGroupName() { public String getRaceGroupName() {
return raceGroup.getName(); return raceGroup.getName();
} }
@@ -50,25 +49,18 @@ public class RaceGroupSeriesFleet {
return series.getName(); return series.getName();
} }
public String getFleetName() {
return fleet.getName();
}
public String getDisplayName() { public String getDisplayName() {
return getDisplayName(false); return getDisplayName(false);
} }
public String getDisplayName(boolean useDisplayName) { public String getDisplayName(boolean useDisplayName) {
String name = raceGroup.getDisplayName(); String name = raceGroup.getDisplayName();
if (!useDisplayName || TextUtils.isEmpty(name)) { if (!useDisplayName || name == null || name.length() == 0) {
name = raceGroup.getName(); name = raceGroup.getName();
} }
if (series != null && !series.getName().equals(LeaderboardNameConstants.DEFAULT_SERIES_NAME)) { if (series != null && !series.getName().equals(LeaderboardNameConstants.DEFAULT_SERIES_NAME)) {
name += " - " + series.getName(); name += " - " + series.getName();
} }
if (fleet != null && !fleet.getName().equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) {
name += " - " + fleet.getName();
}
return name; return name;
} }
@@ -76,19 +68,13 @@ public class RaceGroupSeriesFleet {
return seriesOrder; return seriesOrder;
} }
public int getFleetOrder() {
return fleetOrder;
}
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((raceGroup == null) ? 0 : raceGroup.hashCode()); result = prime * result + ((raceGroup == null) ? 0 : raceGroup.hashCode());
result = prime * result + ((series == null) ? 0 : series.hashCode()); result = prime * result + ((series == null) ? 0 : series.hashCode());
result = prime * result + ((fleet == null) ? 0 : fleet.hashCode());
result = prime * result + seriesOrder; result = prime * result + seriesOrder;
result = prime * result + fleetOrder;
return result; return result;
} }
@@ -100,7 +86,7 @@ public class RaceGroupSeriesFleet {
return false; return false;
if (getClass() != obj.getClass()) if (getClass() != obj.getClass())
return false; return false;
RaceGroupSeriesFleet other = (RaceGroupSeriesFleet) obj; RaceGroupSeries other = (RaceGroupSeries) obj;
if (raceGroup == null) { if (raceGroup == null) {
if (other.raceGroup != null) { if (other.raceGroup != null) {
return false; return false;
@@ -108,13 +94,6 @@ public class RaceGroupSeriesFleet {
} else if (!raceGroup.equals(other.raceGroup)) { } else if (!raceGroup.equals(other.raceGroup)) {
return false; return false;
} }
if (fleet == null) {
if (other.fleet != null) {
return false;
}
} else if (!fleet.equals(other.fleet)) {
return false;
}
if (series == null) { if (series == null) {
if (other.series != null) { if (other.series != null) {
return false; return false;
@@ -125,13 +104,6 @@ public class RaceGroupSeriesFleet {
if (seriesOrder != other.seriesOrder) { if (seriesOrder != other.seriesOrder) {
return false; return false;
} }
if (fleetOrder != other.fleetOrder) {
return false;
}
return true; return true;
} }
private int getFleetIndex(Iterable<? extends Fleet> fleets, Fleet fleet) {
return Util.indexOf(fleets, fleet);
}
} }
@@ -0,0 +1,29 @@
package com.sap.sailing.domain.base.racegroup;
import java.util.Comparator;
/**
* When current races of a regatta are to be displayed to a user, a reasonable ordering is key to usability. This
* comparator sorts by their series index (day 1 before day 2; qualification before final, etc.), then by their index
* within the fleet (R1 before R2, etc.) and lastly by their fleet index (keeping the ordering as set on the server; not
* to be confused with the ranking ordering set for the fleet; so if on the server silver is placed before gold then
* that order is kept, regardless of the 1 and 2 values of these fleets). For a typical regatta with qualification,
* final and medal series, with race columns Q1..Q5, F6..F10, M and fleets Yellow/Blue and Silver/Gold, this would result
* in an ordering Q1-Yellow, Q1-Blue, ..., Q5-Yellow, Q5-Blue, F6-Silver, F6-Gold, ..., F10-Silver, F10-Gold, Medal.
*
* @author Axel Uhl (d043530)
*
*/
public class RaceGroupSeriesComparator implements Comparator<RaceGroupSeries> {
@Override
public int compare(RaceGroupSeries o1, RaceGroupSeries o2) {
int result = o1.getRaceGroup().getName().compareTo(o2.getRaceGroup().getName());
if (result == 0) {
result = o1.getSeriesOrder() - o2.getSeriesOrder();
if (result == 0) {
result = o1.getDisplayName().compareTo(o2.getDisplayName());
}
}
return result;
}
}
@@ -0,0 +1,88 @@
package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sse.common.Util;
/**
* Represents a triple of {@link RaceGroup} (representing a regatta or a flexible leaderboard), a {@link SeriesBase series}
* and a {@link Fleet}. An instance can be constructed for a {@link ManagedRace} which then extracts these three properties
* from the race. Note that equal objects of this type can result for different races as long as they are in the equal
* {@link RaceGroup}, {@link SeriesBase series} and {@link Fleet}.
*
* @author Axel Uhl (d043530)
*
*/
public class RaceGroupSeriesFleet extends RaceGroupSeries {
private final Fleet fleet;
private final int fleetOrder;
public RaceGroupSeriesFleet(FilterableRace race) {
this(race.getRaceGroup(), race.getSeries(), race.getFleet());
}
public RaceGroupSeriesFleet(RaceGroup raceGroup, SeriesBase series, Fleet fleet) {
super(raceGroup, series);
this.fleet = fleet;
fleetOrder = getFleetIndex(series.getFleets(), fleet);
}
private int getFleetIndex(Iterable<? extends Fleet> fleets, Fleet fleet) {
return Util.indexOf(fleets, fleet);
}
public Fleet getFleet() {
return fleet;
}
public String getFleetName() {
return fleet.getName();
}
public String getDisplayName() {
return getDisplayName(false);
}
public String getDisplayName(boolean useDisplayName) {
String name = super.getDisplayName(useDisplayName);
if (fleet != null && !fleet.getName().equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) {
name += " - " + fleet.getName();
}
return name;
}
public int getFleetOrder() {
return fleetOrder;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((fleet == null) ? 0 : fleet.hashCode());
result = prime * result + fleetOrder;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
RaceGroupSeriesFleet other = (RaceGroupSeriesFleet) obj;
if (fleet == null) {
if (other.fleet != null)
return false;
} else if (!fleet.equals(other.fleet))
return false;
if (fleetOrder != other.fleetOrder)
return false;
return true;
}
}
@@ -0,0 +1,61 @@
package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
/**
* Represents a quadruple of {@link RaceGroup} (representing a regatta or a flexible leaderboard), a {@link SeriesBase
* series}, a {@link Fleet} and a race column, identified by its unique name within the leaderboard. An instance can be
* constructed for a {@link FilterableRace} which then extracts these four properties from the race. Equal
* objects of this type can only result for equal races because they identify a "race slot" in a regatta.
*
* @author Axel Uhl (d043530)
*
*/
public class RaceGroupSeriesFleetRaceColumn extends RaceGroupSeriesFleet {
private final String raceColumnName;
public RaceGroupSeriesFleetRaceColumn(FilterableRace race) {
super(race);
raceColumnName = race.getRaceColumnName();
}
public RaceGroupSeriesFleetRaceColumn(RaceGroup raceGroup, SeriesBase series, Fleet fleet, String raceColumnName) {
super(raceGroup, series, fleet);
this.raceColumnName = raceColumnName;
}
public String getRaceColumnName() {
return raceColumnName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((raceColumnName == null) ? 0 : raceColumnName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
RaceGroupSeriesFleetRaceColumn other = (RaceGroupSeriesFleetRaceColumn) obj;
if (raceColumnName == null) {
if (other.raceColumnName != null)
return false;
} else if (!raceColumnName.equals(other.raceColumnName))
return false;
return true;
}
@Override
public String toString() {
return getRaceGroupName() + " - " + getSeriesName() + " - " + getFleetName() + " - " + getRaceColumnName();
}
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.base.racegroup; package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase; import com.sap.sailing.domain.base.SeriesBase;
/** /**
@@ -11,5 +12,7 @@ public interface SeriesWithRows extends SeriesBase {
/** /**
* All {@link RaceRow}s of this series. * All {@link RaceRow}s of this series.
*/ */
public Iterable<RaceRow> getRaceRows(); Iterable<RaceRow> getRaceRows();
RaceRow getRaceRow(Fleet fleet);
} }
@@ -0,0 +1,193 @@
package com.sap.sailing.domain.base.racegroup.impl;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.CurrentRaceFilter;
import com.sap.sailing.domain.base.racegroup.FilterableRace;
import com.sap.sailing.domain.base.racegroup.RaceCell;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleetRaceColumn;
import com.sap.sailing.domain.base.racegroup.RaceRow;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sse.common.Util;
/**
* Determines a subset from a set of {@link FilterableRace} objects based on a definition of "being current." Current
* races are interesting for a race officer to deal with. For example, the next set of races that are eligible for being
* scheduled are part of this set; furthermore all running races. See {@link #getCurrentRaces()} for a more formal
* definition what this filter considers to be a "current" race.
* <p>
*
* An instance of this type is initialized with the full set of races out of which the "current" subset shall be
* extracted by the {@link #getCurrentRaces()} method. The full set of races is expected to skip no race columns and no
* fleets and no series, or in other words, hold all races that form the regatta structure. This is important for the
* "predecessor" relation which uses the fleet and series indices, such as
* {@link FilterableRace#getZeroBasedIndexInFleet()} and {@link FilterableRace#getZeroBasedSeriesIndex()}.
*
* @author Axel Uhl (d043530)
*
* @param <T>
*/
public class CurrentRaceFilterImpl<T extends FilterableRace> implements CurrentRaceFilter<T> {
private final Iterable<T> allRaces;
private final Map<RaceGroupSeriesFleetRaceColumn, T> racesByRaceGroupSeriesFleet;
/**
* Constructs a filter that can determine "current races" from a set of races representing all races of a set of
* regattas / race groups. A copy of {@code allRaces} is constructed here, so later outside modifications to
* {@code allRaces} will not have any effect on this filter. Clients need to construct a new instance if the set of
* all races has changed.
*/
public CurrentRaceFilterImpl(Iterable<T> allRaces) {
final Collection<T> copyOfAllRaces = new HashSet<>();
Util.addAll(allRaces, copyOfAllRaces);
this.allRaces = copyOfAllRaces;
this.racesByRaceGroupSeriesFleet = new HashMap<>();
for (final T race : allRaces) {
racesByRaceGroupSeriesFleet.put(new RaceGroupSeriesFleetRaceColumn(race), race);
}
}
/**
* A race is considered "current" and shall be shown if its status is between (inclusive)
* {@link RaceLogRaceStatus#SCHEDULED} and {@link RaceLogRaceStatus#FINISHING}, or if the race
* has no predecessor in the regatta / leaderboard and therefore could start the regatta,
* or if the race has an immediate predecessor (a race A that must precede the race in question, say X,
* such that there is no other race B that also must precede X with A having to precede B) which
* is at least in state {@link RaceLogRaceStatus#SCHEDULED}.
*/
@Override
public Set<T> getCurrentRaces() {
final Set<T> filteredRaces = new HashSet<>();
for (final T race : allRaces) {
if (race.getStatus().getOrderNumber() <= RaceLogRaceStatus.FINISHING.getOrderNumber()) { // don't show finished races
if (race.getStatus().getOrderNumber() >= RaceLogRaceStatus.SCHEDULED.getOrderNumber()) {
filteredRaces.add(race);
} else {
final boolean hasNoPredecessor=hasNoPredecessor(race);
if ((hasNoPredecessor && race.getStatus() == RaceLogRaceStatus.UNSCHEDULED) ||
(!hasNoPredecessor && hasImmediatePredecessorThatIsAtLeastScheduled(race))) {
// show the race
filteredRaces.add(race);
}
}
}
}
return filteredRaces;
}
/**
* For a race to have no predecessor in the regatta structure it has to be located in the first race column in the first series.
* If the races of the different fleets in the series may {@link SeriesBase#isFleetsRunInParallel() run in parallel} then this is
* already sufficient. If the races of the different fleets in the series are run in sequence, the race must additionally be
* in the first fleet of the series.
*/
private boolean hasNoPredecessor(FilterableRace race) {
return race.getZeroBasedSeriesIndex() == 0 && race.getZeroBasedIndexInFleet() == 0 &&
(race.getSeries().isFleetsCanRunInParallel() || Util.indexOf(race.getSeries().getFleets(), race.getFleet()) == 0);
}
/**
* Two cases need to be distinguished: the races in different fleets of the same series may be run
* in parallel or not.<p>
*
* Parallel fleets in series: the immediate predecessor is the race in the same fleet in the immediately preceding
* race column; or if {@code race} is in the first column of its series, all races in the last column of the immediately
* preceding series.<p>
*
* Sequential fleets in series: the immediate predecessor is the race that immediately precedes {@code race} in
* the list of races constructed by looping over all columns of the series and for each column looping over all
* fleets. If {@code race} is the first in this sequence of its series, then the immediate predecessors is/are the
* last race(s) (multiple if the previous series runs its fleets in parallel) of the immediately preceding series.<p>
*
* Precondition: {@code !hasNoPredecessor(race)}
*
* @return whether any of the predecessors (of which there is least one because of the precondition) have a
* {@link RaceLogRaceStatus race status} whose {@link RaceLogRaceStatus#getOrderNumber() order number} is at least
* {@link RaceLogRaceStatus#SCHEDULED SCHEDULED.getOrderNumber()}.
*/
private boolean hasImmediatePredecessorThatIsAtLeastScheduled(FilterableRace race) {
final Set<FilterableRace> immediatePredecessors = new HashSet<>();
final RaceGroup raceGroup = race.getRaceGroup();
if (race.getSeries().isFleetsCanRunInParallel()) {
if (race.getZeroBasedIndexInFleet() == 0) {
// it's in the first column; for fleets that may run in parallel, the immediate predecessors are
// in the last column of the immediately preceding series:
Util.addAll(getLastRacesFromImmediatelyPrecedingSeries(race, raceGroup), immediatePredecessors);
} else {
// not the first race column in its fleet; for parallel fleets, only the immediate predecessor
// in the same fleet is an immediate predecessor for race
RaceRow raceRow = Util.get(raceGroup.getSeries(), race.getZeroBasedSeriesIndex()).getRaceRow(race.getFleet());
RaceCell previousCellInFleet = Util.get(raceRow.getCells(), race.getZeroBasedIndexInFleet()-1);
immediatePredecessors.add(racesByRaceGroupSeriesFleet.get(
new RaceGroupSeriesFleetRaceColumn(raceGroup, race.getSeries(), race.getFleet(), previousCellInFleet.getName())));
}
} else {
// race's series is run sequentially, within one race column one fleet after the other, in the order of the
// fleets as specified by the series; the immediate predecessor is within this series the previous element in this
// ordering, or the last race(s) from the immediately preceding series if race is in the first column's first fleet.
final int zeroBasedIndexOfFleetInSeries = Util.indexOf(race.getSeries().getFleets(), race.getFleet());
if (race.getZeroBasedIndexInFleet() == 0 && zeroBasedIndexOfFleetInSeries == 0) {
Util.addAll(getLastRacesFromImmediatelyPrecedingSeries(race, raceGroup), immediatePredecessors);
} else {
final Fleet fleetOfImmediatelyPrecedingRace;
final int zeroBasedIndexOfImmediatelyPrecedingRaceInFleet;
if (zeroBasedIndexOfFleetInSeries == 0) {
// race is in first fleet; wrap around to last fleet in immediately preceding column
fleetOfImmediatelyPrecedingRace = Util.last(race.getSeries().getFleets());
zeroBasedIndexOfImmediatelyPrecedingRaceInFleet = race.getZeroBasedIndexInFleet() - 1;
} else {
fleetOfImmediatelyPrecedingRace = Util.get(race.getSeries().getFleets(), zeroBasedIndexOfFleetInSeries-1);
zeroBasedIndexOfImmediatelyPrecedingRaceInFleet = race.getZeroBasedIndexInFleet();
}
final RaceGroupSeriesFleetRaceColumn identifierOfImmediatelyPrecedingRace =
new RaceGroupSeriesFleetRaceColumn(raceGroup, race.getSeries(), fleetOfImmediatelyPrecedingRace,
Util.get(Util.get(raceGroup.getSeries(), race.getZeroBasedSeriesIndex()).getRaceRow(
fleetOfImmediatelyPrecedingRace).getCells(), zeroBasedIndexOfImmediatelyPrecedingRaceInFleet).getName());
immediatePredecessors.add(racesByRaceGroupSeriesFleet.get(identifierOfImmediatelyPrecedingRace));
}
}
final Iterator<FilterableRace> i = immediatePredecessors.iterator();
boolean result = false;
while (!result && i.hasNext()) {
final RaceLogRaceStatus nextStatus = i.next().getStatus();
result = nextStatus.getOrderNumber() >= RaceLogRaceStatus.SCHEDULED.getOrderNumber();
}
return result;
}
private Iterable<T> getLastRacesFromImmediatelyPrecedingSeries(FilterableRace race, final RaceGroup raceGroup) {
final Set<T> lastRacesInImmediatelyPrecedingSeries = new HashSet<>();
final SeriesWithRows immediatelyPrecedingSeries = Util.get(raceGroup.getSeries(), race.getZeroBasedSeriesIndex()-1);
if (immediatelyPrecedingSeries.isFleetsCanRunInParallel()) {
// add all last races of all fleets in immediately preceding series because they may be run in parallel
for (final RaceRow rowInImmediatelyPrecedingSeries : immediatelyPrecedingSeries.getRaceRows()) {
final T lastRaceInRow = getLastRaceInRow(raceGroup, immediatelyPrecedingSeries, rowInImmediatelyPrecedingSeries);
lastRacesInImmediatelyPrecedingSeries.add(lastRaceInRow);
}
} else {
// add only the last race of the last fleet in the immediately preceding series
final RaceRow lastRowInImmediatelyPrecedingSeries = Util.last(immediatelyPrecedingSeries.getRaceRows());
lastRacesInImmediatelyPrecedingSeries.add(getLastRaceInRow(raceGroup, immediatelyPrecedingSeries, lastRowInImmediatelyPrecedingSeries));
}
return lastRacesInImmediatelyPrecedingSeries;
}
private T getLastRaceInRow(final RaceGroup raceGroup, final SeriesWithRows immediatelyPrecedingSeries,
final RaceRow rowInImmediatelyPrecedingSeries) {
final RaceCell lastCell = Util.last(rowInImmediatelyPrecedingSeries.getCells());
RaceGroupSeriesFleetRaceColumn identifierOfALastRaceInImmediatelyPrecedingSeries =
new RaceGroupSeriesFleetRaceColumn(raceGroup, immediatelyPrecedingSeries,
rowInImmediatelyPrecedingSeries.getFleet(), lastCell.getName());
final T lastRaceInRow = racesByRaceGroupSeriesFleet.get(identifierOfALastRaceInImmediatelyPrecedingSeries);
return lastRaceInRow;
}
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.base.racegroup.impl;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.base.racegroup.RaceCell; import com.sap.sailing.domain.base.racegroup.RaceCell;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.NamedImpl; import com.sap.sse.common.impl.NamedImpl;
public class RaceCellImpl extends NamedImpl implements RaceCell { public class RaceCellImpl extends NamedImpl implements RaceCell {
@@ -18,12 +19,27 @@ public class RaceCellImpl extends NamedImpl implements RaceCell {
* See {@code RaceColumn.getExplicitFactor()} * See {@code RaceColumn.getExplicitFactor()}
*/ */
private final Double explicitFactor; private final Double explicitFactor;
private final int zeroBasedIndexInFleet;
public RaceCellImpl(String name, RaceLog raceLog, double factor, Double explicitFactor) { /**
super(name); * @param zeroBasedIndexInFleet
* A Series offers a sequence of RaceColumns, each of them split according to the Fleets modeled for the
* Series. A {@link RaceCell} describes a "slot" in this grid, defined by the series, the fleet and the
* race column. While this object's {@link #getName() name} represents the race column's name, this
* doesn't tell anything about the "horizontal" position in the "grid" or in other words what the index
* is of the race column in which this cell lies. Indices returned by this method start with zero,
* meaning the first race column in the series. This corresponds to what one would get by asking
* {@link Util#indexOf(Iterable, Object) Util.indexOf(series.getRaceColumns(), thisCellsRaceColumn)},
* except in case the first race column is a "virtual" one that holds a non-discardable carry-forward
* result. In this case, the second Race Column, which is the first "non-virtual" one, receives index 0.
*/
public RaceCellImpl(String raceColumnName, RaceLog raceLog, double factor, Double explicitFactor, int zeroBasedIndexInFleet) {
super(raceColumnName);
this.raceLog = raceLog; this.raceLog = raceLog;
this.factor = factor; this.factor = factor;
this.explicitFactor = explicitFactor; this.explicitFactor = explicitFactor;
this.zeroBasedIndexInFleet = zeroBasedIndexInFleet;
} }
@Override @Override
@@ -48,4 +64,15 @@ public class RaceCellImpl extends NamedImpl implements RaceCell {
public Double getExplicitFactor() { public Double getExplicitFactor() {
return explicitFactor; return explicitFactor;
} }
@Override
public int getZeroBasedIndexInFleet() {
return zeroBasedIndexInFleet;
}
@Override
public String toString() {
return "RaceCellImpl [factor=" + factor + ", explicitFactor=" + explicitFactor + ", zeroBasedIndexInFleet="
+ zeroBasedIndexInFleet + ", getName()=" + getName() + "]";
}
} }
@@ -17,6 +17,11 @@ public class RaceGroupImpl extends NamedImpl implements RaceGroup {
private final Iterable<SeriesWithRows> series; private final Iterable<SeriesWithRows> series;
private final RegattaConfiguration regattaConfiguration; private final RegattaConfiguration regattaConfiguration;
/**
* @param series
* the series; for a regatta they are expected to be in the same order as {@code Regatta.getSeries()}
* would deliver them.
*/
public RaceGroupImpl(String name, String displayName, BoatClass boatClass, CourseArea courseArea, public RaceGroupImpl(String name, String displayName, BoatClass boatClass, CourseArea courseArea,
Iterable<SeriesWithRows> series, RegattaConfiguration regattaConfiguration) { Iterable<SeriesWithRows> series, RegattaConfiguration regattaConfiguration) {
super(name); super(name);
@@ -9,6 +9,10 @@ public class RaceRowImpl implements RaceRow {
private Fleet fleet; private Fleet fleet;
private Iterable<RaceCell> races; private Iterable<RaceCell> races;
/**
* @param races
* expected to be provided in the order in which the race columns appear in the series / leaderboard
*/
public RaceRowImpl(Fleet fleet, Iterable<RaceCell> races) { public RaceRowImpl(Fleet fleet, Iterable<RaceCell> races) {
this.fleet = fleet; this.fleet = fleet;
this.races = races; this.races = races;
@@ -24,4 +28,9 @@ public class RaceRowImpl implements RaceRow {
return races; return races;
} }
@Override
public String toString() {
return "RaceRowImpl [fleet=" + fleet + ", races=" + races + "]";
}
} }
@@ -13,25 +13,41 @@ public class SeriesWithRowsImpl implements SeriesWithRows {
private String name; private String name;
private Iterable<RaceRow> raceRows; private Iterable<RaceRow> raceRows;
private boolean isMedal; private boolean isMedal;
private boolean isFleetsCanRunInParallel;
public SeriesWithRowsImpl(String name, boolean isMedal, Iterable<RaceRow> raceRows) {
public SeriesWithRowsImpl(String name, boolean isMedal, boolean isFleetsCanRunInParallel, Iterable<RaceRow> raceRows) {
this.name = name; this.name = name;
this.raceRows = raceRows; this.raceRows = raceRows;
this.isMedal = isMedal; this.isMedal = isMedal;
this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
} }
@Override
public String getName() { public String getName() {
return name; return name;
} }
@Override
public boolean isMedal() { public boolean isMedal() {
return isMedal; return isMedal;
} }
@Override
public Iterable<RaceRow> getRaceRows() { public Iterable<RaceRow> getRaceRows() {
return raceRows; return raceRows;
} }
@Override
public RaceRow getRaceRow(Fleet fleet) {
for (final RaceRow row : getRaceRows()) {
if (row.getFleet() == fleet) {
return row;
}
}
return null;
}
@Override
public Iterable<? extends Fleet> getFleets() { public Iterable<? extends Fleet> getFleets() {
Collection<Fleet> fleets = new ArrayList<Fleet>(); Collection<Fleet> fleets = new ArrayList<Fleet>();
for (RaceRow row : raceRows) { for (RaceRow row : raceRows) {
@@ -46,8 +62,13 @@ public class SeriesWithRowsImpl implements SeriesWithRows {
} }
@Override @Override
public boolean isFleetsRunInParallel() { public boolean isFleetsCanRunInParallel() {
// TODO This is a default implementation so far, allowing for parallel fleet races to not restrict generality. This shall become a configurable series property. See bug 3532 return isFleetsCanRunInParallel;
return true; }
@Override
public String toString() {
return "SeriesWithRowsImpl [name=" + name + ", raceRows=" + raceRows + ", isMedal=" + isMedal
+ ", isFleetsCanRunInParallel=" + isFleetsCanRunInParallel + "]";
} }
} }
@@ -78,7 +78,7 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard
null, /* endDate */null, /* trackedRegattaRegistry */null, new LowPoint(), UUID.randomUUID(), null, /* endDate */null, /* trackedRegattaRegistry */null, new LowPoint(), UUID.randomUUID(),
courseArea); courseArea);
regattaLeaderboard = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0])); regattaLeaderboard = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
regatta.addSeries(new SeriesImpl("Test Series", /* isMedal */false, Arrays.asList(new FleetImpl("Yellow"), regatta.addSeries(new SeriesImpl("Test Series", /* isMedal */false, /* isFleetsCanRunInParallel */ true, Arrays.asList(new FleetImpl("Yellow"),
new FleetImpl("Blue")), Arrays.asList("R1", "R2", "R3"), /* trackedRegattaRegistry */null)); new FleetImpl("Blue")), Arrays.asList("R1", "R2", "R3"), /* trackedRegattaRegistry */null));
for (int l = 0; l < NUMBER_OF_COMP_LISTS; l++) { for (int l = 0; l < NUMBER_OF_COMP_LISTS; l++) {
compLists[l] = new ArrayList<Competitor>(); compLists[l] = new ArrayList<Competitor>();
@@ -175,7 +175,7 @@ public class LeaderboardCourseChangeTest {
Set<Fleet> fleets = new HashSet<>(); Set<Fleet> fleets = new HashSet<>();
fleets.add(fleet); fleets.add(fleet);
Series series = new SeriesImpl("TestSeries", false, fleets, new HashSet<String>(), trackedRegattaRegistry); Series series = new SeriesImpl("TestSeries", false, true, fleets, new HashSet<String>(), trackedRegattaRegistry);
return series; return series;
} }
@@ -1709,7 +1709,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
for (int i = 1; i <= numberOfQualifyingRaces; i++) { for (int i = 1; i <= numberOfQualifyingRaces; i++) {
qualifyingRaceColumnNames.add("Q" + i); qualifyingRaceColumnNames.add("Q" + i);
} }
Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, qualifyingFleets, Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualifyingFleets,
qualifyingRaceColumnNames, /* trackedRegattaRegistry */null); qualifyingRaceColumnNames, /* trackedRegattaRegistry */null);
series.add(qualifyingSeries); series.add(qualifyingSeries);
} }
@@ -1725,7 +1725,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
for (int i = 1; i <= numberOfFinalRaces; i++) { for (int i = 1; i <= numberOfFinalRaces; i++) {
finalRaceColumnNames.add("F" + i); finalRaceColumnNames.add("F" + i);
} }
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null); Series finalSeries = new SeriesImpl("Final", /* isMedal */false, /* isFleetsCanRunInParallel */ true, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(finalSeries); series.add(finalSeries);
} }
@@ -1735,7 +1735,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
medalFleets.add(new FleetImpl("Medal")); medalFleets.add(new FleetImpl("Medal"));
List<String> medalRaceColumnNames = new ArrayList<String>(); List<String> medalRaceColumnNames = new ArrayList<String>();
medalRaceColumnNames.add("M"); medalRaceColumnNames.add("M");
Series medalSeries = new SeriesImpl("Medal", /* isMedal */true, medalFleets, medalRaceColumnNames, /* trackedRegattaRegistry */ null); Series medalSeries = new SeriesImpl("Medal", /* isMedal */true, /* isFleetsCanRunInParallel */ true, medalFleets, medalRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(medalSeries); series.add(medalSeries);
} }
@@ -1774,7 +1774,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
} else { } else {
roundName = "Round "+roundNumber; roundName = "Round "+roundNumber;
} }
Series seriesForRound = new SeriesImpl("E"+elimination+" "+roundName, /* isMedal */ false, fleetsInRound, Series seriesForRound = new SeriesImpl("E"+elimination+" "+roundName, /* isMedal */ false, /* isFleetsCanRunInParallel */ true, fleetsInRound,
raceColumnNameForRound, /* trackedRegattaRegistry */null); raceColumnNameForRound, /* trackedRegattaRegistry */null);
if (isFinalRound) { if (isFinalRound) {
// last "Final" round; here, the fleets are contiguously scored // last "Final" round; here, the fleets are contiguously scored
@@ -1808,7 +1808,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
for (int i = 1; i <= numberOfBeforeRaces; i++) { for (int i = 1; i <= numberOfBeforeRaces; i++) {
beforeRaceColumnNames.add("R" + i); beforeRaceColumnNames.add("R" + i);
} }
Series qualifyingSeries = new SeriesImpl("Before", /* isMedal */false, beforeFleets, Series qualifyingSeries = new SeriesImpl("Before", /* isMedal */false, /* isFleetsCanRunInParallel */ true, beforeFleets,
beforeRaceColumnNames, /* trackedRegattaRegistry */null); beforeRaceColumnNames, /* trackedRegattaRegistry */null);
series.add(qualifyingSeries); series.add(qualifyingSeries);
} }
@@ -1820,7 +1820,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
} }
List<String> qualificationRaceColumnNames = new ArrayList<String>(); List<String> qualificationRaceColumnNames = new ArrayList<String>();
qualificationRaceColumnNames.add("Q"); qualificationRaceColumnNames.add("Q");
Series qualificationSeries = new SeriesImpl("Qualification", /* isMedal */false, qualificationFleets, qualificationRaceColumnNames, /* trackedRegattaRegistry */ null); Series qualificationSeries = new SeriesImpl("Qualification", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualificationFleets, qualificationRaceColumnNames, /* trackedRegattaRegistry */ null);
// discard the one and only qualification race; it doesn't score // discard the one and only qualification race; it doesn't score
qualificationSeries.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(new int[] { 1 })); qualificationSeries.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(new int[] { 1 }));
series.add(qualificationSeries); series.add(qualificationSeries);
@@ -1835,7 +1835,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
} }
List<String> finalRaceColumnNames = new ArrayList<String>(); List<String> finalRaceColumnNames = new ArrayList<String>();
finalRaceColumnNames.add("F"); finalRaceColumnNames.add("F");
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null); Series finalSeries = new SeriesImpl("Final", /* isMedal */false, /* isFleetsCanRunInParallel */ true, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
finalSeries.setSplitFleetContiguousScoring(true); finalSeries.setSplitFleetContiguousScoring(true);
series.add(finalSeries); series.add(finalSeries);
} }
@@ -1847,7 +1847,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
for (int i = numberOfBeforeRaces+1; i <= numberOfBeforeRaces+numberOfFinalRaces; i++) { for (int i = numberOfBeforeRaces+1; i <= numberOfBeforeRaces+numberOfFinalRaces; i++) {
finalRaceColumnNames.add("R" + i); finalRaceColumnNames.add("R" + i);
} }
Series finalSeries = new SeriesImpl("After", /* isMedal */false, afterFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null); Series finalSeries = new SeriesImpl("After", /* isMedal */false, /* isFleetsCanRunInParallel */ true, afterFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(finalSeries); series.add(finalSeries);
} }
final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("Extreme40", /* typicallyStartsUpwind */ false); final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("Extreme40", /* typicallyStartsUpwind */ false);
@@ -1,293 +1,293 @@
package com.sap.sailing.domain.test; package com.sap.sailing.domain.test;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries; import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.base.impl.TrackedRaces; import com.sap.sailing.domain.base.impl.TrackedRaces;
import com.sap.sailing.domain.common.TimingConstants; import com.sap.sailing.domain.common.TimingConstants;
import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider; import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
import com.sap.sse.common.Duration; import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
/** /**
* Covers the linking and unlinking between {@link RaceColumn}s and {@link TrackedRaces}s * Covers the linking and unlinking between {@link RaceColumn}s and {@link TrackedRaces}s
* and tests whether {@link RaceExecutionOrderProvider}s get correctly attached and detached. Also tests * and tests whether {@link RaceExecutionOrderProvider}s get correctly attached and detached. Also tests
* late attaching and detaching of {@link RaceExecutionOrderProvider}s in case the {@link RaceExecutionOrderProvider} * late attaching and detaching of {@link RaceExecutionOrderProvider}s in case the {@link RaceExecutionOrderProvider}
* was <code>null</code> when {@link RaceColumn} was linked to {@link TrackedRace}. * was <code>null</code> when {@link RaceColumn} was linked to {@link TrackedRace}.
* *
* @author Alexander Ries (D062114) * @author Alexander Ries (D062114)
* @author Axel Uhl (D043530) * @author Axel Uhl (D043530)
* *
*/ */
public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest { public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest {
private FlexibleLeaderboard flexibleLeaderboard; private FlexibleLeaderboard flexibleLeaderboard;
private RaceColumnInSeries raceColumnInSeries; private RaceColumnInSeries raceColumnInSeries;
private Fleet fleet; private Fleet fleet;
private TrackedRaceImpl trackedRace; private TrackedRaceImpl trackedRace;
private Regatta regatta; private Regatta regatta;
private Series series; private Series series;
private final String REGATTA = "TestRegatta"; private final String REGATTA = "TestRegatta";
private final String RACE = "TestRace"; private final String RACE = "TestRace";
private final String FLEET = "TestFleet"; private final String FLEET = "TestFleet";
private final String BOATCLASS = "TestClass"; private final String BOATCLASS = "TestClass";
private final String SERIES = "TestSeries"; private final String SERIES = "TestSeries";
private final String FLEXIBLELEADERBOARD = "TestFlexibleLeaderboard"; private final String FLEXIBLELEADERBOARD = "TestFlexibleLeaderboard";
private final String RACECOLUMN_SERIES = "TestSeriesRaceColumn"; private final String RACECOLUMN_SERIES = "TestSeriesRaceColumn";
private final String RACECOLUMN_FLEXIBLELEADERBOARD = "TestFlexibleLeaderboardRaceColumn"; private final String RACECOLUMN_FLEXIBLELEADERBOARD = "TestFlexibleLeaderboardRaceColumn";
@Test @Test
public void testRaceExecutionOrderProviderAttachDetachWithRaceColumn() { public void testRaceExecutionOrderProviderAttachDetachWithRaceColumn() {
trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(),
MillisecondsTimePoint.now()); MillisecondsTimePoint.now());
flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD,
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD, false); flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD, false);
assertTrue(trackedRace.hasRaceExecutionOrderProvidersAttached()); assertTrue(trackedRace.hasRaceExecutionOrderProvidersAttached());
flexibleLeaderboard.removeRaceColumn(RACECOLUMN_FLEXIBLELEADERBOARD); flexibleLeaderboard.removeRaceColumn(RACECOLUMN_FLEXIBLELEADERBOARD);
assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached()); assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached());
} }
@Test @Test
public void testWindInRegularIntervalWithPreviousRaceStillTracking() { public void testWindInRegularIntervalWithPreviousRaceStillTracking() {
final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_MINUTE.times(5)); final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_MINUTE.times(5));
DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace); DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace);
previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace);
trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace); trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace);
flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD,
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false);
flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false); flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false);
Wind wind = new WindImpl(new DegreePosition(12, 13), startOfSecondRace.plus(Duration.ONE_MINUTE), new KnotSpeedWithBearingImpl( Wind wind = new WindImpl(new DegreePosition(12, 13), startOfSecondRace.plus(Duration.ONE_MINUTE), new KnotSpeedWithBearingImpl(
/* speedInKnots */18, new DegreeBearingImpl(185))); /* speedInKnots */18, new DegreeBearingImpl(185)));
assertTrue(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // previous race has tracking still open and takes the fix assertTrue(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // previous race has tracking still open and takes the fix
assertTrue(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // tracked race also needs to take the fix as it falls into the regular tracking interval assertTrue(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // tracked race also needs to take the fix as it falls into the regular tracking interval
} }
@Test @Test
public void testWindInExtendedLeadIntervalWithPreviousRaceStillTracking() { public void testWindInExtendedLeadIntervalWithPreviousRaceStillTracking() {
final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND);
final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_HOUR); final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_HOUR);
DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace); DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace);
previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace);
previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race... previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race...
trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace); trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace);
flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD,
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false);
flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false); flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false);
// the wind fix is after the grace period of the first race's end, so won't be accepted by it, but within the extended // the wind fix is after the grace period of the first race's end, so won't be accepted by it, but within the extended
// time range before the second race, so shall be accepted by it. // time range before the second race, so shall be accepted by it.
Wind wind = new WindImpl(new DegreePosition(12, 13), Wind wind = new WindImpl(new DegreePosition(12, 13),
endOfFirstRace.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).plus(Duration.ONE_MINUTE), // fix is after the grace period endOfFirstRace.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).plus(Duration.ONE_MINUTE), // fix is after the grace period
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))); new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185)));
assertFalse(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // previous race has tracking closed one second after it started and doesn't accept the fix assertFalse(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // previous race has tracking closed one second after it started and doesn't accept the fix
assertTrue(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // tracked race also needs to take the fix as it falls into the regular tracking interval assertTrue(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // tracked race also needs to take the fix as it falls into the regular tracking interval
} }
@Test @Test
public void testWindInExtendedLeadIntervalWithNoPreviousRace() { public void testWindInExtendedLeadIntervalWithNoPreviousRace() {
final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND);
final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_HOUR); final TimePoint startOfSecondRace = startOfFirstRace.plus(Duration.ONE_HOUR);
trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace); trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace);
flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD,
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD, false); flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD, false);
// the wind fix is within the extended time range before the second race, and there is no previous race, so shall be accepted by it // the wind fix is within the extended time range before the second race, and there is no previous race, so shall be accepted by it
Wind wind = new WindImpl(new DegreePosition(12, 13), Wind wind = new WindImpl(new DegreePosition(12, 13),
endOfFirstRace.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).plus(Duration.ONE_MINUTE), // fix is after the grace period endOfFirstRace.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).plus(Duration.ONE_MINUTE), // fix is after the grace period
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))); new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185)));
assertTrue(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); assertTrue(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint()));
} }
@Test @Test
public void testWindInExtendedLeadIntervalWithPreviousRaceLongAgo() { public void testWindInExtendedLeadIntervalWithPreviousRaceLongAgo() {
final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND);
final TimePoint startOfSecondRace = startOfFirstRace.plus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.times(2)); final TimePoint startOfSecondRace = startOfFirstRace.plus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.times(2));
DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace); DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace);
previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace);
previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race... previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race...
trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace); trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace);
flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD,
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false);
flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false); flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false);
// the wind fix is outside the extended time range before the second race, and the previous race doesn't accept it; shall not be accepted // the wind fix is outside the extended time range before the second race, and the previous race doesn't accept it; shall not be accepted
Wind wind = new WindImpl(new DegreePosition(12, 13), Wind wind = new WindImpl(new DegreePosition(12, 13),
endOfFirstRace.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).plus(Duration.ONE_MINUTE), // fix is after the grace period endOfFirstRace.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).plus(Duration.ONE_MINUTE), // fix is after the grace period
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))); new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185)));
assertFalse(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // fix is after first race's end plus grace period assertFalse(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // fix is after first race's end plus grace period
assertFalse(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); assertFalse(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint()));
} }
@Test @Test
public void testWindInExtendedLeadIntervalButStillRecordedByPreviousRace() { public void testWindInExtendedLeadIntervalButStillRecordedByPreviousRace() {
final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_SECOND);
final TimePoint startOfSecondRace = startOfFirstRace.plus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)); final TimePoint startOfSecondRace = startOfFirstRace.plus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2));
DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace); DynamicTrackedRaceImpl previousTrackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), startOfFirstRace);
previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace); previousTrackedRace.setStartOfTrackingReceived(startOfFirstRace);
previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race... previousTrackedRace.setEndOfTrackingReceived(endOfFirstRace); // a very short race...
trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace); trackedRace = createTestTrackedRace(REGATTA, "TestRace2", BOATCLASS, Collections.<Competitor> emptyList(), startOfSecondRace);
flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD, flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD,
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null); new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false); flexibleLeaderboard.addRace(previousTrackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"1", false);
flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false); flexibleLeaderboard.addRace(trackedRace, RACECOLUMN_FLEXIBLELEADERBOARD+"2", false);
// the wind fix is inside the extended time range before the second race, but the previous race still accepts it; shall not be accepted // the wind fix is inside the extended time range before the second race, but the previous race still accepts it; shall not be accepted
Wind wind = new WindImpl(new DegreePosition(12, 13), Wind wind = new WindImpl(new DegreePosition(12, 13),
startOfFirstRace.plus(Duration.ONE_SECOND), // fix is recorded by previous race startOfFirstRace.plus(Duration.ONE_SECOND), // fix is recorded by previous race
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))); new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185)));
assertTrue(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // fix is after first race's end plus grace period assertTrue(previousTrackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); // fix is after first race's end plus grace period
assertFalse(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint())); assertFalse(trackedRace.takesWindFixWithTimePoint(wind.getTimePoint()));
} }
@Test @Test
public void testRaceExecutionOrderProviderAttachDetachWithRaceCollumnInSeries() { public void testRaceExecutionOrderProviderAttachDetachWithRaceCollumnInSeries() {
createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */true); createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */true);
raceColumnInSeries.setTrackedRace(fleet, trackedRace); raceColumnInSeries.setTrackedRace(fleet, trackedRace);
assertTrue(trackedRace.hasRaceExecutionOrderProvidersAttached()); assertTrue(trackedRace.hasRaceExecutionOrderProvidersAttached());
raceColumnInSeries.releaseTrackedRace(fleet); raceColumnInSeries.releaseTrackedRace(fleet);
assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached()); assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached());
} }
@Test @Test
public void testRaceExecutionOrderProviderAttachDetachWhenSeriesRegattaIsSetAndRemovedAfterTrackedRaceHaveBeenSetToRaceColumns() { public void testRaceExecutionOrderProviderAttachDetachWhenSeriesRegattaIsSetAndRemovedAfterTrackedRaceHaveBeenSetToRaceColumns() {
createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */false); createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */false);
raceColumnInSeries.setTrackedRace(fleet, trackedRace); raceColumnInSeries.setTrackedRace(fleet, trackedRace);
assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached()); assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached());
series.setRegatta(regatta); series.setRegatta(regatta);
assertTrue(trackedRace.hasRaceExecutionOrderProvidersAttached()); assertTrue(trackedRace.hasRaceExecutionOrderProvidersAttached());
series.setRegatta(null); series.setRegatta(null);
assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached()); assertFalse(trackedRace.hasRaceExecutionOrderProvidersAttached());
} }
@Test @Test
public void testThatTrackedRaceReceivesRaceLogWhenSeriesIsLinkedToRegatta() { public void testThatTrackedRaceReceivesRaceLogWhenSeriesIsLinkedToRegatta() {
createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */false); createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */false);
raceColumnInSeries.setTrackedRace(fleet, trackedRace); raceColumnInSeries.setTrackedRace(fleet, trackedRace);
assertNull(raceColumnInSeries.getRaceLog(fleet)); assertNull(raceColumnInSeries.getRaceLog(fleet));
regatta.addSeries(series); regatta.addSeries(series);
final RaceLog raceLog = raceColumnInSeries.getRaceLog(fleet); final RaceLog raceLog = raceColumnInSeries.getRaceLog(fleet);
assertNotNull(raceLog); assertNotNull(raceLog);
assertNotNull(trackedRace.getRaceLog(raceLog.getId())); assertNotNull(trackedRace.getRaceLog(raceLog.getId()));
} }
private void createTestSetupWithRegattaAndSeries(boolean linkSeriesToRegatta) { private void createTestSetupWithRegattaAndSeries(boolean linkSeriesToRegatta) {
trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(), trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(),
MillisecondsTimePoint.now()); MillisecondsTimePoint.now());
fleet = new FleetImpl(FLEET); fleet = new FleetImpl(FLEET);
Set<Fleet> fleets = new HashSet<>(); Set<Fleet> fleets = new HashSet<>();
fleets.add(fleet); fleets.add(fleet);
series = new SeriesImpl(SERIES, false, fleets, new HashSet<String>(), null); series = new SeriesImpl(SERIES, false, /* isFleetsCanRunInParallel */ true, fleets, new HashSet<String>(), null);
Set<Series> seriesSet = new HashSet<>(); Set<Series> seriesSet = new HashSet<>();
if (linkSeriesToRegatta) { if (linkSeriesToRegatta) {
seriesSet.add(series); seriesSet.add(series);
} }
BoatClass boatClass = new BoatClassImpl(BOATCLASS, true); BoatClass boatClass = new BoatClassImpl(BOATCLASS, true);
raceColumnInSeries = series.addRaceColumn(RACECOLUMN_SERIES, null); raceColumnInSeries = series.addRaceColumn(RACECOLUMN_SERIES, null);
ScoringScheme scoringScheme = new LowPoint(); ScoringScheme scoringScheme = new LowPoint();
regatta = new RegattaImpl(RegattaImpl.getDefaultName(REGATTA, boatClass.getName()), boatClass, regatta = new RegattaImpl(RegattaImpl.getDefaultName(REGATTA, boatClass.getName()), boatClass,
/* startDate */null, /* endDate */null, seriesSet, false, scoringScheme, UUID.randomUUID(), null, /* startDate */null, /* endDate */null, seriesSet, false, scoringScheme, UUID.randomUUID(), null,
OneDesignRankingMetric::new); OneDesignRankingMetric::new);
} }
/** /**
* See bug 3173. When a race is linked more than once to a fleet in a series, the "previous race" * See bug 3173. When a race is linked more than once to a fleet in a series, the "previous race"
* definition's transitive closure may become cyclic, and a race may be considered its own * definition's transitive closure may become cyclic, and a race may be considered its own
* direct or transitive predecessor. This can lead to endless recursion in the {@link TrackedRace#takesWindFix(Wind)} * direct or transitive predecessor. This can lead to endless recursion in the {@link TrackedRace#takesWindFix(Wind)}
* implementation which traverses a race's predecessors recursively. * implementation which traverses a race's predecessors recursively.
*/ */
@Test @Test
public void testCyclicPreviousRacesSequence() { public void testCyclicPreviousRacesSequence() {
createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */ true); createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */ true);
final RaceColumnInSeries r1 = series.addRaceColumn("R1", /* trackedRegattaRegistry */ null); final RaceColumnInSeries r1 = series.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r2 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null); final RaceColumnInSeries r2 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r3 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null); final RaceColumnInSeries r3 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r4 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null); final RaceColumnInSeries r4 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_MINUTE); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_MINUTE);
final TimePoint startOfSecondRace = endOfFirstRace.plus(Duration.ONE_MINUTE); final TimePoint startOfSecondRace = endOfFirstRace.plus(Duration.ONE_MINUTE);
final TimePoint endOfSecondRace = startOfSecondRace.plus(Duration.ONE_MINUTE); final TimePoint endOfSecondRace = startOfSecondRace.plus(Duration.ONE_MINUTE);
final TimePoint startOfThirdRace = endOfSecondRace.plus(Duration.ONE_MINUTE); final TimePoint startOfThirdRace = endOfSecondRace.plus(Duration.ONE_MINUTE);
final TimePoint endOfThirdRace = startOfThirdRace.plus(Duration.ONE_MINUTE); final TimePoint endOfThirdRace = startOfThirdRace.plus(Duration.ONE_MINUTE);
final TrackedRace tr1 = createTrackedRace("FirstRace", startOfFirstRace, endOfFirstRace); final TrackedRace tr1 = createTrackedRace("FirstRace", startOfFirstRace, endOfFirstRace);
final TrackedRace tr2 = createTrackedRace("SecondRace", startOfSecondRace, endOfSecondRace); final TrackedRace tr2 = createTrackedRace("SecondRace", startOfSecondRace, endOfSecondRace);
final TrackedRace tr3 = createTrackedRace("ThirdRace", startOfThirdRace, endOfThirdRace); final TrackedRace tr3 = createTrackedRace("ThirdRace", startOfThirdRace, endOfThirdRace);
r1.setTrackedRace(fleet, tr3); r1.setTrackedRace(fleet, tr3);
r2.setTrackedRace(fleet, tr1); r2.setTrackedRace(fleet, tr1);
r3.setTrackedRace(fleet, tr2); r3.setTrackedRace(fleet, tr2);
r4.setTrackedRace(fleet, tr3); // produce a cycle because now transitively tr1 has itself as a predecessor r4.setTrackedRace(fleet, tr3); // produce a cycle because now transitively tr1 has itself as a predecessor
assertTrue(tr3.takesWindFixWithTimePoint(new WindImpl(new DegreePosition(12, 13), assertTrue(tr3.takesWindFixWithTimePoint(new WindImpl(new DegreePosition(12, 13),
startOfThirdRace.minus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)), startOfThirdRace.minus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)),
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))).getTimePoint())); new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))).getTimePoint()));
} }
/** /**
* See bug 3173. When a race is linked more than once to a fleet in a series, the "previous race" * See bug 3173. When a race is linked more than once to a fleet in a series, the "previous race"
* definition's transitive closure may become cyclic, and a race may be considered its own * definition's transitive closure may become cyclic, and a race may be considered its own
* direct or transitive predecessor. This can lead to endless recursion in the {@link TrackedRace#takesWindFix(Wind)} * direct or transitive predecessor. This can lead to endless recursion in the {@link TrackedRace#takesWindFix(Wind)}
* implementation which traverses a race's predecessors recursively. * implementation which traverses a race's predecessors recursively.
*/ */
@Test @Test
public void testCyclicPreviousRacesSequenceUsingSingleRace() { public void testCyclicPreviousRacesSequenceUsingSingleRace() {
createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */ true); createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */ true);
final RaceColumnInSeries r1 = series.addRaceColumn("R1", /* trackedRegattaRegistry */ null); final RaceColumnInSeries r1 = series.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r2 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null); final RaceColumnInSeries r2 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final TimePoint startOfFirstRace = MillisecondsTimePoint.now(); final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_MINUTE); final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_MINUTE);
final TrackedRace tr1 = createTrackedRace("FirstRace", startOfFirstRace, endOfFirstRace); final TrackedRace tr1 = createTrackedRace("FirstRace", startOfFirstRace, endOfFirstRace);
r1.setTrackedRace(fleet, tr1); r1.setTrackedRace(fleet, tr1);
r2.setTrackedRace(fleet, tr1); r2.setTrackedRace(fleet, tr1);
assertTrue(tr1.takesWindFixWithTimePoint(new WindImpl(new DegreePosition(12, 13), assertTrue(tr1.takesWindFixWithTimePoint(new WindImpl(new DegreePosition(12, 13),
startOfFirstRace.minus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)), startOfFirstRace.minus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)),
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))).getTimePoint())); new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185))).getTimePoint()));
} }
private DynamicTrackedRace createTrackedRace(final String name, final TimePoint startOfRace, final TimePoint endOfRace) { private DynamicTrackedRace createTrackedRace(final String name, final TimePoint startOfRace, final TimePoint endOfRace) {
DynamicTrackedRace trackedRace = createTestTrackedRace(REGATTA, name, BOATCLASS, Collections.<Competitor> emptyList(), startOfRace); DynamicTrackedRace trackedRace = createTestTrackedRace(REGATTA, name, BOATCLASS, Collections.<Competitor> emptyList(), startOfRace);
trackedRace.setStartOfTrackingReceived(startOfRace); trackedRace.setStartOfTrackingReceived(startOfRace);
trackedRace.setEndOfTrackingReceived(endOfRace); trackedRace.setEndOfTrackingReceived(endOfRace);
return trackedRace; return trackedRace;
} }
} }
@@ -178,7 +178,8 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa
} }
private Regatta createRegatta() { private Regatta createRegatta() {
Series series = new SeriesImpl("Test Series", /* isMedal */ false, /* fleets */ Collections.singleton(new FleetImpl("Default")), Series series = new SeriesImpl("Test Series", /* isMedal */ false, /* isFleetsCanRunInParallel */ true,
/* fleets */ Collections.singleton(new FleetImpl("Default")),
Collections.singleton("R1"), /* trackedRegattaRegistry */ null); Collections.singleton("R1"), /* trackedRegattaRegistry */ null);
Regatta regatta = new RegattaImpl("test", null, null, null, Collections.singleton(series), false, Regatta regatta = new RegattaImpl("test", null, null, null, Collections.singleton(series), false,
new LowPoint(), "test", null, OneDesignRankingMetric::new); new LowPoint(), "test", null, OneDesignRankingMetric::new);
@@ -68,7 +68,7 @@ public class AbstractMockedRaceMarkPassingTest {
Waypoint w5 = new WaypointImpl(cp, PassingInstruction.Line); Waypoint w5 = new WaypointImpl(cp, PassingInstruction.Line);
waypoints = Arrays.asList(w1, w2, w3, w4, w5); waypoints = Arrays.asList(w1, w2, w3, w4, w5);
Regatta r = new RegattaImpl(RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, Regatta r = new RegattaImpl(RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass,
/*startDate*/ null, /*endDate*/ null, Arrays.asList(new SeriesImpl("Series", true, Arrays.asList(new FleetImpl("fleet")), /*startDate*/ null, /*endDate*/ null, Arrays.asList(new SeriesImpl("Series", true, /* isFleetsCanRunInParallel */ true, Arrays.asList(new FleetImpl("fleet")),
new ArrayList<String>(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)), OneDesignRankingMetric::new); new ArrayList<String>(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)), OneDesignRankingMetric::new);
Course course = new CourseImpl("course", waypoints); Course course = new CourseImpl("course", waypoints);
RaceDefinition raceDef = new RaceDefinitionImpl("Performance Race", course, boatClass, Arrays.asList(ron, tom, ben)); RaceDefinition raceDef = new RaceDefinitionImpl("Performance Race", course, boatClass, Arrays.asList(ron, tom, ben));
@@ -14,7 +14,9 @@ public interface RaceColumnListener extends Serializable {
void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace); void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace);
void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace); void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace);
void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel);
void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore); void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore);
void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward); void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward);
@@ -41,6 +41,8 @@ public interface Series extends SeriesBase {
void setIsMedal(boolean isMedal); void setIsMedal(boolean isMedal);
void setIsFleetsCanRunInParallel(boolean isFleetsCanRunInParallel);
Fleet getFleetByName(String fleetName); Fleet getFleetByName(String fleetName);
/** /**
@@ -1,107 +1,112 @@
package com.sap.sailing.domain.base.impl; package com.sap.sailing.domain.base.impl;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener; import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
/** /**
* Delegates all listener operations to a default action which is implemented here to do nothing. This way, subclasses * Delegates all listener operations to a default action which is implemented here to do nothing. This way, subclasses
* can easily provide a default action to be executed for all callback methods except for maybe a few which then need to * can easily provide a default action to be executed for all callback methods except for maybe a few which then need to
* be explicitly overridden. If not most methods need to perform the same action then instead of implementing a default * be explicitly overridden. If not most methods need to perform the same action then instead of implementing a default
* action subclasses should rather override the {@link RaceColumnListener} operations individually. * action subclasses should rather override the {@link RaceColumnListener} operations individually.
* *
* @author Alexander Ries (D062114) * @author Alexander Ries (D062114)
* *
*/ */
public interface RaceColumnListenerWithDefaultAction extends RaceColumnListener { public interface RaceColumnListenerWithDefaultAction extends RaceColumnListener {
/** /**
* This implementation of the default action to which all other operations delegate does nothing. Subclasses can * This implementation of the default action to which all other operations delegate does nothing. Subclasses can
* choose to override this method to provide this as the default behavior for all other methods and then still * choose to override this method to provide this as the default behavior for all other methods and then still
* override individual {@link RaceColumnListener} methods, or they can leave the default action empty and just * override individual {@link RaceColumnListener} methods, or they can leave the default action empty and just
* override {@link RaceColumnListener} methods. * override {@link RaceColumnListener} methods.
*/ */
void defaultAction(); void defaultAction();
@Override @Override
default void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) { default void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
defaultAction(); defaultAction();
} }
@Override @Override
default void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) { default void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
defaultAction(); defaultAction();
} }
@Override @Override
default void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { default void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
defaultAction(); defaultAction();
} }
@Override @Override
default void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { default void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
defaultAction(); defaultAction();
} }
@Override @Override
default void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, default void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
boolean firstColumnIsNonDiscardableCarryForward) { defaultAction();
defaultAction(); }
}
@Override
@Override default void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn,
default void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) { boolean firstColumnIsNonDiscardableCarryForward) {
defaultAction(); defaultAction();
} }
@Override @Override
default void raceColumnAddedToContainer(RaceColumn raceColumn) { default void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
defaultAction(); defaultAction();
} }
@Override @Override
default void raceColumnRemovedFromContainer(RaceColumn raceColumn) { default void raceColumnAddedToContainer(RaceColumn raceColumn) {
defaultAction(); defaultAction();
} }
@Override @Override
default void raceColumnMoved(RaceColumn raceColumn, int newIndex) { default void raceColumnRemovedFromContainer(RaceColumn raceColumn) {
defaultAction(); defaultAction();
} }
@Override @Override
default void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) { default void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
defaultAction(); defaultAction();
} }
@Override @Override
default void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName) { default void factorChanged(RaceColumn raceColumn, Double oldFactor, Double newFactor) {
defaultAction(); defaultAction();
} }
@Override @Override
default void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, default void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName) {
ResultDiscardingRule newDiscardingRule) { defaultAction();
defaultAction(); }
}
@Override
@Override default void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule,
default void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogEvent event) { ResultDiscardingRule newDiscardingRule) {
defaultAction(); defaultAction();
} }
@Override @Override
default void regattaLogEventAdded(RegattaLogEvent event) { default void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogEvent event) {
defaultAction(); defaultAction();
} }
@Override @Override
default boolean isTransient() { default void regattaLogEventAdded(RegattaLogEvent event) {
return false; defaultAction();
} }
}
@Override
default boolean isTransient() {
return false;
}
}
@@ -182,7 +182,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
RankingMetricConstructor rankingMetricConstructor) { RankingMetricConstructor rankingMetricConstructor) {
this(raceLogStore, regattaLogStore, name, boatClass, startDate, endDate, Collections this(raceLogStore, regattaLogStore, name, boatClass, startDate, endDate, Collections
.singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME, .singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME,
/* isMedal */false, Collections /* isMedal */false, /* isFleetsCanRunInParallel */ true, Collections
.singletonList(new FleetImpl(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), .singletonList(new FleetImpl(LeaderboardNameConstants.DEFAULT_FLEET_NAME)),
/* race column names */new ArrayList<String>(), trackedRegattaRegistry)), /* persistent */false, /* race column names */new ArrayList<String>(), trackedRegattaRegistry)), /* persistent */false,
scoringScheme, id, courseArea, /* useStartTimeInference */true, rankingMetricConstructor); scoringScheme, id, courseArea, /* useStartTimeInference */true, rankingMetricConstructor);
@@ -429,6 +429,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
} }
@Override
public void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
raceColumnListeners.notifyListenersAboutIsFleetsCanRunInParallelChanged(raceColumn, newIsFleetsCanRunInParallel);
}
@Override @Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
@@ -36,6 +36,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
private final List<Fleet> fleetsInAscendingOrder; private final List<Fleet> fleetsInAscendingOrder;
private final List<RaceColumnInSeries> raceColumns; private final List<RaceColumnInSeries> raceColumns;
private boolean isMedal; private boolean isMedal;
private boolean isFleetsCanRunInParallel;
private Regatta regatta; private Regatta regatta;
private final RaceColumnListeners raceColumnListeners; private final RaceColumnListeners raceColumnListeners;
private ThresholdBasedResultDiscardingRule resultDiscardingRule; private ThresholdBasedResultDiscardingRule resultDiscardingRule;
@@ -71,7 +72,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
* this column's series {@link Regatta}, respectively. If <code>null</code>, the re-association won't be * this column's series {@link Regatta}, respectively. If <code>null</code>, the re-association won't be
* carried out. * carried out.
*/ */
public SeriesImpl(String name, boolean isMedal, Iterable<? extends Fleet> fleets, Iterable<String> raceColumnNames, public SeriesImpl(String name, boolean isMedal, boolean isFleetsCanRunInParallel, Iterable<? extends Fleet> fleets, Iterable<String> raceColumnNames,
TrackedRegattaRegistry trackedRegattaRegistry) { TrackedRegattaRegistry trackedRegattaRegistry) {
super(name); super(name);
if (fleets == null || Util.isEmpty(fleets)) { if (fleets == null || Util.isEmpty(fleets)) {
@@ -86,6 +87,7 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
Collections.sort(fleetsInAscendingOrder); Collections.sort(fleetsInAscendingOrder);
this.raceColumns = new ArrayList<RaceColumnInSeries>(); this.raceColumns = new ArrayList<RaceColumnInSeries>();
this.isMedal = isMedal; this.isMedal = isMedal;
this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
this.raceColumnListeners = new RaceColumnListeners(); this.raceColumnListeners = new RaceColumnListeners();
for (String raceColumnName : raceColumnNames) { for (String raceColumnName : raceColumnNames) {
addRaceColumn(raceColumnName, trackedRegattaRegistry); addRaceColumn(raceColumnName, trackedRegattaRegistry);
@@ -284,6 +286,22 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
} }
} }
@Override
public boolean isFleetsCanRunInParallel() {
return isFleetsCanRunInParallel;
}
@Override
public void setIsFleetsCanRunInParallel(boolean isFleetsCanRunInParallel) {
boolean oldIsFleetsCanRunInParallel = this.isFleetsCanRunInParallel;
this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
if (oldIsFleetsCanRunInParallel != isFleetsCanRunInParallel) {
for (RaceColumn raceColumn : getRaceColumns()) {
raceColumnListeners.notifyListenersAboutIsFleetsCanRunInParallelChanged(raceColumn, isFleetsCanRunInParallel);
}
}
}
@Override @Override
public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) { public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) {
raceColumnListeners.notifyListenersAboutTrackedRaceLinked(raceColumn, fleet, trackedRace); raceColumnListeners.notifyListenersAboutTrackedRaceLinked(raceColumn, fleet, trackedRace);
@@ -299,6 +317,10 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
} }
@Override
public void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
raceColumnListeners.notifyListenersAboutIsFleetsCanRunInParallelChanged(raceColumn, newIsFleetsCanRunInParallel);
}
@Override @Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
@@ -434,10 +456,4 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
public boolean hasSplitFleetContiguousScoring() { public boolean hasSplitFleetContiguousScoring() {
return hasSplitFleetContiguousScoring; return hasSplitFleetContiguousScoring;
} }
@Override
public boolean isFleetsRunInParallel() {
// TODO This is a default implementation so far, allowing for parallel fleet races to not restrict generality. This shall become a configurable series property. See bug 3532
return true;
}
} }
@@ -278,6 +278,10 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
} }
@Override
public void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
}
@Override @Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
} }
@@ -121,6 +121,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
} }
@Override
public void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
getRaceColumnListeners().notifyListenersAboutIsFleetsCanRunInParallelChanged(raceColumn, newIsFleetsCanRunInParallel);
}
@Override @Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
@@ -86,6 +86,12 @@ public class RaceColumnListeners implements Serializable {
} }
} }
public void notifyListenersAboutIsFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.isFleetsCanRunInParallelChanged(raceColumn, newIsFleetsCanRunInParallel);
}
}
public void notifyListenersAboutIsStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { public void notifyListenersAboutIsStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
for (RaceColumnListener listener : getRaceColumnListeners()) { for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.isStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); listener.isStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
@@ -1,53 +1,53 @@
package com.sap.sailing.gwt.ui.test; package com.sap.sailing.gwt.ui.test;
import java.util.Collections; import java.util.Collections;
import java.util.UUID; import java.util.UUID;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RankingMetrics; import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException; import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.ranking.RankingMetricsFactory; import com.sap.sailing.domain.ranking.RankingMetricsFactory;
import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
public class PingMarkViaRegattaLogTest { public class PingMarkViaRegattaLogTest {
private SailingServiceImplMock sailingService; private SailingServiceImplMock sailingService;
private RacingEventService service; private RacingEventService service;
private final String columnName = "column"; private final String columnName = "column";
private final Fleet fleet = new FleetImpl("fleet"); private final Fleet fleet = new FleetImpl("fleet");
@Before @Before
public void prepare() { public void prepare() {
sailingService = new SailingServiceImplMock(); sailingService = new SailingServiceImplMock();
service = sailingService.getRacingEventService(); service = sailingService.getRacingEventService();
} }
@Test @Test
public void testPinging() throws DoesNotHaveRegattaLogException { public void testPinging() throws DoesNotHaveRegattaLogException {
service.getMongoObjectFactory().getDatabase().dropDatabase(); service.getMongoObjectFactory().getDatabase().dropDatabase();
Series series = new SeriesImpl("series", false, Collections.singletonList(fleet), Series series = new SeriesImpl("series", false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(fleet),
Collections.singletonList(columnName), service); Collections.singletonList(columnName), service);
Regatta regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", /*startDate*/ null, /*endDate*/ null, Regatta regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", /*startDate*/ null, /*endDate*/ null,
UUID.randomUUID(), Collections.<Series>singletonList(series), UUID.randomUUID(), Collections.<Series>singletonList(series),
false, new HighPoint(), UUID.randomUUID(), /* useStartTimeInference */ true, false, new HighPoint(), UUID.randomUUID(), /* useStartTimeInference */ true,
RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.ONE_DESIGN)); RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.ONE_DESIGN));
RegattaLeaderboard leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {}); RegattaLeaderboard leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
MarkDTO mark = new MarkDTO("mark", "mark"); MarkDTO mark = new MarkDTO("mark", "mark");
Position position = new DegreePosition(30, 40); Position position = new DegreePosition(30, 40);
sailingService.pingMark(leaderboard.getName(), mark, position); sailingService.pingMark(leaderboard.getName(), mark, position);
} }
} }
@@ -293,7 +293,7 @@ public class RaceContext {
case FINISHING: case FINISHING:
case RUNNING: case RUNNING:
case SCHEDULED: case SCHEDULED:
case PRESCHEDULED: case PRESCHEDULED:
case STARTPHASE: case STARTPHASE:
case UNKNOWN: case UNKNOWN:
break; break;
@@ -58,7 +58,7 @@ public class CreateRegattaCallback implements DialogCallback<RegattaDTO>{
LinkedHashMap<String, SeriesCreationParametersDTO> seriesStructure = new LinkedHashMap<String, SeriesCreationParametersDTO>(); LinkedHashMap<String, SeriesCreationParametersDTO> seriesStructure = new LinkedHashMap<String, SeriesCreationParametersDTO>();
for (SeriesDTO seriesDTO : newRegatta.series) { for (SeriesDTO seriesDTO : newRegatta.series) {
SeriesCreationParametersDTO seriesPair = new SeriesCreationParametersDTO(seriesDTO.getFleets(), SeriesCreationParametersDTO seriesPair = new SeriesCreationParametersDTO(seriesDTO.getFleets(),
seriesDTO.isMedal(), seriesDTO.isStartsWithZeroScore(), seriesDTO.isMedal(), seriesDTO.isFleetsCanRunInParallel(), seriesDTO.isStartsWithZeroScore(),
seriesDTO.isFirstColumnIsNonDiscardableCarryForward(), seriesDTO.getDiscardThresholds(), seriesDTO.isFirstColumnIsNonDiscardableCarryForward(), seriesDTO.getDiscardThresholds(),
seriesDTO.hasSplitFleetContiguousScoring()); seriesDTO.hasSplitFleetContiguousScoring());
seriesStructure.put(seriesDTO.getName(), seriesPair); seriesStructure.put(seriesDTO.getName(), seriesPair);
@@ -120,41 +120,43 @@ public class RegattaDetailsComposite extends Composite {
private CellTable<SeriesDTO> createRegattaSeriesTable() { private CellTable<SeriesDTO> createRegattaSeriesTable() {
CellTable<SeriesDTO> table = new BaseCelltable<SeriesDTO>(/* pageSize */10000, tableRes); CellTable<SeriesDTO> table = new BaseCelltable<SeriesDTO>(/* pageSize */10000, tableRes);
table.setWidth("100%"); table.setWidth("100%");
TextColumn<SeriesDTO> seriesNameColumn = new TextColumn<SeriesDTO>() { TextColumn<SeriesDTO> seriesNameColumn = new TextColumn<SeriesDTO>() {
@Override @Override
public String getValue(SeriesDTO series) { public String getValue(SeriesDTO series) {
return series.getName(); return series.getName();
} }
}; };
TextColumn<SeriesDTO> isMedalSeriesColumn = new TextColumn<SeriesDTO>() { TextColumn<SeriesDTO> isMedalSeriesColumn = new TextColumn<SeriesDTO>() {
@Override @Override
public String getValue(SeriesDTO series) { public String getValue(SeriesDTO series) {
return series.isMedal() ? stringMessages.yes() : stringMessages.no(); return series.isMedal() ? stringMessages.yes() : stringMessages.no();
} }
}; };
TextColumn<SeriesDTO> startsWithZeroScoreColumn = new TextColumn<SeriesDTO>() { TextColumn<SeriesDTO> startsWithZeroScoreColumn = new TextColumn<SeriesDTO>() {
@Override @Override
public String getValue(SeriesDTO series) { public String getValue(SeriesDTO series) {
return series.isStartsWithZeroScore() ? stringMessages.yes() : stringMessages.no(); return series.isStartsWithZeroScore() ? stringMessages.yes() : stringMessages.no();
} }
}; };
TextColumn<SeriesDTO> hasSplitFleetContiguousScoringColumn = new TextColumn<SeriesDTO>() { TextColumn<SeriesDTO> hasSplitFleetContiguousScoringColumn = new TextColumn<SeriesDTO>() {
@Override @Override
public String getValue(SeriesDTO series) { public String getValue(SeriesDTO series) {
return series.hasSplitFleetContiguousScoring() ? stringMessages.yes() : stringMessages.no(); return series.hasSplitFleetContiguousScoring() ? stringMessages.yes() : stringMessages.no();
} }
}; };
TextColumn<SeriesDTO> isFirstColumnIsNonDiscardableCarryForwardColumn = new TextColumn<SeriesDTO>() { TextColumn<SeriesDTO> isFirstColumnIsNonDiscardableCarryForwardColumn = new TextColumn<SeriesDTO>() {
@Override @Override
public String getValue(SeriesDTO series) { public String getValue(SeriesDTO series) {
return series.isFirstColumnIsNonDiscardableCarryForward() ? stringMessages.yes() : stringMessages.no(); return series.isFirstColumnIsNonDiscardableCarryForward() ? stringMessages.yes() : stringMessages.no();
} }
}; };
TextColumn<SeriesDTO> isFleetsCanRunInParallelColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
return series.isFleetsCanRunInParallel() ? stringMessages.yes() : stringMessages.no();
}
};
TextColumn<SeriesDTO> racesColumn = new TextColumn<SeriesDTO>() { TextColumn<SeriesDTO> racesColumn = new TextColumn<SeriesDTO>() {
@Override @Override
@@ -253,6 +255,7 @@ public class RegattaDetailsComposite extends Composite {
table.addColumn(isFirstColumnIsNonDiscardableCarryForwardColumn, stringMessages.firstRaceIsNonDiscardableCarryForward()); table.addColumn(isFirstColumnIsNonDiscardableCarryForwardColumn, stringMessages.firstRaceIsNonDiscardableCarryForward());
table.addColumn(startsWithZeroScoreColumn, stringMessages.startsWithZeroScore()); table.addColumn(startsWithZeroScoreColumn, stringMessages.startsWithZeroScore());
table.addColumn(hasSplitFleetContiguousScoringColumn, stringMessages.hasSplitFleetContiguousScoring()); table.addColumn(hasSplitFleetContiguousScoringColumn, stringMessages.hasSplitFleetContiguousScoring());
table.addColumn(isFleetsCanRunInParallelColumn, stringMessages.canFleetsRunInParallel());
table.addColumn(seriesActionColumn, stringMessages.actions()); table.addColumn(seriesActionColumn, stringMessages.actions());
return table; return table;
@@ -287,6 +290,7 @@ public class RegattaDetailsComposite extends Composite {
final SeriesDTO series = seriesDescriptor.getSeries(); final SeriesDTO series = seriesDescriptor.getSeries();
final List<RaceColumnDTO> newRaceColumns = seriesDescriptor.getRaces(); final List<RaceColumnDTO> newRaceColumns = seriesDescriptor.getRaces();
final boolean isMedalChanged = series.isMedal() != seriesDescriptor.isMedal(); final boolean isMedalChanged = series.isMedal() != seriesDescriptor.isMedal();
final boolean isFleetsCanRunInParallelChanged = series.isFleetsCanRunInParallel() != seriesDescriptor.isFleetsCanRunInParallel();
final boolean isStartsWithZeroScoreChanged = series.isStartsWithZeroScore() != seriesDescriptor.isStartsWithZeroScore(); final boolean isStartsWithZeroScoreChanged = series.isStartsWithZeroScore() != seriesDescriptor.isStartsWithZeroScore();
final boolean isFirstColumnIsNonDiscardableCarryForwardChanged = series.isFirstColumnIsNonDiscardableCarryForward() != seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(); final boolean isFirstColumnIsNonDiscardableCarryForwardChanged = series.isFirstColumnIsNonDiscardableCarryForward() != seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward();
final boolean hasSplitFleetContiguousScoringChanged = series.hasSplitFleetContiguousScoring() != seriesDescriptor.hasSplitFleetContiguousScoring(); final boolean hasSplitFleetContiguousScoringChanged = series.hasSplitFleetContiguousScoring() != seriesDescriptor.hasSplitFleetContiguousScoring();
@@ -350,11 +354,11 @@ public class RegattaDetailsComposite extends Composite {
}); });
} }
}); });
if (isMedalChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged if (isMedalChanged || isFleetsCanRunInParallelChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged
|| isFirstColumnIsNonDiscardableCarryForwardChanged || hasSplitFleetContiguousScoringChanged || isFirstColumnIsNonDiscardableCarryForwardChanged || hasSplitFleetContiguousScoringChanged
|| seriesNameChanged) { || seriesNameChanged) {
sailingService.updateSeries(regattaIdentifier, series.getName(), seriesDescriptor.getSeriesName(), sailingService.updateSeries(regattaIdentifier, series.getName(), seriesDescriptor.getSeriesName(),
seriesDescriptor.isMedal(), seriesDescriptor.getResultDiscardingThresholds(), seriesDescriptor.isMedal(), seriesDescriptor.isFleetsCanRunInParallel(), seriesDescriptor.getResultDiscardingThresholds(),
seriesDescriptor.isStartsWithZeroScore(), seriesDescriptor.isStartsWithZeroScore(),
seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(), seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(),
seriesDescriptor.hasSplitFleetContiguousScoring(), series.getFleets(), seriesDescriptor.hasSplitFleetContiguousScoring(), series.getFleets(),
@@ -309,7 +309,7 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer
if (seriesIter.hasNext()) { if (seriesIter.hasNext()) {
final SeriesDTO series = seriesIter.next(); final SeriesDTO series = seriesIter.next();
sailingService.updateSeries(regattaName, series.getName(), series.getName(), series.isMedal(), sailingService.updateSeries(regattaName, series.getName(), series.getName(), series.isMedal(),
series.getDiscardThresholds(), series.isStartsWithZeroScore(), series.isFleetsCanRunInParallel(), series.getDiscardThresholds(), series.isStartsWithZeroScore(),
series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring(), series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring(),
series.getFleets(), new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() { series.getFleets(), new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@Override @Override
@@ -1,69 +1,75 @@
package com.sap.sailing.gwt.ui.adminconsole; package com.sap.sailing.gwt.ui.adminconsole;
import java.util.List; import java.util.List;
import com.sap.sailing.domain.common.dto.RaceColumnDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO; import com.sap.sailing.gwt.ui.shared.SeriesDTO;
/** /**
* Used by {@link SeriesEditDialog} to capture the result of the user's configuration changes, * Used by {@link SeriesEditDialog} to capture the result of the user's configuration changes,
* including the race columns that the series shall define, the medal status and the discarding rules, if any. * including the race columns that the series shall define, the medal status and the discarding rules, if any.
* *
* @author Axel Uhl (d043530) * @author Axel Uhl (d043530)
* *
*/ */
public class SeriesDescriptor { public class SeriesDescriptor {
private final SeriesDTO series; private final SeriesDTO series;
private final List<RaceColumnDTO> races; private final List<RaceColumnDTO> races;
private final String seriesName; private final String seriesName;
private final boolean isMedal; private final boolean isMedal;
private final boolean startsWithZeroScore; private final boolean isFleetsCanRunInParallel;
private final boolean hasSplitFleetContiguousScoring; private final boolean startsWithZeroScore;
private final boolean firstColumnIsNonDiscardableCarryForward; private final boolean hasSplitFleetContiguousScoring;
private final int[] resultDiscardingThresholds; private final boolean firstColumnIsNonDiscardableCarryForward;
private final int[] resultDiscardingThresholds;
public SeriesDescriptor(SeriesDTO series, String seriesName, List<RaceColumnDTO> races, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore, public SeriesDescriptor(SeriesDTO series, String seriesName, List<RaceColumnDTO> races, boolean isMedal, boolean isFleetsCanRunInParallel,
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring) { int[] resultDiscardingThresholds, boolean startsWithZeroScore,
this.seriesName = seriesName; boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring) {
this.series = series; this.seriesName = seriesName;
this.races = races; this.series = series;
this.isMedal = isMedal; this.races = races;
this.startsWithZeroScore = startsWithZeroScore; this.isMedal = isMedal;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring; this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
this.resultDiscardingThresholds = resultDiscardingThresholds; this.startsWithZeroScore = startsWithZeroScore;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
} this.resultDiscardingThresholds = resultDiscardingThresholds;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
public SeriesDTO getSeries() { }
return series;
} public SeriesDTO getSeries() {
return series;
public String getSeriesName() { }
return seriesName;
} public String getSeriesName() {
return seriesName;
public List<RaceColumnDTO> getRaces() { }
return races;
} public List<RaceColumnDTO> getRaces() {
return races;
public boolean isMedal() { }
return isMedal;
} public boolean isMedal() {
return isMedal;
public int[] getResultDiscardingThresholds() { }
return resultDiscardingThresholds;
} public boolean isFleetsCanRunInParallel() {
return isFleetsCanRunInParallel;
public boolean isStartsWithZeroScore() { }
return startsWithZeroScore;
} public int[] getResultDiscardingThresholds() {
return resultDiscardingThresholds;
public boolean hasSplitFleetContiguousScoring() { }
return hasSplitFleetContiguousScoring;
} public boolean isStartsWithZeroScore() {
return startsWithZeroScore;
public boolean isFirstColumnIsNonDiscardableCarryForward() { }
return firstColumnIsNonDiscardableCarryForward;
} public boolean hasSplitFleetContiguousScoring() {
} return hasSplitFleetContiguousScoring;
}
public boolean isFirstColumnIsNonDiscardableCarryForward() {
return firstColumnIsNonDiscardableCarryForward;
}
}
@@ -39,6 +39,7 @@ import com.sap.sse.gwt.client.dialog.DataEntryDialog;
public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> { public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
private TextBox seriesNameTextBox; private TextBox seriesNameTextBox;
private CheckBox isMedalCheckbox; private CheckBox isMedalCheckbox;
private CheckBox fleetsCanRunInParallelCheckbox;
private CheckBox startWithZeroScoreCheckbox; private CheckBox startWithZeroScoreCheckbox;
private CheckBox hasSplitFleetContiguousScoringCheckbox; private CheckBox hasSplitFleetContiguousScoringCheckbox;
private CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox; private CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox;
@@ -133,6 +134,7 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
races.add(raceColumnDTO); races.add(raceColumnDTO);
} }
return new SeriesDescriptor(selectedSeries, seriesNameTextBox.getValue(), races, isMedalCheckbox.getValue(), return new SeriesDescriptor(selectedSeries, seriesNameTextBox.getValue(), races, isMedalCheckbox.getValue(),
fleetsCanRunInParallelCheckbox.getValue(),
useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds() useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds()
: null, startWithZeroScoreCheckbox.getValue(), : null, startWithZeroScoreCheckbox.getValue(),
firstColumnIsNonDiscardableCarryForwardCheckbox.getValue(), hasSplitFleetContiguousScoringCheckbox.getValue()); firstColumnIsNonDiscardableCarryForwardCheckbox.getValue(), hasSplitFleetContiguousScoringCheckbox.getValue());
@@ -171,7 +173,12 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
isMedalCheckbox.ensureDebugId("MedalSeriesCheckbox"); isMedalCheckbox.ensureDebugId("MedalSeriesCheckbox");
isMedalCheckbox.setValue(selectedSeries.isMedal()); isMedalCheckbox.setValue(selectedSeries.isMedal());
additionalWidgetPanel.add(isMedalCheckbox); additionalWidgetPanel.add(isMedalCheckbox);
fleetsCanRunInParallelCheckbox = createCheckbox(stringMessages.canFleetsRunInParallel());
fleetsCanRunInParallelCheckbox.ensureDebugId("FleetsCanRaceInParallelSeriesCheckbox");
fleetsCanRunInParallelCheckbox.setValue(selectedSeries.isFleetsCanRunInParallel());
additionalWidgetPanel.add(fleetsCanRunInParallelCheckbox);
startWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore()); startWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
startWithZeroScoreCheckbox.ensureDebugId("StartsWithZeroScoreCheckbox"); startWithZeroScoreCheckbox.ensureDebugId("StartsWithZeroScoreCheckbox");
startWithZeroScoreCheckbox.setValue(selectedSeries.isStartsWithZeroScore()); startWithZeroScoreCheckbox.setValue(selectedSeries.isStartsWithZeroScore());
@@ -34,6 +34,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
protected final TextBox nameEntryField; protected final TextBox nameEntryField;
protected CheckBox isMedalSeriesCheckbox; protected CheckBox isMedalSeriesCheckbox;
protected CheckBox fleetsCanRunInParallelCheckbox;
protected CheckBox startsWithZeroScoreCheckbox; protected CheckBox startsWithZeroScoreCheckbox;
protected CheckBox hasSplitFleetContiguousScoringCheckbox; protected CheckBox hasSplitFleetContiguousScoringCheckbox;
protected CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox; protected CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox;
@@ -122,6 +123,10 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
isMedalSeriesCheckbox = createCheckbox(stringMessages.medalSeries()); isMedalSeriesCheckbox = createCheckbox(stringMessages.medalSeries());
isMedalSeriesCheckbox.ensureDebugId("MedalSeriesCheckbox"); isMedalSeriesCheckbox.ensureDebugId("MedalSeriesCheckbox");
fleetsCanRunInParallelCheckbox = createCheckbox(stringMessages.canFleetsRunInParallel());
fleetsCanRunInParallelCheckbox.setValue(true);
fleetsCanRunInParallelCheckbox.ensureDebugId("FleetsCanRaceInParallelSeriesCheckbox");
startsWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore()); startsWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
startsWithZeroScoreCheckbox.ensureDebugId("StartsWithZeroScoreCheckbox"); startsWithZeroScoreCheckbox.ensureDebugId("StartsWithZeroScoreCheckbox");
@@ -162,6 +167,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
protected SeriesDTO getResult() { protected SeriesDTO getResult() {
series.setName(nameEntryField.getText()); series.setName(nameEntryField.getText());
series.setMedal(isMedalSeriesCheckbox.getValue()); series.setMedal(isMedalSeriesCheckbox.getValue());
series.setFleetsCanRunInParallel(fleetsCanRunInParallelCheckbox.getValue());
series.setStartsWithZeroScore(startsWithZeroScoreCheckbox.getValue()); series.setStartsWithZeroScore(startsWithZeroScoreCheckbox.getValue());
series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoringCheckbox.getValue()); series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoringCheckbox.getValue());
series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForwardCheckbox.getValue()); series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForwardCheckbox.getValue());
@@ -177,16 +183,17 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
if (additionalWidget != null) { if (additionalWidget != null) {
panel.add(additionalWidget); panel.add(additionalWidget);
} }
Grid formGrid = new Grid(7, 2); Grid formGrid = new Grid(8, 2);
panel.add(formGrid); panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":")); formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField); formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 1, isMedalSeriesCheckbox); formGrid.setWidget(1, 1, isMedalSeriesCheckbox);
formGrid.setWidget(2, 1, startsWithZeroScoreCheckbox); formGrid.setWidget(2, 1, fleetsCanRunInParallelCheckbox);
formGrid.setWidget(3, 1, hasSplitFleetContiguousScoringCheckbox); formGrid.setWidget(3, 1, startsWithZeroScoreCheckbox);
formGrid.setWidget(4, 1, firstColumnIsNonDiscardableCarryForwardCheckbox); formGrid.setWidget(4, 1, hasSplitFleetContiguousScoringCheckbox);
formGrid.setWidget(5, 1, useSeriesResultDiscardingThresholdsCheckbox); formGrid.setWidget(5, 1, firstColumnIsNonDiscardableCarryForwardCheckbox);
formGrid.setWidget(6, 1, discardThresholdBoxes.getWidget()); formGrid.setWidget(6, 1, useSeriesResultDiscardingThresholdsCheckbox);
formGrid.setWidget(7, 1, discardThresholdBoxes.getWidget());
TabPanel tabPanel = new TabPanel(); TabPanel tabPanel = new TabPanel();
tabPanel.setWidth("100%"); tabPanel.setWidth("100%");
@@ -32,6 +32,7 @@ public class SeriesWithFleetsDefaultCreateDialog extends SeriesWithFleetsCreateD
super(Collections.<SeriesDTO> emptyList(), stringMessages, defaultSeries.getDiscardThresholds(), callback); super(Collections.<SeriesDTO> emptyList(), stringMessages, defaultSeries.getDiscardThresholds(), callback);
nameEntryField.setText(defaultSeries.getName()); // Otherwise an errorMessage will pop up nameEntryField.setText(defaultSeries.getName()); // Otherwise an errorMessage will pop up
isMedalSeriesCheckbox.setValue(defaultSeries.isFirstColumnIsNonDiscardableCarryForward()); isMedalSeriesCheckbox.setValue(defaultSeries.isFirstColumnIsNonDiscardableCarryForward());
fleetsCanRunInParallelCheckbox.setValue(defaultSeries.isFleetsCanRunInParallel());
startsWithZeroScoreCheckbox.setValue(defaultSeries.isStartsWithZeroScore()); startsWithZeroScoreCheckbox.setValue(defaultSeries.isStartsWithZeroScore());
hasSplitFleetContiguousScoringCheckbox.setValue(defaultSeries.hasSplitFleetContiguousScoring()); hasSplitFleetContiguousScoringCheckbox.setValue(defaultSeries.hasSplitFleetContiguousScoring());
firstColumnIsNonDiscardableCarryForwardCheckbox.setValue(defaultSeries.isFirstColumnIsNonDiscardableCarryForward()); firstColumnIsNonDiscardableCarryForwardCheckbox.setValue(defaultSeries.isFirstColumnIsNonDiscardableCarryForward());
@@ -227,7 +227,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
List<Pair<String, Integer>> columnNames); List<Pair<String, Integer>> columnNames);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, List<FleetDTO> fleets); boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, List<FleetDTO> fleets);
RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName); RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
@@ -443,7 +443,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
List<Pair<String, Integer>> columnNames, AsyncCallback<List<RaceColumnInSeriesDTO>> callback); List<Pair<String, Integer>> columnNames, AsyncCallback<List<RaceColumnInSeriesDTO>> callback);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean isFleetsCanRunInParallel, int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, List<FleetDTO> fleets, boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, List<FleetDTO> fleets,
AsyncCallback<Void> callback); AsyncCallback<Void> callback);
@@ -346,6 +346,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String splitFleet(); String splitFleet();
String color(); String color();
String medalSeries(); String medalSeries();
String canFleetsRunInParallel();
String noColor(); String noColor();
String pleaseSelectARegatta(); String pleaseSelectARegatta();
String addFleet(); String addFleet();
@@ -358,6 +358,7 @@ series=Series
splitFleet=Split fleet splitFleet=Split fleet
color=Color color=Color
medalSeries=Medal Series medalSeries=Medal Series
canFleetsRunInParallel=Fleets can run in parallel
noColor=No color noColor=No color
pleaseSelectARegatta=Please select a regatta. pleaseSelectARegatta=Please select a regatta.
addFleet=Add fleet addFleet=Add fleet
@@ -359,6 +359,7 @@ fleets=Flotten
splitFleet=Aufteilung in Gruppen splitFleet=Aufteilung in Gruppen
color=Farbe color=Farbe
medalSeries=Medal Series medalSeries=Medal Series
canFleetsRunInParallel=Flotten können parallel segeln
noColor=Keine Farbe noColor=Keine Farbe
pleaseSelectARegatta=Bitte wählen Sie eine Regatta aus. pleaseSelectARegatta=Bitte wählen Sie eine Regatta aus.
addFleet=Gruppe hinzufügen addFleet=Gruppe hinzufügen
@@ -356,6 +356,7 @@ series=Серия
splitFleet=Разделить флот splitFleet=Разделить флот
color=Цвет color=Цвет
medalSeries=Серия на медали medalSeries=Серия на медали
canFleetsRunInParallel=Fleets can run in parallel
noColor=Без цвета noColor=Без цвета
pleaseSelectARegatta=Выберите регату. pleaseSelectARegatta=Выберите регату.
addFleet=Добавить флот addFleet=Добавить флот
@@ -356,6 +356,7 @@ series=系列
splitFleet=船队区分 splitFleet=船队区分
color=颜色 color=颜色
medalSeries=奖牌轮系列 medalSeries=奖牌轮系列
canFleetsRunInParallel=Fleets can run in parallel
noColor=没有颜色 noColor=没有颜色
pleaseSelectARegatta=请选择比赛。 pleaseSelectARegatta=请选择比赛。
addFleet=添加船队 addFleet=添加船队
@@ -356,6 +356,7 @@ series=系列
splitFleet=船队区分 splitFleet=船队区分
color=颜色 color=颜色
medalSeries=奖牌轮系列 medalSeries=奖牌轮系列
canFleetsRunInParallel=Fleets can run in parallel
noColor=没有颜色 noColor=没有颜色
pleaseSelectARegatta=请选择比赛。 pleaseSelectARegatta=请选择比赛。
addFleet=添加船队 addFleet=添加船队
@@ -847,7 +847,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
fleets.add(baseDomainFactory.convertToFleetDTO(fleet)); fleets.add(baseDomainFactory.convertToFleetDTO(fleet));
} }
List<RaceColumnDTO> raceColumns = convertToRaceColumnDTOs(series.getRaceColumns()); List<RaceColumnDTO> raceColumns = convertToRaceColumnDTOs(series.getRaceColumns());
SeriesDTO result = new SeriesDTO(series.getName(), fleets, raceColumns, series.isMedal(), SeriesDTO result = new SeriesDTO(series.getName(), fleets, raceColumns, series.isMedal(), series.isFleetsCanRunInParallel(),
series.getResultDiscardingRule() == null ? null : series.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), series.getResultDiscardingRule() == null ? null : series.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(),
series.isStartsWithZeroScore(), series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring()); series.isStartsWithZeroScore(), series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring());
return result; return result;
@@ -2804,7 +2804,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
/* startDate*/ null, /*endDate*/ null, /* startDate*/ null, /*endDate*/ null,
RegattaImpl.getDefaultName(replayRaceDTO.rsc, replayRaceDTO.boat_class), RegattaImpl.getDefaultName(replayRaceDTO.rsc, replayRaceDTO.boat_class),
Collections.singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME, Collections.singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME,
/* isMedal */false, Collections.singletonList(new FleetImpl( /* isMedal */false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(new FleetImpl(
LeaderboardNameConstants.DEFAULT_FLEET_NAME)), LeaderboardNameConstants.DEFAULT_FLEET_NAME)),
/* race column names */new ArrayList<String>(), getService())), false, /* race column names */new ArrayList<String>(), getService())), false,
baseDomainFactory.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, baseDomainFactory.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true,
@@ -3918,7 +3918,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} }
@Override @Override
public void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, public void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, boolean isFleetsCanRunInParallel,
int[] resultDiscardingThresholds, boolean startsWithZeroScore, int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring,
List<FleetDTO> fleets) { List<FleetDTO> fleets) {
@@ -3927,7 +3927,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
SecurityUtils.getSubject().checkPermission(Permission.REGATTA.getStringPermissionForObjects(Mode.UPDATE, regatta.getName())); SecurityUtils.getSubject().checkPermission(Permission.REGATTA.getStringPermissionForObjects(Mode.UPDATE, regatta.getName()));
} }
getService().apply( getService().apply(
new UpdateSeries(regattaIdentifier, seriesName, newSeriesName, isMedal, resultDiscardingThresholds, new UpdateSeries(regattaIdentifier, seriesName, newSeriesName, isMedal, isFleetsCanRunInParallel, resultDiscardingThresholds,
startsWithZeroScore, firstColumnIsNonDiscardableCarryForward, hasSplitFleetContiguousScoring, startsWithZeroScore, firstColumnIsNonDiscardableCarryForward, hasSplitFleetContiguousScoring,
fleets)); fleets));
} }
@@ -4258,7 +4258,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
for (SeriesDTO series : regattaDTO.series){ for (SeriesDTO series : regattaDTO.series){
SeriesParameters seriesParameters = getSeriesParameters(series); SeriesParameters seriesParameters = getSeriesParameters(series);
seriesCreationParams.put(series.getName(), new SeriesCreationParametersDTO(series.getFleets(), seriesCreationParams.put(series.getName(), new SeriesCreationParametersDTO(series.getFleets(),
false, seriesParameters.isStartswithZeroScore(), seriesParameters.isFirstColumnIsNonDiscardableCarryForward(), false, true, seriesParameters.isStartswithZeroScore(), seriesParameters.isFirstColumnIsNonDiscardableCarryForward(),
seriesParameters.getDiscardingThresholds(), seriesParameters.isHasSplitFleetContiguousScoring())); seriesParameters.getDiscardingThresholds(), seriesParameters.isHasSplitFleetContiguousScoring()));
} }
return seriesCreationParams; return seriesCreationParams;
@@ -12,6 +12,7 @@ public class SeriesDTO extends NamedDTO {
private List<FleetDTO> fleets; private List<FleetDTO> fleets;
private List<RaceColumnDTO> raceColumns; private List<RaceColumnDTO> raceColumns;
private boolean isMedal; private boolean isMedal;
private boolean isFleetsCanRunInParallel;
private int[] discardThresholds; private int[] discardThresholds;
private Boolean startsWithZeroScore; private Boolean startsWithZeroScore;
private boolean firstColumnIsNonDiscardableCarryForward; private boolean firstColumnIsNonDiscardableCarryForward;
@@ -19,13 +20,14 @@ public class SeriesDTO extends NamedDTO {
public SeriesDTO() {} public SeriesDTO() {}
public SeriesDTO(String name, List<FleetDTO> fleets, List<RaceColumnDTO> raceColumns, boolean isMedal, public SeriesDTO(String name, List<FleetDTO> fleets, List<RaceColumnDTO> raceColumns, boolean isMedal, boolean isFleetsCanRunInParallel,
int[] discardThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward, int[] discardThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward,
boolean hasSplitFleetContiguousScoring) { boolean hasSplitFleetContiguousScoring) {
super(name); super(name);
this.fleets = fleets; this.fleets = fleets;
this.raceColumns = raceColumns; this.raceColumns = raceColumns;
this.isMedal = isMedal; this.isMedal = isMedal;
this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
this.startsWithZeroScore = startsWithZeroScore; this.startsWithZeroScore = startsWithZeroScore;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring; this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
this.discardThresholds = discardThresholds; this.discardThresholds = discardThresholds;
@@ -40,7 +42,7 @@ public class SeriesDTO extends NamedDTO {
public SeriesDTO(SeriesDTO otherSeries) { public SeriesDTO(SeriesDTO otherSeries) {
this(otherSeries.getName(), otherSeries.getFleets(), this(otherSeries.getName(), otherSeries.getFleets(),
otherSeries.getRaceColumns() == null ? null : new ArrayList<RaceColumnDTO>(otherSeries.getRaceColumns()), otherSeries.getRaceColumns() == null ? null : new ArrayList<RaceColumnDTO>(otherSeries.getRaceColumns()),
otherSeries.isMedal(), otherSeries.getDiscardThresholds(), otherSeries.isStartsWithZeroScore(), otherSeries.isMedal(), otherSeries.isFleetsCanRunInParallel(), otherSeries.getDiscardThresholds(), otherSeries.isStartsWithZeroScore(),
otherSeries.isFirstColumnIsNonDiscardableCarryForward(), otherSeries.hasSplitFleetContiguousScoring()); otherSeries.isFirstColumnIsNonDiscardableCarryForward(), otherSeries.hasSplitFleetContiguousScoring());
} }
@@ -48,10 +50,6 @@ public class SeriesDTO extends NamedDTO {
return hasSplitFleetContiguousScoring; return hasSplitFleetContiguousScoring;
} }
public boolean isMedal() {
return isMedal;
}
public List<FleetDTO> getFleets() { public List<FleetDTO> getFleets() {
return fleets; return fleets;
} }
@@ -60,10 +58,22 @@ public class SeriesDTO extends NamedDTO {
this.fleets = fleets; this.fleets = fleets;
} }
public boolean isMedal() {
return isMedal;
}
public void setMedal(boolean isMedal) { public void setMedal(boolean isMedal) {
this.isMedal = isMedal; this.isMedal = isMedal;
} }
public boolean isFleetsCanRunInParallel() {
return isFleetsCanRunInParallel;
}
public void setFleetsCanRunInParallel(boolean isFleetsCanRunInParallel) {
this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
}
public List<RaceColumnDTO> getRaceColumns() { public List<RaceColumnDTO> getRaceColumns() {
return raceColumns; return raceColumns;
} }
@@ -1,81 +1,81 @@
package com.sap.sailing.mongodb.test; package com.sap.sailing.mongodb.test;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Before; import org.junit.Before;
import com.mongodb.MongoException; import com.mongodb.MongoException;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.PersistenceFactory; import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public abstract class AbstractTestStoringAndRetrievingRaceLogInRegatta extends RaceLogMongoDBTest { public abstract class AbstractTestStoringAndRetrievingRaceLogInRegatta extends RaceLogMongoDBTest {
protected final String raceColumnName = "My.First$Race$1"; protected final String raceColumnName = "My.First$Race$1";
protected final String regattaName = "TestRegatta"; protected final String regattaName = "TestRegatta";
protected final String yellowFleetName = "Yellow"; protected final String yellowFleetName = "Yellow";
protected final String seriesName = "Qualifying"; protected final String seriesName = "Qualifying";
protected MongoObjectFactory mongoObjectFactory = null; protected MongoObjectFactory mongoObjectFactory = null;
protected DomainObjectFactory domainObjectFactory = null; protected DomainObjectFactory domainObjectFactory = null;
protected Regatta regatta = null; protected Regatta regatta = null;
protected AbstractLogEventAuthor author = new LogEventAuthorImpl("Test Author", 1); protected AbstractLogEventAuthor author = new LogEventAuthorImpl("Test Author", 1);
public AbstractTestStoringAndRetrievingRaceLogInRegatta() throws UnknownHostException, MongoException { public AbstractTestStoringAndRetrievingRaceLogInRegatta() throws UnknownHostException, MongoException {
super(); super();
} }
@Before @Before
public void setUp() { public void setUp() {
now = MillisecondsTimePoint.now(); now = MillisecondsTimePoint.now();
mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE); domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true);
regatta = createRegattaAndAddRaceColumns(1, regattaName, boatClass, true, regatta = createRegattaAndAddRaceColumns(1, regattaName, boatClass, true,
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT)); DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
} }
private Regatta createRegattaAndAddRaceColumns(final int numberOfQualifyingRaces, final String regattaBaseName, BoatClass boatClass, boolean persistent, ScoringScheme scoringScheme) { private Regatta createRegattaAndAddRaceColumns(final int numberOfQualifyingRaces, final String regattaBaseName, BoatClass boatClass, boolean persistent, ScoringScheme scoringScheme) {
Regatta regatta = createRegattaWithoutRaceColumns(regattaBaseName, boatClass, persistent, scoringScheme, null); Regatta regatta = createRegattaWithoutRaceColumns(regattaBaseName, boatClass, persistent, scoringScheme, null);
regatta.getSeriesByName(seriesName).addRaceColumn(raceColumnName, /* trackedRegattaRegistry */ null); regatta.getSeriesByName(seriesName).addRaceColumn(raceColumnName, /* trackedRegattaRegistry */ null);
return regatta; return regatta;
} }
private Regatta createRegattaWithoutRaceColumns(final String regattaBaseName, BoatClass boatClass, private Regatta createRegattaWithoutRaceColumns(final String regattaBaseName, BoatClass boatClass,
boolean persistent, ScoringScheme scoringScheme, CourseArea courseArea) { boolean persistent, ScoringScheme scoringScheme, CourseArea courseArea) {
List<Series> series = createSeriesForTestRegatta(); List<Series> series = createSeriesForTestRegatta();
Regatta regatta = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), RegattaImpl.getDefaultName(regattaBaseName, Regatta regatta = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), RegattaImpl.getDefaultName(regattaBaseName,
boatClass == null ? null : boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, series, persistent, scoringScheme, "123", boatClass == null ? null : boatClass.getName()), boatClass, /*startDate*/ null, /*endDate*/ null, series, persistent, scoringScheme, "123",
courseArea, /* useStartTimeInference */true, OneDesignRankingMetric::new); courseArea, /* useStartTimeInference */true, OneDesignRankingMetric::new);
return regatta; return regatta;
} }
private List<Series> createSeriesForTestRegatta() { private List<Series> createSeriesForTestRegatta() {
List<Series> series = new ArrayList<Series>(); List<Series> series = new ArrayList<Series>();
// -------- qualifying series ------------ // -------- qualifying series ------------
List<String> emptyRaceColumnNames = Collections.emptyList(); List<String> emptyRaceColumnNames = Collections.emptyList();
List<Fleet> qualifyingFleets = createQualifyingFleets(); List<Fleet> qualifyingFleets = createQualifyingFleets();
Series qualifyingSeries = new SeriesImpl(seriesName, /* isMedal */false, qualifyingFleets, Series qualifyingSeries = new SeriesImpl(seriesName, /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualifyingFleets,
emptyRaceColumnNames, /* trackedRegattaRegistry */ null); emptyRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(qualifyingSeries); series.add(qualifyingSeries);
return series; return series;
} }
abstract protected List<Fleet> createQualifyingFleets(); abstract protected List<Fleet> createQualifyingFleets();
} }
@@ -637,6 +637,28 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
assertTrue(loadedRegatta.getSeriesByName("Medal").isStartsWithZeroScore()); assertTrue(loadedRegatta.getSeriesByName("Medal").isStartsWithZeroScore());
} }
@Test
public void testLoadStoreRegattaWithSeriesWhereFleetsCanRunInParallel() {
final int numberOfQualifyingRaces = 5;
final int numberOfFinalRaces = 7;
final String regattaBaseName = "Kieler Woche";
BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true);
Regatta regatta = createRegattaAndAddRaceColumns(numberOfQualifyingRaces, numberOfFinalRaces, RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()),
boatClass, regattaStartDate, regattaEndDate, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), OneDesignRankingMetric::new);
regatta.getSeriesByName("Qualifying").setIsFleetsCanRunInParallel(true);
regatta.getSeriesByName("Final").setIsFleetsCanRunInParallel(false);
regatta.getSeriesByName("Medal").setIsFleetsCanRunInParallel(false);
MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
mof.storeRegatta(regatta);
DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null);
assertTrue(loadedRegatta.getSeriesByName("Qualifying").isFleetsCanRunInParallel());
assertFalse(loadedRegatta.getSeriesByName("Final").isFleetsCanRunInParallel());
assertFalse(loadedRegatta.getSeriesByName("Medal").isFleetsCanRunInParallel());
}
@Test @Test
public void testLoadStoreSimpleRegattaWithHighPointScoringScheme() { public void testLoadStoreSimpleRegattaWithHighPointScoringScheme() {
final int numberOfQualifyingRaces = 5; final int numberOfQualifyingRaces = 5;
@@ -787,7 +809,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
List<Fleet> qualifyingFleets = new ArrayList<Fleet>(); List<Fleet> qualifyingFleets = new ArrayList<Fleet>();
qualifyingFleets.add(new FleetImpl("Yellow")); qualifyingFleets.add(new FleetImpl("Yellow"));
qualifyingFleets.add(new FleetImpl("Blue")); qualifyingFleets.add(new FleetImpl("Blue"));
Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, qualifyingFleets, Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualifyingFleets,
emptyRaceColumnNames, /* trackedRegattaRegistry */ null); emptyRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(qualifyingSeries); series.add(qualifyingSeries);
@@ -795,13 +817,13 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
List<Fleet> finalFleets = new ArrayList<Fleet>(); List<Fleet> finalFleets = new ArrayList<Fleet>();
finalFleets.add(new FleetImpl("Gold", 1)); finalFleets.add(new FleetImpl("Gold", 1));
finalFleets.add(new FleetImpl("Silver", 2)); finalFleets.add(new FleetImpl("Silver", 2));
Series finalSeries = new SeriesImpl("Final", /* isMedal */ false, finalFleets, emptyRaceColumnNames, /* trackedRegattaRegistry */ null); Series finalSeries = new SeriesImpl("Final", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, finalFleets, emptyRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(finalSeries); series.add(finalSeries);
// ------------ medal -------------- // ------------ medal --------------
List<Fleet> medalFleets = new ArrayList<Fleet>(); List<Fleet> medalFleets = new ArrayList<Fleet>();
medalFleets.add(new FleetImpl("Medal")); medalFleets.add(new FleetImpl("Medal"));
Series medalSeries = new SeriesImpl("Medal", /* isMedal */ true, medalFleets, emptyRaceColumnNames, /* trackedRegattaRegistry */ null); Series medalSeries = new SeriesImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true, medalFleets, emptyRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(medalSeries); series.add(medalSeries);
Regatta regatta = new RegattaImpl(regattaName, boatClass, startDate, endDate, series, persistent, scoringScheme, "123", courseArea, rankingMetricConstructor); Regatta regatta = new RegattaImpl(regattaName, boatClass, startDate, endDate, series, persistent, scoringScheme, "123", courseArea, rankingMetricConstructor);
return regatta; return regatta;
@@ -24,9 +24,10 @@ public class RaceCellDeserializer implements JsonDeserializer<RaceCell> {
final Object factorJson = object.get(RaceCellJsonSerializer.FIELD_FACTOR); final Object factorJson = object.get(RaceCellJsonSerializer.FIELD_FACTOR);
final double factor = factorJson == null ? 1 : ((Number) factorJson).doubleValue(); final double factor = factorJson == null ? 1 : ((Number) factorJson).doubleValue();
final Double explicitFactor = (Double) object.get(RaceCellJsonSerializer.FIELD_EXPLICIT_FACTOR); final Double explicitFactor = (Double) object.get(RaceCellJsonSerializer.FIELD_EXPLICIT_FACTOR);
final Number zeroBasedIndexInFleet = (Number) object.get(RaceCellJsonSerializer.FIELD_ZERO_BASED_INDEX_IN_FLEET);
JSONObject logJson = Helpers.getNestedObjectSafe(object, RaceCellJsonSerializer.FIELD_RACE_LOG); JSONObject logJson = Helpers.getNestedObjectSafe(object, RaceCellJsonSerializer.FIELD_RACE_LOG);
RaceLog log = logDeserializer.deserialize(logJson); RaceLog log = logDeserializer.deserialize(logJson);
return new RaceCellImpl(name, log, factor, explicitFactor); return new RaceCellImpl(name, log, factor, explicitFactor, zeroBasedIndexInFleet == null ? -1 : zeroBasedIndexInFleet.intValue());
} }
} }
@@ -28,6 +28,7 @@ public class RaceRowDeserializer implements JsonDeserializer<RaceRow> {
public RaceRow deserialize(JSONObject object) throws JsonDeserializationException { public RaceRow deserialize(JSONObject object) throws JsonDeserializationException {
JSONObject fleetObject = Helpers.toJSONObjectSafe(object.get(RaceRowJsonSerializer.FIELD_FLEET)); JSONObject fleetObject = Helpers.toJSONObjectSafe(object.get(RaceRowJsonSerializer.FIELD_FLEET));
// the race cells are expected to be written in the order of the race columns in the series / leaderboard
JSONArray cellsObject = Helpers.getNestedArraySafe(object, RaceRowJsonSerializer.FIELD_RACE_CELLS); JSONArray cellsObject = Helpers.getNestedArraySafe(object, RaceRowJsonSerializer.FIELD_RACE_CELLS);
Collection<RaceCell> races = new ArrayList<RaceCell>(); Collection<RaceCell> races = new ArrayList<RaceCell>();
@@ -1,37 +1,42 @@
package com.sap.sailing.server.gateway.deserialization.racegroup.impl; package com.sap.sailing.server.gateway.deserialization.racegroup.impl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.racegroup.RaceRow; import com.sap.sailing.domain.base.racegroup.RaceRow;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows; import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.domain.base.racegroup.impl.SeriesWithRowsImpl; import com.sap.sailing.domain.base.racegroup.impl.SeriesWithRowsImpl;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers; import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
import com.sap.sailing.server.gateway.serialization.racegroup.impl.RaceRowsOfSeriesWithRowsSerializer; import com.sap.sailing.server.gateway.serialization.racegroup.impl.RaceRowsOfSeriesWithRowsSerializer;
import com.sap.sailing.server.gateway.serialization.racegroup.impl.SeriesWithRowsJsonSerializer;
public class SeriesWithRowsDeserializer implements JsonDeserializer<SeriesWithRows> {
public class SeriesWithRowsDeserializer implements JsonDeserializer<SeriesWithRows> {
private JsonDeserializer<RaceRow> raceRowDeserializer;
private JsonDeserializer<RaceRow> raceRowDeserializer;
public SeriesWithRowsDeserializer(JsonDeserializer<RaceRow> raceRowDeserializer) {
this.raceRowDeserializer = raceRowDeserializer; public SeriesWithRowsDeserializer(JsonDeserializer<RaceRow> raceRowDeserializer) {
} this.raceRowDeserializer = raceRowDeserializer;
}
public SeriesWithRows deserialize(JSONObject object) throws JsonDeserializationException {
String name = object.get("name").toString(); public SeriesWithRows deserialize(JSONObject object) throws JsonDeserializationException {
boolean isMedal = (Boolean) object.get("isMedal"); String name = object.get(SeriesWithRowsJsonSerializer.FIELD_NAME).toString();
boolean isMedal = (Boolean) object.get(SeriesWithRowsJsonSerializer.FIELD_IS_MEDAL);
Collection<RaceRow> rows = new ArrayList<RaceRow>(); Boolean isFleetsCanRunInParallel = (Boolean) object.get(SeriesWithRowsJsonSerializer.FIELD_FLEETS_CAN_RUN_IN_PARALLEL);
for (Object fleetObject : Helpers.getNestedArraySafe(object, RaceRowsOfSeriesWithRowsSerializer.FIELD_FLEETS)) { if (isFleetsCanRunInParallel == null) {
JSONObject fleetJson = Helpers.toJSONObjectSafe(fleetObject); isFleetsCanRunInParallel = true; // that's the default
rows.add(raceRowDeserializer.deserialize(fleetJson)); }
}
Collection<RaceRow> rows = new ArrayList<RaceRow>();
return new SeriesWithRowsImpl(name, isMedal, rows); for (Object fleetObject : Helpers.getNestedArraySafe(object, RaceRowsOfSeriesWithRowsSerializer.FIELD_FLEETS)) {
} JSONObject fleetJson = Helpers.toJSONObjectSafe(fleetObject);
rows.add(raceRowDeserializer.deserialize(fleetJson));
}
return new SeriesWithRowsImpl(name, isMedal, isFleetsCanRunInParallel, rows);
}
} }
@@ -10,8 +10,9 @@ public class RaceCellJsonSerializer implements JsonSerializer<RaceCell> {
public static final String FIELD_NAME = "name"; public static final String FIELD_NAME = "name";
public static final String FIELD_RACE_LOG = "raceLog"; public static final String FIELD_RACE_LOG = "raceLog";
public static final String FIELD_COMPETITORS = "competitors"; public static final String FIELD_COMPETITORS = "competitors";
public static final Object FIELD_FACTOR = "factor"; public static final String FIELD_FACTOR = "factor";
public static final Object FIELD_EXPLICIT_FACTOR = "explicitfactor"; public static final String FIELD_EXPLICIT_FACTOR = "explicitfactor";
public static final String FIELD_ZERO_BASED_INDEX_IN_FLEET = "zerobasedindexinfleet";
private JsonSerializer<RaceLog> logSerializer; private JsonSerializer<RaceLog> logSerializer;
@@ -25,6 +26,7 @@ public class RaceCellJsonSerializer implements JsonSerializer<RaceCell> {
result.put(FIELD_NAME, object.getName()); result.put(FIELD_NAME, object.getName());
result.put(FIELD_FACTOR, object.getFactor()); result.put(FIELD_FACTOR, object.getFactor());
result.put(FIELD_EXPLICIT_FACTOR, object.getExplicitFactor()); result.put(FIELD_EXPLICIT_FACTOR, object.getExplicitFactor());
result.put(FIELD_ZERO_BASED_INDEX_IN_FLEET, object.getZeroBasedIndexInFleet());
result.put(FIELD_RACE_LOG, logSerializer.serialize(object.getRaceLog())); result.put(FIELD_RACE_LOG, logSerializer.serialize(object.getRaceLog()));
return result; return result;
} }
@@ -10,6 +10,11 @@ import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class RaceRowJsonSerializer implements JsonSerializer<RaceRow> { public class RaceRowJsonSerializer implements JsonSerializer<RaceRow> {
public static final String FIELD_FLEET = "fleet"; public static final String FIELD_FLEET = "fleet";
/**
* The race cells in the JSONArray keyed by this name are expected to be written
* in the order in which their race columns appear in the series / leaderboard
*/
public static final String FIELD_RACE_CELLS = "races"; public static final String FIELD_RACE_CELLS = "races";
private JsonSerializer<Fleet> fleetSerializer; private JsonSerializer<Fleet> fleetSerializer;
@@ -25,14 +30,12 @@ public class RaceRowJsonSerializer implements JsonSerializer<RaceRow> {
@Override @Override
public JSONObject serialize(RaceRow object) { public JSONObject serialize(RaceRow object) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put(FIELD_FLEET, fleetSerializer.serialize(object.getFleet())); result.put(FIELD_FLEET, fleetSerializer.serialize(object.getFleet()));
JSONArray cells = new JSONArray(); JSONArray cells = new JSONArray();
for (RaceCell cell : object.getCells()) { for (RaceCell cell : object.getCells()) {
cells.add(cellSerializer.serialize(cell)); cells.add(cellSerializer.serialize(cell));
} }
result.put(FIELD_RACE_CELLS, cells); result.put(FIELD_RACE_CELLS, cells);
return result; return result;
} }
@@ -9,6 +9,7 @@ import com.sap.sailing.server.gateway.serialization.ExtensionJsonSerializer;
public class SeriesWithRowsJsonSerializer extends ExtendableJsonSerializer<SeriesWithRows> { public class SeriesWithRowsJsonSerializer extends ExtendableJsonSerializer<SeriesWithRows> {
public static final String FIELD_NAME = "name"; public static final String FIELD_NAME = "name";
public static final String FIELD_IS_MEDAL = "isMedal"; public static final String FIELD_IS_MEDAL = "isMedal";
public static final String FIELD_FLEETS_CAN_RUN_IN_PARALLEL = "isFleetsCanRunInParallel";
public SeriesWithRowsJsonSerializer(ExtensionJsonSerializer<SeriesWithRows, ?> extensionSerializer) { public SeriesWithRowsJsonSerializer(ExtensionJsonSerializer<SeriesWithRows, ?> extensionSerializer) {
super(extensionSerializer); super(extensionSerializer);
@@ -17,10 +18,9 @@ public class SeriesWithRowsJsonSerializer extends ExtendableJsonSerializer<Serie
@Override @Override
protected JSONObject serializeFields(SeriesWithRows object) { protected JSONObject serializeFields(SeriesWithRows object) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
result.put(FIELD_NAME, object.getName()); result.put(FIELD_NAME, object.getName());
result.put(FIELD_IS_MEDAL, object.isMedal()); result.put(FIELD_IS_MEDAL, object.isMedal());
result.put(FIELD_FLEETS_CAN_RUN_IN_PARALLEL, object.isFleetsCanRunInParallel());
return result; return result;
} }
@@ -1,82 +1,84 @@
package com.sap.sailing.server.gateway.serialization.impl; package com.sap.sailing.server.gateway.serialization.impl;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.gateway.serialization.JsonSerializer; import com.sap.sailing.server.gateway.serialization.JsonSerializer;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class SeriesJsonSerializer implements JsonSerializer<Series> { public class SeriesJsonSerializer implements JsonSerializer<Series> {
public static final String FIELD_NAME = "name"; public static final String FIELD_NAME = "name";
public static final String FIELD_FLEETS = "fleets"; public static final String FIELD_FLEETS = "fleets";
public static final String FIELD_RACES = "races"; // TODO: 'races' is wrong here... it's actually 'raceColumns' public static final String FIELD_RACES = "races"; // TODO: 'races' is wrong here... it's actually 'raceColumns'
public static final String FIELD_TRACKED_RACES = "trackedRaces"; public static final String FIELD_TRACKED_RACES = "trackedRaces";
public static final String FIELD_STARTS_WITH_ZERO_SCORE = "startsWithZeroScore"; public static final String FIELD_STARTS_WITH_ZERO_SCORE = "startsWithZeroScore";
public static final String FIELD_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING = "hasSplitFleetContiguousScoring"; public static final String FIELD_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING = "hasSplitFleetContiguousScoring";
public static final String FIELD_IS_MEDAL_SERIES = "isMedalSeries"; public static final String FIELD_IS_MEDAL_SERIES = "isMedalSeries";
public static final String FIELD_FLEETS_CAN_RUN_IN_PARALLEL = "fleetsCanRunInParallel";
private final JsonSerializer<Fleet> fleetSerializer;
private final JsonSerializer<Fleet> fleetSerializer;
public SeriesJsonSerializer(JsonSerializer<Fleet> fleetSerializer) {
this.fleetSerializer = fleetSerializer; public SeriesJsonSerializer(JsonSerializer<Fleet> fleetSerializer) {
} this.fleetSerializer = fleetSerializer;
}
public JSONObject serialize(Series series) {
JSONObject result = new JSONObject(); public JSONObject serialize(Series series) {
JSONObject result = new JSONObject();
result.put(FIELD_NAME, series.getName());
result.put(FIELD_IS_MEDAL_SERIES, series.isMedal()); result.put(FIELD_NAME, series.getName());
result.put(FIELD_STARTS_WITH_ZERO_SCORE, series.isStartsWithZeroScore()); result.put(FIELD_IS_MEDAL_SERIES, series.isMedal());
result.put(FIELD_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING, series.hasSplitFleetContiguousScoring()); result.put(FIELD_FLEETS_CAN_RUN_IN_PARALLEL, series.isFleetsCanRunInParallel());
result.put(FIELD_STARTS_WITH_ZERO_SCORE, series.isStartsWithZeroScore());
JSONArray fleetsJson = new JSONArray(); result.put(FIELD_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING, series.hasSplitFleetContiguousScoring());
for (Fleet fleet : series.getFleets()) {
fleetsJson.add(fleetSerializer.serialize(fleet)); JSONArray fleetsJson = new JSONArray();
} for (Fleet fleet : series.getFleets()) {
result.put(FIELD_FLEETS, fleetsJson); fleetsJson.add(fleetSerializer.serialize(fleet));
}
JSONArray racesJson = new JSONArray(); result.put(FIELD_FLEETS, fleetsJson);
for (RaceColumn raceColumn : series.getRaceColumns()) {
racesJson.add(raceColumn.getName()); JSONArray racesJson = new JSONArray();
} for (RaceColumn raceColumn : series.getRaceColumns()) {
result.put(FIELD_RACES, racesJson); racesJson.add(raceColumn.getName());
}
JSONObject trackedRacesJson = new JSONObject(); result.put(FIELD_RACES, racesJson);
JSONArray trackedFleetsJson = new JSONArray();
for (Fleet fleet : series.getFleets()) { JSONObject trackedRacesJson = new JSONObject();
JSONObject trackedFleetJson = new JSONObject(); JSONArray trackedFleetsJson = new JSONArray();
trackedFleetJson.put(FIELD_NAME, fleet.getName()); for (Fleet fleet : series.getFleets()) {
JSONArray racesPerFleetJson = new JSONArray(); JSONObject trackedFleetJson = new JSONObject();
for (RaceColumn raceColumn : series.getRaceColumns()) { trackedFleetJson.put(FIELD_NAME, fleet.getName());
TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); JSONArray racesPerFleetJson = new JSONArray();
JSONObject raceColumnJson = new JSONObject(); for (RaceColumn raceColumn : series.getRaceColumns()) {
raceColumnJson.put(FIELD_NAME, raceColumn.getName()); TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
raceColumnJson.put("isMedalRace" , raceColumn.isMedalRace()); JSONObject raceColumnJson = new JSONObject();
if (trackedRace != null) { raceColumnJson.put(FIELD_NAME, raceColumn.getName());
raceColumnJson.put("isLive", trackedRace.isLive(MillisecondsTimePoint.now())); raceColumnJson.put("isMedalRace" , raceColumn.isMedalRace());
raceColumnJson.put("isTracked", true); if (trackedRace != null) {
raceColumnJson.put("trackedRaceName", trackedRace.getRace().getName()); raceColumnJson.put("isLive", trackedRace.isLive(MillisecondsTimePoint.now()));
raceColumnJson.put("hasGpsData", trackedRace.hasGPSData()); raceColumnJson.put("isTracked", true);
raceColumnJson.put("hasWindData", trackedRace.hasWindData()); raceColumnJson.put("trackedRaceName", trackedRace.getRace().getName());
} else { raceColumnJson.put("hasGpsData", trackedRace.hasGPSData());
raceColumnJson.put("isLive", false); raceColumnJson.put("hasWindData", trackedRace.hasWindData());
raceColumnJson.put("isTracked", false); } else {
raceColumnJson.put("trackedRaceName", null); raceColumnJson.put("isLive", false);
raceColumnJson.put("hasGpsData", false); raceColumnJson.put("isTracked", false);
raceColumnJson.put("hasWindData", false); raceColumnJson.put("trackedRaceName", null);
} raceColumnJson.put("hasGpsData", false);
racesPerFleetJson.add(raceColumnJson); raceColumnJson.put("hasWindData", false);
} }
trackedFleetJson.put(FIELD_RACES, racesPerFleetJson); racesPerFleetJson.add(raceColumnJson);
trackedFleetsJson.add(trackedFleetJson); }
} trackedFleetJson.put(FIELD_RACES, racesPerFleetJson);
trackedRacesJson.put(FIELD_FLEETS, trackedFleetsJson); trackedFleetsJson.add(trackedFleetJson);
result.put(FIELD_TRACKED_RACES, trackedRacesJson); }
trackedRacesJson.put(FIELD_FLEETS, trackedFleetsJson);
return result; result.put(FIELD_TRACKED_RACES, trackedRacesJson);
}
} return result;
}
}
@@ -1,92 +1,92 @@
package com.sap.sailing.server.gateway.test.jaxrs; package com.sap.sailing.server.gateway.test.jaxrs;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.gateway.jaxrs.api.LeaderboardGroupsResource; import com.sap.sailing.server.gateway.jaxrs.api.LeaderboardGroupsResource;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LeaderboardGroupResourceTest extends AbstractJaxRsApiTest { public class LeaderboardGroupResourceTest extends AbstractJaxRsApiTest {
private Regatta regatta; private Regatta regatta;
private LeaderboardGroup leaderboardGroup; private LeaderboardGroup leaderboardGroup;
private String boatClassName = "49er"; private String boatClassName = "49er";
private String regattaName = "TestRegatta"; private String regattaName = "TestRegatta";
private String leaderboardGroupName = "TestLeaderboardGroup"; private String leaderboardGroupName = "TestLeaderboardGroup";
@Before @Before
public void setUp() { public void setUp() {
super.setUp(); super.setUp();
List<Series> series = new ArrayList<Series>(); List<Series> series = new ArrayList<Series>();
List<Fleet> fleets = new ArrayList<Fleet>(); List<Fleet> fleets = new ArrayList<Fleet>();
List<String> raceColumnNames = new ArrayList<String>(); List<String> raceColumnNames = new ArrayList<String>();
fleets.add(new FleetImpl("Fleet1")); fleets.add(new FleetImpl("Fleet1"));
fleets.add(new FleetImpl("Fleet2")); fleets.add(new FleetImpl("Fleet2"));
Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, fleets, Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, /* isFleetsCanRunInParallel */ true, fleets,
raceColumnNames, /* trackedRegattaRegistry */null); raceColumnNames, /* trackedRegattaRegistry */null);
series.add(testSeries); series.add(testSeries);
regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName, regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName,
/*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true,
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new); DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new);
testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null); testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
List<Competitor> competitors = createCompetitors(4); List<Competitor> competitors = createCompetitors(4);
List<Competitor> fleet1Competitors = competitors.subList(0, 2); List<Competitor> fleet1Competitors = competitors.subList(0, 2);
List<Competitor> fleet2Competitors = competitors.subList(2, 4); List<Competitor> fleet2Competitors = competitors.subList(2, 4);
TimePoint now = MillisecondsTimePoint.now(); TimePoint now = MillisecondsTimePoint.now();
RaceColumn r1Column = series.get(0).getRaceColumnByName("R1"); RaceColumn r1Column = series.get(0).getRaceColumnByName("R1");
TrackedRace r1Fleet1 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet1Competitors); TrackedRace r1Fleet1 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet1Competitors);
TrackedRace r1Fleet2 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet2Competitors); TrackedRace r1Fleet2 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet2Competitors);
r1Column.setTrackedRace(r1Column.getFleetByName("Fleet1"), r1Fleet1); r1Column.setTrackedRace(r1Column.getFleetByName("Fleet1"), r1Fleet1);
r1Column.setTrackedRace(r1Column.getFleetByName("Fleet2"), r1Fleet2); r1Column.setTrackedRace(r1Column.getFleetByName("Fleet2"), r1Fleet2);
racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "Testregatta displayName", new int[] { 3, 5 }); racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "Testregatta displayName", new int[] { 3, 5 });
List<String> leaderboardNames = new ArrayList<String>(); List<String> leaderboardNames = new ArrayList<String>();
leaderboardNames.add(regatta.getName()); leaderboardNames.add(regatta.getName());
leaderboardGroup = racingEventService.addLeaderboardGroup(UUID.randomUUID(), leaderboardGroupName, "description", leaderboardGroup = racingEventService.addLeaderboardGroup(UUID.randomUUID(), leaderboardGroupName, "description",
"The LG displayName", /* displayGroupsInReverseOrder */ false, "The LG displayName", /* displayGroupsInReverseOrder */ false,
leaderboardNames, /* overallLeaderboardDiscardThresholds */ null, /* overallLeaderboardScoringSchemeType */ null); leaderboardNames, /* overallLeaderboardDiscardThresholds */ null, /* overallLeaderboardScoringSchemeType */ null);
} }
@Test @Test
public void testExportLeaderboardGroupsAsJson() throws Exception { public void testExportLeaderboardGroupsAsJson() throws Exception {
LeaderboardGroupsResource resource = new LeaderboardGroupsResource(); LeaderboardGroupsResource resource = new LeaderboardGroupsResource();
LeaderboardGroupsResource spyResource = spyResource(resource); LeaderboardGroupsResource spyResource = spyResource(resource);
Response leaderboardGroupsResponse = spyResource.getLeaderboardGroups(); Response leaderboardGroupsResponse = spyResource.getLeaderboardGroups();
String jsonString = (String) leaderboardGroupsResponse.getEntity(); String jsonString = (String) leaderboardGroupsResponse.getEntity();
Object obj= JSONValue.parse(jsonString); Object obj= JSONValue.parse(jsonString);
JSONArray array= (JSONArray) obj; JSONArray array= (JSONArray) obj;
assertTrue(array.size() == 1); assertTrue(array.size() == 1);
String jsonFirstLeaderboardGroup = (String) array.get(0); String jsonFirstLeaderboardGroup = (String) array.get(0);
assertTrue(leaderboardGroup.getName().equals(jsonFirstLeaderboardGroup)); assertTrue(leaderboardGroup.getName().equals(jsonFirstLeaderboardGroup));
} }
} }
@@ -49,7 +49,7 @@ public class LeaderboardsResourceCheckinAndOutTest extends AbstractJaxRsApiTest
c.getColor(), c.getEmail(), c.getFlagImage(), (DynamicTeam) c.getTeam(), (DynamicBoat) c.getBoat(), c.getColor(), c.getEmail(), c.getFlagImage(), (DynamicTeam) c.getTeam(), (DynamicBoat) c.getBoat(),
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null);
Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), MillisecondsTimePoint.now(), Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), MillisecondsTimePoint.now(),
MillisecondsTimePoint.now(), Collections.singleton(new SeriesImpl("series", false, Collections MillisecondsTimePoint.now(), Collections.singleton(new SeriesImpl("series", false, /* isFleetsCanRunInParallel */ true, Collections
.singleton(new FleetImpl("fleet")), Arrays.asList("column"), racingEventService)), false, .singleton(new FleetImpl("fleet")), Arrays.asList("column"), racingEventService)), false,
new HighPoint(), 0, null, OneDesignRankingMetric::new); new HighPoint(), 0, null, OneDesignRankingMetric::new);
racingEventService.addRegattaWithoutReplication(regatta); racingEventService.addRegattaWithoutReplication(regatta);
@@ -1,105 +1,105 @@
package com.sap.sailing.server.gateway.test.jaxrs; package com.sap.sailing.server.gateway.test.jaxrs;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMarkMappingFinder; import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMarkMappingFinder;
import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException; import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.HighPoint; import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping; import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.server.gateway.jaxrs.api.LeaderboardsResource; import com.sap.sailing.server.gateway.jaxrs.api.LeaderboardsResource;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException; import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LeaderboardsResourcePingMarkTest extends AbstractJaxRsApiTest { public class LeaderboardsResourcePingMarkTest extends AbstractJaxRsApiTest {
private Mark mark; private Mark mark;
private RegattaLog log; private RegattaLog log;
private RegattaLeaderboard leaderboard; private RegattaLeaderboard leaderboard;
public static final String PING_MARK_JSON = public static final String PING_MARK_JSON =
" {\n" + " {\n" +
" \"timestamp\" : 1427142552000,\n" + " \"timestamp\" : 1427142552000,\n" +
" \"latitude\" : 54.325246,\n" + " \"latitude\" : 54.325246,\n" +
" \"longitude\" : 10.148556,\n" + " \"longitude\" : 10.148556,\n" +
" \"speed\" : 3.61,\n" + " \"speed\" : 3.61,\n" +
" \"course\" : 258.11,\n" + " \"course\" : 258.11,\n" +
" }"; " }";
public static final String PING2_MARK_JSON = public static final String PING2_MARK_JSON =
" {\n" + " {\n" +
" \"timestamp\" : 1427142562000,\n" + " \"timestamp\" : 1427142562000,\n" +
" \"latitude\" : 54.425246,\n" + " \"latitude\" : 54.425246,\n" +
" \"longitude\" : 10.248556,\n" + " \"longitude\" : 10.248556,\n" +
" \"speed\" : 3.61,\n" + " \"speed\" : 3.61,\n" +
" \"course\" : 258.11,\n" + " \"course\" : 258.11,\n" +
" }"; " }";
@Before @Before
public void setUp() { public void setUp() {
super.setUp(); super.setUp();
mark = racingEventService.getBaseDomainFactory().getOrCreateMark("id", "name"); mark = racingEventService.getBaseDomainFactory().getOrCreateMark("id", "name");
Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), MillisecondsTimePoint.now(), Regatta regatta = new RegattaImpl("regatta", new BoatClassImpl("49er", false), MillisecondsTimePoint.now(),
MillisecondsTimePoint.now(), Collections.singleton(new SeriesImpl("series", false, Collections MillisecondsTimePoint.now(), Collections.singleton(new SeriesImpl("series", false, /* isFleetsCanRunInParallel */ true, Collections
.singleton(new FleetImpl("fleet")), Arrays.asList("column"), racingEventService)), false, .singleton(new FleetImpl("fleet")), Arrays.asList("column"), racingEventService)), false,
new HighPoint(), 0, null, OneDesignRankingMetric::new); new HighPoint(), 0, null, OneDesignRankingMetric::new);
racingEventService.addRegattaWithoutReplication(regatta); racingEventService.addRegattaWithoutReplication(regatta);
leaderboard = racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "regatta", new int[] {}); leaderboard = racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "regatta", new int[] {});
log = leaderboard.getRegattaLike().getRegattaLog(); log = leaderboard.getRegattaLike().getRegattaLog();
} }
@Test @Test
public void testCheckinAndCheckout() throws Exception { public void testCheckinAndCheckout() throws Exception {
LeaderboardsResource resource = spyResource(new LeaderboardsResource()); LeaderboardsResource resource = spyResource(new LeaderboardsResource());
doReturn(RaceLogTrackingAdapterFactory.INSTANCE.getAdapter(racingEventService.getBaseDomainFactory())).when(resource).getRaceLogTrackingAdapter(); doReturn(RaceLogTrackingAdapterFactory.INSTANCE.getAdapter(racingEventService.getBaseDomainFactory())).when(resource).getRaceLogTrackingAdapter();
{ {
Response response = resource.pingMark(PING_MARK_JSON, leaderboard.getName(), mark.getId().toString()); Response response = resource.pingMark(PING_MARK_JSON, leaderboard.getName(), mark.getId().toString());
assertThat("response is ok", response.getStatus(), equalTo(Response.Status.OK.getStatusCode())); assertThat("response is ok", response.getStatus(), equalTo(Response.Status.OK.getStatusCode()));
Map<Mark, List<DeviceMapping<Mark>>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze(); Map<Mark, List<DeviceMapping<Mark>>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze();
List<DeviceMapping<Mark>> mappingsForMark = mappings.get(mark); List<DeviceMapping<Mark>> mappingsForMark = mappings.get(mark);
assertThat("one mapping was created for the one ping", mappingsForMark.size(), equalTo(1)); assertThat("one mapping was created for the one ping", mappingsForMark.size(), equalTo(1));
assertOneFixPerMapping(mappingsForMark); assertOneFixPerMapping(mappingsForMark);
} }
{ {
// now produce a second ping; this should produce also only one fix; no additional artificial fixes are // now produce a second ping; this should produce also only one fix; no additional artificial fixes are
// created (anymore; they used to be before bug2851) // created (anymore; they used to be before bug2851)
Response response = resource.pingMark(PING2_MARK_JSON, leaderboard.getName(), mark.getId().toString()); Response response = resource.pingMark(PING2_MARK_JSON, leaderboard.getName(), mark.getId().toString());
assertThat("response is ok", response.getStatus(), equalTo(Response.Status.OK.getStatusCode())); assertThat("response is ok", response.getStatus(), equalTo(Response.Status.OK.getStatusCode()));
Map<Mark, List<DeviceMapping<Mark>>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze(); Map<Mark, List<DeviceMapping<Mark>>> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze();
List<DeviceMapping<Mark>> mappingsForMark = mappings.get(mark); List<DeviceMapping<Mark>> mappingsForMark = mappings.get(mark);
assertThat("Assert no additional mapping was created for the second ping", mappingsForMark.size(), equalTo(2)); assertThat("Assert no additional mapping was created for the second ping", mappingsForMark.size(), equalTo(2));
assertOneFixPerMapping(mappingsForMark); assertOneFixPerMapping(mappingsForMark);
} }
} }
private void assertOneFixPerMapping(List<DeviceMapping<Mark>> mappingsForMark) throws TransformationException, private void assertOneFixPerMapping(List<DeviceMapping<Mark>> mappingsForMark) throws TransformationException,
NoCorrespondingServiceRegisteredException { NoCorrespondingServiceRegisteredException {
for (DeviceMapping<Mark> i : mappingsForMark) { for (DeviceMapping<Mark> i : mappingsForMark) {
DeviceIdentifier device = i.getDevice(); DeviceIdentifier device = i.getDevice();
// first ping produces exactly one fix: // first ping produces exactly one fix:
assertThat("all fixes stored", racingEventService.getGPSFixStore().getNumberOfFixes(device), equalTo(1L)); assertThat("all fixes stored", racingEventService.getGPSFixStore().getNumberOfFixes(device), equalTo(1L));
} }
} }
} }
@@ -1,266 +1,266 @@
package com.sap.sailing.server.gateway.test.jaxrs; package com.sap.sailing.server.gateway.test.jaxrs;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks; import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.server.gateway.jaxrs.api.LeaderboardsResource; import com.sap.sailing.server.gateway.jaxrs.api.LeaderboardsResource;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LeaderboardsResourceTest extends AbstractJaxRsApiTest { public class LeaderboardsResourceTest extends AbstractJaxRsApiTest {
private Regatta regatta; private Regatta regatta;
private RegattaLeaderboard regattaLeaderboard; private RegattaLeaderboard regattaLeaderboard;
private String boatClassName = "49er"; private String boatClassName = "49er";
private String regattaBaseName = "TestRegatta"; private String regattaBaseName = "TestRegatta";
@Before @Before
public void setUp() { public void setUp() {
super.setUp(); super.setUp();
} }
private void dropAndCreateRegatta() { private void dropAndCreateRegatta() {
service.getDB().dropDatabase(); service.getDB().dropDatabase();
List<Series> series = new ArrayList<Series>(); List<Series> series = new ArrayList<Series>();
List<Fleet> fleets = new ArrayList<Fleet>(); List<Fleet> fleets = new ArrayList<Fleet>();
List<String> raceColumnNames = new ArrayList<String>(); List<String> raceColumnNames = new ArrayList<String>();
fleets.add(new FleetImpl("Fleet1")); fleets.add(new FleetImpl("Fleet1"));
fleets.add(new FleetImpl("Fleet2")); fleets.add(new FleetImpl("Fleet2"));
Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, fleets, Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, /* isFleetsCanRunInParallel */ true, fleets,
raceColumnNames, /* trackedRegattaRegistry */null); raceColumnNames, /* trackedRegattaRegistry */null);
series.add(testSeries); series.add(testSeries);
regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClassName), boatClassName, regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClassName), boatClassName,
/*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true, /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), series, /*persistent*/ true,
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new); DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new);
testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null); testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
List<Competitor> competitors = createCompetitors(4); List<Competitor> competitors = createCompetitors(4);
List<Competitor> fleet1Competitors = competitors.subList(0, 2); List<Competitor> fleet1Competitors = competitors.subList(0, 2);
List<Competitor> fleet2Competitors = competitors.subList(2, 4); List<Competitor> fleet2Competitors = competitors.subList(2, 4);
TimePoint now = MillisecondsTimePoint.now(); TimePoint now = MillisecondsTimePoint.now();
RaceColumn r1Column = series.get(0).getRaceColumnByName("R1"); RaceColumn r1Column = series.get(0).getRaceColumnByName("R1");
TrackedRace r1Fleet1 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet1Competitors, regatta); TrackedRace r1Fleet1 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet1Competitors, regatta);
TrackedRace r1Fleet2 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet2Competitors, regatta); TrackedRace r1Fleet2 = new MockedTrackedRaceWithStartTimeAndRanks(now, fleet2Competitors, regatta);
r1Column.setTrackedRace(r1Column.getFleetByName("Fleet1"), r1Fleet1); r1Column.setTrackedRace(r1Column.getFleetByName("Fleet1"), r1Fleet1);
r1Column.setTrackedRace(r1Column.getFleetByName("Fleet2"), r1Fleet2); r1Column.setTrackedRace(r1Column.getFleetByName("Fleet2"), r1Fleet2);
regattaLeaderboard = racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "Testregatta displayName", new int[] { 3, 5 }); regattaLeaderboard = racingEventService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "Testregatta displayName", new int[] { 3, 5 });
} }
@Test @Test
public void testExportEmptyLeaderboardAsJson() throws Exception { public void testExportEmptyLeaderboardAsJson() throws Exception {
dropAndCreateRegatta(); dropAndCreateRegatta();
LeaderboardsResource resource = spyResource(new LeaderboardsResource()); LeaderboardsResource resource = spyResource(new LeaderboardsResource());
Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
String jsonString = (String) leaderboardReponse.getEntity(); String jsonString = (String) leaderboardReponse.getEntity();
Object obj= JSONValue.parse(jsonString); Object obj= JSONValue.parse(jsonString);
JSONObject jsonObject = (JSONObject) obj; JSONObject jsonObject = (JSONObject) obj;
String jsonLeaderboardName = (String) jsonObject.get("name"); String jsonLeaderboardName = (String) jsonObject.get("name");
String jsonResultState = (String) jsonObject.get("resultState"); String jsonResultState = (String) jsonObject.get("resultState");
assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName)); assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName));
assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState)); assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState));
// resultTimepoint should be null if there are no 'final' results yet // resultTimepoint should be null if there are no 'final' results yet
TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint")); TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint"));
assertNull(resultTimePoint); assertNull(resultTimePoint);
JSONArray jsonCompetitors = (JSONArray) jsonObject.get("competitors"); JSONArray jsonCompetitors = (JSONArray) jsonObject.get("competitors");
assertTrue(jsonCompetitors.size() == 4); assertTrue(jsonCompetitors.size() == 4);
} }
@Test @Test
public void testExportLeaderboardWithFinalResultStateAsJson() throws Exception { public void testExportLeaderboardWithFinalResultStateAsJson() throws Exception {
dropAndCreateRegatta(); dropAndCreateRegatta();
LeaderboardsResource resource = spyResource(new LeaderboardsResource()); LeaderboardsResource resource = spyResource(new LeaderboardsResource());
Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
String jsonString = (String) leaderboardReponse.getEntity(); String jsonString = (String) leaderboardReponse.getEntity();
Object obj= JSONValue.parse(jsonString); Object obj= JSONValue.parse(jsonString);
JSONObject jsonObject = (JSONObject) obj; JSONObject jsonObject = (JSONObject) obj;
String jsonLeaderboardName = (String) jsonObject.get("name"); String jsonLeaderboardName = (String) jsonObject.get("name");
String jsonResultState = (String) jsonObject.get("resultState"); String jsonResultState = (String) jsonObject.get("resultState");
assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName)); assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName));
assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState)); assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState));
TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint")); TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint"));
assertNull(resultTimePoint); assertNull(resultTimePoint);
regattaLeaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(MillisecondsTimePoint.now()); regattaLeaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(MillisecondsTimePoint.now());
LeaderboardsResource resource2 = spyResource(new LeaderboardsResource()); LeaderboardsResource resource2 = spyResource(new LeaderboardsResource());
Response leaderboardReponse2 = resource2.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse2 = resource2.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
String jsonString2 = (String) leaderboardReponse2.getEntity(); String jsonString2 = (String) leaderboardReponse2.getEntity();
obj= JSONValue.parse(jsonString2); obj= JSONValue.parse(jsonString2);
jsonObject = (JSONObject) obj; jsonObject = (JSONObject) obj;
resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint")); resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint"));
assertNotNull(resultTimePoint); assertNotNull(resultTimePoint);
} }
@Test @Test
public void testExportLeaderboardWithLiveResultStateAsJson() throws Exception { public void testExportLeaderboardWithLiveResultStateAsJson() throws Exception {
dropAndCreateRegatta(); dropAndCreateRegatta();
LeaderboardsResource resource = spyResource(new LeaderboardsResource()); LeaderboardsResource resource = spyResource(new LeaderboardsResource());
Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Live, null); Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Live, null);
String jsonString = (String) leaderboardReponse.getEntity(); String jsonString = (String) leaderboardReponse.getEntity();
Object obj= JSONValue.parse(jsonString); Object obj= JSONValue.parse(jsonString);
JSONObject jsonObject = (JSONObject) obj; JSONObject jsonObject = (JSONObject) obj;
String jsonLeaderboardName = (String) jsonObject.get("name"); String jsonLeaderboardName = (String) jsonObject.get("name");
String jsonResultState = (String) jsonObject.get("resultState"); String jsonResultState = (String) jsonObject.get("resultState");
assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName)); assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName));
assertTrue(LeaderboardsResource.ResultStates.Live.name().equals(jsonResultState)); assertTrue(LeaderboardsResource.ResultStates.Live.name().equals(jsonResultState));
TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint")); TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint"));
assertNotNull(resultTimePoint); assertNotNull(resultTimePoint);
JSONArray jsonCompetitors = (JSONArray) jsonObject.get("competitors"); JSONArray jsonCompetitors = (JSONArray) jsonObject.get("competitors");
assertTrue(jsonCompetitors.size() == 4); assertTrue(jsonCompetitors.size() == 4);
} }
@Test @Test
public void testSmartFutureCacheInExportLeaderboardAsJson() throws Exception { public void testSmartFutureCacheInExportLeaderboardAsJson() throws Exception {
dropAndCreateRegatta(); dropAndCreateRegatta();
TimePoint now = MillisecondsTimePoint.now(); TimePoint now = MillisecondsTimePoint.now();
regattaLeaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(now); regattaLeaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(now);
Thread.sleep(100); Thread.sleep(100);
// call the servlet for the first time // call the servlet for the first time
LeaderboardsResource resource = spyResource(new LeaderboardsResource()); LeaderboardsResource resource = spyResource(new LeaderboardsResource());
Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
String jsonString = (String) leaderboardReponse.getEntity(); String jsonString = (String) leaderboardReponse.getEntity();
Object obj= JSONValue.parse(jsonString); Object obj= JSONValue.parse(jsonString);
JSONObject jsonObject = (JSONObject) obj; JSONObject jsonObject = (JSONObject) obj;
String jsonLeaderboardName = (String) jsonObject.get("name"); String jsonLeaderboardName = (String) jsonObject.get("name");
String jsonResultState = (String) jsonObject.get("resultState"); String jsonResultState = (String) jsonObject.get("resultState");
assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName)); assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName));
assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState)); assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState));
TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint")); TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint"));
assertNotNull(resultTimePoint); assertNotNull(resultTimePoint);
// second call - it's expected to get the same result from the cache // second call - it's expected to get the same result from the cache
Thread.sleep(100); Thread.sleep(100);
LeaderboardsResource resource2 = spyResource(new LeaderboardsResource()); LeaderboardsResource resource2 = spyResource(new LeaderboardsResource());
Response leaderboardReponse2 = resource2.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse2 = resource2.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
String jsonString2 = (String) leaderboardReponse2.getEntity(); String jsonString2 = (String) leaderboardReponse2.getEntity();
Object obj2= JSONValue.parse(jsonString2); Object obj2= JSONValue.parse(jsonString2);
JSONObject jsonObject2 = (JSONObject) obj2; JSONObject jsonObject2 = (JSONObject) obj2;
String jsonLeaderboardName2 = (String) jsonObject2.get("name"); String jsonLeaderboardName2 = (String) jsonObject2.get("name");
String jsonResultState2 = (String) jsonObject2.get("resultState"); String jsonResultState2 = (String) jsonObject2.get("resultState");
assertTrue(jsonLeaderboardName.equals(jsonLeaderboardName2)); assertTrue(jsonLeaderboardName.equals(jsonLeaderboardName2));
assertTrue(jsonResultState.equals(jsonResultState2)); assertTrue(jsonResultState.equals(jsonResultState2));
TimePoint resultTimePoint2 = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint")); TimePoint resultTimePoint2 = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint"));
assertNotNull(resultTimePoint2); assertNotNull(resultTimePoint2);
assertTrue(resultTimePoint.equals(resultTimePoint2)); assertTrue(resultTimePoint.equals(resultTimePoint2));
} }
@Test @Test
public void testExportLeaderboardWithMaxCompetitorsCountAsJson() throws Exception { public void testExportLeaderboardWithMaxCompetitorsCountAsJson() throws Exception {
dropAndCreateRegatta(); dropAndCreateRegatta();
Integer maxCompetitorsCount = 2; Integer maxCompetitorsCount = 2;
LeaderboardsResource resource = spyResource(new LeaderboardsResource()); LeaderboardsResource resource = spyResource(new LeaderboardsResource());
Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse = resource.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
String jsonString = (String) leaderboardReponse.getEntity(); String jsonString = (String) leaderboardReponse.getEntity();
Object obj= JSONValue.parse(jsonString); Object obj= JSONValue.parse(jsonString);
JSONObject jsonObject = (JSONObject) obj; JSONObject jsonObject = (JSONObject) obj;
String jsonLeaderboardName = (String) jsonObject.get("name"); String jsonLeaderboardName = (String) jsonObject.get("name");
String jsonResultState = (String) jsonObject.get("resultState"); String jsonResultState = (String) jsonObject.get("resultState");
assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName)); assertTrue(regattaLeaderboard.getName().equals(jsonLeaderboardName));
assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState)); assertTrue(LeaderboardsResource.ResultStates.Final.name().equals(jsonResultState));
TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint")); TimePoint resultTimePoint = parseTimepointFromJsonNumber((Long) jsonObject.get("resultTimepoint"));
assertNull(resultTimePoint); assertNull(resultTimePoint);
regattaLeaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(MillisecondsTimePoint.now()); regattaLeaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(MillisecondsTimePoint.now());
// first call for 'all' competitors // first call for 'all' competitors
LeaderboardsResource resource2 = spyResource(new LeaderboardsResource()); LeaderboardsResource resource2 = spyResource(new LeaderboardsResource());
Response leaderboardReponse2 = resource2.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse2 = resource2.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
jsonString = (String) leaderboardReponse2.getEntity(); jsonString = (String) leaderboardReponse2.getEntity();
obj= JSONValue.parse(jsonString); obj= JSONValue.parse(jsonString);
jsonObject = (JSONObject) obj; jsonObject = (JSONObject) obj;
JSONArray jsonCompetitors = (JSONArray) jsonObject.get("competitors"); JSONArray jsonCompetitors = (JSONArray) jsonObject.get("competitors");
assertEquals(jsonCompetitors.size(), 4); assertEquals(jsonCompetitors.size(), 4);
Thread.sleep(100); Thread.sleep(100);
// second call with maxCompetitorsCount set // second call with maxCompetitorsCount set
LeaderboardsResource resource3 = spyResource(new LeaderboardsResource()); LeaderboardsResource resource3 = spyResource(new LeaderboardsResource());
Response leaderboardReponse3 = resource3.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, maxCompetitorsCount); Response leaderboardReponse3 = resource3.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, maxCompetitorsCount);
jsonString = (String) leaderboardReponse3.getEntity(); jsonString = (String) leaderboardReponse3.getEntity();
obj= JSONValue.parse(jsonString); obj= JSONValue.parse(jsonString);
jsonObject = (JSONObject) obj; jsonObject = (JSONObject) obj;
jsonCompetitors = (JSONArray) jsonObject.get("competitors"); jsonCompetitors = (JSONArray) jsonObject.get("competitors");
assertEquals((Integer) jsonCompetitors.size(), maxCompetitorsCount); assertEquals((Integer) jsonCompetitors.size(), maxCompetitorsCount);
Thread.sleep(100); Thread.sleep(100);
// third call for 'all' competitors // third call for 'all' competitors
LeaderboardsResource resource4 = spyResource(new LeaderboardsResource()); LeaderboardsResource resource4 = spyResource(new LeaderboardsResource());
Response leaderboardReponse4 = resource4.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null); Response leaderboardReponse4 = resource4.getLeaderboard(regatta.getName(), LeaderboardsResource.ResultStates.Final, null);
jsonString = (String) leaderboardReponse4.getEntity(); jsonString = (String) leaderboardReponse4.getEntity();
obj= JSONValue.parse(jsonString); obj= JSONValue.parse(jsonString);
jsonObject = (JSONObject) obj; jsonObject = (JSONObject) obj;
jsonCompetitors = (JSONArray) jsonObject.get("competitors"); jsonCompetitors = (JSONArray) jsonObject.get("competitors");
assertEquals(jsonCompetitors.size(), 4); assertEquals(jsonCompetitors.size(), 4);
} }
} }
@@ -1,78 +1,78 @@
package com.sap.sailing.server.gateway.test.jaxrs; package com.sap.sailing.server.gateway.test.jaxrs;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.JSONValue; import org.json.simple.JSONValue;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.server.gateway.jaxrs.api.RegattasResource; import com.sap.sailing.server.gateway.jaxrs.api.RegattasResource;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
public class RegattasResourceTest extends AbstractJaxRsApiTest { public class RegattasResourceTest extends AbstractJaxRsApiTest {
private String boatClassName = "49er"; private String boatClassName = "49er";
private String regattaName = "TestRegatta"; private String regattaName = "TestRegatta";
@Before @Before
public void setUp() { public void setUp() {
super.setUp(); super.setUp();
List<Series> series = new ArrayList<Series>(); List<Series> series = new ArrayList<Series>();
List<Fleet> fleets = new ArrayList<Fleet>(); List<Fleet> fleets = new ArrayList<Fleet>();
List<String> raceColumnNames = new ArrayList<String>(); List<String> raceColumnNames = new ArrayList<String>();
fleets.add(new FleetImpl("Fleet1")); fleets.add(new FleetImpl("Fleet1"));
fleets.add(new FleetImpl("Fleet2")); fleets.add(new FleetImpl("Fleet2"));
final Calendar cal = new GregorianCalendar(); final Calendar cal = new GregorianCalendar();
cal.set(2014, 5, 6, 10, 00); cal.set(2014, 5, 6, 10, 00);
final TimePoint startDate = new MillisecondsTimePoint(cal.getTime()); final TimePoint startDate = new MillisecondsTimePoint(cal.getTime());
cal.set(2014, 5, 8, 16, 00); cal.set(2014, 5, 8, 16, 00);
final TimePoint endDate = new MillisecondsTimePoint(cal.getTime()); final TimePoint endDate = new MillisecondsTimePoint(cal.getTime());
Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, fleets, raceColumnNames, /* trackedRegattaRegistry */null); Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, /* isFleetsCanRunInParallel */ true, fleets, raceColumnNames, /* trackedRegattaRegistry */null);
series.add(testSeries); series.add(testSeries);
racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName, racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName,
startDate, endDate, UUID.randomUUID(), series, /*persistent*/ true, startDate, endDate, UUID.randomUUID(), series, /*persistent*/ true,
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new); DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new);
testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null); testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
} }
@Test @Test
public void testGetRegattas() throws Exception { public void testGetRegattas() throws Exception {
RegattasResource resource = new RegattasResource(); RegattasResource resource = new RegattasResource();
RegattasResource spyResource = spyResource(resource); RegattasResource spyResource = spyResource(resource);
Response regattasResponse = spyResource.getRegattas(); Response regattasResponse = spyResource.getRegattas();
String jsonString = (String) regattasResponse.getEntity(); String jsonString = (String) regattasResponse.getEntity();
Object obj= JSONValue.parse(jsonString); Object obj= JSONValue.parse(jsonString);
JSONArray array= (JSONArray) obj; JSONArray array= (JSONArray) obj;
assertTrue(array.size() == 1); assertTrue(array.size() == 1);
JSONObject firstElement = (JSONObject) array.get(0); JSONObject firstElement = (JSONObject) array.get(0);
String jsonName = (String) firstElement.get("name"); String jsonName = (String) firstElement.get("name");
String jsonBoatClass = (String) firstElement.get("boatclass"); String jsonBoatClass = (String) firstElement.get("boatclass");
assertTrue(RegattaImpl.getDefaultName(regattaName, boatClassName).equals(jsonName)); assertTrue(RegattaImpl.getDefaultName(regattaName, boatClassName).equals(jsonName));
assertTrue(boatClassName.equals(jsonBoatClass)); assertTrue(boatClassName.equals(jsonBoatClass));
} }
} }
@@ -62,11 +62,11 @@ public class RaceGroupFactory {
return new RaceGroupImpl(name, leaderboard.getDisplayName(), boatClass, courseArea, series, configuration); return new RaceGroupImpl(name, leaderboard.getDisplayName(), boatClass, courseArea, series, configuration);
} }
public Iterable<SeriesWithRows> getSeries(Leaderboard leaderboard) { private Iterable<SeriesWithRows> getSeries(Leaderboard leaderboard) {
Map<Series, List<RaceColumn>> seriesToRaceColumns = getSeriesToRaceColumns(leaderboard); Map<Series, List<RaceColumn>> seriesToRaceColumns = getSeriesToRaceColumns(leaderboard);
Collection<SeriesWithRows> seriesWithRows = new ArrayList<>(); Collection<SeriesWithRows> seriesWithRows = new ArrayList<>();
for (Series series : getSeriesIterable(leaderboard, seriesToRaceColumns)) { for (Series series : getSeriesIterable(leaderboard, seriesToRaceColumns)) {
seriesWithRows.add(new SeriesWithRowsImpl(series.getName(), series.isMedal(), getRows(series, seriesWithRows.add(new SeriesWithRowsImpl(series.getName(), series.isMedal(), series.isFleetsCanRunInParallel(), getRows(series,
seriesToRaceColumns.get(series)))); seriesToRaceColumns.get(series))));
} }
return seriesWithRows; return seriesWithRows;
@@ -80,6 +80,9 @@ public class RaceGroupFactory {
return seriesToRaceColumns.keySet(); return seriesToRaceColumns.keySet();
} }
/**
* @param raceColumns must be provided in the same order in which they appear in the series / leaderboard
*/
private Collection<RaceRow> getRows(Series series, List<RaceColumn> raceColumns) { private Collection<RaceRow> getRows(Series series, List<RaceColumn> raceColumns) {
Collection<RaceRow> rows = new ArrayList<>(); Collection<RaceRow> rows = new ArrayList<>();
for (Fleet fleet : series.getFleets()) { for (Fleet fleet : series.getFleets()) {
@@ -94,16 +97,21 @@ public class RaceGroupFactory {
return series.isFirstColumnIsNonDiscardableCarryForward(); return series.isFirstColumnIsNonDiscardableCarryForward();
} }
private Collection<RaceCell> getCells(String fleetName, List<RaceColumn> raceColumns, boolean isFirstRaceColumnVirtual) { /**
* Delivers the race cell in the order of the {@link RaceColumn}s provided in {@code raceColumns}.
*/
private List<RaceCell> getCells(String fleetName, List<RaceColumn> raceColumns, boolean isFirstRaceColumnVirtual) {
boolean skippedFirst = false; boolean skippedFirst = false;
Collection<RaceCell> cells = new ArrayList<>(); List<RaceCell> cells = new ArrayList<>();
if (raceColumns != null) { if (raceColumns != null) {
for (RaceColumn raceColumn : raceColumns) { int zeroBasedIndexOfRaceInFleet = 0;
for (final RaceColumn raceColumn : raceColumns) {
if (isFirstRaceColumnVirtual && !skippedFirst) { if (isFirstRaceColumnVirtual && !skippedFirst) {
skippedFirst = true; skippedFirst = true;
} else { } else {
Fleet fleet = raceColumn.getFleetByName(fleetName); Fleet fleet = raceColumn.getFleetByName(fleetName);
cells.add(new RaceCellImpl(raceColumn.getName(), raceColumn.getRaceLog(fleet), raceColumn.getFactor(), raceColumn.getExplicitFactor())); cells.add(new RaceCellImpl(raceColumn.getName(), raceColumn.getRaceLog(fleet),
raceColumn.getFactor(), raceColumn.getExplicitFactor(), zeroBasedIndexOfRaceInFleet++));
} }
} }
} }
@@ -112,7 +120,9 @@ public class RaceGroupFactory {
/** /**
* Returns a series to race column mapping. If there are no series all race columns will * Returns a series to race column mapping. If there are no series all race columns will
* be mapped from a default series. * be mapped from a default series. The race column lists that appear as values in the result
* maintain the order in which the race column objects appear in the original {@link Series}
* or leaderboard.
*/ */
private Map<Series, List<RaceColumn>> getSeriesToRaceColumns(Leaderboard leaderboard) { private Map<Series, List<RaceColumn>> getSeriesToRaceColumns(Leaderboard leaderboard) {
Map<Series, List<RaceColumn>> seriesToRaceColumns = new HashMap<>(); Map<Series, List<RaceColumn>> seriesToRaceColumns = new HashMap<>();
@@ -135,7 +145,7 @@ public class RaceGroupFactory {
private Series createDefaultSeries(Iterable<? extends Fleet> fleets) { private Series createDefaultSeries(Iterable<? extends Fleet> fleets) {
Series defaultSeries; Series defaultSeries;
defaultSeries = new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME, false, defaultSeries = new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME, false, true,
fleets, Collections.<String> emptyList(), null); fleets, Collections.<String> emptyList(), null);
return defaultSeries; return defaultSeries;
} }
@@ -1,117 +1,117 @@
package com.sap.sailing.server.replication.test; package com.sap.sailing.server.replication.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import com.sap.sailing.domain.abstractlog.AbstractLog; import com.sap.sailing.domain.abstractlog.AbstractLog;
import com.sap.sailing.domain.abstractlog.AbstractLogEvent; import com.sap.sailing.domain.abstractlog.AbstractLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RankingMetrics; import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO; import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO; import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.server.impl.RacingEventServiceImpl; import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta; import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta;
import com.sap.sailing.server.operationaltransformation.CreateFlexibleLeaderboard; import com.sap.sailing.server.operationaltransformation.CreateFlexibleLeaderboard;
import com.sap.sse.common.Color; import com.sap.sse.common.Color;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
public abstract class AbstractLogReplicationTest<LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT> public abstract class AbstractLogReplicationTest<LogT extends AbstractLog<EventT, VisitorT>, EventT extends AbstractLogEvent<VisitorT>, VisitorT>
extends AbstractServerReplicationTest { extends AbstractServerReplicationTest {
protected static final String BOAT_CLASS_NAME_49er = "49er"; protected static final String BOAT_CLASS_NAME_49er = "49er";
/** /**
* Uses a new master that loads the existing regatta and race log to append an event to the race log. This will store it to the DB so that if the original * Uses a new master that loads the existing regatta and race log to append an event to the race log. This will store it to the DB so that if the original
* master re-loads the race log it should see the new race log event. * master re-loads the race log it should see the new race log event.
* @throws Exception * @throws Exception
*/ */
protected void addEventToDB(RaceLogIdentifier raceLogIdentifier, RaceLogRaceStatusEvent createRaceStatusEvent, String regattaName, String raceColumnName, String fleetName) throws Exception { protected void addEventToDB(RaceLogIdentifier raceLogIdentifier, RaceLogRaceStatusEvent createRaceStatusEvent, String regattaName, String raceColumnName, String fleetName) throws Exception {
final RacingEventServiceImpl temporaryMaster = createNewMaster(); final RacingEventServiceImpl temporaryMaster = createNewMaster();
Regatta regatta = temporaryMaster.getRegatta(new RegattaName(regattaName+" ("+BOAT_CLASS_NAME_49er+")")); Regatta regatta = temporaryMaster.getRegatta(new RegattaName(regattaName+" ("+BOAT_CLASS_NAME_49er+")"));
Series series = regatta.getSeries().iterator().next(); Series series = regatta.getSeries().iterator().next();
RaceLog masterLog = series.getRaceColumnByName(raceColumnName).getRaceLog(series.getFleetByName(fleetName)); RaceLog masterLog = series.getRaceColumnByName(raceColumnName).getRaceLog(series.getFleetByName(fleetName));
masterLog.add(createRaceStatusEvent); masterLog.add(createRaceStatusEvent);
} }
/** /**
* Validation is done based only on the identifier and type of the {@link RaceLogEvent}s. * Validation is done based only on the identifier and type of the {@link RaceLogEvent}s.
*/ */
protected void addAndValidateEventIds(LogT masterLog, LogT replicaLog, @SuppressWarnings("unchecked") EventT... addedEvents) throws InterruptedException { protected void addAndValidateEventIds(LogT masterLog, LogT replicaLog, @SuppressWarnings("unchecked") EventT... addedEvents) throws InterruptedException {
// 1. Check state of replica after initial load... // 1. Check state of replica after initial load...
assertEqualsOnId(masterLog, replicaLog); assertEqualsOnId(masterLog, replicaLog);
// 2. ... add all incoming events... // 2. ... add all incoming events...
for (EventT event : addedEvents) { for (EventT event : addedEvents) {
masterLog.add(event); masterLog.add(event);
} }
// 3. ... and give replication some time to deliver messages. // 3. ... and give replication some time to deliver messages.
Thread.sleep(3000); Thread.sleep(3000);
assertEqualsOnId(masterLog, replicaLog); assertEqualsOnId(masterLog, replicaLog);
} }
/** /**
* Equality of the events is done based only on the identifier and type. * Equality of the events is done based only on the identifier and type.
*/ */
protected void assertEqualsOnId(LogT masterLog, LogT replicaLog) { protected void assertEqualsOnId(LogT masterLog, LogT replicaLog) {
replicaLog.lockForRead(); replicaLog.lockForRead();
try { try {
masterLog.lockForRead(); masterLog.lockForRead();
try { try {
assertEqualsOnId(masterLog.getRawFixes(), replicaLog.getRawFixes()); assertEqualsOnId(masterLog.getRawFixes(), replicaLog.getRawFixes());
} finally { } finally {
masterLog.unlockAfterRead(); masterLog.unlockAfterRead();
} }
} finally { } finally {
replicaLog.unlockAfterRead(); replicaLog.unlockAfterRead();
} }
} }
/** /**
* Equality of the events is done based only on the {@link RaceLogEvent}'s identifier and type. * Equality of the events is done based only on the {@link RaceLogEvent}'s identifier and type.
*/ */
private void assertEqualsOnId(Iterable<EventT> expectedEvents, Iterable<EventT> actualEvents) { private void assertEqualsOnId(Iterable<EventT> expectedEvents, Iterable<EventT> actualEvents) {
List<EventT> expectedCollection = new ArrayList<>(); List<EventT> expectedCollection = new ArrayList<>();
Util.addAll(expectedEvents, expectedCollection); Util.addAll(expectedEvents, expectedCollection);
List<EventT> actualCollection = new ArrayList<>(); List<EventT> actualCollection = new ArrayList<>();
Util.addAll(actualEvents, actualCollection); Util.addAll(actualEvents, actualCollection);
assertEquals(Util.size(expectedEvents), Util.size(actualEvents)); assertEquals(Util.size(expectedEvents), Util.size(actualEvents));
for (int i = 0; i < expectedCollection.size(); i++) { for (int i = 0; i < expectedCollection.size(); i++) {
assertEquals(expectedCollection.get(i).getClass(), actualCollection.get(i).getClass()); assertEquals(expectedCollection.get(i).getClass(), actualCollection.get(i).getClass());
assertEquals(expectedCollection.get(i).getId(), actualCollection.get(i).getId()); assertEquals(expectedCollection.get(i).getId(), actualCollection.get(i).getId());
} }
} }
protected Regatta setupRegatta(final String regattaName, String seriesName, String fleetName, String boatClassName) { protected Regatta setupRegatta(final String regattaName, String seriesName, String fleetName, String boatClassName) {
LinkedHashMap<String, SeriesCreationParametersDTO> seriesCreationParameters = new LinkedHashMap<>(); LinkedHashMap<String, SeriesCreationParametersDTO> seriesCreationParameters = new LinkedHashMap<>();
SeriesCreationParametersDTO creationParametersForDefaultSeries = new SeriesCreationParametersDTO( SeriesCreationParametersDTO creationParametersForDefaultSeries = new SeriesCreationParametersDTO(
Arrays.asList(new FleetDTO[] { new FleetDTO(fleetName, 0, Color.BLACK), }), /* medal */false, /* startsWithZero */ Arrays.asList(new FleetDTO[] { new FleetDTO(fleetName, 0, Color.BLACK), }), /* medal */false, /* fleetsCanRunInParallel */ true, /* startsWithZero */
false, /* firstColumnIsNonDiscardableCarryForward */false, /* discardingThresholds */new int[0], /* hasSplitFleetContiguousScoring */ false, /* firstColumnIsNonDiscardableCarryForward */false, /* discardingThresholds */new int[0], /* hasSplitFleetContiguousScoring */
false); false);
seriesCreationParameters.put(seriesName, creationParametersForDefaultSeries); seriesCreationParameters.put(seriesName, creationParametersForDefaultSeries);
// 1. Install some race column on master... // 1. Install some race column on master...
RegattaCreationParametersDTO regattaCreationParams = new RegattaCreationParametersDTO(seriesCreationParameters); RegattaCreationParametersDTO regattaCreationParams = new RegattaCreationParametersDTO(seriesCreationParameters);
AddSpecificRegatta addRegattaOperation = new AddSpecificRegatta(regattaName, boatClassName, AddSpecificRegatta addRegattaOperation = new AddSpecificRegatta(regattaName, boatClassName,
/*startDate*/ null, /*endDate*/ null, /* regatta ID */ UUID.randomUUID(), regattaCreationParams, /* persistent */ true, /*startDate*/ null, /*endDate*/ null, /* regatta ID */ UUID.randomUUID(), regattaCreationParams, /* persistent */ true,
new LowPoint(), /* default course area ID */ UUID.randomUUID(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN); new LowPoint(), /* default course area ID */ UUID.randomUUID(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN);
return master.apply(addRegattaOperation); return master.apply(addRegattaOperation);
} }
protected FlexibleLeaderboard setupFlexibleLeaderboard(final String leaderboardName) { protected FlexibleLeaderboard setupFlexibleLeaderboard(final String leaderboardName) {
CreateFlexibleLeaderboard createTestLeaderboard = new CreateFlexibleLeaderboard(leaderboardName, leaderboardName, new int[] { 19, 44 }, new LowPoint(), null); CreateFlexibleLeaderboard createTestLeaderboard = new CreateFlexibleLeaderboard(leaderboardName, leaderboardName, new int[] { 19, 44 }, new LowPoint(), null);
FlexibleLeaderboard masterLeaderboard = master.apply(createTestLeaderboard); FlexibleLeaderboard masterLeaderboard = master.apply(createTestLeaderboard);
return masterLeaderboard; return masterLeaderboard;
} }
} }
@@ -185,11 +185,11 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest {
final String baseEventName = "Kiel Week 2012"; final String baseEventName = "Kiel Week 2012";
final String boatClassName = "49er"; final String boatClassName = "49er";
final List<String> emptyRaceColumnNamesList = Collections.emptyList(); final List<String> emptyRaceColumnNamesList = Collections.emptyList();
Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, /* isFleetsCanRunInParallel */ true,
Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null);
Series finals = new SeriesImpl("Finals", /* isMedal */ false, Series finals = new SeriesImpl("Finals", /* isMedal */ false, /* isFleetsCanRunInParallel */ true,
Arrays.asList(new Fleet[] { new FleetImpl("Gold", 1), new FleetImpl("Silver", 2) }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); Arrays.asList(new Fleet[] { new FleetImpl("Gold", 1), new FleetImpl("Silver", 2) }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null);
Series medal = new SeriesImpl("Medal", /* isMedal */ true, Series medal = new SeriesImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true,
Arrays.asList(new Fleet[] { new FleetImpl("Medal") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); Arrays.asList(new Fleet[] { new FleetImpl("Medal") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null);
Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null,
UUID.randomUUID(), Arrays.asList(new Series[] { qualification, finals, medal }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new); UUID.randomUUID(), Arrays.asList(new Series[] { qualification, finals, medal }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new);
@@ -224,7 +224,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest {
final String baseEventName = "Extreme Sailing Series 2020"; final String baseEventName = "Extreme Sailing Series 2020";
final String boatClassName = "Extreme40"; final String boatClassName = "Extreme40";
final List<String> emptyRaceColumnNamesList = Collections.emptyList(); final List<String> emptyRaceColumnNamesList = Collections.emptyList();
Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, /* isFleetsCanRunInParallel */ true,
Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null);
Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null,
UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new); UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new);
@@ -238,10 +238,10 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest {
assertEquals("Qualification", replicatedQualification.getName()); assertEquals("Qualification", replicatedQualification.getName());
assertEquals(2, Util.size(replicatedQualification.getFleets())); assertEquals(2, Util.size(replicatedQualification.getFleets()));
assertFalse(seriesIter.hasNext()); assertFalse(seriesIter.hasNext());
Series finals = new SeriesImpl("Finals", /* isMedal */ false, Series finals = new SeriesImpl("Finals", /* isMedal */ false, /* isFleetsCanRunInParallel */ true,
Arrays.asList(new Fleet[] { new FleetImpl("Gold", 1) }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); Arrays.asList(new Fleet[] { new FleetImpl("Gold", 1) }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null);
FleetDTO finalsGoldFleet = new FleetDTO("Gold", 1, Color.GRAY); FleetDTO finalsGoldFleet = new FleetDTO("Gold", 1, Color.GRAY);
master.apply(new UpdateSeries(masterRegatta.getRegattaIdentifier(), finals.getName(), finals.getName(), finals.isMedal(), master.apply(new UpdateSeries(masterRegatta.getRegattaIdentifier(), finals.getName(), finals.getName(), finals.isMedal(), finals.isFleetsCanRunInParallel(),
new int[] {}, new int[] {},
finals.isStartsWithZeroScore(), finals.isFirstColumnIsNonDiscardableCarryForward(), finals.isStartsWithZeroScore(), finals.isFirstColumnIsNonDiscardableCarryForward(),
finals.hasSplitFleetContiguousScoring(), Arrays.asList(new FleetDTO[] { finalsGoldFleet }))); finals.hasSplitFleetContiguousScoring(), Arrays.asList(new FleetDTO[] { finalsGoldFleet })));
@@ -267,7 +267,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest {
final String baseEventName = "Extreme Sailing Series 2021"; final String baseEventName = "Extreme Sailing Series 2021";
final String boatClassName = "Extreme40"; final String boatClassName = "Extreme40";
final List<String> emptyRaceColumnNamesList = Collections.emptyList(); final List<String> emptyRaceColumnNamesList = Collections.emptyList();
Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, Series qualification = new SeriesImpl("Qualification", /* isMedal */ false, /* isFleetsCanRunInParallel */ true,
Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null); Arrays.asList(new Fleet[] { new FleetImpl("Yellow"), new FleetImpl("Blue") }), emptyRaceColumnNamesList, /* trackedRegattaRegistry */ null);
Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null, Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /*startDate*/ null, /*endDate*/ null,
UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new); UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /* useStartTimeInference */ true, OneDesignRankingMetric::new);
@@ -279,7 +279,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest {
Series replicatedQualification = seriesIter.next(); Series replicatedQualification = seriesIter.next();
assertEquals("Qualification", replicatedQualification.getName()); assertEquals("Qualification", replicatedQualification.getName());
master.apply(new UpdateSeries(masterRegatta.getRegattaIdentifier(), qualification.getName(), "Simons Quali", master.apply(new UpdateSeries(masterRegatta.getRegattaIdentifier(), qualification.getName(), "Simons Quali",
qualification.isMedal(), qualification.isMedal(), qualification.isFleetsCanRunInParallel(),
new int[] {}, new int[] {},
qualification.isStartsWithZeroScore(), qualification.isFirstColumnIsNonDiscardableCarryForward(), qualification.isStartsWithZeroScore(), qualification.isFirstColumnIsNonDiscardableCarryForward(),
qualification.hasSplitFleetContiguousScoring(), Arrays.asList(new FleetDTO[] { }))); qualification.hasSplitFleetContiguousScoring(), Arrays.asList(new FleetDTO[] { })));
@@ -1,190 +1,190 @@
package com.sap.sailing.server.impl; package com.sap.sailing.server.impl;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.CourseAreaImpl; import com.sap.sailing.domain.base.impl.CourseAreaImpl;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.BoatClassMasterdata; import com.sap.sailing.domain.common.BoatClassMasterdata;
import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.domain.common.media.MediaTrack;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl; import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl; import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.masterdataimport.TopLevelMasterData; import com.sap.sailing.domain.masterdataimport.TopLevelMasterData;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sse.common.Duration; import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.media.MimeType; import com.sap.sse.common.media.MimeType;
public class MediaMasterDataExportTest { public class MediaMasterDataExportTest {
static final String matchingRegattaName = "Matching Regatta"; static final String matchingRegattaName = "Matching Regatta";
static final String missingRegattaName = "Missing Regatta"; static final String missingRegattaName = "Missing Regatta";
static final String matchingRace1 = "matching race 1"; static final String matchingRace1 = "matching race 1";
static final String matchingRace2 = "matching race 2"; static final String matchingRace2 = "matching race 2";
static final String matchingRace3 = "matching race 3"; static final String matchingRace3 = "matching race 3";
static final String missingRace1 = "missing race 1"; static final String missingRace1 = "missing race 1";
static final String missingRace2 = "missing race 2"; static final String missingRace2 = "missing race 2";
static final String missingRace3 = "missing race 3"; static final String missingRace3 = "missing race 3";
static final boolean displayGroupsInReverseOrder = true; static final boolean displayGroupsInReverseOrder = true;
static final ThresholdBasedResultDiscardingRule resultDiscardingRule = new ThresholdBasedResultDiscardingRuleImpl(new int[0]); static final ThresholdBasedResultDiscardingRule resultDiscardingRule = new ThresholdBasedResultDiscardingRuleImpl(new int[0]);
static final BoatClass boatClass = new BoatClassImpl("boat class name", BoatClassMasterdata._12M); static final BoatClass boatClass = new BoatClassImpl("boat class name", BoatClassMasterdata._12M);
static final TimePoint startDate = MillisecondsTimePoint.now(); static final TimePoint startDate = MillisecondsTimePoint.now();
static final TimePoint endDate = startDate.plus(MillisecondsDurationImpl.ONE_DAY); static final TimePoint endDate = startDate.plus(MillisecondsDurationImpl.ONE_DAY);
static final boolean isMedal = false; static final boolean isMedal = false;
static final Fleet regattaFleet = new FleetImpl("fleet name"); static final Fleet regattaFleet = new FleetImpl("fleet name");
static final boolean persistent = false; static final boolean persistent = false;
static final ScoringScheme scoringScheme = new LowPoint(); static final ScoringScheme scoringScheme = new LowPoint();
static final CourseArea courseArea = new CourseAreaImpl("Course Area", UUID.randomUUID()); static final CourseArea courseArea = new CourseAreaImpl("Course Area", UUID.randomUUID());
static final Serializable regatteId = "regatta id"; static final Serializable regatteId = "regatta id";
@Test @Test
public void testTrackWithAssignedRaceButEmptyLeaderboardGroup() { public void testTrackWithAssignedRaceButEmptyLeaderboardGroup() {
TimePoint startTime = null; TimePoint startTime = null;
Duration duration = null; Duration duration = null;
MimeType mimeType = MimeType.mp4; MimeType mimeType = MimeType.mp4;
Collection<MediaTrack> allMediaTracks = Arrays.asList( Collection<MediaTrack> allMediaTracks = Arrays.asList(
new MediaTrack("dbId 1", "title 1 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))), new MediaTrack("dbId 1", "title 1 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))),
new MediaTrack("dbId 2", "title 2 for existing one regatta and race and one missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2)))), new MediaTrack("dbId 2", "title 2 for existing one regatta and race and one missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2)))),
new MediaTrack("dbId 3", "title 1 for missing regatta", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(missingRegattaName, matchingRace1))), new MediaTrack("dbId 3", "title 1 for missing regatta", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(missingRegattaName, matchingRace1))),
new MediaTrack("dbId 4", "title 2 for missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace3), new RegattaNameAndRaceName(matchingRegattaName, missingRace1)))) new MediaTrack("dbId 4", "title 2 for missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace3), new RegattaNameAndRaceName(matchingRegattaName, missingRace1))))
); );
Collection<RaceIdentifier> regattaRaces = Collections.emptyList(); Collection<RaceIdentifier> regattaRaces = Collections.emptyList();
Collection<RaceIdentifier> flexibleRaces = Collections.emptyList(); Collection<RaceIdentifier> flexibleRaces = Collections.emptyList();
TopLevelMasterData topLevelMasterData = createTopLevelMasterData(regattaRaces , flexibleRaces, allMediaTracks); TopLevelMasterData topLevelMasterData = createTopLevelMasterData(regattaRaces , flexibleRaces, allMediaTracks);
Collection<MediaTrack> filteredMediaTracks = topLevelMasterData.getFilteredMediaTracks(); Collection<MediaTrack> filteredMediaTracks = topLevelMasterData.getFilteredMediaTracks();
assertEquals(0, filteredMediaTracks.size()); assertEquals(0, filteredMediaTracks.size());
} }
@Test @Test
public void testTracksWithAssignedRaceAndMatchingRegattaInLeaderboardGroup() { public void testTracksWithAssignedRaceAndMatchingRegattaInLeaderboardGroup() {
TimePoint startTime = null; TimePoint startTime = null;
Duration duration = null; Duration duration = null;
MimeType mimeType = MimeType.mp4; MimeType mimeType = MimeType.mp4;
Collection<MediaTrack> allMediaTracks = Arrays.asList( Collection<MediaTrack> allMediaTracks = Arrays.asList(
new MediaTrack("dbId 1", "title 1 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))), new MediaTrack("dbId 1", "title 1 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))),
new MediaTrack("dbId 2", "title 2 for existing one regatta and race and one missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2)))), new MediaTrack("dbId 2", "title 2 for existing one regatta and race and one missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2)))),
new MediaTrack("dbId 3", "title 3 for missing regatta", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(missingRegattaName, matchingRace1))), new MediaTrack("dbId 3", "title 3 for missing regatta", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(missingRegattaName, matchingRace1))),
new MediaTrack("dbId 4", "title 4 for missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace3), new RegattaNameAndRaceName(matchingRegattaName, missingRace1)))), new MediaTrack("dbId 4", "title 4 for missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace3), new RegattaNameAndRaceName(matchingRegattaName, missingRace1)))),
new MediaTrack("dbId 5", "title 5 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))) new MediaTrack("dbId 5", "title 5 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1)))
); );
Collection<RaceIdentifier> regattaRaces = Arrays.asList( Collection<RaceIdentifier> regattaRaces = Arrays.asList(
new RegattaNameAndRaceName(matchingRegattaName, matchingRace1), new RegattaNameAndRaceName(matchingRegattaName, matchingRace1),
new RegattaNameAndRaceName(matchingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2),
new RegattaNameAndRaceName(matchingRegattaName, matchingRace3) new RegattaNameAndRaceName(matchingRegattaName, matchingRace3)
); );
Collection<RaceIdentifier> flexibleRaces = Collections.emptyList(); Collection<RaceIdentifier> flexibleRaces = Collections.emptyList();
TopLevelMasterData topLevelMasterData = createTopLevelMasterData(regattaRaces , flexibleRaces, allMediaTracks); TopLevelMasterData topLevelMasterData = createTopLevelMasterData(regattaRaces , flexibleRaces, allMediaTracks);
Collection<MediaTrack> filteredMediaTracks = topLevelMasterData.getFilteredMediaTracks(); Collection<MediaTrack> filteredMediaTracks = topLevelMasterData.getFilteredMediaTracks();
assertEquals(3, filteredMediaTracks.size()); assertEquals(3, filteredMediaTracks.size());
} }
@Test @Test
public void testTrackWithAssignedRaceAndMatchingFlexibleLeaderboardInLeaderboardGroup() { public void testTrackWithAssignedRaceAndMatchingFlexibleLeaderboardInLeaderboardGroup() {
TimePoint startTime = null; TimePoint startTime = null;
Duration duration = null; Duration duration = null;
MimeType mimeType = MimeType.mp4; MimeType mimeType = MimeType.mp4;
Collection<MediaTrack> allMediaTracks = Arrays.asList( Collection<MediaTrack> allMediaTracks = Arrays.asList(
new MediaTrack("dbId 1", "title 1 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))), new MediaTrack("dbId 1", "title 1 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))),
new MediaTrack("dbId 2", "title 2 for existing one regatta and race and one missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2)))), new MediaTrack("dbId 2", "title 2 for existing one regatta and race and one missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2)))),
new MediaTrack("dbId 3", "title 3 for missing regatta", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(missingRegattaName, matchingRace1))), new MediaTrack("dbId 3", "title 3 for missing regatta", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(missingRegattaName, matchingRace1))),
new MediaTrack("dbId 4", "title 4 for missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace3), new RegattaNameAndRaceName(matchingRegattaName, missingRace1)))), new MediaTrack("dbId 4", "title 4 for missing regatta", "url", startTime, duration, mimeType, new HashSet<RegattaAndRaceIdentifier>(Arrays.asList(new RegattaNameAndRaceName(missingRegattaName, matchingRace3), new RegattaNameAndRaceName(matchingRegattaName, missingRace1)))),
new MediaTrack("dbId 5", "title 5 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1))) new MediaTrack("dbId 5", "title 5 for existing regatta and race", "url", startTime, duration, mimeType, Collections.singleton(new RegattaNameAndRaceName(matchingRegattaName, matchingRace1)))
); );
Collection<RaceIdentifier> regattaRaces = Collections.emptyList(); Collection<RaceIdentifier> regattaRaces = Collections.emptyList();
Collection<RaceIdentifier> flexibleRaces = Arrays.asList( Collection<RaceIdentifier> flexibleRaces = Arrays.asList(
new RegattaNameAndRaceName(matchingRegattaName, matchingRace1), new RegattaNameAndRaceName(matchingRegattaName, matchingRace1),
new RegattaNameAndRaceName(matchingRegattaName, matchingRace2), new RegattaNameAndRaceName(matchingRegattaName, matchingRace2),
new RegattaNameAndRaceName(matchingRegattaName, matchingRace3) new RegattaNameAndRaceName(matchingRegattaName, matchingRace3)
); );
TopLevelMasterData topLevelMasterData = createTopLevelMasterData(regattaRaces , flexibleRaces, allMediaTracks); TopLevelMasterData topLevelMasterData = createTopLevelMasterData(regattaRaces , flexibleRaces, allMediaTracks);
Collection<MediaTrack> filteredMediaTracks = topLevelMasterData.getFilteredMediaTracks(); Collection<MediaTrack> filteredMediaTracks = topLevelMasterData.getFilteredMediaTracks();
assertEquals(3, filteredMediaTracks.size()); assertEquals(3, filteredMediaTracks.size());
} }
private TopLevelMasterData createTopLevelMasterData(Collection<RaceIdentifier> regattaRaces, Collection<RaceIdentifier> flexibleRaces, Collection<MediaTrack> allMediaTracks) { private TopLevelMasterData createTopLevelMasterData(Collection<RaceIdentifier> regattaRaces, Collection<RaceIdentifier> flexibleRaces, Collection<MediaTrack> allMediaTracks) {
Set<String> raceColumnNames = new HashSet<>(); Set<String> raceColumnNames = new HashSet<>();
for (RaceIdentifier regattaAndRaceIdentifier : regattaRaces) { for (RaceIdentifier regattaAndRaceIdentifier : regattaRaces) {
raceColumnNames.add(regattaAndRaceIdentifier.getRaceName()); raceColumnNames.add(regattaAndRaceIdentifier.getRaceName());
} }
Regatta regatta = createTestRegatta("regatta name", raceColumnNames); Regatta regatta = createTestRegatta("regatta name", raceColumnNames);
RegattaLeaderboard regattaLeaderboard = new RegattaLeaderboardImpl(regatta, resultDiscardingRule); RegattaLeaderboard regattaLeaderboard = new RegattaLeaderboardImpl(regatta, resultDiscardingRule);
assignRacesToRegattaLeaderboardColumns(regattaLeaderboard, regattaRaces); assignRacesToRegattaLeaderboardColumns(regattaLeaderboard, regattaRaces);
FlexibleLeaderboard flexibleLeaderboard = new FlexibleLeaderboardImpl("flexible leaderboard", resultDiscardingRule, scoringScheme , courseArea); FlexibleLeaderboard flexibleLeaderboard = new FlexibleLeaderboardImpl("flexible leaderboard", resultDiscardingRule, scoringScheme , courseArea);
for (RaceIdentifier regattaAndRaceIdentifier : flexibleRaces) { for (RaceIdentifier regattaAndRaceIdentifier : flexibleRaces) {
RaceColumn raceColumn = flexibleLeaderboard.addRaceColumn(regattaAndRaceIdentifier.getRaceName(), isMedal); RaceColumn raceColumn = flexibleLeaderboard.addRaceColumn(regattaAndRaceIdentifier.getRaceName(), isMedal);
Fleet defaultFleet = raceColumn.getFleets().iterator().next(); Fleet defaultFleet = raceColumn.getFleets().iterator().next();
raceColumn.setRaceIdentifier(defaultFleet, regattaAndRaceIdentifier); raceColumn.setRaceIdentifier(defaultFleet, regattaAndRaceIdentifier);
} }
List<? extends Leaderboard> leaderboards = Arrays.asList(regattaLeaderboard, flexibleLeaderboard); List<? extends Leaderboard> leaderboards = Arrays.asList(regattaLeaderboard, flexibleLeaderboard);
LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("name", "description", "displayName", displayGroupsInReverseOrder , leaderboards); LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("name", "description", "displayName", displayGroupsInReverseOrder , leaderboards);
Set<LeaderboardGroup> groupsToExport = Collections.singleton(leaderboardGroup); Set<LeaderboardGroup> groupsToExport = Collections.singleton(leaderboardGroup);
Iterable<Event> allEvents = Collections.emptyList(); Iterable<Event> allEvents = Collections.emptyList();
Map<String, Regatta> regattaForRaceIdString = Collections.emptyMap(); Map<String, Regatta> regattaForRaceIdString = Collections.emptyMap();
GPSFixStore gpsFixStore = mock(GPSFixStore.class); GPSFixStore gpsFixStore = mock(GPSFixStore.class);
boolean exportWind = false; boolean exportWind = false;
TopLevelMasterData topLevelMasterData = new TopLevelMasterData(groupsToExport , allEvents , regattaForRaceIdString, allMediaTracks , gpsFixStore , exportWind, new HashMap<>()); TopLevelMasterData topLevelMasterData = new TopLevelMasterData(groupsToExport , allEvents , regattaForRaceIdString, allMediaTracks , gpsFixStore , exportWind, new HashMap<>());
return topLevelMasterData; return topLevelMasterData;
} }
public static void assignRacesToRegattaLeaderboardColumns(RegattaLeaderboard leaderboard, Collection<RaceIdentifier> raceIdentifiers) { public static void assignRacesToRegattaLeaderboardColumns(RegattaLeaderboard leaderboard, Collection<RaceIdentifier> raceIdentifiers) {
Iterator<RaceIdentifier> regattaRacesIterator = raceIdentifiers.iterator(); Iterator<RaceIdentifier> regattaRacesIterator = raceIdentifiers.iterator();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
raceColumn.setRaceIdentifier(regattaFleet, regattaRacesIterator.next()); raceColumn.setRaceIdentifier(regattaFleet, regattaRacesIterator.next());
} }
} }
public static RegattaImpl createTestRegatta(String regattaName, Iterable<String> raceColumnNames) { public static RegattaImpl createTestRegatta(String regattaName, Iterable<String> raceColumnNames) {
Iterable<? extends Fleet> regattaFleets = Collections.singleton(regattaFleet); Iterable<? extends Fleet> regattaFleets = Collections.singleton(regattaFleet);
TrackedRegattaRegistry trackedRegattaRegistry = mock(TrackedRegattaRegistry.class); TrackedRegattaRegistry trackedRegattaRegistry = mock(TrackedRegattaRegistry.class);
Series series = new SeriesImpl("series name", isMedal, regattaFleets, raceColumnNames, trackedRegattaRegistry); Series series = new SeriesImpl("series name", isMedal, /* isFleetsCanRunInParallel */ true, regattaFleets, raceColumnNames, trackedRegattaRegistry);
Iterable<? extends Series> regattaSeries = Collections.singleton(series); Iterable<? extends Series> regattaSeries = Collections.singleton(series);
return new RegattaImpl(regattaName, boatClass, startDate, endDate, regattaSeries, persistent, scoringScheme, regatteId , courseArea, OneDesignRankingMetric::new); return new RegattaImpl(regattaName, boatClass, startDate, endDate, regattaSeries, persistent, scoringScheme, regatteId , courseArea, OneDesignRankingMetric::new);
} }
} }
@@ -204,7 +204,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl(testFleet1Name); FleetImpl testFleet1 = new FleetImpl(testFleet1Name);
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
@@ -467,7 +467,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
@@ -626,7 +626,7 @@ public class MasterDataImportTest {
List<Fleet> fleets = new ArrayList<Fleet>(); List<Fleet> fleets = new ArrayList<Fleet>();
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta(RegattaImpl.getDefaultName("testRegatta", "29er"), "29er", Regatta regatta = sourceService.createRegatta(RegattaImpl.getDefaultName("testRegatta", "29er"), "29er",
/* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /* useStartTimeInference */ /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /* useStartTimeInference */
@@ -789,7 +789,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
@@ -949,7 +949,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
@@ -1063,7 +1063,7 @@ public class MasterDataImportTest {
List<Fleet> fleetsNotToOverride = new ArrayList<Fleet>(); List<Fleet> fleetsNotToOverride = new ArrayList<Fleet>();
FleetImpl testFleet1NotToOverride = new FleetImpl("testFleet1"); FleetImpl testFleet1NotToOverride = new FleetImpl("testFleet1");
fleetsNotToOverride.add(testFleet1NotToOverride); fleetsNotToOverride.add(testFleet1NotToOverride);
seriesNotToOverride.add(new SeriesImpl("testSeries", false, fleetsNotToOverride, emptyRaceColumnNamesList, seriesNotToOverride.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleetsNotToOverride, emptyRaceColumnNamesList,
destService)); destService));
Regatta regattaNotToOverride = destService.createRegatta( Regatta regattaNotToOverride = destService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */ RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */
@@ -1151,7 +1151,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
@@ -1261,7 +1261,7 @@ public class MasterDataImportTest {
List<Fleet> fleetsToOverride = new ArrayList<Fleet>(); List<Fleet> fleetsToOverride = new ArrayList<Fleet>();
FleetImpl testFleet1ToOverride = new FleetImpl("testFleet1"); FleetImpl testFleet1ToOverride = new FleetImpl("testFleet1");
fleetsToOverride.add(testFleet1ToOverride); fleetsToOverride.add(testFleet1ToOverride);
seriesToOverride.add(new SeriesImpl("testSeries", false, fleetsToOverride, emptyRaceColumnNamesList, seriesToOverride.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleetsToOverride, emptyRaceColumnNamesList,
destService)); destService));
Regatta regattaToOverride = destService.createRegatta( Regatta regattaToOverride = destService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */ RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */
@@ -1452,7 +1452,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
@@ -1587,7 +1587,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
@@ -1833,7 +1833,7 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl("testFleet1"); FleetImpl testFleet1 = new FleetImpl("testFleet1");
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
series.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); series.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME,
@@ -2063,8 +2063,8 @@ public class MasterDataImportTest {
FleetImpl testFleet1 = new FleetImpl(testFleet1Name); FleetImpl testFleet1 = new FleetImpl(testFleet1Name);
fleets.add(testFleet1); fleets.add(testFleet1);
fleets.add(new FleetImpl("testFleet2")); fleets.add(new FleetImpl("testFleet2"));
seriesOnSource.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); seriesOnSource.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
seriesOnTarget.add(new SeriesImpl("testSeries", false, fleets, emptyRaceColumnNamesList, sourceService)); seriesOnTarget.add(new SeriesImpl("testSeries", false, /* isFleetsCanRunInParallel */ true, fleets, emptyRaceColumnNamesList, sourceService));
UUID regattaUUID = UUID.randomUUID(); UUID regattaUUID = UUID.randomUUID();
Regatta regatta = sourceService.createRegatta( Regatta regatta = sourceService.createRegatta(
RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */ RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* startDate */
@@ -1,322 +1,322 @@
package com.sap.sailing.server.test; package com.sap.sailing.server.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.LeaderboardGroupBase; import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.LeaderboardSearchResult; import com.sap.sailing.domain.base.LeaderboardSearchResult;
import com.sap.sailing.domain.base.LeaderboardSearchResultBase; import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Venue; import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.CourseAreaImpl; import com.sap.sailing.domain.base.impl.CourseAreaImpl;
import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.common.RankingMetrics; import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO; import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO; import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO;
import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore; import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.test.AbstractTracTracLiveTest; import com.sap.sailing.domain.test.AbstractTracTracLiveTest;
import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardGroupBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardSearchResultBaseJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.LeaderboardSearchResultBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer; import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer;
import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.EventBaseJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.EventBaseJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.LeaderboardGroupBaseJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.LeaderboardGroupBaseJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.LeaderboardSearchResultJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.LeaderboardSearchResultJsonSerializer;
import com.sap.sailing.server.gateway.serialization.impl.VenueJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.VenueJsonSerializer;
import com.sap.sailing.server.impl.RacingEventServiceImpl; import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sailing.server.operationaltransformation.AddColumnToSeries; import com.sap.sailing.server.operationaltransformation.AddColumnToSeries;
import com.sap.sailing.server.operationaltransformation.AddRaceDefinition; import com.sap.sailing.server.operationaltransformation.AddRaceDefinition;
import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta; import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta;
import com.sap.sailing.server.operationaltransformation.CreateEvent; import com.sap.sailing.server.operationaltransformation.CreateEvent;
import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboard; import com.sap.sailing.server.operationaltransformation.CreateRegattaLeaderboard;
import com.sap.sailing.server.operationaltransformation.CreateTrackedRace; import com.sap.sailing.server.operationaltransformation.CreateTrackedRace;
import com.sap.sailing.server.operationaltransformation.RemoveEvent; import com.sap.sailing.server.operationaltransformation.RemoveEvent;
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard; import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard;
import com.sap.sailing.server.operationaltransformation.RemoveRegatta; import com.sap.sailing.server.operationaltransformation.RemoveRegatta;
import com.sap.sse.common.Color; import com.sap.sse.common.Color;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util; import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.common.search.KeywordQuery; import com.sap.sse.common.search.KeywordQuery;
import com.sap.sse.common.search.Result; import com.sap.sse.common.search.Result;
import com.sap.sse.shared.media.ImageDescriptor; import com.sap.sse.shared.media.ImageDescriptor;
import com.sap.sse.shared.media.VideoDescriptor; import com.sap.sse.shared.media.VideoDescriptor;
public class SearchServiceTest { public class SearchServiceTest {
private RacingEventService server; private RacingEventService server;
private Venue kiel; private Venue kiel;
private Venue flensburg; private Venue flensburg;
private Event pfingstbusch; private Event pfingstbusch;
private Regatta pfingstbusch29er; private Regatta pfingstbusch29er;
private Competitor hassoPlattner; private Competitor hassoPlattner;
private Competitor alexanderRies; private Competitor alexanderRies;
private Competitor antonKoch; private Competitor antonKoch;
private Competitor tobiasSchadewaldt; private Competitor tobiasSchadewaldt;
private Competitor philippBuhl; private Competitor philippBuhl;
private Competitor dennisGehrlein; private Competitor dennisGehrlein;
private Regatta pfingstbusch470; private Regatta pfingstbusch470;
private Regatta aalRegatta; private Regatta aalRegatta;
private DynamicTrackedRace pfingstbusch29erTrackedR1; private DynamicTrackedRace pfingstbusch29erTrackedR1;
private DynamicTrackedRace pfingstbusch29erTrackedR3; private DynamicTrackedRace pfingstbusch29erTrackedR3;
private Event aalEvent; private Event aalEvent;
private DynamicTrackedRace pfingstbusch470TrackedR1; private DynamicTrackedRace pfingstbusch470TrackedR1;
private DynamicTrackedRace pfingstbusch470TrackedR2; private DynamicTrackedRace pfingstbusch470TrackedR2;
private DynamicTrackedRace aalOrcTrackedR1; private DynamicTrackedRace aalOrcTrackedR1;
private DynamicTrackedRace aalOrcTrackedR2; private DynamicTrackedRace aalOrcTrackedR2;
@Before @Before
public void setUp() { public void setUp() {
server = new RacingEventServiceImpl(); server = new RacingEventServiceImpl();
List<Event> allEvents = new ArrayList<>(); List<Event> allEvents = new ArrayList<>();
Util.addAll(server.getAllEvents(), allEvents); Util.addAll(server.getAllEvents(), allEvents);
for (Event e : allEvents) { for (Event e : allEvents) {
server.apply(new RemoveEvent(e.getId())); server.apply(new RemoveEvent(e.getId()));
} }
Map<String, Leaderboard> allLeaderboards = new HashMap<>(server.getLeaderboards()); Map<String, Leaderboard> allLeaderboards = new HashMap<>(server.getLeaderboards());
for (String leaderboardName : allLeaderboards.keySet()) { for (String leaderboardName : allLeaderboards.keySet()) {
server.apply(new RemoveLeaderboard(leaderboardName)); server.apply(new RemoveLeaderboard(leaderboardName));
} }
server.apply(new RemoveRegatta(new RegattaName("Pfingstbusch (29er)"))); server.apply(new RemoveRegatta(new RegattaName("Pfingstbusch (29er)")));
server.apply(new RemoveRegatta(new RegattaName("Pfingstbusch (470)"))); server.apply(new RemoveRegatta(new RegattaName("Pfingstbusch (470)")));
server.apply(new RemoveRegatta(new RegattaName("Aalregatta (ORC)"))); server.apply(new RemoveRegatta(new RegattaName("Aalregatta (ORC)")));
final Calendar cal = new GregorianCalendar(); final Calendar cal = new GregorianCalendar();
cal.set(2014, 5, 6, 10, 00); cal.set(2014, 5, 6, 10, 00);
final TimePoint pfingstbuschStartDate = new MillisecondsTimePoint(cal.getTime()); final TimePoint pfingstbuschStartDate = new MillisecondsTimePoint(cal.getTime());
cal.set(2014, 5, 8, 16, 00); cal.set(2014, 5, 8, 16, 00);
final TimePoint pfingstbuschEndDate = new MillisecondsTimePoint(cal.getTime()); final TimePoint pfingstbuschEndDate = new MillisecondsTimePoint(cal.getTime());
pfingstbusch = server.apply(new CreateEvent("Pfingsbusch", /* eventDescription */ null, pfingstbuschStartDate, pfingstbuschEndDate, /* isPublic */ pfingstbusch = server.apply(new CreateEvent("Pfingsbusch", /* eventDescription */ null, pfingstbuschStartDate, pfingstbuschEndDate, /* isPublic */
"Kiel", true, UUID.randomUUID(), /* officialWebsiteURLAsString */ null, /* sailorsInfoWebsiteURLAsString */ null, "Kiel", true, UUID.randomUUID(), /* officialWebsiteURLAsString */ null, /* sailorsInfoWebsiteURLAsString */ null,
/* images */Collections.<ImageDescriptor> emptyList(), /* videos */Collections.<VideoDescriptor> emptyList())); /* images */Collections.<ImageDescriptor> emptyList(), /* videos */Collections.<VideoDescriptor> emptyList()));
kiel = pfingstbusch.getVenue(); kiel = pfingstbusch.getVenue();
final CourseAreaImpl kielAlpha = new CourseAreaImpl("Alpha", UUID.randomUUID()); final CourseAreaImpl kielAlpha = new CourseAreaImpl("Alpha", UUID.randomUUID());
kiel.addCourseArea(kielAlpha); kiel.addCourseArea(kielAlpha);
final CourseAreaImpl kielBravo = new CourseAreaImpl("Bravo", UUID.randomUUID()); final CourseAreaImpl kielBravo = new CourseAreaImpl("Bravo", UUID.randomUUID());
kiel.addCourseArea(kielBravo); kiel.addCourseArea(kielBravo);
final LinkedHashMap<String, SeriesCreationParametersDTO> seriesCreationParams = new LinkedHashMap<String, SeriesCreationParametersDTO>(); final LinkedHashMap<String, SeriesCreationParametersDTO> seriesCreationParams = new LinkedHashMap<String, SeriesCreationParametersDTO>();
seriesCreationParams.put("Default", seriesCreationParams.put("Default",
new SeriesCreationParametersDTO(Collections.singletonList(new FleetDTO("Default", /* order */-1, Color.RED)), new SeriesCreationParametersDTO(Collections.singletonList(new FleetDTO("Default", /* order */-1, Color.RED)),
/* medal */false, /* startsWithZero */false, /* firstColumnIsNonDiscardableCarryForward */false, /* medal */false, /* fleetsCanRunInParallel */ true, /* startsWithZero */false, /* firstColumnIsNonDiscardableCarryForward */false,
/* discardingThresholds */null, /* hasSplitFleetContiguousScoring */false)); /* discardingThresholds */null, /* hasSplitFleetContiguousScoring */false));
pfingstbusch29er = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Pfingstbusch", "29er"), "29er", pfingstbusch29er = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Pfingstbusch", "29er"), "29er",
/*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(),
new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */
true, new LowPoint(), kielAlpha.getId(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN)); true, new LowPoint(), kielAlpha.getId(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN));
server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R1")); server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R1"));
server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R2")); server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R2"));
server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R3")); server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R3"));
RegattaLeaderboard pfingstbusch29erLeaderboard = server.apply(new CreateRegattaLeaderboard(pfingstbusch29er.getRegattaIdentifier(), RegattaLeaderboard pfingstbusch29erLeaderboard = server.apply(new CreateRegattaLeaderboard(pfingstbusch29er.getRegattaIdentifier(),
/* leaderboardDisplayName */ null, /* discardThresholds */ new int[0])); /* leaderboardDisplayName */ null, /* discardThresholds */ new int[0]));
pfingstbusch470 = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Pfingstbusch", "470"), "470", pfingstbusch470 = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Pfingstbusch", "470"), "470",
/*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */
true, new LowPoint(), kielBravo.getId(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN)); true, new LowPoint(), kielBravo.getId(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN));
server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R1")); server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R1"));
server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R2")); server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R2"));
server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R3")); server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R3"));
RegattaLeaderboard pfingstbusch470Leaderboard = server.apply(new CreateRegattaLeaderboard(pfingstbusch470.getRegattaIdentifier(), RegattaLeaderboard pfingstbusch470Leaderboard = server.apply(new CreateRegattaLeaderboard(pfingstbusch470.getRegattaIdentifier(),
/* leaderboardDisplayName */ null, /* discardThresholds */ new int[0])); /* leaderboardDisplayName */ null, /* discardThresholds */ new int[0]));
cal.set(2014, 5, 7, 10, 00); cal.set(2014, 5, 7, 10, 00);
final TimePoint aalStartDate = new MillisecondsTimePoint(cal.getTime()); final TimePoint aalStartDate = new MillisecondsTimePoint(cal.getTime());
cal.set(2014, 5, 8, 18, 00); cal.set(2014, 5, 8, 18, 00);
final TimePoint aalEndDate = new MillisecondsTimePoint(cal.getTime()); final TimePoint aalEndDate = new MillisecondsTimePoint(cal.getTime());
aalEvent = server.apply(new CreateEvent("Aalregatta", /* eventDescription */ null, aalStartDate, aalEndDate, /* isPublic */ aalEvent = server.apply(new CreateEvent("Aalregatta", /* eventDescription */ null, aalStartDate, aalEndDate, /* isPublic */
"Flensburg", true, UUID.randomUUID(), /* officialWebsiteURLAsString */ null, /*sailorsInfoWebsiteURLAsString */ null, "Flensburg", true, UUID.randomUUID(), /* officialWebsiteURLAsString */ null, /*sailorsInfoWebsiteURLAsString */ null,
/* images */Collections.<ImageDescriptor> emptyList(), /* videos */Collections.<VideoDescriptor> emptyList())); /* images */Collections.<ImageDescriptor> emptyList(), /* videos */Collections.<VideoDescriptor> emptyList()));
flensburg = aalEvent.getVenue(); flensburg = aalEvent.getVenue();
final CourseAreaImpl flensburgStandard = new CourseAreaImpl("Standard", UUID.randomUUID()); final CourseAreaImpl flensburgStandard = new CourseAreaImpl("Standard", UUID.randomUUID());
flensburg.addCourseArea(flensburgStandard); flensburg.addCourseArea(flensburgStandard);
aalRegatta = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Aalregatta", "ORC"), "ORC", aalRegatta = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Aalregatta", "ORC"), "ORC",
/*startDate*/ null, /*endDate*/ null, UUID.randomUUID(), /*startDate*/ null, /*endDate*/ null, UUID.randomUUID(),
new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */
true, new LowPoint(), flensburgStandard.getId(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN)); true, new LowPoint(), flensburgStandard.getId(), /* useStartTimeInference */ true, RankingMetrics.ONE_DESIGN));
server.apply(new AddColumnToSeries(aalRegatta.getRegattaIdentifier(), "Default", "R1")); server.apply(new AddColumnToSeries(aalRegatta.getRegattaIdentifier(), "Default", "R1"));
server.apply(new AddColumnToSeries(aalRegatta.getRegattaIdentifier(), "Default", "R2")); server.apply(new AddColumnToSeries(aalRegatta.getRegattaIdentifier(), "Default", "R2"));
RegattaLeaderboard aalRegattaLeaderboard = server.apply(new CreateRegattaLeaderboard(aalRegatta.getRegattaIdentifier(), RegattaLeaderboard aalRegattaLeaderboard = server.apply(new CreateRegattaLeaderboard(aalRegatta.getRegattaIdentifier(),
/* leaderboardDisplayName */ null, /* discardThresholds */ new int[0])); /* leaderboardDisplayName */ null, /* discardThresholds */ new int[0]));
hassoPlattner = AbstractTracTracLiveTest.createCompetitor("Hasso Plattner"); hassoPlattner = AbstractTracTracLiveTest.createCompetitor("Hasso Plattner");
alexanderRies = AbstractTracTracLiveTest.createCompetitor("Alexander Ries"); alexanderRies = AbstractTracTracLiveTest.createCompetitor("Alexander Ries");
antonKoch = AbstractTracTracLiveTest.createCompetitor("Anton Koch"); antonKoch = AbstractTracTracLiveTest.createCompetitor("Anton Koch");
tobiasSchadewaldt = AbstractTracTracLiveTest.createCompetitor("Tobias Schadewaldt"); tobiasSchadewaldt = AbstractTracTracLiveTest.createCompetitor("Tobias Schadewaldt");
philippBuhl = AbstractTracTracLiveTest.createCompetitor("Philipp Buhl"); philippBuhl = AbstractTracTracLiveTest.createCompetitor("Philipp Buhl");
dennisGehrlein = AbstractTracTracLiveTest.createCompetitor("Dennis Gehrlein"); dennisGehrlein = AbstractTracTracLiveTest.createCompetitor("Dennis Gehrlein");
final RaceDefinitionImpl pfingstbusch29erR1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch29er.getBoatClass(), final RaceDefinitionImpl pfingstbusch29erR1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch29er.getBoatClass(),
Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt })); Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt }));
final RaceDefinitionImpl pfingstbusch29erR2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch29er.getBoatClass(), final RaceDefinitionImpl pfingstbusch29erR2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch29er.getBoatClass(),
Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt })); Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt }));
final RaceDefinitionImpl pfingstbusch29erR3 = new RaceDefinitionImpl("R3", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch29er.getBoatClass(), final RaceDefinitionImpl pfingstbusch29erR3 = new RaceDefinitionImpl("R3", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch29er.getBoatClass(),
Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt })); Arrays.asList(new Competitor[] { alexanderRies, tobiasSchadewaldt }));
server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR1)); server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR1));
server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR2)); server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR2));
server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR3)); server.apply(new AddRaceDefinition(pfingstbusch29er.getRegattaIdentifier(), pfingstbusch29erR3));
// track only R1 and R3 // track only R1 and R3
pfingstbusch29erTrackedR1 = server.apply(new CreateTrackedRace(pfingstbusch29er.getRaceIdentifier(pfingstbusch29erR1), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000, pfingstbusch29erTrackedR1 = server.apply(new CreateTrackedRace(pfingstbusch29er.getRaceIdentifier(pfingstbusch29erR1), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000)); /* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000));
pfingstbusch29erTrackedR1.setStartOfTrackingReceived(pfingstbuschStartDate.plus(1)); pfingstbusch29erTrackedR1.setStartOfTrackingReceived(pfingstbuschStartDate.plus(1));
pfingstbusch29erLeaderboard.getRaceColumnByName("R1").setTrackedRace(pfingstbusch29erLeaderboard.getRaceColumnByName("R1").getFleetByName("Default"), pfingstbusch29erTrackedR1); pfingstbusch29erLeaderboard.getRaceColumnByName("R1").setTrackedRace(pfingstbusch29erLeaderboard.getRaceColumnByName("R1").getFleetByName("Default"), pfingstbusch29erTrackedR1);
pfingstbusch29erTrackedR3 = server.apply(new CreateTrackedRace(pfingstbusch29er.getRaceIdentifier(pfingstbusch29erR3), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000, pfingstbusch29erTrackedR3 = server.apply(new CreateTrackedRace(pfingstbusch29er.getRaceIdentifier(pfingstbusch29erR3), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000)); /* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000));
pfingstbusch29erLeaderboard.getRaceColumnByName("R3").setTrackedRace(pfingstbusch29erLeaderboard.getRaceColumnByName("R3").getFleetByName("Default"), pfingstbusch29erTrackedR3); pfingstbusch29erLeaderboard.getRaceColumnByName("R3").setTrackedRace(pfingstbusch29erLeaderboard.getRaceColumnByName("R3").getFleetByName("Default"), pfingstbusch29erTrackedR3);
final RaceDefinitionImpl pfingstbush470R1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch470.getBoatClass(), final RaceDefinitionImpl pfingstbush470R1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch470.getBoatClass(),
Arrays.asList(new Competitor[] { philippBuhl, antonKoch })); Arrays.asList(new Competitor[] { philippBuhl, antonKoch }));
final RaceDefinitionImpl pfingstbush470R2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch470.getBoatClass(), final RaceDefinitionImpl pfingstbush470R2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), pfingstbusch470.getBoatClass(),
Arrays.asList(new Competitor[] { philippBuhl, antonKoch })); Arrays.asList(new Competitor[] { philippBuhl, antonKoch }));
server.apply(new AddRaceDefinition(pfingstbusch470.getRegattaIdentifier(), pfingstbush470R1)); server.apply(new AddRaceDefinition(pfingstbusch470.getRegattaIdentifier(), pfingstbush470R1));
server.apply(new AddRaceDefinition(pfingstbusch470.getRegattaIdentifier(), pfingstbush470R2)); server.apply(new AddRaceDefinition(pfingstbusch470.getRegattaIdentifier(), pfingstbush470R2));
// track only R1 and R2 // track only R1 and R2
pfingstbusch470TrackedR1 = server.apply(new CreateTrackedRace(pfingstbusch470.getRaceIdentifier(pfingstbush470R1), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000, pfingstbusch470TrackedR1 = server.apply(new CreateTrackedRace(pfingstbusch470.getRaceIdentifier(pfingstbush470R1), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000)); /* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000));
pfingstbusch470TrackedR1.setStartOfTrackingReceived(pfingstbuschStartDate.plus(2)); // starts later than 29er pfingstbusch470TrackedR1.setStartOfTrackingReceived(pfingstbuschStartDate.plus(2)); // starts later than 29er
pfingstbusch470Leaderboard.getRaceColumnByName("R1").setTrackedRace(pfingstbusch470Leaderboard.getRaceColumnByName("R1").getFleetByName("Default"), pfingstbusch470TrackedR1); pfingstbusch470Leaderboard.getRaceColumnByName("R1").setTrackedRace(pfingstbusch470Leaderboard.getRaceColumnByName("R1").getFleetByName("Default"), pfingstbusch470TrackedR1);
pfingstbusch470TrackedR2 = server.apply(new CreateTrackedRace(pfingstbusch470.getRaceIdentifier(pfingstbush470R2), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000, pfingstbusch470TrackedR2 = server.apply(new CreateTrackedRace(pfingstbusch470.getRaceIdentifier(pfingstbush470R2), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000)); /* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000));
pfingstbusch470Leaderboard.getRaceColumnByName("R2").setTrackedRace(pfingstbusch470Leaderboard.getRaceColumnByName("R2").getFleetByName("Default"), pfingstbusch470TrackedR2); pfingstbusch470Leaderboard.getRaceColumnByName("R2").setTrackedRace(pfingstbusch470Leaderboard.getRaceColumnByName("R2").getFleetByName("Default"), pfingstbusch470TrackedR2);
final RaceDefinitionImpl aalOrcR1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), aalRegatta.getBoatClass(), final RaceDefinitionImpl aalOrcR1 = new RaceDefinitionImpl("R1", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), aalRegatta.getBoatClass(),
Arrays.asList(new Competitor[] { hassoPlattner, dennisGehrlein, philippBuhl })); Arrays.asList(new Competitor[] { hassoPlattner, dennisGehrlein, philippBuhl }));
final RaceDefinitionImpl aalOrcR2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), aalRegatta.getBoatClass(), final RaceDefinitionImpl aalOrcR2 = new RaceDefinitionImpl("R2", new CourseImpl("up/down", Collections.<Waypoint>emptyList()), aalRegatta.getBoatClass(),
Arrays.asList(new Competitor[] { hassoPlattner, dennisGehrlein, philippBuhl })); Arrays.asList(new Competitor[] { hassoPlattner, dennisGehrlein, philippBuhl }));
server.apply(new AddRaceDefinition(aalRegatta.getRegattaIdentifier(), aalOrcR1)); server.apply(new AddRaceDefinition(aalRegatta.getRegattaIdentifier(), aalOrcR1));
server.apply(new AddRaceDefinition(aalRegatta.getRegattaIdentifier(), aalOrcR2)); server.apply(new AddRaceDefinition(aalRegatta.getRegattaIdentifier(), aalOrcR2));
// track only R1 and R2 // track only R1 and R2
aalOrcTrackedR1 = server.apply(new CreateTrackedRace(aalRegatta.getRaceIdentifier(aalOrcR1), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000, aalOrcTrackedR1 = server.apply(new CreateTrackedRace(aalRegatta.getRaceIdentifier(aalOrcR1), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000)); /* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000));
aalOrcTrackedR1.setStartOfTrackingReceived(aalStartDate); aalOrcTrackedR1.setStartOfTrackingReceived(aalStartDate);
aalRegattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(aalRegattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Default"), aalOrcTrackedR1); aalRegattaLeaderboard.getRaceColumnByName("R1").setTrackedRace(aalRegattaLeaderboard.getRaceColumnByName("R1").getFleetByName("Default"), aalOrcTrackedR1);
aalOrcTrackedR2 = server.apply(new CreateTrackedRace(aalRegatta.getRaceIdentifier(aalOrcR2), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000, aalOrcTrackedR2 = server.apply(new CreateTrackedRace(aalRegatta.getRaceIdentifier(aalOrcR2), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000)); /* millisecondsOverWhichToAverageWind */ 15000, /* millisecondsOverWhichToAverageSpeed */ 15000));
aalRegattaLeaderboard.getRaceColumnByName("R2").setTrackedRace(aalRegattaLeaderboard.getRaceColumnByName("R2").getFleetByName("Default"), aalOrcTrackedR2); aalRegattaLeaderboard.getRaceColumnByName("R2").setTrackedRace(aalRegattaLeaderboard.getRaceColumnByName("R2").getFleetByName("Default"), aalOrcTrackedR2);
} }
@Test @Test
public void testSetup() { public void testSetup() {
assertNotNull(pfingstbusch29er); assertNotNull(pfingstbusch29er);
assertNotNull(pfingstbusch29erTrackedR1); assertNotNull(pfingstbusch29erTrackedR1);
assertNotNull(pfingstbusch29erTrackedR3); assertNotNull(pfingstbusch29erTrackedR3);
assertNotNull(pfingstbusch470TrackedR1); assertNotNull(pfingstbusch470TrackedR1);
assertNotNull(pfingstbusch470TrackedR2); assertNotNull(pfingstbusch470TrackedR2);
assertNotNull(aalEvent); assertNotNull(aalEvent);
assertNotNull(aalRegatta); assertNotNull(aalRegatta);
assertNotNull(aalOrcTrackedR1); assertNotNull(aalOrcTrackedR1);
assertNotNull(aalOrcTrackedR2); assertNotNull(aalOrcTrackedR2);
} }
@Test @Test
public void testSimpleSearchByCompetitorName() { public void testSimpleSearchByCompetitorName() {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Tobi" }))); Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Tobi" })));
assertEquals(1, Util.size(searchResults.getHits())); assertEquals(1, Util.size(searchResults.getHits()));
Regatta foundRegatta = searchResults.getHits().iterator().next().getRegatta(); Regatta foundRegatta = searchResults.getHits().iterator().next().getRegatta();
assertSame(pfingstbusch29er, foundRegatta); assertSame(pfingstbusch29er, foundRegatta);
} }
@Test @Test
public void testSimpleSearchByCompetitorName2() { public void testSimpleSearchByCompetitorName2() {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Hasso" }))); Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Hasso" })));
assertEquals(1, Util.size(searchResults.getHits())); assertEquals(1, Util.size(searchResults.getHits()));
Regatta foundRegatta = searchResults.getHits().iterator().next().getRegatta(); Regatta foundRegatta = searchResults.getHits().iterator().next().getRegatta();
assertSame(aalRegatta, foundRegatta); assertSame(aalRegatta, foundRegatta);
} }
@Test @Test
public void testSimpleSearchByVenueName() { public void testSimpleSearchByVenueName() {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Flensburg" }))); Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Flensburg" })));
assertEquals(1, Util.size(searchResults.getHits())); assertEquals(1, Util.size(searchResults.getHits()));
Regatta foundRegatta = searchResults.getHits().iterator().next().getRegatta(); Regatta foundRegatta = searchResults.getHits().iterator().next().getRegatta();
assertSame(aalRegatta, foundRegatta); assertSame(aalRegatta, foundRegatta);
} }
@Test @Test
public void testSimpleSearchByVenueName2() { public void testSimpleSearchByVenueName2() {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Kiel" }))); Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Kiel" })));
assertEquals(2, Util.size(searchResults.getHits())); assertEquals(2, Util.size(searchResults.getHits()));
final Iterator<LeaderboardSearchResult> iterator = searchResults.getHits().iterator(); final Iterator<LeaderboardSearchResult> iterator = searchResults.getHits().iterator();
final LeaderboardSearchResult firstMatch = iterator.next(); final LeaderboardSearchResult firstMatch = iterator.next();
Regatta firstFoundRegatta = firstMatch.getRegatta(); Regatta firstFoundRegatta = firstMatch.getRegatta();
assertSame(pfingstbusch29er, firstFoundRegatta); assertSame(pfingstbusch29er, firstFoundRegatta);
assertSame(pfingstbusch, firstMatch.getEvents().iterator().next()); assertSame(pfingstbusch, firstMatch.getEvents().iterator().next());
final LeaderboardSearchResult secondMatch = iterator.next(); final LeaderboardSearchResult secondMatch = iterator.next();
Regatta secondFoundRegatta = secondMatch.getRegatta(); Regatta secondFoundRegatta = secondMatch.getRegatta();
assertSame(pfingstbusch470, secondFoundRegatta); assertSame(pfingstbusch470, secondFoundRegatta);
assertSame(pfingstbusch, secondMatch.getEvents().iterator().next()); assertSame(pfingstbusch, secondMatch.getEvents().iterator().next());
} }
@Test @Test
public void testMultipleMatchesSortedCorrectly() { public void testMultipleMatchesSortedCorrectly() {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Buhl" }))); Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Buhl" })));
assertEquals(2, Util.size(searchResults.getHits())); assertEquals(2, Util.size(searchResults.getHits()));
final Iterator<LeaderboardSearchResult> iter = searchResults.getHits().iterator(); final Iterator<LeaderboardSearchResult> iter = searchResults.getHits().iterator();
final LeaderboardSearchResult firstMatch = iter.next(); final LeaderboardSearchResult firstMatch = iter.next();
Regatta earlierStartRegatta = firstMatch.getRegatta(); Regatta earlierStartRegatta = firstMatch.getRegatta();
assertSame(pfingstbusch470, earlierStartRegatta); assertSame(pfingstbusch470, earlierStartRegatta);
assertSame(pfingstbusch, firstMatch.getEvents().iterator().next()); assertSame(pfingstbusch, firstMatch.getEvents().iterator().next());
final LeaderboardSearchResult secondMatch = iter.next(); final LeaderboardSearchResult secondMatch = iter.next();
Regatta laterStartRegatta = secondMatch.getRegatta(); Regatta laterStartRegatta = secondMatch.getRegatta();
assertSame(aalRegatta, laterStartRegatta); assertSame(aalRegatta, laterStartRegatta);
assertSame(aalEvent, secondMatch.getEvents().iterator().next()); assertSame(aalEvent, secondMatch.getEvents().iterator().next());
} }
@Test @Test
public void testSerializeAndDeserializeSearchResult() throws JsonDeserializationException { public void testSerializeAndDeserializeSearchResult() throws JsonDeserializationException {
Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Buhl" }))); Result<LeaderboardSearchResult> searchResults = server.search(new KeywordQuery(Arrays.asList(new String[] { "Buhl" })));
LeaderboardGroupBaseJsonSerializer leaderboardGroupBaseJsonSerializer = new LeaderboardGroupBaseJsonSerializer(); LeaderboardGroupBaseJsonSerializer leaderboardGroupBaseJsonSerializer = new LeaderboardGroupBaseJsonSerializer();
LeaderboardSearchResultJsonSerializer serializer = new LeaderboardSearchResultJsonSerializer(new EventBaseJsonSerializer(new VenueJsonSerializer(new CourseAreaJsonSerializer()), LeaderboardSearchResultJsonSerializer serializer = new LeaderboardSearchResultJsonSerializer(new EventBaseJsonSerializer(new VenueJsonSerializer(new CourseAreaJsonSerializer()),
leaderboardGroupBaseJsonSerializer), leaderboardGroupBaseJsonSerializer),
leaderboardGroupBaseJsonSerializer); leaderboardGroupBaseJsonSerializer);
LeaderboardGroupBaseJsonDeserializer leaderboardGroupBaseJsonDeserializer = new LeaderboardGroupBaseJsonDeserializer(); LeaderboardGroupBaseJsonDeserializer leaderboardGroupBaseJsonDeserializer = new LeaderboardGroupBaseJsonDeserializer();
LeaderboardSearchResultBaseJsonDeserializer deserializer = new LeaderboardSearchResultBaseJsonDeserializer( LeaderboardSearchResultBaseJsonDeserializer deserializer = new LeaderboardSearchResultBaseJsonDeserializer(
new EventBaseJsonDeserializer(new VenueJsonDeserializer(new CourseAreaJsonDeserializer(DomainFactory.INSTANCE)), new EventBaseJsonDeserializer(new VenueJsonDeserializer(new CourseAreaJsonDeserializer(DomainFactory.INSTANCE)),
leaderboardGroupBaseJsonDeserializer), leaderboardGroupBaseJsonDeserializer); leaderboardGroupBaseJsonDeserializer), leaderboardGroupBaseJsonDeserializer);
final LeaderboardSearchResult expected = searchResults.getHits().iterator().next(); final LeaderboardSearchResult expected = searchResults.getHits().iterator().next();
LeaderboardSearchResultBase deserialized = deserializer.deserialize(serializer.serialize(expected)); LeaderboardSearchResultBase deserialized = deserializer.deserialize(serializer.serialize(expected));
assertEquals("Pfingstbusch (470)", deserialized.getRegattaName()); assertEquals("Pfingstbusch (470)", deserialized.getRegattaName());
assertEquals(expected.getEvents().iterator().next().getName(), deserialized.getEvents().iterator().next().getName()); assertEquals(expected.getEvents().iterator().next().getName(), deserialized.getEvents().iterator().next().getName());
assertEquals(expected.getEvents().iterator().next().getId(), deserialized.getEvents().iterator().next().getId()); assertEquals(expected.getEvents().iterator().next().getId(), deserialized.getEvents().iterator().next().getId());
assertEquals(expected.getEvents().iterator().next().getStartDate(), deserialized.getEvents().iterator().next().getStartDate()); assertEquals(expected.getEvents().iterator().next().getStartDate(), deserialized.getEvents().iterator().next().getStartDate());
assertEquals(expected.getEvents().iterator().next().getEndDate(), deserialized.getEvents().iterator().next().getEndDate()); assertEquals(expected.getEvents().iterator().next().getEndDate(), deserialized.getEvents().iterator().next().getEndDate());
assertEquals(expected.getEvents().iterator().next().getVenue().getName(), deserialized.getEvents().iterator().next().getVenue().getName()); assertEquals(expected.getEvents().iterator().next().getVenue().getName(), deserialized.getEvents().iterator().next().getVenue().getName());
Iterator<LeaderboardGroup> expectedLGs = expected.getLeaderboardGroups().iterator(); Iterator<LeaderboardGroup> expectedLGs = expected.getLeaderboardGroups().iterator();
Iterator<? extends LeaderboardGroupBase> deserializedLGs = deserialized.getLeaderboardGroups().iterator(); Iterator<? extends LeaderboardGroupBase> deserializedLGs = deserialized.getLeaderboardGroups().iterator();
while (expectedLGs.hasNext()) { while (expectedLGs.hasNext()) {
assertTrue(deserializedLGs.hasNext()); assertTrue(deserializedLGs.hasNext());
LeaderboardGroup expectedLG = expectedLGs.next(); LeaderboardGroup expectedLG = expectedLGs.next();
LeaderboardGroupBase deserializedLG = deserializedLGs.next(); LeaderboardGroupBase deserializedLG = deserializedLGs.next();
assertEquals(expectedLG.getId(), deserializedLG.getId()); assertEquals(expectedLG.getId(), deserializedLG.getId());
assertEquals(expectedLG.getName(), deserializedLG.getName()); assertEquals(expectedLG.getName(), deserializedLG.getName());
assertEquals(expectedLG.getDescription(), deserializedLG.getDescription()); assertEquals(expectedLG.getDescription(), deserializedLG.getDescription());
} }
} }
} }
@@ -78,6 +78,10 @@ public class RaceLogReplicator implements RaceColumnListener {
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
} }
@Override
public void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
}
@Override @Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
} }
@@ -51,6 +51,10 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
} }
@Override
public void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) {
}
@Override @Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
} }
@@ -1444,7 +1444,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
} }
result.put( result.put(
s.getName(), s.getName(),
new SeriesCreationParametersDTO(fleetNamesAndOrdering, s.isMedal(), s.isStartsWithZeroScore(), s new SeriesCreationParametersDTO(fleetNamesAndOrdering, s.isMedal(), s.isFleetsCanRunInParallel(), s.isStartsWithZeroScore(), s
.isFirstColumnIsNonDiscardableCarryForward(), s.getResultDiscardingRule() == null ? null .isFirstColumnIsNonDiscardableCarryForward(), s.getResultDiscardingRule() == null ? null
: s.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), s : s.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), s
.hasSplitFleetContiguousScoring())); .hasSplitFleetContiguousScoring()));
@@ -56,7 +56,7 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation {
List<Series> result = new ArrayList<Series>(); List<Series> result = new ArrayList<Series>();
for (Map.Entry<String, SeriesCreationParametersDTO> e : seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds.getSeriesCreationParameters().entrySet()) { for (Map.Entry<String, SeriesCreationParametersDTO> e : seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds.getSeriesCreationParameters().entrySet()) {
final List<String> emptyRaceColumnNamesList = Collections.emptyList(); final List<String> emptyRaceColumnNamesList = Collections.emptyList();
Series s = new SeriesImpl(e.getKey(), e.getValue().isMedal(), createFleets(e.getValue().getFleets()), Series s = new SeriesImpl(e.getKey(), e.getValue().isMedal(), e.getValue().isFleetsCanRunInParallel(), createFleets(e.getValue().getFleets()),
emptyRaceColumnNamesList, trackedRegattaRegistry); emptyRaceColumnNamesList, trackedRegattaRegistry);
if (e.getValue().getDiscardingThresholds() != null) { if (e.getValue().getDiscardingThresholds() != null) {
s.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(e.getValue().getDiscardingThresholds())); s.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(e.getValue().getDiscardingThresholds()));
@@ -1,90 +1,93 @@
package com.sap.sailing.server.operationaltransformation; package com.sap.sailing.server.operationaltransformation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation; import com.sap.sailing.server.RacingEventServiceOperation;
public class UpdateSeries extends AbstractSeriesOperation<Void> { public class UpdateSeries extends AbstractSeriesOperation<Void> {
private static final long serialVersionUID = 6356089749049112710L; private static final long serialVersionUID = 6356089749049112710L;
private final List<FleetDTO> fleets; private final List<FleetDTO> fleets;
private final boolean isMedal; private final boolean isMedal;
private final int[] resultDiscardingThresholds; private final boolean isFleetsCanRunInParallel;
private final boolean startsWithZeroScore; private final int[] resultDiscardingThresholds;
private final boolean firstColumnIsNonDiscardableCarryForward; private final boolean startsWithZeroScore;
private final boolean hasSplitFleetContiguousScoring; private final boolean firstColumnIsNonDiscardableCarryForward;
private final boolean seriesNameChanged; private final boolean hasSplitFleetContiguousScoring;
private final String newSeriesName; private final boolean seriesNameChanged;
private final String newSeriesName;
public UpdateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore, public UpdateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal, boolean isFleetsCanRunInParallel,
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring, int[] resultDiscardingThresholds, boolean startsWithZeroScore,
List<FleetDTO> fleets) { boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring,
super(regattaIdentifier, seriesName); List<FleetDTO> fleets) {
this.seriesNameChanged = !seriesName.equals(newSeriesName); super(regattaIdentifier, seriesName);
this.newSeriesName = newSeriesName; this.seriesNameChanged = !seriesName.equals(newSeriesName);
this.isMedal = isMedal; this.newSeriesName = newSeriesName;
this.resultDiscardingThresholds = resultDiscardingThresholds; this.isMedal = isMedal;
this.startsWithZeroScore = startsWithZeroScore; this.isFleetsCanRunInParallel = isFleetsCanRunInParallel;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; this.resultDiscardingThresholds = resultDiscardingThresholds;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring; this.startsWithZeroScore = startsWithZeroScore;
this.fleets = fleets; this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
} this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
this.fleets = fleets;
@Override }
public Void internalApplyTo(RacingEventService toState) throws Exception {
Series series = getSeries(toState); @Override
if (series == null) { public Void internalApplyTo(RacingEventService toState) throws Exception {
series = createSeries(toState); Series series = getSeries(toState);
} if (series == null) {
if (seriesNameChanged) { series = createSeries(toState);
series.setName(newSeriesName); }
} if (seriesNameChanged) {
series.setIsMedal(isMedal); series.setName(newSeriesName);
series.setResultDiscardingRule(resultDiscardingThresholds == null ? }
null : new ThresholdBasedResultDiscardingRuleImpl(resultDiscardingThresholds)); series.setIsMedal(isMedal);
series.setStartsWithZeroScore(startsWithZeroScore); series.setIsFleetsCanRunInParallel(isFleetsCanRunInParallel);
series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForward); series.setResultDiscardingRule(resultDiscardingThresholds == null ?
series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoring); null : new ThresholdBasedResultDiscardingRuleImpl(resultDiscardingThresholds));
if (series.getRegatta().isPersistent()) { series.setStartsWithZeroScore(startsWithZeroScore);
toState.updateStoredRegatta(series.getRegatta()); series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForward);
} series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoring);
return null; if (series.getRegatta().isPersistent()) {
} toState.updateStoredRegatta(series.getRegatta());
}
private Series createSeries(RacingEventService toState) { return null;
Regatta regatta = toState.getRegatta(getRegattaIdentifier()); }
final List<String> emptyRaceColumnNames = Collections.emptyList();
List<Fleet> result = new ArrayList<Fleet>(); private Series createSeries(RacingEventService toState) {
for (FleetDTO fleetNameAndOrderingAndColor : fleets) { Regatta regatta = toState.getRegatta(getRegattaIdentifier());
Fleet fleet = new FleetImpl(fleetNameAndOrderingAndColor.getName(), fleetNameAndOrderingAndColor.getOrderNo(), fleetNameAndOrderingAndColor.getColor()); final List<String> emptyRaceColumnNames = Collections.emptyList();
result.add(fleet); List<Fleet> result = new ArrayList<Fleet>();
} for (FleetDTO fleetNameAndOrderingAndColor : fleets) {
regatta.addSeries(new SeriesImpl(getSeriesName(), isMedal, result, emptyRaceColumnNames, (TrackedRegattaRegistry)toState)); Fleet fleet = new FleetImpl(fleetNameAndOrderingAndColor.getName(), fleetNameAndOrderingAndColor.getOrderNo(), fleetNameAndOrderingAndColor.getColor());
return regatta.getSeriesByName(getSeriesName()); result.add(fleet);
} }
regatta.addSeries(new SeriesImpl(getSeriesName(), isMedal, isFleetsCanRunInParallel, result, emptyRaceColumnNames, (TrackedRegattaRegistry)toState));
@Override return regatta.getSeriesByName(getSeriesName());
public RacingEventServiceOperation<?> transformClientOp(RacingEventServiceOperation<?> serverOp) { }
// TODO Auto-generated method stub
return null; @Override
} public RacingEventServiceOperation<?> transformClientOp(RacingEventServiceOperation<?> serverOp) {
// TODO Auto-generated method stub
@Override return null;
public RacingEventServiceOperation<?> transformServerOp(RacingEventServiceOperation<?> clientOp) { }
// TODO Auto-generated method stub
return null; @Override
} public RacingEventServiceOperation<?> transformServerOp(RacingEventServiceOperation<?> clientOp) {
} // TODO Auto-generated method stub
return null;
}
}
@@ -143,7 +143,7 @@ public class StructureImporter {
for (Series raceType : regattaStructure.getSeries()) { for (Series raceType : regattaStructure.getSeries()) {
List<com.sap.sailing.domain.base.Fleet> fleets = getFleets(raceType.getFleets()); List<com.sap.sailing.domain.base.Fleet> fleets = getFleets(raceType.getFleets());
setRaceNames(index, raceType, raceType.getFleets()); setRaceNames(index, raceType, raceType.getFleets());
series.add(new SeriesImpl(raceType.getSeries(), raceType.isMedal(), fleets, raceType.getRaceNames(), null)); series.add(new SeriesImpl(raceType.getSeries(), raceType.isMedal(), true, fleets, raceType.getRaceNames(), null));
} }
} }
return series; return series;
@@ -253,7 +253,7 @@ public class Util {
List<T> l = (List<T>) iterable; List<T> l = (List<T>) iterable;
return l.get(i); return l.get(i);
} else { } else {
Iterator<T> iter = iterable.iterator(); final Iterator<T> iter = iterable.iterator();
T result = iter.next(); T result = iter.next();
for (int j=0; j<i; j++) { for (int j=0; j<i; j++) {
result = iter.next(); result = iter.next();
@@ -261,6 +261,27 @@ public class Util {
return result; return result;
} }
} }
public static <T> T first(Iterable<T> iterable) {
final Iterator<T> iter = iterable.iterator();
final T result;
if (iter.hasNext()) {
result = iter.next();
} else {
result = null;
}
return result;
}
public static <T> T last(Iterable<T> iterable) {
final T result;
if (isEmpty(iterable)) {
result = null;
} else {
result = get(iterable, size(iterable)-1);
}
return result;
}
/** /**
* A null-safe check whether <code>t</code> is contained in <code>ts</code>. For <code>ts==null</code> the method * A null-safe check whether <code>t</code> is contained in <code>ts</code>. For <code>ts==null</code> the method
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="about_partnership_text" translatable="false">The SAP Sail InSight app supports live tracking of race boats by leveraging mobile phone technology, allowing the ability for friends and fans to follow racing whether onsite or online through live leaderboards and 2D visualization. Following racing sailors can replay their races allowing the opportunity to review performance and optimize strategies all available at: https://www.sapsailing.com\n\nSAP supports US Sailing through the implementation of a range of innovative technology solutions, delivering analytics to help teams and sailors improve performance whilst also providing applications to help sailing operations run more efficiently.\n\nAs the national governing body for sailing, US Sailing provides leadership, integrity and growth for the sport including training and education programs for instructors and race officials alongside administration and oversight of competitive sailing across the country.</string> <string name="about_partnership_text">The SAP Sail InSight app supports live tracking of race boats by leveraging mobile phone technology, allowing the ability for friends and fans to follow racing whether onsite or online through live leaderboards and 2D visualization. Following racing sailors can replay their races allowing the opportunity to review performance and optimize strategies all available at: https://www.sapsailing.com\n\nSAP supports US Sailing through the implementation of a range of innovative technology solutions, delivering analytics to help teams and sailors improve performance whilst also providing applications to help sailing operations run more efficiently.\n\nAs the national governing body for sailing, US Sailing provides leadership, integrity and growth for the sport including training and education programs for instructors and race officials alongside administration and oversight of competitive sailing across the country.</string>
<string name="about_partnership_clickable" translatable="false">https://www.sapsailing.com</string> <string name="about_partnership_clickable">https://www.sapsailing.com</string>
</resources> </resources>
@@ -80,6 +80,10 @@ public class AppConstants {
public final static String INTENT_ACTION_SHOW_MAIN_CONTENT = PACKAGE_NAME + ".action.show.main"; public final static String INTENT_ACTION_SHOW_MAIN_CONTENT = PACKAGE_NAME + ".action.show.main";
public final static String INTENT_ACTION_SHOW_SUMMARY_CONTENT = PACKAGE_NAME + ".action.show.summary"; public final static String INTENT_ACTION_SHOW_SUMMARY_CONTENT = PACKAGE_NAME + ".action.show.summary";
/**
* As extra this intent expects the String obtained for a FilterableRace / ManagedRace using
* {@code new RaceGroupSeries(race.getRaceGroup(), race.getSeries()).getDisplayName()}.
*/
public final static String INTENT_ACTION_SHOW_PROTEST = PACKAGE_NAME + ".action.show.protest"; public final static String INTENT_ACTION_SHOW_PROTEST = PACKAGE_NAME + ".action.show.protest";
public final static String INTENT_ACTION_REMOVE_PROTEST = PACKAGE_NAME + ".action.remove.protest"; public final static String INTENT_ACTION_REMOVE_PROTEST = PACKAGE_NAME + ".action.remove.protest";
@@ -189,7 +189,7 @@ public enum InMemoryDataStore implements DataStore {
} }
private SimpleRaceLogIdentifier convertManagedRaceIdentifierToSimpleRaceLogIdentifier(ManagedRaceIdentifier id) { private SimpleRaceLogIdentifier convertManagedRaceIdentifierToSimpleRaceLogIdentifier(ManagedRaceIdentifier id) {
return new SimpleRaceLogIdentifierImpl(id.getRaceGroup().getName(), id.getRaceName(), id.getFleet().getName()); return new SimpleRaceLogIdentifierImpl(id.getRaceGroup().getName(), id.getRaceColumnName(), id.getFleet().getName());
} }
@Override @Override
@@ -87,8 +87,8 @@ public class OfflineDataManager extends DataManager {
newEvent.getVenue().addCourseArea(new CourseAreaImpl("Stadium", UUID.randomUUID())); newEvent.getVenue().addCourseArea(new CourseAreaImpl("Stadium", UUID.randomUUID()));
dataStore.addEvent(newEvent); dataStore.addEvent(newEvent);
SeriesWithRows qualifying = new SeriesWithRowsImpl("Qualifying", false, null); SeriesWithRows qualifying = new SeriesWithRowsImpl("Qualifying", false, /* isFleetsCanRunInParallel */ true, null);
SeriesWithRows medal = new SeriesWithRowsImpl("Medal", true, null); SeriesWithRows medal = new SeriesWithRowsImpl("Medal", true, /* isFleetsCanRunInParallel */ true, null);
RaceGroup raceGroup = new RaceGroupImpl("ESS", /* displayName */ null, new BoatClassImpl("X40", false), null, Arrays.asList(qualifying, RaceGroup raceGroup = new RaceGroupImpl("ESS", /* displayName */ null, new BoatClassImpl("X40", false), null, Arrays.asList(qualifying,
medal), new EmptyRegattaConfiguration()); medal), new EmptyRegattaConfiguration());
@@ -111,7 +111,8 @@ public class OfflineDataManager extends DataManager {
AppPreferences.on(context).getAuthor(), 1, RaceLogRaceStatus.FINISHING)); AppPreferences.on(context).getAuthor(), 1, RaceLogRaceStatus.FINISHING));
ManagedRace q1 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("A.B", new FleetImpl("A"), qualifying, raceGroup), ManagedRace q1 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("A.B", new FleetImpl("A"), qualifying, raceGroup),
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration)); RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration),
/* zeroBasedIndexInFleet */ 0);
log = new RaceLogImpl(UUID.randomUUID()); log = new RaceLogImpl(UUID.randomUUID());
/* /*
@@ -120,7 +121,8 @@ public class OfflineDataManager extends DataManager {
*/ */
ManagedRace q2 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("B", new FleetImpl("A.A"), qualifying, raceGroup), ManagedRace q2 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("B", new FleetImpl("A.A"), qualifying, raceGroup),
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration)); RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration),
/* zeroBasedIndexInFleet */ 1);
log = new RaceLogImpl(UUID.randomUUID()); log = new RaceLogImpl(UUID.randomUUID());
/* /*
@@ -128,7 +130,8 @@ public class OfflineDataManager extends DataManager {
* RaceLogRaceStatus.FINISHED)); * RaceLogRaceStatus.FINISHED));
*/ */
ManagedRace q3 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("Q3", new FleetImpl("Default"), qualifying, raceGroup), ManagedRace q3 = new ManagedRaceImpl(new ManagedRaceIdentifierImpl("Q3", new FleetImpl("Default"), qualifying, raceGroup),
RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration)); RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration),
/* zeroBasedIndexInFleet */ 2);
/* /*
* ManagedRace m1 = new ManagedRaceImpl( new ManagedRaceIdentifierImpl( "M1", new FleetImpl("Default"), medal, * ManagedRace m1 = new ManagedRaceImpl( new ManagedRaceIdentifierImpl( "M1", new FleetImpl("Default"), medal,
* raceGroup), null); * raceGroup), null);
@@ -202,7 +202,7 @@ public class OnlineDataManager extends DataManager {
ManagedRaceIdentifier identifier = managedRace.getIdentifier(); ManagedRaceIdentifier identifier = managedRace.getIdentifier();
// no parameter encoding required here; the UrlHelper.generateUrl call uses Url.Builder which handles encoding // no parameter encoding required here; the UrlHelper.generateUrl call uses Url.Builder which handles encoding
String raceGroupName = identifier.getRaceGroup().getName(); String raceGroupName = identifier.getRaceGroup().getName();
String raceColumnName = identifier.getRaceName(); String raceColumnName = identifier.getRaceColumnName();
String fleetName = identifier.getFleet().getName(); String fleetName = identifier.getFleet().getName();
List<Util.Pair<String, Object>> params = new ArrayList<>(); List<Util.Pair<String, Object>> params = new ArrayList<>();
@@ -229,7 +229,7 @@ public class OnlineDataManager extends DataManager {
ManagedRaceIdentifier identifier = managedRace.getIdentifier(); ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = identifier.getRaceGroup().getName(); String raceGroupName = identifier.getRaceGroup().getName();
String raceColumnName = identifier.getRaceName(); String raceColumnName = identifier.getRaceColumnName();
String fleetName = identifier.getFleet().getName(); String fleetName = identifier.getFleet().getName();
List<Util.Pair<String, Object>> params = new ArrayList<>(); List<Util.Pair<String, Object>> params = new ArrayList<>();
@@ -257,7 +257,7 @@ public class OnlineDataManager extends DataManager {
ManagedRaceIdentifier identifier = managedRace.getIdentifier(); ManagedRaceIdentifier identifier = managedRace.getIdentifier();
String raceGroupName = identifier.getRaceGroup().getName(); String raceGroupName = identifier.getRaceGroup().getName();
String raceColumnName = identifier.getRaceName(); String raceColumnName = identifier.getRaceColumnName();
String fleetName = identifier.getFleet().getName(); String fleetName = identifier.getFleet().getName();
List<Util.Pair<String, Object>> params = new ArrayList<>(); List<Util.Pair<String, Object>> params = new ArrayList<>();
@@ -11,7 +11,6 @@ import org.json.simple.JSONValue;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.configuration.ConfigurationLoader; import com.sap.sailing.domain.base.configuration.ConfigurationLoader;
import com.sap.sailing.domain.base.configuration.RegattaConfiguration; import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.base.racegroup.RaceCell; import com.sap.sailing.domain.base.racegroup.RaceCell;
@@ -29,6 +28,11 @@ import com.sap.sailing.racecommittee.app.utils.ManagedRaceCalculator;
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.Helpers; import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
/**
* Produces an collection of {@link ManagedRace} objects whose order is determined by the order of the {@link RaceGroup}s
* as provided by the JSON object, and within each {@link RaceGroup} by the order of the series in the group, and within
* the series by the order of the "race rows" as provided by the server.
*/
public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace>> { public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace>> {
private final JsonDeserializer<RaceGroup> deserializer; private final JsonDeserializer<RaceGroup> deserializer;
@@ -45,11 +49,9 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
public Collection<ManagedRace> parse(Reader reader) throws Exception { public Collection<ManagedRace> parse(Reader reader) throws Exception {
Object parsedResult = JSONValue.parse(reader); Object parsedResult = JSONValue.parse(reader);
JSONArray jsonArray = Helpers.toJSONArraySafe(parsedResult); JSONArray jsonArray = Helpers.toJSONArraySafe(parsedResult);
Collection<ManagedRace> managedRaces = new ArrayList<ManagedRace>(); Collection<ManagedRace> managedRaces = new ArrayList<ManagedRace>();
for (Object element : jsonArray) { for (Object element : jsonArray) {
JSONObject json = Helpers.toJSONObjectSafe(element); JSONObject json = Helpers.toJSONObjectSafe(element);
RaceGroup group = deserializer.deserialize(json); RaceGroup group = deserializer.deserialize(json);
addManagedRaces(managedRaces, group); addManagedRaces(managedRaces, group);
} }
@@ -61,23 +63,25 @@ public class ManagedRacesDataParser implements DataParser<Collection<ManagedRace
for (RaceRow raceRow : series.getRaceRows()) { for (RaceRow raceRow : series.getRaceRows()) {
Fleet fleet = raceRow.getFleet(); Fleet fleet = raceRow.getFleet();
for (RaceCell cell : raceRow.getCells()) { for (RaceCell cell : raceRow.getCells()) {
ManagedRace race = createManagedRace(raceGroup, series, fleet, cell.getName(), cell.getRaceLog(), cell.getFactor(), cell.getExplicitFactor()); ManagedRace race = createManagedRace(raceGroup, series, fleet, cell.getName(), cell.getRaceLog(),
cell.getFactor(), cell.getExplicitFactor(), cell.getZeroBasedIndexInFleet());
target.add(race); target.add(race);
} }
} }
} }
} }
private ManagedRace createManagedRace(RaceGroup raceGroup, SeriesBase series, Fleet fleet, String name, private ManagedRace createManagedRace(RaceGroup raceGroup, SeriesWithRows series, Fleet fleet, String raceColumnName,
RaceLog raceLog, double factor, Double explicitFactor) { RaceLog raceLog, double factor, Double explicitFactor, int zeroBasedIndexInFleet) {
ConfigurationLoader<RegattaConfiguration> configurationLoader = globalConfigurationLoader; ConfigurationLoader<RegattaConfiguration> configurationLoader = globalConfigurationLoader;
RegattaConfiguration localConfiguration = raceGroup.getRegattaConfiguration(); RegattaConfiguration localConfiguration = raceGroup.getRegattaConfiguration();
if (localConfiguration != null) { if (localConfiguration != null) {
configurationLoader = new MergingRegattaConfigurationLoader(localConfiguration, globalConfigurationLoader); configurationLoader = new MergingRegattaConfigurationLoader(localConfiguration, globalConfigurationLoader);
} }
FleetIdentifier fleetIdentifier = new FleetIdentifierImpl(fleet, series, raceGroup); FleetIdentifier fleetIdentifier = new FleetIdentifierImpl(fleet, series, raceGroup);
ManagedRaceIdentifier identifier = new ManagedRaceIdentifierImpl(name, fleetIdentifier); ManagedRaceIdentifier identifier = new ManagedRaceIdentifierImpl(raceColumnName, fleetIdentifier);
return new ManagedRaceImpl(identifier, new ManagedRaceCalculator(raceLog, author, configurationLoader), factor, explicitFactor); return new ManagedRaceImpl(identifier, new ManagedRaceCalculator(raceLog, author, configurationLoader),
factor, explicitFactor, zeroBasedIndexInFleet);
} }
} }
@@ -1,14 +1,14 @@
package com.sap.sailing.racecommittee.app.domain; package com.sap.sailing.racecommittee.app.domain;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup; import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
public interface FleetIdentifier { public interface FleetIdentifier {
public Fleet getFleet(); public Fleet getFleet();
public SeriesBase getSeries(); public SeriesWithRows getSeries();
public RaceGroup getRaceGroup(); public RaceGroup getRaceGroup();
@@ -7,13 +7,20 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.state.RaceState; import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.racegroup.FilterableRace;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.racecommittee.app.domain.impl.Result; import com.sap.sailing.racecommittee.app.domain.impl.Result;
import com.sap.sse.common.Named; import com.sap.sse.common.Named;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID; import com.sap.sse.common.WithID;
public interface ManagedRace extends ManagedRaceIdentifier, Named, WithID { /**
* A managed race's {@link #getName()} is the race column's name.
*
* @author Axel Uhl (d043530)
*
*/
public interface ManagedRace extends FilterableRace, ManagedRaceIdentifier, Named, WithID {
/** /**
* @return the identifier of the race. * @return the identifier of the race.
@@ -1,5 +1,5 @@
package com.sap.sailing.racecommittee.app.domain; package com.sap.sailing.racecommittee.app.domain;
public interface ManagedRaceIdentifier extends FleetIdentifier { public interface ManagedRaceIdentifier extends FleetIdentifier {
public String getRaceName(); public String getRaceColumnName();
} }
@@ -1,18 +1,18 @@
package com.sap.sailing.racecommittee.app.domain.impl; package com.sap.sailing.racecommittee.app.domain.impl;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup; import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.racecommittee.app.domain.FleetIdentifier; import com.sap.sailing.racecommittee.app.domain.FleetIdentifier;
import com.sap.sse.common.Util.Triple; import com.sap.sse.common.Util.Triple;
public class FleetIdentifierImpl implements FleetIdentifier { public class FleetIdentifierImpl implements FleetIdentifier {
private Fleet fleet; private Fleet fleet;
private SeriesBase series; private SeriesWithRows series;
private RaceGroup raceGroup; private RaceGroup raceGroup;
public FleetIdentifierImpl(Fleet fleet, SeriesBase series, RaceGroup raceGroup) { public FleetIdentifierImpl(Fleet fleet, SeriesWithRows series, RaceGroup raceGroup) {
this.fleet = fleet; this.fleet = fleet;
this.series = series; this.series = series;
this.raceGroup = raceGroup; this.raceGroup = raceGroup;
@@ -22,7 +22,7 @@ public class FleetIdentifierImpl implements FleetIdentifier {
return fleet; return fleet;
} }
public SeriesBase getSeries() { public SeriesWithRows getSeries() {
return series; return series;
} }
@@ -1,35 +1,35 @@
package com.sap.sailing.racecommittee.app.domain.impl; package com.sap.sailing.racecommittee.app.domain.impl;
import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.SeriesBase;
import com.sap.sailing.domain.base.racegroup.RaceGroup; import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
import com.sap.sailing.racecommittee.app.domain.FleetIdentifier; import com.sap.sailing.racecommittee.app.domain.FleetIdentifier;
import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier; import com.sap.sailing.racecommittee.app.domain.ManagedRaceIdentifier;
public class ManagedRaceIdentifierImpl extends FleetIdentifierImpl implements ManagedRaceIdentifier { public class ManagedRaceIdentifierImpl extends FleetIdentifierImpl implements ManagedRaceIdentifier {
private String raceName; private String raceColumnName;
public ManagedRaceIdentifierImpl(String raceName, Fleet fleetWithRaceNames, SeriesBase series, RaceGroup raceGroup) { public ManagedRaceIdentifierImpl(String raceColumnName, Fleet fleetWithRaceNames, SeriesWithRows series, RaceGroup raceGroup) {
super(fleetWithRaceNames, series, raceGroup); super(fleetWithRaceNames, series, raceGroup);
this.raceName = raceName; this.raceColumnName = raceColumnName;
} }
public ManagedRaceIdentifierImpl(String raceName, FleetIdentifier identifier) { public ManagedRaceIdentifierImpl(String raceColumnName, FleetIdentifier identifier) {
this(raceName, identifier.getFleet(), identifier.getSeries(), identifier.getRaceGroup()); this(raceColumnName, identifier.getFleet(), identifier.getSeries(), identifier.getRaceGroup());
} }
public String getRaceName() { public String getRaceColumnName() {
return raceName; return raceColumnName;
} }
@Override @Override
public String getId() { public String getId() {
return String.format("%s.%s", super.getId(), escapeIdentifierFragment(getRaceName())); return String.format("%s.%s", super.getId(), escapeIdentifierFragment(getRaceColumnName()));
} }
@Override @Override
public String toString() { public String toString() {
return getRaceName() + " - " + getFleet().getName(); return getRaceColumnName() + " - " + getFleet().getName();
} }
} }
Loaded 100 of 115 files, more files were not shown because too many files have changed in this diff. Show more