Merge remote-tracking branch 'origin/master' into mark-rounding-inference

This commit is contained in:
Nick Klose committed 2014-02-12 10:24:46 +01:00
commit 29b2d8af88
52 files changed
+476 -50

No files matched your search

@@ -15,15 +15,18 @@ public class SeriesCreationParametersDTO implements Serializable {
private boolean firstColumnIsNonDiscardableCarryForward;
private int[] discardingThresholds;
private boolean hasSplitFleetContiguousScoring;
SeriesCreationParametersDTO() {}
public SeriesCreationParametersDTO(List<FleetDTO> fleets, boolean medal, boolean startsWithZero, boolean firstColumnIsNonDiscardableCarryForward,
int[] discardingThresholds) {
int[] discardingThresholds, boolean hasSplitFleetContiguousScoring) {
super();
this.fleets = fleets;
this.medal = medal;
this.startsWithZero = startsWithZero;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
this.discardingThresholds = discardingThresholds;
}
@@ -40,6 +43,10 @@ public class SeriesCreationParametersDTO implements Serializable {
return startsWithZero;
}
public boolean hasSplitFleetContiguousScoring() {
return hasSplitFleetContiguousScoring;
}
public int[] getDiscardingThresholds() {
return discardingThresholds;
}
@@ -948,6 +948,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
String name = (String) dbSeries.get(FieldNames.SERIES_NAME.name());
boolean isMedal = (Boolean) dbSeries.get(FieldNames.SERIES_IS_MEDAL.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 firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name());
final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name());
Map<String, Fleet> fleetsByName = loadFleets(dbFleets);
@@ -961,6 +962,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
if (startsWithZeroScore != null) {
series.setStartsWithZeroScore(startsWithZeroScore);
}
if (hasSplitFleetContiguousScoring != null) {
series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoring);
}
if (firstColumnIsNonDiscardableCarryForward != null) {
series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForward);
}
@@ -22,6 +22,7 @@ public enum FieldNames {
SERIES_NAME,
SERIES_IS_MEDAL,
SERIES_STARTS_WITH_ZERO_SCORE,
SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING,
SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD,
SERIES_FLEETS,
SERIES_RACE_COLUMNS,
@@ -509,6 +509,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
DBObject dbSeries = new BasicDBObject();
dbSeries.put(FieldNames.SERIES_NAME.name(), s.getName());
dbSeries.put(FieldNames.SERIES_IS_MEDAL.name(), s.isMedal());
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_NON_DISCARDABLE_CARRY_FORWARD.name(), s.isFirstColumnIsNonDiscardableCarryForward());
BasicDBList dbFleets = new BasicDBList();
@@ -10,9 +10,11 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;
import org.junit.Test;
@@ -24,8 +26,8 @@ import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.MarkImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
@@ -43,6 +45,7 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10LastBreaksTie;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
@@ -764,7 +767,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
}
@Test
public void testTieBreakWithEqualWinsAndTwoVersusOneSecondsWithHighPointScoringScheme() throws NoWindException {
Competitor[] c = createCompetitors(4).toArray(new Competitor[0]);
@@ -785,6 +788,46 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
}
@Test
public void testCompetitorsRankedEleventhOrLowerGetOnePointScore() throws NoWindException {
Competitor[] competitors = createCompetitors(16).toArray(new Competitor[0]);
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
FlexibleLeaderboard leaderboardHighPoint10LastBreaksTie = new FlexibleLeaderboardImpl("Test ESS Highpoint 10LastBreaksTie", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new HighPointFirstGets10LastBreaksTie(), null);
leaderboardHighPoint10LastBreaksTie.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitors)), "R1", /* medalRace */ false,
leaderboardHighPoint10LastBreaksTie.getFleet(null));
assertTrue(leaderboardHighPoint10LastBreaksTie.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboardHighPoint10LastBreaksTie.getTotalPoints(competitors[0], later), leaderboardHighPoint10LastBreaksTie.getTotalPoints(competitors[3], later)) < 0); // c0 better than c3
assertEquals(16, leaderboardHighPoint10LastBreaksTie.getCompetitorsFromBestToWorst(later).size());
assertEquals(new Double(10), leaderboardHighPoint10LastBreaksTie.getTotalPoints(competitors[0], later));
assertEquals(new Double(1), leaderboardHighPoint10LastBreaksTie.getTotalPoints(competitors[15], later));
// Normal HighPoint leaderboard has no max so that winner gets as many points as there are competitors
FlexibleLeaderboard leaderboardHighPoint = new FlexibleLeaderboardImpl("Test ESS Highpoint", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new HighPoint(), null);
leaderboardHighPoint.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitors)), "R1", /* medalRace */ false,
leaderboardHighPoint.getFleet(null));
assertEquals(16, leaderboardHighPoint.getCompetitorsFromBestToWorst(later).size());
assertEquals(new Double(16), leaderboardHighPoint.getTotalPoints(competitors[0], later));
FlexibleLeaderboard leaderboardHighPointESSOverall = new FlexibleLeaderboardImpl("Test ESS Highpoint Overall", new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]),
new HighPointExtremeSailingSeriesOverall(), null);
leaderboardHighPointESSOverall.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitors)), "R1", /* medalRace */ false,
leaderboardHighPointESSOverall.getFleet(null));
assertEquals(16, leaderboardHighPointESSOverall.getCompetitorsFromBestToWorst(later).size());
assertEquals(new Double(10), leaderboardHighPointESSOverall.getTotalPoints(competitors[0], later));
assertEquals(new Double(9), leaderboardHighPointESSOverall.getTotalPoints(competitors[1], later));
assertEquals(new Double(8), leaderboardHighPointESSOverall.getTotalPoints(competitors[2], later));
assertEquals(new Double(7), leaderboardHighPointESSOverall.getTotalPoints(competitors[3], later));
assertEquals(new Double(6), leaderboardHighPointESSOverall.getTotalPoints(competitors[4], later));
assertEquals(new Double(5), leaderboardHighPointESSOverall.getTotalPoints(competitors[5], later));
assertEquals(new Double(4), leaderboardHighPointESSOverall.getTotalPoints(competitors[6], later));
assertEquals(new Double(3), leaderboardHighPointESSOverall.getTotalPoints(competitors[7], later));
assertEquals(new Double(2), leaderboardHighPointESSOverall.getTotalPoints(competitors[8], later));
assertEquals(new Double(1), leaderboardHighPointESSOverall.getTotalPoints(competitors[9], later));
assertEquals(new Double(1), leaderboardHighPointESSOverall.getTotalPoints(competitors[14], later));
assertEquals(new Double(1), leaderboardHighPointESSOverall.getTotalPoints(competitors[15], later));
}
@Test
public void testOverallLeaderboardWithESSHighPointScoring() throws NoWindException {
// Let c0 lead the series, c3 trail it, c1 and c2 are one-time competitors which are then suppressed in the
@@ -1024,4 +1067,126 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
Regatta regatta = new RegattaImpl(regattaBaseName, boatClass, series, /* persistent */ false, scoringScheme, "123", null);
return regatta;
}
/**
* See bug 1260. There must be a possibility to have ordered fleets that are scored such that the winner of the race of the best fleet
* gets the best score in that column; and the winner of the second-best fleet gets the n-th best score in the column with n being the
* number of competitors in the best fleet plus one; and so on. At the same time, while for a regular ISAF regatta format the participation
* in a better fleet will always let those competitors rank better than all participants of worse fleets, here the fleet pertinence shall
* not matter for the global ranking.
*/
@Test
public void testTotalRankComparatorForOrderedSplitFleetsWhoseOrderingDoesNotPersist() throws NoWindException {
series = new ArrayList<Series>();
final int numberOfBeforeRaces = 1;
// -------- before series ------------
{
Set<? extends Fleet> beforeFleets = Collections.singleton(new FleetImpl("Default"));
List<String> beforeRaceColumnNames = new ArrayList<String>();
for (int i = 1; i <= numberOfBeforeRaces; i++) {
beforeRaceColumnNames.add("R" + i);
}
Series qualifyingSeries = new SeriesImpl("Before", /* isMedal */false, beforeFleets,
beforeRaceColumnNames, /* trackedRegattaRegistry */null);
series.add(qualifyingSeries);
}
// -------- knock-out qualification series ------------
{
List<Fleet> qualificationFleets = new ArrayList<Fleet>();
for (String qualificationFleetName : new String[] { "Yellow", "Blue" }) {
qualificationFleets.add(new FleetImpl(qualificationFleetName));
}
List<String> qualificationRaceColumnNames = new ArrayList<String>();
qualificationRaceColumnNames.add("Q");
Series qualificationSeries = new SeriesImpl("Qualification", /* isMedal */false, qualificationFleets, qualificationRaceColumnNames, /* trackedRegattaRegistry */ null);
// discard the one and only qualification race; it doesn't score
qualificationSeries.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(new int[] { 1 }));
series.add(qualificationSeries);
}
// -------- knock-out final series ------------
{
List<Fleet> finalFleets = new ArrayList<Fleet>();
int fleetOrdering = 1;
for (String finalFleetName : new String[] { "Gold", "Silver" }) {
finalFleets.add(new FleetImpl(finalFleetName, fleetOrdering++));
}
List<String> finalRaceColumnNames = new ArrayList<String>();
finalRaceColumnNames.add("F");
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
finalSeries.setSplitFleetContiguousScoring(true);
series.add(finalSeries);
}
// -------- after series ------------
{
final int numberOfFinalRaces = 1;
Set<? extends Fleet> afterFleets = Collections.singleton(new FleetImpl("Default"));
List<String> finalRaceColumnNames = new ArrayList<String>();
for (int i = numberOfBeforeRaces+1; i <= numberOfBeforeRaces+numberOfFinalRaces; i++) {
finalRaceColumnNames.add("R" + i);
}
Series finalSeries = new SeriesImpl("After", /* isMedal */false, afterFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
series.add(finalSeries);
}
Regatta regatta = new RegattaImpl("Test Regatta",DomainFactory.INSTANCE.getOrCreateBoatClass("Extreme40", /* typicallyStartsUpwind */ false),
series, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.HIGH_POINT_FIRST_GETS_TEN), "123", /* course area */null);
List<Competitor> competitors = createCompetitors(12);
final int firstYellowCompetitorIndex = 3;
List<Competitor> yellow = new ArrayList<>(competitors.subList(firstYellowCompetitorIndex, firstYellowCompetitorIndex+6));
List<Competitor> blue = new ArrayList<>(competitors);
blue.removeAll(yellow);
Collections.shuffle(yellow);
Collections.shuffle(blue);
final int firstGoldCompetitorIndex = 5;
List<Competitor> gold = new ArrayList<>(competitors.subList(firstGoldCompetitorIndex, firstGoldCompetitorIndex+6));
List<Competitor> silver = new ArrayList<>(competitors);
silver.removeAll(gold);
Collections.shuffle(gold);
Collections.shuffle(silver);
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
RaceColumn r1Column = series.get(0).getRaceColumnByName("R1");
TrackedRace r1Default = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors);
r1Column.setTrackedRace(r1Column.getFleetByName("Default"), r1Default);
RaceColumn qColumn = series.get(1).getRaceColumnByName("Q");
TrackedRace qYellow = new MockedTrackedRaceWithStartTimeAndRanks(now, yellow);
qColumn.setTrackedRace(qColumn.getFleetByName("Yellow"), qYellow);
TrackedRace qBlue = new MockedTrackedRaceWithStartTimeAndRanks(now, blue);
qColumn.setTrackedRace(qColumn.getFleetByName("Blue"), qBlue);
RaceColumn f1Column = series.get(2).getRaceColumnByName("F");
TrackedRace f1Gold = new MockedTrackedRaceWithStartTimeAndRanks(now, gold);
f1Column.setTrackedRace(f1Column.getFleetByName("Gold"), f1Gold);
TrackedRace f1Silver = new MockedTrackedRaceWithStartTimeAndRanks(now, silver);
f1Column.setTrackedRace(f1Column.getFleetByName("Silver"), f1Silver);
RaceColumn r2Column = series.get(3).getRaceColumnByName("R2");
TrackedRace r2Default = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors);
r2Column.setTrackedRace(r2Column.getFleetByName("Default"), r2Default);
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
Map<Competitor, Double> totalPoints = new LinkedHashMap<>();
for (Competitor rankedCompetitor : rankedCompetitors) {
totalPoints.put(rankedCompetitor, leaderboard.getTotalPoints(rankedCompetitor, later));
}
// assert that the final column competitors are ranked from top to bottom:
double scoreInFinalRace = 10.0;
for (Competitor rankedCompetitor : leaderboard.getCompetitorsFromBestToWorst(f1Column, later)) {
assertEquals(scoreInFinalRace, leaderboard.getTotalPoints(rankedCompetitor, f1Column, later), 0.000001);
scoreInFinalRace = Math.max(scoreInFinalRace-1, 1);
}
double lastScore = Double.MAX_VALUE;
// assert that only the points matter for ranking; not the fleet assignment
for (Competitor rankedCompetitor : rankedCompetitors) {
// with a high-point scoring scheme, assert that as competitors get worse, scores get less
double rankedCompetitorScore = totalPoints.get(rankedCompetitor);
assertTrue("Expected " + rankedCompetitor + " with rank "
+ (rankedCompetitors.indexOf(rankedCompetitor) + 1)
+ " to have worse (lesser) score than its immediate better competitor who scored " + lastScore
+ " but was " + rankedCompetitorScore, rankedCompetitorScore <= lastScore);
lastScore = rankedCompetitorScore;
// assert that the qualification race consistently has zero points for all competitors because it is discarded
assertTrue(leaderboard.isDiscarded(rankedCompetitor, qColumn, later));
}
}
}
@@ -209,4 +209,22 @@ public interface RaceColumn extends Named {
* set to false for default behavior, set to true to exclude tracked races
*/
public void setMasterDataExportOngoingThreadFlag(boolean flagValue);
/**
* Usually, when a regatta has split fleets that are {@link Fleet#getOrdering() ordered}, a competitor participating in a
* better fleet is always scored better than all competitors participating in worse fleets. However, under some circumstances
* it may be desirable to model a regatta series such that the fleet pertinence does not lead to a persistent scoring ordering
* throughout the regatta. For example, the Extreme Sailing Series Knock-Out Races use ordered fleets, but the fleet that a
* competitor qualified for does not decide about total regatta ranking. It may, however, decide for in-column ranking. See also
* {@link #isSplit}.
*/
boolean isTotalOrderDefinedByFleet();
/**
* When a column has more than one fleet, there are two different options for scoring it. Either the scoring scheme is applied
* to the sequence of competitors one gets when first ordering the competitors by fleets and then within each fleet by their
* rank in the fleet's race; or the scoring scheme is applied to each fleet separately, leading to the best score being awarded
* in the column as many times as there are fleets in the column. For the latter case, this method returns <code>true</code>.
*/
boolean hasSplitFleetContiguousScoring();
}
@@ -18,6 +18,8 @@ public interface RaceColumnListener extends Serializable {
void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward);
void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring);
boolean canAddRaceColumnToContainer(RaceColumn raceColumn);
void raceColumnAddedToContainer(RaceColumn raceColumn);
@@ -93,4 +93,14 @@ public interface Series extends SeriesBase {
boolean isFirstColumnIsNonDiscardableCarryForward();
void setFirstColumnIsNonDiscardableCarryForward(boolean firstColumnIsNonDiscardableCarryForward);
/**
* When a series has more than one fleet, there are two different options for scoring it. Either the scoring scheme is applied
* to the sequence of competitors one gets when first ordering the competitors by fleets and then within each fleet by their
* rank in the fleet's race; or the scoring scheme is applied to each fleet separately, leading to the best score being awarded
* in the column as many times as there are fleets in the column. For the former case, this method returns <code>true</code>.
*/
boolean hasSplitFleetContiguousScoring();
void setSplitFleetContiguousScoring(boolean hasSplitFleetScore);
}
@@ -111,4 +111,14 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo
public boolean isCarryForward() {
return isFirstColumnInSeries() && getSeries().isFirstColumnIsNonDiscardableCarryForward();
}
/**
* Delegates to {@link Series#hasSplitFleetContiguousScoring()}.
*/
@Override
public boolean hasSplitFleetContiguousScoring() {
return getSeries().hasSplitFleetContiguousScoring();
}
}
@@ -287,6 +287,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
raceColumnListeners.notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward);
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
raceColumnListeners.notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return raceColumnListeners.canAddRaceColumnToContainer(raceColumn);
@@ -47,6 +47,15 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
*/
private boolean firstColumnIsNonDiscardableCarryForward;
/**
* When a column has more than one fleet, there are two different options for scoring it. Either the scoring scheme is applied
* to the sequence of competitors one gets when first ordering the competitors by fleets and then within each fleet by their
* rank in the fleet's race; or the scoring scheme is applied to each fleet separately, leading to the best score being awarded
* in the column as many times as there are fleets in the column. For the latter case, this field is <code>false</code> which is
* also the default.
*/
private boolean hasSplitFleetContiguousScoring;
/**
* @param fleets
* must be non-empty
@@ -226,7 +235,13 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
@Override
public void setIsMedal(boolean isMedal) {
boolean oldIsMedal = this.isMedal;
this.isMedal = isMedal;
if (oldIsMedal != isMedal) {
for (RaceColumn raceColumn : getRaceColumns()) {
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, isMedal);
}
}
}
@Override
@@ -249,6 +264,11 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
raceColumnListeners.notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring);
}
@Override
public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) {
raceColumnListeners.notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward);
@@ -352,6 +372,16 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
}
}
@Override
public void setSplitFleetContiguousScoring(boolean hasSplitFleetContiguousScoring) {
if (hasSplitFleetContiguousScoring != this.hasSplitFleetContiguousScoring) {
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
for (RaceColumn raceColumn : getRaceColumns()) {
raceColumnListeners.notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring);
}
}
}
@Override
public boolean isFirstColumnIsNonDiscardableCarryForward() {
return firstColumnIsNonDiscardableCarryForward;
@@ -370,5 +400,8 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
}
@Override
public boolean hasSplitFleetContiguousScoring() {
return hasSplitFleetContiguousScoring;
}
}
@@ -95,4 +95,19 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn {
return false;
}
/**
* Implements this by delegating to and negating the result of {@link #hasSplitFleetContiguousScoring()}. This is a
* reasonable default implementation because if the score is not split up by fleet but scored contiguously
* ascending/descending, this usually means that the fleets re-convene after this series instead of remaining split.
*/
@Override
public boolean isTotalOrderDefinedByFleet() {
return !hasSplitFleetContiguousScoring();
}
@Override
public boolean hasSplitFleetContiguousScoring() {
return false;
}
}
@@ -457,4 +457,6 @@ public interface Leaderboard extends Named {
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails,
TrackedRegattaRegistry trackedRegattaRegistry, DomainFactory baseDomainFactory) throws NoWindException,
InterruptedException, ExecutionException;
NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher();
}
@@ -249,6 +249,11 @@ public class LeaderboardCacheManager {
removeFromCache(leaderboard);
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
removeFromCache(leaderboard);
}
@Override
public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn,
boolean firstColumnIsNonDiscardableCarryForward) {
@@ -47,9 +47,11 @@ public interface ScoringScheme extends Serializable {
*
* If the <code>competitor</code> has no {@link RaceColumn#getTrackedRace(Competitor) tracked race} in the column in which
* the competitor participated, <code>null</code> is returned, meaning the competitor has no score assigned for that
* race.
* race.
*/
Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher);
Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank,
Callable<Integer> numberOfCompetitorsInRaceFetcher,
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher);
/**
* If a competitor is disqualified, a penalty score is attributed by this scoring scheme. Some schemes require to
@@ -170,4 +170,16 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
}
return result;
}
protected int getNumberOfCompetitorsInBetterFleets(RaceColumn raceColumn, int competitorFleetOrdering) {
int numberOfCompetitorsInBetterFleets = 0;
for (Fleet fleet : raceColumn.getFleets()) {
TrackedRace trackedRaceOfBetterFleet;
if (fleet.getOrdering() != 0 && fleet.getOrdering() < competitorFleetOrdering &&
(trackedRaceOfBetterFleet=raceColumn.getTrackedRace(fleet)) != null) {
numberOfCompetitorsInBetterFleets += Util.size(trackedRaceOfBetterFleet.getRace().getCompetitors());
}
}
return numberOfCompetitorsInBetterFleets;
}
}
@@ -751,6 +751,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
getRaceColumnListeners().notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward);
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
getRaceColumnListeners().notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring);
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
@@ -1680,4 +1685,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
return getName() + " " + (getDefaultCourseArea() != null ? getDefaultCourseArea().getName() : "<No course area defined>") + " " + (getScoringScheme() != null ? getScoringScheme().getType().name() : "<No scoring scheme set>");
}
@Override
public NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher() {
return new NumberOfCompetitorsFetcherImpl();
}
}
@@ -410,6 +410,10 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
}
@Override
public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) {
}
@@ -13,7 +13,8 @@ import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetche
/**
* The scoring system as used by the Extreme Sailing Series. Disqualified competitors get 0 points; the winner gets as
* many points as there are competitors in the race. Higher scores are better.
* many points as there are competitors in the race. Higher scores are better. No negative scores are returned. Worst
* score a competitor can get is 1.0.
*
* @author Axel Uhl (D043530)
*
@@ -26,7 +27,9 @@ public class HighPoint extends AbstractScoringSchemeImpl {
}
@Override
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher) {
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank,
Callable<Integer> numberOfCompetitorsInRaceFetcher,
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
Double result;
if (rank == 0) {
result = null;
@@ -34,10 +37,23 @@ public class HighPoint extends AbstractScoringSchemeImpl {
Integer numberOfCompetitorsInRace;
try {
numberOfCompetitorsInRace = numberOfCompetitorsInRaceFetcher.call();
int competitorFleetOrdering;
if (numberOfCompetitorsInRace == null) {
result = null;
} else {
result = (double) (numberOfCompetitorsInRace - rank + 1);
final int effectiveRank;
final int numberOfCompetitorsDeterminingMaxPoints;
if (raceColumn.hasSplitFleetContiguousScoring()
&& (competitorFleetOrdering = raceColumn.getFleetOfCompetitor(competitor).getOrdering()) != 0) {
int numberOfCompetitorsInBetterFleets = getNumberOfCompetitorsInBetterFleets(raceColumn,
competitorFleetOrdering);
effectiveRank = rank + numberOfCompetitorsInBetterFleets;
numberOfCompetitorsDeterminingMaxPoints = numberOfCompetitorsInLeaderboardFetcher.getNumberOfCompetitorsInLeaderboard();
} else {
effectiveRank = rank;
numberOfCompetitorsDeterminingMaxPoints = numberOfCompetitorsInRace;
}
result = (double) (numberOfCompetitorsDeterminingMaxPoints - effectiveRank + 1);
}
} catch (Exception e) {
throw new RuntimeException(e);
@@ -7,6 +7,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
/**
* A variant of the {@link HighPoint} scoring scheme which breaks ties differently and which assigns a score of 10 to
@@ -30,7 +31,7 @@ public class HighPointExtremeSailingSeriesOverall extends HighPoint {
@Override
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank,
Callable<Integer> numberOfCompetitorsInRaceFetcher) {
Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
Double result;
if (rank == 0) {
result = null;
@@ -4,11 +4,12 @@ import java.util.concurrent.Callable;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
/**
* A variant of the {@link HighPoint} scoring system where the winner gets a fixed number of points, and
* all subsequent ranks get one point less. No negative scores are returned. 0.0 is the worst score a
* all subsequent ranks get one point less. No negative scores are returned. 1.0 is the worst score a
* competitor can score.
*
* @author Axel Uhl (D043530)
@@ -24,12 +25,22 @@ public abstract class HighPointFirstGetsFixedScore extends HighPoint {
}
@Override
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher) {
Double result;
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
final int effectiveRank;
final Double result;
int competitorFleetOrdering;
if (rank == 0) {
effectiveRank = 0;
} else if (raceColumn.hasSplitFleetContiguousScoring() && (competitorFleetOrdering=raceColumn.getFleetOfCompetitor(competitor).getOrdering()) != 0) {
int numberOfCompetitorsInBetterFleets = getNumberOfCompetitorsInBetterFleets(raceColumn, competitorFleetOrdering);
effectiveRank = rank + numberOfCompetitorsInBetterFleets;
} else {
effectiveRank = rank;
}
if (effectiveRank == 0) {
result = null;
} else {
result = Math.max(1.0, (double) (scoreForRaceWinner - rank + 1));
result = Math.max(1.0, (double) (scoreForRaceWinner - effectiveRank + 1));
}
return result;
}
@@ -140,7 +140,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
// (tracked or manual) is sufficient
preemptiveColumnResult = compareByMedalRaceParticipation(o1Score, o2Score);
}
if (preemptiveColumnResult == 0) {
if (preemptiveColumnResult == 0 && raceColumn.isTotalOrderDefinedByFleet()) {
preemptiveColumnResult = compareByFleet(raceColumn, o1, o2);
}
if (preemptiveColumnResult != 0) {
@@ -27,15 +27,27 @@ public class LowPoint extends AbstractScoringSchemeImpl {
}
@Override
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher) {
return rank == 0 ? null : (double) rank;
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
final int effectiveRank;
final Double result;
int competitorFleetOrdering;
if (rank == 0) {
effectiveRank = 0;
} else if (raceColumn.hasSplitFleetContiguousScoring() && (competitorFleetOrdering=raceColumn.getFleetOfCompetitor(competitor).getOrdering()) != 0) {
int numberOfCompetitorsInBetterFleets = getNumberOfCompetitorsInBetterFleets(raceColumn, competitorFleetOrdering);
effectiveRank = rank + numberOfCompetitorsInBetterFleets;
} else {
effectiveRank = rank;
}
result = effectiveRank == 0 ? null : (double) effectiveRank;
return result;
}
@Override
public Double getPenaltyScore(RaceColumn raceColumn, Competitor competitor, MaxPointsReason maxPointsReason,
Integer numberOfCompetitorsInRace, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
Double result;
if (numberOfCompetitorsInRace == null) {
if (numberOfCompetitorsInRace == null || raceColumn.hasSplitFleetContiguousScoring()) {
result = (double) (numberOfCompetitorsInLeaderboardFetcher.getNumberOfCompetitorsInLeaderboard()+1);
} else {
result = (double) (numberOfCompetitorsInRace+1);
@@ -5,6 +5,7 @@ import java.util.concurrent.Callable;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
/**
* As compared to the regular low-point scheme, the 2013 ISAF World Cup will score the winner of a race with
@@ -30,7 +31,7 @@ public class LowPointWinnerGetsZero extends LowPoint {
private static final long serialVersionUID = 3405249102284162690L;
@Override
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRacefetcher) {
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRacefetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
return rank == 0 ? null : rank == 1 ? 0.0 : (double) rank;
}
@@ -365,7 +365,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
public Integer call() {
return getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher);
}
});
}, numberOfCompetitorsInLeaderboardFetcher);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -437,7 +437,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
public Integer call() {
return getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher);
}
});
}, numberOfCompetitorsInLeaderboardFetcher);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -131,6 +131,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
getRaceColumnListeners().notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward);
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
getRaceColumnListeners().notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn);
@@ -91,6 +91,12 @@ public class RaceColumnListeners implements Serializable {
}
}
public void notifyListenersAboutHasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.hasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring);
}
}
public void notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.isFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward);
@@ -151,4 +157,5 @@ public class RaceColumnListeners implements Serializable {
listener.raceLogEventAdded(raceColumn, raceLogIdentifier, event);
}
}
}
@@ -123,6 +123,13 @@ public class RegattaDetailsComposite extends Composite {
}
};
TextColumn<SeriesDTO> hasSplitFleetContiguousScoringColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
return series.hasSplitFleetContiguousScoring() ? stringMessages.yes() : stringMessages.no();
}
};
TextColumn<SeriesDTO> isFirstColumnIsNonDiscardableCarryForwardColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
@@ -213,6 +220,7 @@ public class RegattaDetailsComposite extends Composite {
table.addColumn(discardsColumn, stringMessages.discarding());
table.addColumn(isFirstColumnIsNonDiscardableCarryForwardColumn, stringMessages.firstRaceIsNonDiscardableCarryForward());
table.addColumn(startsWithZeroScoreColumn, stringMessages.startsWithZeroScore());
table.addColumn(hasSplitFleetContiguousScoringColumn, stringMessages.hasSplitFleetContiguousScoring());
table.addColumn(seriesActionColumn, stringMessages.actions());
return table;
@@ -248,6 +256,7 @@ public class RegattaDetailsComposite extends Composite {
final boolean isMedalChanged = series.isMedal() != seriesDescriptor.isMedal();
final boolean isStartsWithZeroScoreChanged = series.isStartsWithZeroScore() != seriesDescriptor.isStartsWithZeroScore();
final boolean isFirstColumnIsNonDiscardableCarryForwardChanged = series.isFirstColumnIsNonDiscardableCarryForward() != seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward();
final boolean hasSplitFleetContiguousScoringChanged = series.hasSplitFleetContiguousScoring() != seriesDescriptor.hasSplitFleetContiguousScoring();
final boolean seriesResultDiscardingThresholdsChanged = !Arrays.equals(series.getDiscardThresholds(),
seriesDescriptor.getResultDiscardingThresholds());
final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.getName());
@@ -294,10 +303,11 @@ public class RegattaDetailsComposite extends Composite {
regattaRefresher.fillRegattas();
}
});
if (isMedalChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged || isFirstColumnIsNonDiscardableCarryForwardChanged) {
if (isMedalChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged || isFirstColumnIsNonDiscardableCarryForwardChanged
|| hasSplitFleetContiguousScoringChanged) {
sailingService.updateSeries(regattaIdentifier, series.getName(), seriesDescriptor.isMedal(),
seriesDescriptor.getResultDiscardingThresholds(), seriesDescriptor.isStartsWithZeroScore(),
seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(),
seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(), seriesDescriptor.hasSplitFleetContiguousScoring(),
new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
@@ -160,7 +160,8 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
for (SeriesDTO seriesDTO : newRegatta.series) {
SeriesCreationParametersDTO seriesPair = new SeriesCreationParametersDTO(seriesDTO.getFleets(),
seriesDTO.isMedal(), seriesDTO.isStartsWithZeroScore(),
seriesDTO.isFirstColumnIsNonDiscardableCarryForward(), seriesDTO.getDiscardThresholds());
seriesDTO.isFirstColumnIsNonDiscardableCarryForward(), seriesDTO.getDiscardThresholds(),
seriesDTO.hasSplitFleetContiguousScoring());
seriesStructure.put(seriesDTO.getName(), seriesPair);
}
sailingService.createRegatta(newRegatta.getName(), newRegatta.boatClass==null?null:newRegatta.boatClass.getName(),
@@ -17,14 +17,18 @@ public class SeriesDescriptor {
private final List<RaceColumnDTO> races;
private final boolean isMedal;
private final boolean startsWithZeroScore;
private final boolean hasSplitFleetContiguousScoring;
private final boolean firstColumnIsNonDiscardableCarryForward;
private final int[] resultDiscardingThresholds;
public SeriesDescriptor(SeriesDTO series, List<RaceColumnDTO> races, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward) {
public SeriesDescriptor(SeriesDTO series, List<RaceColumnDTO> races, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring) {
this.series = series;
this.races = races;
this.isMedal = isMedal;
this.startsWithZeroScore = startsWithZeroScore;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
this.resultDiscardingThresholds = resultDiscardingThresholds;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
}
@@ -48,6 +52,10 @@ public class SeriesDescriptor {
public boolean isStartsWithZeroScore() {
return startsWithZeroScore;
}
public boolean hasSplitFleetContiguousScoring() {
return hasSplitFleetContiguousScoring;
}
public boolean isFirstColumnIsNonDiscardableCarryForward() {
return firstColumnIsNonDiscardableCarryForward;
@@ -39,6 +39,7 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
private CheckBox isMedalCheckbox;
private CheckBox startWithZeroScoreCheckbox;
private CheckBox hasSplitFleetContiguousScoringCheckbox;
private CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox;
private CheckBox useSeriesResultDiscardingThresholdsCheckbox;
private final StringMessages stringMessages;
@@ -133,7 +134,7 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
return new SeriesDescriptor(selectedSeries, races, isMedalCheckbox.getValue(),
useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds()
: null, startWithZeroScoreCheckbox.getValue(),
firstColumnIsNonDiscardableCarryForwardCheckbox.getValue());
firstColumnIsNonDiscardableCarryForwardCheckbox.getValue(), hasSplitFleetContiguousScoringCheckbox.getValue());
}
private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) {
@@ -167,6 +168,9 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
startWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
startWithZeroScoreCheckbox.setValue(selectedSeries.isStartsWithZeroScore());
additionalWidgetPanel.add(startWithZeroScoreCheckbox);
hasSplitFleetContiguousScoringCheckbox = createCheckbox(stringMessages.hasSplitFleetContiguousScoring());
hasSplitFleetContiguousScoringCheckbox.setValue(selectedSeries.hasSplitFleetContiguousScoring());
additionalWidgetPanel.add(hasSplitFleetContiguousScoringCheckbox);
firstColumnIsNonDiscardableCarryForwardCheckbox = createCheckbox(stringMessages.firstRaceIsNonDiscardableCarryForward());
firstColumnIsNonDiscardableCarryForwardCheckbox.setValue(selectedSeries.isFirstColumnIsNonDiscardableCarryForward());
additionalWidgetPanel.add(firstColumnIsNonDiscardableCarryForwardCheckbox);
@@ -32,6 +32,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
private TextBox nameEntryField;
private CheckBox isMedalSeriesCheckbox;
private CheckBox startsWithZeroScoreCheckbox;
private CheckBox hasSplitFleetContiguousScoringCheckbox;
private CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox;
private CheckBox useSeriesResultDiscardingThresholdsCheckbox;
private DiscardThresholdBoxes discardThresholdBoxes;
@@ -105,6 +106,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
nameEntryField.setVisibleLength(40);
isMedalSeriesCheckbox = createCheckbox(stringMessages.medalSeries());
startsWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
hasSplitFleetContiguousScoringCheckbox = createCheckbox(stringMessages.hasSplitFleetContiguousScoring());
firstColumnIsNonDiscardableCarryForwardCheckbox = createCheckbox(stringMessages.firstRaceIsNonDiscardableCarryForward());
useSeriesResultDiscardingThresholdsCheckbox = createCheckbox(stringMessages.seriesDefinesResultDiscardingRule());
useSeriesResultDiscardingThresholdsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@@ -117,7 +119,6 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
discardThresholdBoxes.getWidget().setVisible(false);
fleetListComposite = new FleetListEditorComposite(Arrays.asList(new FleetDTO("Default", 0, null)), stringMessages, resources.removeIcon());
fleetListComposite.addValueChangeHandler(new ValueChangeHandler<List<FleetDTO>>() {
@Override
public void onValueChange(ValueChangeEvent<List<FleetDTO>> event) {
validate();
@@ -130,6 +131,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
series.setName(nameEntryField.getText());
series.setMedal(isMedalSeriesCheckbox.getValue());
series.setStartsWithZeroScore(startsWithZeroScoreCheckbox.getValue());
series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoringCheckbox.getValue());
series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForwardCheckbox.getValue());
series.setFleets(fleetListComposite.getValue());
series.setDiscardThresholds(useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds() : null);
@@ -143,15 +145,16 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(6, 2);
Grid formGrid = new Grid(7, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 1, isMedalSeriesCheckbox);
formGrid.setWidget(2, 1, startsWithZeroScoreCheckbox);
formGrid.setWidget(3, 1, firstColumnIsNonDiscardableCarryForwardCheckbox);
formGrid.setWidget(4, 1, useSeriesResultDiscardingThresholdsCheckbox);
formGrid.setWidget(5, 1, discardThresholdBoxes.getWidget());
formGrid.setWidget(3, 1, hasSplitFleetContiguousScoringCheckbox);
formGrid.setWidget(4, 1, firstColumnIsNonDiscardableCarryForwardCheckbox);
formGrid.setWidget(5, 1, useSeriesResultDiscardingThresholdsCheckbox);
formGrid.setWidget(6, 1, discardThresholdBoxes.getWidget());
TabPanel tabPanel = new TabPanel();
tabPanel.setWidth("100%");
@@ -169,7 +169,8 @@ public interface SailingService extends RemoteService {
List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean firstRaceIsNonDiscardableCarryForward);
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore);
RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
@@ -368,8 +368,9 @@ public interface SailingServiceAsync {
void addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<List<RaceColumnInSeriesDTO>> callback);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal, int[] resultDiscardingThresholds,
boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward, AsyncCallback<Void> callback);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, AsyncCallback<Void> callback);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<Void> callback);
@@ -957,4 +957,5 @@ public interface StringMessages extends Messages {
String groupingProvider();
String use();
String releaseNotes();
String hasSplitFleetContiguousScoring();
}
@@ -946,3 +946,4 @@ toCalculateThe=to calculate the
use=Use
groupingProvider=Grouping Provider
releaseNotes=News and Release History
hasSplitFleetContiguousScoring=Split fleets scored contiguously
@@ -942,3 +942,4 @@ toCalculateThe=zur Berechnung von
use=Verwende
groupingProvider=Gruppierungs-Versorger
releaseNotes=Neuigkeiten und Änderungshistorie
hasSplitFleetContiguousScoring=Gruppen durchgängig bepunkten
@@ -703,7 +703,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
SeriesDTO result = new SeriesDTO(series.getName(), fleets, raceColumns, series.isMedal(),
series.getResultDiscardingRule() == null ? null : series.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(),
series.isStartsWithZeroScore(), series.isFirstColumnIsNonDiscardableCarryForward());
series.isStartsWithZeroScore(), series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring());
return result;
}
@@ -2942,8 +2942,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward) {
getService().apply(new UpdateSeries(regattaIdentifier, seriesName, isMedal, resultDiscardingThresholds, startsWithZeroScore, firstColumnIsNonDiscardableCarryForward));
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring) {
getService().apply(
new UpdateSeries(regattaIdentifier, seriesName, isMedal, resultDiscardingThresholds,
startsWithZeroScore, firstColumnIsNonDiscardableCarryForward, hasSplitFleetContiguousScoring));
}
@Override
@@ -14,19 +14,28 @@ public class SeriesDTO extends NamedDTO {
private int[] discardThresholds;
private Boolean startsWithZeroScore;
private boolean firstColumnIsNonDiscardableCarryForward;
private boolean hasSplitFleetContiguousScoring;
public SeriesDTO() {}
public SeriesDTO(String name, List<FleetDTO> fleets, List<RaceColumnDTO> raceColumns, boolean isMedal, int[] discardThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward) {
public SeriesDTO(String name, List<FleetDTO> fleets, List<RaceColumnDTO> raceColumns, boolean isMedal,
int[] discardThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward,
boolean hasSplitFleetContiguousScoring) {
super(name);
this.fleets = fleets;
this.raceColumns = raceColumns;
this.isMedal = isMedal;
this.startsWithZeroScore = startsWithZeroScore;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
this.discardThresholds = discardThresholds;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
}
public boolean hasSplitFleetContiguousScoring() {
return hasSplitFleetContiguousScoring;
}
public boolean isMedal() {
return isMedal;
}
@@ -83,4 +92,8 @@ public class SeriesDTO extends NamedDTO {
public boolean isFirstColumnIsNonDiscardableCarryForward() {
return firstColumnIsNonDiscardableCarryForward;
}
public void setSplitFleetContiguousScoring(Boolean hasSplitFleetContiguousScoring) {
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
}
}
@@ -16,6 +16,7 @@ public class SeriesJsonSerializer implements JsonSerializer<Series> {
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_STARTS_WITH_ZERO_SCORE = "startsWithZeroScore";
public static final String FIELD_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING = "hasSplitFleetContiguousScoring";
public static final String FIELD_IS_MEDAL_SERIES = "isMedalSeries";
private final JsonSerializer<Fleet> fleetSerializer;
@@ -30,6 +31,7 @@ public class SeriesJsonSerializer implements JsonSerializer<Series> {
result.put(FIELD_NAME, series.getName());
result.put(FIELD_IS_MEDAL_SERIES, series.isMedal());
result.put(FIELD_STARTS_WITH_ZERO_SCORE, series.isStartsWithZeroScore());
result.put(FIELD_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING, series.hasSplitFleetContiguousScoring());
JSONArray fleetsJson = new JSONArray();
for (Fleet fleet : series.getFleets()) {
@@ -37,7 +37,7 @@
</tr>
<tr>
<td>Example:</td>
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/regatta/Kieler Woche (5o5)">http://www.sapsailing.com/sailingserver/api/v1/regatta/Kieler Woche (5o5)</a></td>
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/regattas/Kieler Woche (5o5)">http://www.sapsailing.com/sailingserver/api/v1/regatta/Kieler Woche (5o5)</a></td>
</tr>
</table>
<div style="height: 1em;"></div>
@@ -76,6 +76,10 @@ public class RaceLogReplicator implements RaceColumnListener {
public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) {
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
@@ -55,6 +55,10 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override
public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) {
}
@Override
public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) {
}
@@ -184,7 +188,7 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
public Integer call() {
return numberOfCompetitorsInRace;
}
});
}, leaderboard.getNumberOfCompetitorsInLeaderboardFetcher());
// Do ALWAYS apply score corrections from race committee
applyScoreCorrectionOperation(leaderboard, raceColumn, competitor, scoreByRaceCommittee, timePoint);
@@ -936,7 +936,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
s.getName(),
new SeriesCreationParametersDTO(fleetNamesAndOrdering, s.isMedal(), s.isStartsWithZeroScore(), s
.isFirstColumnIsNonDiscardableCarryForward(), s.getResultDiscardingRule() == null ? null
: s.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()));
: s.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), s.hasSplitFleetContiguousScoring()));
}
return new RegattaCreationParametersDTO(result);
}
@@ -53,6 +53,7 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation {
s.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(e.getValue().getDiscardingThresholds()));
}
s.setStartsWithZeroScore(e.getValue().isStartsWithZero());
s.setSplitFleetContiguousScoring(e.getValue().hasSplitFleetContiguousScoring());
s.setFirstColumnIsNonDiscardableCarryForward(e.getValue().isFirstColumnIsNonDiscardableCarryForward());
result.add(s);
}
@@ -13,13 +13,17 @@ public class UpdateSeries extends AbstractSeriesOperation<Void> {
private final int[] resultDiscardingThresholds;
private final boolean startsWithZeroScore;
private final boolean firstColumnIsNonDiscardableCarryForward;
private final boolean hasSplitFleetContiguousScoring;
public UpdateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward) {
public UpdateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstColumnIsNonDiscardableCarryForward, boolean hasSplitFleetContiguousScoring) {
super(regattaIdentifier, seriesName);
this.isMedal = isMedal;
this.resultDiscardingThresholds = resultDiscardingThresholds;
this.startsWithZeroScore = startsWithZeroScore;
this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward;
this.hasSplitFleetContiguousScoring = hasSplitFleetContiguousScoring;
}
@Override
@@ -30,6 +34,7 @@ public class UpdateSeries extends AbstractSeriesOperation<Void> {
null : new ThresholdBasedResultDiscardingRuleImpl(resultDiscardingThresholds));
series.setStartsWithZeroScore(startsWithZeroScore);
series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForward);
series.setSplitFleetContiguousScoring(hasSplitFleetContiguousScoring);
if (series.getRegatta().isPersistent()) {
toState.updateStoredRegatta(series.getRegatta());
}
+1 -1
View File
@@ -24,7 +24,7 @@
<div id='keyVisual'>
<img alt='Sailer' src='images/key-visual.jpg' />
<div id='infoBox'>
<a href="release_notes_analyze.html">Release History</a>
<a href="release_notes_analyze.html">New Features</a>
<h1 id='pageHeadline'>SAP Sailing Analytics</h1>
<p>Understanding what happens out on the race course isn't always easy. To help solve this challenge and bring the excitement of sailing to the fans, we have developed the SAP sailing analytics.</p>
+8 -4
View File
@@ -105,7 +105,7 @@ a {
/* line 47, site.css.sass */
#yield_content #pageHeadline {
color: white;
margin: 0 0 10px 0;
margin: 10px 0 10px 0;
font-size: 18px; }
/* line 53, site.css.sass */
#yield_content #keyVisual {
@@ -115,7 +115,7 @@ a {
/* line 58, site.css.sass */
#yield_content #keyVisual #infoBox {
width: 502px;
height: 80px;
height: 100px;
background: #999999;
position: absolute;
right: 0;
@@ -126,9 +126,13 @@ a {
color: white;
font-size: 12px; }
#yield_content #keyVisual #infoBox a {
font-size: 12px;
text-decoration: none;
float: right; }
float: right;
background: #fcb913;
color: white;
font-size: 18px;
font-family: OpenSansRegular, Arial, Verdana, sans-serif;
padding: 6px 12px 6px 12px; }
/* line 72, site.css.sass */
#yield_content #mainContent {
@@ -9,6 +9,7 @@ body{
margin: 0 0 10px 0;
border-bottom: 2px solid #222222;
}
#yield_content #mainContent .articleHeadline {
border-bottom: none;
}
@@ -15,7 +15,7 @@
<img alt="SAP Website" src="images/sap-logo.png" />
</a>
<ul class="headNavigation">
<li><a href=""></a></li>
<li><a href="http://www.sapsailing.com/">sapsailing.com</a></li>
</ul>
</div>
<div id="contentWrapper">
@@ -15,7 +15,7 @@
<img alt="SAP Website" src="images/sap-logo.png" />
</a>
<ul class="headNavigation">
<li><a href="http://www.sapsailing.com/gwt/AdminConsole.html">Admin Console</a></li>
<li><a href="http://www.sapsailing.com/">sapsailing.com</a></li>
</ul>
</div>
<div id="contentWrapper">
@@ -15,7 +15,7 @@
<img alt="SAP Website" src="images/sap-logo.png" />
</a>
<ul class="headNavigation">
<li><a href="http://www.sapsailing.com/">HOME</a></li>
<li><a href="http://www.sapsailing.com/">sapsailing.com</a></li>
</ul>
</div>
<div id="contentWrapper">