Merge branch 'racecommittee_app' of ssh://sapsailing.com/home/trac/git into racecommittee_app

This commit is contained in:
Fabian Tschirschnitz committed 2013-06-09 13:44:14 +02:00
commit c438711f3a
96 files changed
+1477 -570

No files matched your search

+18
View File
@@ -351,6 +351,7 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/
cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/start $ACDIR/
cp -v $PROJECT_HOME/java/target/stop $ACDIR/
cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/
@@ -368,6 +369,23 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
cp -v $PROJECT_HOME/configuration/buildAndUpdateProduct.sh $ACDIR/
echo $VERSION_INFO > $ACDIR/configuration/jetty/version.txt
# make sure to save the information from env.sh
. $ACDIR/env.sh
sed -i "s/mongo.port=.*$/mongo.port=$MONGODB_PORT/g" $ACDIR/configuration/config.ini
sed -i "s/expedition.udp.port=.*$/expedition.udp.port=$EXPEDITION_PORT/g" $ACDIR/configuration/config.ini
sed -i "s/replication.exchangeName=.*$/replication.exchangeName=$REPLICATION_CHANNEL/g" $ACDIR/configuration/config.ini
echo "I have updated the configuration with the following data:"
echo "SERVER_NAME: $SERVER_NAME"
echo "SERVER_PORT: $SERVER_PORT"
echo "MEMORY: $MEMORY"
echo "TELNET_PORT: $TELNET_PORT"
echo "MONGODB_PORT: $MONGODB_PORT"
echo "EXPEDITION_PORT: $EXPEDITION_PORT"
echo "REPLICATION_CHANNEL: $REPLICATION_CHANNEL"
echo ""
echo "Installation complete. You may now start the server using ./start"
fi
+22 -7
View File
@@ -1,13 +1,28 @@
import socket
import socket, sys
UDP_IP = "195.227.44.85"
if len(sys.argv) <= 2:
print "Please provide ip and mode (TCP, UDP)"
sys.exit(1)
UDP_IP = sys.argv[1]
UDP_PORT = 2013
MODE = sys.argv[2]
MESSAGE = "Hello, World!"
print "UDP target IP:", UDP_IP
print "UDP target port:", UDP_PORT
print "message:", MESSAGE
print "Target IP:", UDP_IP
print "Target port:", UDP_PORT
print "Message:", MESSAGE
print "Mode: ", MODE
sock = socket.socket(socket.AF_INET, # Internet
if MODE == 'UDP':
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
else:
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_STREAM) # TCP
sock.connect((UDP_IP, UDP_PORT))
sock.send(MESSAGE)
data = sock.recv(1024)
sock.close()
+29 -7
View File
@@ -1,13 +1,35 @@
import socket
import sys
UDP_IP = "195.227.44.85"
if len(sys.argv) <= 2:
print "Please provide an IP address and mode"
sys.exit(1)
UDP_IP = sys.argv[1]
MODE = sys.argv[2]
UDP_PORT = 2013
sock = socket.socket(socket.AF_INET, # Internet
print "Listening on %s port %s (%s)" % (UDP_IP, UDP_PORT, MODE)
if MODE == 'UDP':
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print "received message:", data
else:
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_STREAM) # TCP
sock.bind((UDP_IP, UDP_PORT))
sock.listen(1)
conn, addr = sock.accept()
while True:
data = conn.recv(1024)
if not data:
break
conn.send(data) # echo
print "received message:", data
conn.close()
print "Listening on 195.227.44.85 port %s" % UDP_PORT
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print "received message:", data
@@ -87,20 +87,6 @@ public abstract class AbstractLeaderboardDTO implements Serializable {
return false;
}
public Double getTotalPoints(LeaderboardRowDTO object) {
Double totalPoints = object.carriedPoints == null ? null : object.carriedPoints;
for (Map.Entry<String, LeaderboardEntryDTO> e : object.fieldsByRaceColumnName.entrySet()) {
if (e.getValue().totalPoints != null && getOrCreateRaceColumn(e.getKey()).isValidInTotalScore()) {
if (totalPoints == null) {
totalPoints = e.getValue().totalPoints;
} else {
totalPoints += e.getValue().totalPoints;
}
}
}
return totalPoints;
}
public Double getNetPoints(CompetitorDTO competitor, String nameOfLastRaceSoFar) {
Double netPoints = null;
LeaderboardRowDTO row = rows.get(competitor);
@@ -15,6 +15,7 @@ public class LeaderboardRowDTO implements Serializable {
public CompetitorDTO competitor;
public Map<String, LeaderboardEntryDTO> fieldsByRaceColumnName;
public Double carriedPoints;
public Double totalPoints;
public Double totalTimeSailedInSeconds;
public Double totalTimeSailedDownwindInSeconds;
public Double maximumSpeedOverGroundInKnots;
@@ -30,6 +31,22 @@ public class LeaderboardRowDTO implements Serializable {
result = prime * result + ((carriedPoints == null) ? 0 : carriedPoints.hashCode());
result = prime * result + ((competitor == null) ? 0 : competitor.hashCode());
result = prime * result + ((fieldsByRaceColumnName == null) ? 0 : fieldsByRaceColumnName.hashCode());
result = prime * result
+ ((maximumSpeedOverGroundInKnots == null) ? 0 : maximumSpeedOverGroundInKnots.hashCode());
result = prime * result
+ ((totalDistanceTraveledInMeters == null) ? 0 : totalDistanceTraveledInMeters.hashCode());
result = prime * result + ((totalPoints == null) ? 0 : totalPoints.hashCode());
result = prime * result
+ ((totalTimeSailedDownwindInSeconds == null) ? 0 : totalTimeSailedDownwindInSeconds.hashCode());
result = prime * result + ((totalTimeSailedInSeconds == null) ? 0 : totalTimeSailedInSeconds.hashCode());
result = prime * result
+ ((totalTimeSailedReachingInSeconds == null) ? 0 : totalTimeSailedReachingInSeconds.hashCode());
result = prime * result
+ ((totalTimeSailedUpwindInSeconds == null) ? 0 : totalTimeSailedUpwindInSeconds.hashCode());
result = prime
* result
+ ((whenMaximumSpeedOverGroundWasAchieved == null) ? 0 : whenMaximumSpeedOverGroundWasAchieved
.hashCode());
return result;
}
@@ -57,6 +74,46 @@ public class LeaderboardRowDTO implements Serializable {
return false;
} else if (!fieldsByRaceColumnName.equals(other.fieldsByRaceColumnName))
return false;
if (maximumSpeedOverGroundInKnots == null) {
if (other.maximumSpeedOverGroundInKnots != null)
return false;
} else if (!maximumSpeedOverGroundInKnots.equals(other.maximumSpeedOverGroundInKnots))
return false;
if (totalDistanceTraveledInMeters == null) {
if (other.totalDistanceTraveledInMeters != null)
return false;
} else if (!totalDistanceTraveledInMeters.equals(other.totalDistanceTraveledInMeters))
return false;
if (totalPoints == null) {
if (other.totalPoints != null)
return false;
} else if (!totalPoints.equals(other.totalPoints))
return false;
if (totalTimeSailedDownwindInSeconds == null) {
if (other.totalTimeSailedDownwindInSeconds != null)
return false;
} else if (!totalTimeSailedDownwindInSeconds.equals(other.totalTimeSailedDownwindInSeconds))
return false;
if (totalTimeSailedInSeconds == null) {
if (other.totalTimeSailedInSeconds != null)
return false;
} else if (!totalTimeSailedInSeconds.equals(other.totalTimeSailedInSeconds))
return false;
if (totalTimeSailedReachingInSeconds == null) {
if (other.totalTimeSailedReachingInSeconds != null)
return false;
} else if (!totalTimeSailedReachingInSeconds.equals(other.totalTimeSailedReachingInSeconds))
return false;
if (totalTimeSailedUpwindInSeconds == null) {
if (other.totalTimeSailedUpwindInSeconds != null)
return false;
} else if (!totalTimeSailedUpwindInSeconds.equals(other.totalTimeSailedUpwindInSeconds))
return false;
if (whenMaximumSpeedOverGroundWasAchieved == null) {
if (other.whenMaximumSpeedOverGroundWasAchieved != null)
return false;
} else if (!whenMaximumSpeedOverGroundWasAchieved.equals(other.whenMaximumSpeedOverGroundWasAchieved))
return false;
return true;
}
}
@@ -16,7 +16,7 @@ import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.leaderboard.DelayedLeaderboardCorrections;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -375,6 +375,10 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
@@ -401,7 +405,7 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
}
@Override
@@ -83,7 +83,7 @@ import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
@@ -238,7 +238,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
if (result == null) {
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard);
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS);
String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name());
if (groupForMetaLeaderboard != null) {
result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard, loadScoringScheme(dbLeaderboard), resultDiscardingRule);
@@ -314,17 +314,16 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
}
/**
* @param dbLeaderboard expects to find a field named {@link FieldNames#LEADERBOARD_DISCARDING_THRESHOLDS}
* @param dbObject expects to find a field identified by <code>field</code> which holds a {@link BasicDBList}
*/
private ThresholdBasedResultDiscardingRule loadResultDiscardingRule(DBObject dbLeaderboard) {
BasicDBList dbDiscardIndexResultsStartingWithHowManyRaces = (BasicDBList) dbLeaderboard
.get(FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS.name());
private ThresholdBasedResultDiscardingRule loadResultDiscardingRule(DBObject dbObject, FieldNames field) {
BasicDBList dbDiscardIndexResultsStartingWithHowManyRaces = (BasicDBList) dbObject.get(field.name());
int[] discardIndexResultsStartingWithHowManyRaces = new int[dbDiscardIndexResultsStartingWithHowManyRaces.size()];
int i = 0;
for (Object discardingThresholdAsObject : dbDiscardIndexResultsStartingWithHowManyRaces) {
discardIndexResultsStartingWithHowManyRaces[i++] = (Integer) discardingThresholdAsObject;
}
ThresholdBasedResultDiscardingRule resultDiscardingRule = new ResultDiscardingRuleImpl(
ThresholdBasedResultDiscardingRule resultDiscardingRule = new ThresholdBasedResultDiscardingRuleImpl(
discardIndexResultsStartingWithHowManyRaces);
return resultDiscardingRule;
}
@@ -907,16 +906,19 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private Series loadSeries(DBObject dbSeries, TrackedRegattaRegistry trackedRegattaRegistry) {
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());
final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name());
Map<String, Fleet> fleetsByName = loadFleets(dbFleets);
BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name());
Iterable<String> raceColumnNames = loadRaceColumnNames(dbRaceColumns, fleetsByName);
Series series = new SeriesImpl(
name,
isMedal,
fleetsByName.values(),
raceColumnNames,
trackedRegattaRegistry);
Series series = new SeriesImpl(name, isMedal, fleetsByName.values(), raceColumnNames, trackedRegattaRegistry);
if (dbSeries.get(FieldNames.SERIES_DISCARDING_THRESHOLDS.name()) != null) {
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, FieldNames.SERIES_DISCARDING_THRESHOLDS);
series.setResultDiscardingRule(resultDiscardingRule);
}
if (startsWithZeroScore != null) {
series.setStartsWithZeroScore(startsWithZeroScore);
}
loadRaceColumnRaceLinks(dbRaceColumns, series);
return series;
}
@@ -997,7 +999,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
@Override
public RaceLog loadRaceLog(RaceLogIdentifier identifier) {
RaceLog result = new RaceLogImpl(RaceLogImpl.class.getSimpleName());
RaceLog result = new RaceLogImpl(RaceLogImpl.class.getSimpleName(), identifier.getIdentifier());
try {
BasicDBObject query = new BasicDBObject();
query.put(FieldNames.RACE_LOG_IDENTIFIER.name(), MongoUtils.escapeDollarAndDot(identifier.getIdentifier().toString()));
@@ -20,8 +20,10 @@ public enum FieldNames {
REGATTA_SERIES,
SERIES_NAME,
SERIES_IS_MEDAL,
SERIES_STARTS_WITH_ZERO_SCORE,
SERIES_FLEETS,
SERIES_RACE_COLUMNS,
SERIES_DISCARDING_THRESHOLDS,
FLEET_NAME,
FLEET_ORDERING,
FLEET_COLOR,
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
@@ -237,8 +238,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
BasicDBObject dbScoreCorrections = new BasicDBObject();
storeScoreCorrections(leaderboard, dbScoreCorrections);
dbLeaderboard.put(FieldNames.LEADERBOARD_SCORE_CORRECTIONS.name(), dbScoreCorrections);
final ThresholdBasedResultDiscardingRule resultDiscardingRule = leaderboard.getResultDiscardingRule();
storeResultDiscardingRule(dbLeaderboard, resultDiscardingRule);
final ResultDiscardingRule resultDiscardingRule = leaderboard.getResultDiscardingRule();
storeResultDiscardingRule(dbLeaderboard, resultDiscardingRule, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS);
BasicDBList competitorDisplayNames = new BasicDBList();
for (Competitor competitor : leaderboard.getCompetitors()) {
String displayNameForCompetitor = leaderboard.getDisplayName(competitor);
@@ -252,13 +253,20 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbLeaderboard.put(FieldNames.LEADERBOARD_COMPETITOR_DISPLAY_NAMES.name(), competitorDisplayNames);
}
private void storeResultDiscardingRule(BasicDBObject dbLeaderboard,
final ThresholdBasedResultDiscardingRule resultDiscardingRule) {
BasicDBList dbResultDiscardingThresholds = new BasicDBList();
for (int threshold : resultDiscardingRule.getDiscardIndexResultsStartingWithHowManyRaces()) {
dbResultDiscardingThresholds.add(threshold);
/**
* Stores the result discarding rule to <code>dbObject</code>'s field identified by <code>field</code> if the result discarding
* rule is not <code>null</code> and is of type {@link ThresholdBasedResultDiscardingRule}. Otherwise, it is assumed that the
* result discarding rule is otherwise implicitly obtained, e.g., from a definition of a regatta with its series, stored elsewhere.
*/
private void storeResultDiscardingRule(DBObject dbObject,
final ResultDiscardingRule resultDiscardingRule, FieldNames field) {
if (resultDiscardingRule != null && resultDiscardingRule instanceof ThresholdBasedResultDiscardingRule) {
BasicDBList dbResultDiscardingThresholds = new BasicDBList();
for (int threshold : ((ThresholdBasedResultDiscardingRule) resultDiscardingRule).getDiscardIndexResultsStartingWithHowManyRaces()) {
dbResultDiscardingThresholds.add(threshold);
}
dbObject.put(field.name(), dbResultDiscardingThresholds);
}
dbLeaderboard.put(FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS.name(), dbResultDiscardingThresholds);
}
private BasicDBObject storeRaceColumn(RaceColumn raceColumn) {
@@ -468,6 +476,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_STARTS_WITH_ZERO_SCORE.name(), s.isStartsWithZeroScore());
BasicDBList dbFleets = new BasicDBList();
for (Fleet fleet : s.getFleets()) {
dbFleets.add(storeFleet(fleet));
@@ -478,6 +487,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbRaceColumns.add(storeRaceColumn(raceColumn));
}
dbSeries.put(FieldNames.SERIES_RACE_COLUMNS.name(), dbRaceColumns);
if (s.getResultDiscardingRule() != null) {
storeResultDiscardingRule(dbSeries, s.getResultDiscardingRule(), FieldNames.SERIES_DISCARDING_THRESHOLDS);
}
return dbSeries;
}
@@ -17,7 +17,7 @@ public class RaceLogAnalyzerTest {
@Test
public void testLastFlagFinder() {
RaceLog raceLog = new RaceLogImpl("RaceLogTest");
RaceLog raceLog = new RaceLogImpl("RaceLogTest", "test-identifier");
TimePoint t1 = MillisecondsTimePoint.now();
int passId = 0;
boolean isDisplayed = true;
@@ -28,7 +28,7 @@ public class RaceLogTest {
@Before
public void setUp() {
raceLog = new RaceLogImpl("testlock");
raceLog = new RaceLogImpl("testlock", "test-identifier");
}
@Test(expected = IllegalStateException.class)
@@ -43,6 +43,10 @@ public class RaceLogTest {
raceLog.unlockAfterRead();
}
public void testIdentifier() {
assertEquals("test-identifier", raceLog.getId());
}
@Test
public void testAddEvent() {
RaceLogEvent event = mock(RaceLogEvent.class);
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.racelog;
import com.sap.sailing.domain.common.WithID;
import com.sap.sailing.domain.racelog.impl.RaceLogEventComparator;
import com.sap.sailing.domain.tracking.Track;
@@ -15,7 +16,7 @@ import com.sap.sailing.domain.tracking.Track;
* Implementations should use the {@link RaceLogEventComparator} for sorting its content.
* </p>
*/
public interface RaceLog extends Track<RaceLogEvent> {
public interface RaceLog extends Track<RaceLogEvent>, WithID {
/**
* Gets the current pass id.
*
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.racelog.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.NavigableSet;
import java.util.Set;
@@ -24,6 +25,7 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
private static final String DefaultLockName = RaceLogImpl.class.getName() + ".lock";
private final static Logger logger = Logger.getLogger(RaceLogImpl.class.getName());
private final Serializable id;
private transient Set<RaceLogEventVisitor> listeners;
private int currentPassId;
@@ -32,19 +34,25 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
/**
* Initializes a new {@link RaceLogImpl} with the default lock name.
*/
public RaceLogImpl() {
this(DefaultLockName);
public RaceLogImpl(Serializable identifier) {
this(DefaultLockName, identifier);
}
/**
* Initializes a new {@link RaceLogImpl}.
* @param nameForReadWriteLock name of lock.
*/
public RaceLogImpl(String nameForReadWriteLock) {
public RaceLogImpl(String nameForReadWriteLock, Serializable identifier) {
super(new ArrayListNavigableSet<Timed>(RaceLogEventComparator.INSTANCE), nameForReadWriteLock);
this.listeners = new HashSet<RaceLogEventVisitor>();
this.currentPassId = DefaultPassId;
this.id = identifier;
}
@Override
public Serializable getId() {
return this.id;
}
@Override
@@ -132,4 +140,5 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
ois.defaultReadObject();
listeners = new HashSet<RaceLogEventVisitor>();
}
}
@@ -21,7 +21,7 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
@@ -37,7 +37,7 @@ public class LeaderboardForKielWeekTest extends OnlineTracTracBasedTest {
@Test
public void leaderboardWithOneRaceTest() throws URISyntaxException, NoWindException, IOException, InterruptedException {
leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
MillisecondsTimePoint now = MillisecondsTimePoint.now();
loadRace("357c700a-9d9a-11e0-85be-406186cbf87c"); // 505 Race 2
@@ -37,7 +37,7 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRankAndManyCompetitors;
@@ -100,7 +100,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
@Test
public void ensureMedalRaceParamIsIgnoredIfRaceColumnAlreadyExists() {
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 5, 8 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
final String columnName = "abc";
@@ -113,7 +113,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
@Test
public void testRepeatedLeaderboardDTOCacheInvalidation() throws NoWindException, InterruptedException, ExecutionException {
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 5, 8 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
final String columnName = "abc";
@@ -169,7 +169,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRaces.add(r); // hash set should take care of more or less randomly permuting the races
}
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 1 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
int i=0;
@@ -203,7 +203,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRace.addCompetitor(c2);
testRace.addCompetitor(c3); // this makes maxPoints==4
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 2 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet);
@@ -232,7 +232,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRace.addCompetitor(c2);
testRace.addCompetitor(c3); // this makes maxPoints==4
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 2 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
RaceColumn r1 = leaderboard.addRace(testRace, "R1", /* medalRace */ false, defaultFleet);
@@ -260,7 +260,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
testRaces.add(r); // hash set should take care of more or less randomly permuting the races
}
ScoreCorrectionImpl scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", scoreCorrection, new ThresholdBasedResultDiscardingRuleImpl(
new int[] { 1 }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
int i=0;
@@ -287,7 +287,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
protected void testLeaderboard(int numberOfStartedRaces, int numberOfNotStartedRaces, int firstDiscardingThreshold,
int secondDiscardingThreshold, Integer carry, boolean addOneMedalRace, int numberOfUntrackedRaces) throws NoWindException {
setupRaces(numberOfStartedRaces, numberOfNotStartedRaces);
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl("Test Leaderboard", new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
new int[] { firstDiscardingThreshold, secondDiscardingThreshold }), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
int i=0;
@@ -45,7 +45,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
@@ -60,7 +60,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
ScoreCorrectionImpl scoreCorrections = new ScoreCorrectionImpl();
ResultDiscardingRuleImpl discardingRules = new ResultDiscardingRuleImpl(discardingThresholds);
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
return new RegattaLeaderboardImpl(regatta, scoreCorrections, discardingRules);
}
@@ -801,7 +801,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
FlexibleLeaderboard leaderboard1 = new FlexibleLeaderboardImpl("Leaderboard 1", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f1)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null));
leaderboard1.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f2)), "R2", /* medalRace */ false,
@@ -809,7 +809,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertTrue(leaderboard1.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboard1.getTotalPoints(c[0], later), leaderboard1.getTotalPoints(c[3], later)) < 0); // c0 better than c3
FlexibleLeaderboard leaderboard2 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f3)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null));
leaderboard2.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f4)), "R2", /* medalRace */ false,
@@ -817,7 +817,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
assertTrue(leaderboard2.getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare(
leaderboard2.getTotalPoints(c[3], later), leaderboard2.getTotalPoints(c[0], later)) < 0); // c3 better than c0
FlexibleLeaderboard leaderboard3 = new FlexibleLeaderboardImpl("Leaderboard 3", new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(/* discarding thresholds */ new int[0]), new HighPoint(), null);
leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f5)), "R1", /* medalRace */ false,
leaderboard1.getFleet(null));
leaderboard3.addRace(new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(f6)), "R2", /* medalRace */ false,
@@ -827,7 +827,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl("Leaderboard Group", "Leaderboard Group", false, Arrays.asList(leaderboard1,
leaderboard2, leaderboard3));
leaderboardGroup.setOverallLeaderboard(new LeaderboardGroupMetaLeaderboard(leaderboardGroup, new HighPointExtremeSailingSeriesOverall(),
new ResultDiscardingRuleImpl(new int[0])));
new ThresholdBasedResultDiscardingRuleImpl(new int[0])));
leaderboardGroup.getOverallLeaderboard().setSuppressed(c[1], true);
leaderboardGroup.getOverallLeaderboard().setSuppressed(c[2], true);
List<Competitor> rankedCompetitors = leaderboardGroup.getOverallLeaderboard().getCompetitorsFromBestToWorst(later);
@@ -25,7 +25,7 @@ import com.sap.sailing.domain.base.impl.DomainFactoryImpl;
import com.sap.sailing.domain.base.impl.PersonImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
public class OfflineSerializationTest extends AbstractSerializationTest {
/**
@@ -81,8 +81,8 @@ public class OfflineSerializationTest extends AbstractSerializationTest {
@Test
public void testSerializingResultDiscardingRuleImpl() throws ClassNotFoundException, IOException {
DomainFactory receiverDomainFactory = new DomainFactoryImpl();
ResultDiscardingRuleImpl rdri = new ResultDiscardingRuleImpl(new int[] { 5, 8 });
ResultDiscardingRuleImpl clone = cloneBySerialization(rdri, receiverDomainFactory);
ThresholdBasedResultDiscardingRuleImpl rdri = new ThresholdBasedResultDiscardingRuleImpl(new int[] { 5, 8 });
ThresholdBasedResultDiscardingRuleImpl clone = cloneBySerialization(rdri, receiverDomainFactory);
assertTrue(Arrays.equals(rdri.getDiscardIndexResultsStartingWithHowManyRaces(),
clone.getDiscardIndexResultsStartingWithHowManyRaces()));
}
@@ -4,6 +4,8 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import java.io.Serializable;
import org.junit.Before;
import org.junit.Test;
@@ -27,7 +29,7 @@ public class RaceLogAttachOnTrackedRaceTest {
private RaceLog raceLog;
@Override
public void detachRaceLog() {
public void detachRaceLog(Serializable identifier) {
this.raceLog = null;
}
@@ -37,7 +39,7 @@ public class RaceLogAttachOnTrackedRaceTest {
}
@Override
public RaceLog getRaceLog() {
public RaceLog getRaceLog(Serializable identifier) {
return raceLog;
}
}
@@ -59,7 +61,7 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(defaultFleet, trackedRace);
assertSame(column.getRaceLog(defaultFleet), trackedRace.getRaceLog());
assertSame(column.getRaceLog(defaultFleet), trackedRace.getRaceLog(column.getRaceLogIdentifier(defaultFleet)));
}
@Test
@@ -72,7 +74,7 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(fleet, trackedRace);
column.setTrackedRace(fleet, null);
assertNull(trackedRace.getRaceLog());
assertNull(trackedRace.getRaceLog(column.getRaceLogIdentifier(fleet)));
}
@Test
@@ -86,8 +88,8 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(aFleet, trackedRace);
assertSame(column.getRaceLog(aFleet), trackedRace.getRaceLog());
assertNotSame(column.getRaceLog(bFleet), trackedRace.getRaceLog());
assertSame(column.getRaceLog(aFleet), trackedRace.getRaceLog(column.getRaceLogIdentifier(aFleet)));
assertNotSame(column.getRaceLog(bFleet), trackedRace.getRaceLog(column.getRaceIdentifier(bFleet)));
}
@Test
@@ -102,8 +104,8 @@ public class RaceLogAttachOnTrackedRaceTest {
column.setTrackedRace(fleet, firstRace);
column.setTrackedRace(fleet, secondRace);
assertNull(firstRace.getRaceLog());
assertSame(column.getRaceLog(fleet), secondRace.getRaceLog());
assertNull(firstRace.getRaceLog(column.getRaceLogIdentifier(fleet)));
assertSame(column.getRaceLog(fleet), secondRace.getRaceLog(column.getRaceLogIdentifier(fleet)));
}
}
@@ -29,7 +29,7 @@ public class RaceLogTest {
public void testAddingTwoEventsOfDifferentPassesButSameTimestamps() {
TimePoint t1 = MillisecondsTimePoint.now();
int passId = 0;
RaceLog raceLog = new RaceLogImpl("RaceLogTest");
RaceLog raceLog = new RaceLogImpl("RaceLogTest", "test-identifier");
RaceLogFlagEvent rcEvent1 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), passId, Flags.CLASS, Flags.NONE, false);
RaceLogFlagEvent rcEvent2 = RaceLogEventFactory.INSTANCE.createFlagEvent(t1, UUID.randomUUID(), new ArrayList<Competitor>(), ++passId, Flags.CLASS, Flags.NONE, false);
@@ -48,7 +48,7 @@ public class RaceLogTest {
@Test
public void testAddOfEvent() {
RaceLog rcEventTrack = new RaceLogImpl("RaceLogTest");
RaceLog rcEventTrack = new RaceLogImpl("RaceLogTest", "test-identifier");
TimePoint t1 = MillisecondsTimePoint.now();
int passId = 0;
boolean isDisplayed = true;
@@ -75,7 +75,7 @@ public class RaceLogTest {
@Test
public void testTrackAPIForEventLogUsage() {
RaceLog rcEventTrack = new RaceLogImpl("RaceLog");
RaceLog rcEventTrack = new RaceLogImpl("RaceLog", "test-identifier");
List<RaceLogEvent> expectedOrderingList = new ArrayList<RaceLogEvent>();
TimePoint t1 = MillisecondsTimePoint.now();
TimePoint t2 = new MillisecondsTimePoint(t1.asMillis() - 1000);
@@ -405,6 +405,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
public void setDefaultCourseArea(CourseArea newCourseArea) {
// TODO Auto-generated method stub
}
@Override
public boolean definesSeriesDiscardThresholds() {
// TODO Auto-generated method stub
return false;
}
};
}
@@ -741,7 +747,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public void detachRaceLog() {
public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
}
@@ -753,7 +759,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public RaceLog getRaceLog() {
public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
return null;
}
@@ -781,4 +787,22 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub
return null;
}
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
@Override
public void invalidateStartTime() {
// TODO Auto-generated method stub
}
@Override
public void invalidateEndTime() {
// TODO Auto-generated method stub
}
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.test.mock;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -477,7 +478,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
}
@Override
public void detachRaceLog() {
public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
}
@@ -489,7 +490,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
}
@Override
public RaceLog getRaceLog() {
public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
return null;
}
@@ -512,4 +513,10 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
return null;
}
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
}
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogInformation;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.util.impl.RaceColumnListeners;
@@ -148,6 +149,15 @@ public interface RaceColumn extends Named {
*/
double getFactor();
/**
* By default, a competitor's total score is computed by summing up the non-discarded total points of each race
* across the leaderboard, considering the {@link RaceColumn#getFactor() column factors}. Some race columns,
* however, are defined such that participating competitors start with a zero score from this race column on. If
* this method returns <code>true</code>, this column advises the leaderboard and scoring scheme to start counting
* the total points at this column with zero.
*/
boolean isStartsWithZeroScore();
/**
* @param factor if <code>null</code>, {@link #getFactor()} will again compute a default value; otherwise, {@link #getFactor()} will
* then return the double value of <code>factor</code>.
@@ -158,5 +168,12 @@ public interface RaceColumn extends Named {
* If <code>null</code>, the {@link #getFactor() factor} defaults to 1 for non-medal and {@link #DEFAULT_MEDAL_RACE_FACTOR} for
* medal races. Otherwise, the explicit factor is used.
*/
Double getExplicitFactor();
Double getExplicitFactor();
/**
* Returns the race log identifier associated with this fleet and race log
* @param fleet
* @return
*/
RaceLogIdentifier getRaceLogIdentifier(Fleet fleet);
}
@@ -2,7 +2,7 @@ package com.sap.sailing.domain.base;
import java.io.Serializable;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -14,6 +14,8 @@ public interface RaceColumnListener extends Serializable {
void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace);
void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore);
boolean canAddRaceColumnToContainer(RaceColumn raceColumn);
void raceColumnAddedToContainer(RaceColumn raceColumn);
@@ -26,7 +28,7 @@ public interface RaceColumnListener extends Serializable {
void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName);
void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule, ThresholdBasedResultDiscardingRule newDiscardingRule);
void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule);
void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogEvent event);
@@ -93,4 +93,11 @@ public interface Regatta extends Named, WithID {
void addRaceColumnListener(RaceColumnListener listener);
void removeRaceColumnListener(RaceColumnListener listener);
/**
* @return whether this regatta defines its local per-series result discarding rules; if so, any leaderboard based
* on the regatta has to respect this and has to use a result discarding rule implementation that
* keeps discards local to each series rather than spreading them across the entire leaderboard.
*/
boolean definesSeriesDiscardThresholds();
}
@@ -1,17 +1,23 @@
package com.sap.sailing.domain.base;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
/**
* A series is a part of a {@link Regatta}. Rounds are ordered within the regatta, and rules for who is assigned to
* A series is a part of a {@link Regatta}. Series are ordered within the regatta, and rules for who is assigned to
* which series may exist on the regatta. For example, a regatta may have a qualification series, a final series, and a
* medal "series" with usually only a single medal race. Each series has one or more fleets, deciding how many races per
* race column have to be run in this round. For example, if the 49er regatta has so many competitors that they cannot
* all start in one race, the qualification round can be split into two {@link Fleet}s, "Yellow" and "Blue," each
* getting their separate races. Fleet assignment may or may not vary. This usually depends on the round's
* all start in one race, the qualification series can be split into two {@link Fleet}s, "Yellow" and "Blue," each
* getting their separate races. Fleet assignment may or may not vary. This usually depends on the series'
* characteristics of having ordered or unordered fleets.<p>
*
* A series may define its result discarding scheme. If it does, a regatta leaderboard for the containing regatta
* has to adhere to this and may not define its own cross-cutting result discarding scheme. If one or more series
* in the regatta choose to define their own result discarding scheme, discards are determined per series and not
* per leaderboard.<p>
*
* To receive notifications when {@link TrackedRace tracked races} are linked to or unlinked from any of this series'
* columns, {@link RaceColumnListener}s can be added / removed.
*
@@ -42,6 +48,14 @@ public interface Series extends SeriesBase {
void removeRaceColumn(String raceColumnName);
/**
* If not <code>null</code>, a containing regatta's leaderboard must obey this rule and in particular cannot define
* a "cross-cutting" result discarding rule where discards may be arbitrarily distributed across series.
*/
ThresholdBasedResultDiscardingRule getResultDiscardingRule();
void setResultDiscardingRule(ThresholdBasedResultDiscardingRule resultDiscardingRule);
Regatta getRegatta();
/**
@@ -52,4 +66,27 @@ public interface Series extends SeriesBase {
void addRaceColumnListener(RaceColumnListener listener);
void removeRaceColumnListener(RaceColumnListener listener);
/**
* @return whether this series defines its local result discarding rule; if so, any leaderboard based on the
* enclosing regatta has to respect this and has to use a result discarding rule implementation that keeps
* discards local to each series rather than spreading them across the entire leaderboard.
*/
boolean definesSeriesDiscardThresholds();
/**
* By default, a competitor's total score is computed by summing up the non-discarded total points of each race
* across the leaderboard, considering the {@link RaceColumn#getFactor() column factors}. Some series, however, are
* defined such that participating competitors start the series with a zero score. Any carry-forward would then have
* to be modeled as a first carry-forward "race" in the series which the ISAF in 2013 has defined as being a race
* logically, therefore also being discardable. If this method returns <code>true</code>, this series advises the
* leaderboard and scoring scheme to start counting the total points at this series with zero.
* <p>
*
* This condition propagates to the first race column of the series which is then used by the leaderboard and
* scoring scheme.
*/
boolean isStartsWithZeroScore();
void setStartsWithZeroScore(boolean startsWithZeroScore);
}
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplate;
import com.sap.sailing.domain.racelog.RaceLogInformation;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.impl.RaceLogIdentifierImpl;
import com.sap.sailing.domain.tracking.TrackedRace;
public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implements RaceColumn {
@@ -25,6 +26,7 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
private final Map<Fleet, RaceIdentifier> raceIdentifiers;
private final Map<Fleet, RaceLog> raceLogs;
/**
* holds the race log identifer template needed to create the appropriate RaceLogIdentifer that is constructed from the
* parent object name, name of this raceColumn and the name of a fleet of this raceColumn to access the RaceLog in the
@@ -55,6 +57,11 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
public RaceLog getRaceLog(Fleet fleet) {
return raceLogs.get(fleet);
}
@Override
public RaceLogIdentifier getRaceLogIdentifier(Fleet fleet) {
return new RaceLogIdentifierImpl(raceLogsIdentifierTemplate, getName(), fleet);
}
@Override
public TrackedRace getTrackedRace(Fleet fleet) {
@@ -75,7 +82,8 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
}
}
if (previouslyLinkedRace != null) {
previouslyLinkedRace.detachRaceLog();
RaceLogIdentifierImpl identifier = new RaceLogIdentifierImpl(raceLogsIdentifierTemplate, getName(), fleet);
previouslyLinkedRace.detachRaceLog(identifier);
getRaceColumnListeners().notifyListenersAboutTrackedRaceUnlinked(this, fleet, previouslyLinkedRace);
}
if (trackedRace != null) {
@@ -1,6 +1,8 @@
package com.sap.sailing.domain.base.impl;
import java.util.Iterator;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.RaceDefinition;
@@ -89,5 +91,14 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo
// re-associating the TrackedRace needs to happen before the super call because the RaceIdentifier may have changed
super.setTrackedRace(fleet, trackedRace);
}
private boolean isFirstColumnInSeries() {
Iterator<? extends RaceColumnInSeries> columnIter = getSeries().getRaceColumns().iterator();
return columnIter.hasNext() && columnIter.next() == this;
}
@Override
public boolean isStartsWithZeroScore() {
return getSeries().isStartsWithZeroScore() && isFirstColumnInSeries();
}
}
@@ -9,7 +9,6 @@ import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
@@ -24,8 +23,8 @@ import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
@@ -269,6 +268,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return raceColumnListeners.canAddRaceColumnToContainer(raceColumn);
@@ -302,8 +306,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
raceColumnListeners.notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
}
@@ -342,4 +345,19 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
this.defaultCourseArea = newCourseArea;
}
/**
* @return whether this regatta defines its local per-series result discarding rules; if so, any leaderboard based
* on the regatta has to respect this and has to use a result discarding rule implementation that
* keeps discards local to each series rather than spreading them across the entire leaderboard.
*/
@Override
public boolean definesSeriesDiscardThresholds() {
for (Series s : series) {
if (s.definesSeriesDiscardThresholds()) {
return true;
}
}
return false;
}
}
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.impl.NamedImpl;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
@@ -32,6 +33,8 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
private boolean isMedal;
private Regatta regatta;
private final RaceColumnListeners raceColumnListeners;
private ThresholdBasedResultDiscardingRule resultDiscardingRule;
private boolean startsWithZeroScore;
/**
* @param fleets
@@ -136,32 +139,53 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
@Override
public void moveRaceColumnUp(String raceColumnName) {
boolean didFirstColumnChange = false; // if it changes and the series starts scoring with zero, notify the change
// start at second element because first can't be moved up
for (int i=1; i<raceColumns.size(); i++) {
RaceColumnInSeries rc = raceColumns.get(i);
if (rc.getName().equals(raceColumnName)) {
raceColumns.remove(i);
if (i==1) {
didFirstColumnChange = true;
}
raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(rc);
raceColumns.add(i-1, rc);
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(rc);
break;
}
}
if (didFirstColumnChange && startsWithZeroScore) {
notifyIsStartsWithZeroScoreChangedForFirstTwoColumns();
}
}
private void notifyIsStartsWithZeroScoreChangedForFirstTwoColumns() {
final RaceColumnInSeries first = raceColumns.get(0);
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(first, first.isStartsWithZeroScore());
final RaceColumnInSeries second = raceColumns.get(1);
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(second, second.isStartsWithZeroScore());
}
@Override
public void moveRaceColumnDown(String raceColumnName) {
boolean didFirstColumnChange = false; // if it changes and the series starts scoring with zero, notify the change
// end at second-last element because last can't be moved down
for (int i=0; i<raceColumns.size()-1; i++) {
RaceColumnInSeries rc = raceColumns.get(i);
if (rc.getName().equals(raceColumnName)) {
raceColumns.remove(i);
if (i==0) {
didFirstColumnChange = true;
}
raceColumnListeners.notifyListenersAboutRaceColumnRemovedFromContainer(rc);
raceColumns.add(i+1, rc);
raceColumnListeners.notifyListenersAboutRaceColumnAddedToContainer(rc);
break;
}
}
if (didFirstColumnChange && startsWithZeroScore) {
notifyIsStartsWithZeroScoreChangedForFirstTwoColumns();
}
}
@Override
@@ -209,6 +233,11 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
raceColumnListeners.notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
/**
* A series listens on its columns; individual columns, however, don't ask whether they can be added; the series itself does.
*
@@ -245,8 +274,7 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
raceColumnListeners.notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
}
@@ -259,4 +287,48 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener
public boolean isTransient() {
return false;
}
@Override
public ThresholdBasedResultDiscardingRule getResultDiscardingRule() {
return resultDiscardingRule;
}
@Override
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule resultDiscardingRule) {
this.resultDiscardingRule = resultDiscardingRule;
}
@Override
public boolean definesSeriesDiscardThresholds() {
return getResultDiscardingRule() != null;
}
@Override
public boolean isStartsWithZeroScore() {
return startsWithZeroScore;
}
/**
* @return <code>null</code> if the series doesn't currently have any columns
*/
private RaceColumn getFirstRaceColumn() {
RaceColumn result = null;
if (!raceColumns.isEmpty()) {
result = raceColumns.get(0);
}
return result;
}
@Override
public void setStartsWithZeroScore(boolean startsWithZeroScore) {
boolean oldStartsWithZeroScore = this.startsWithZeroScore;
if (oldStartsWithZeroScore != startsWithZeroScore) {
this.startsWithZeroScore = startsWithZeroScore;
RaceColumn firstRaceColumnInSeries = getFirstRaceColumn();
if (firstRaceColumnInSeries != null) {
raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(firstRaceColumnInSeries, startsWithZeroScore);
}
}
}
}
@@ -75,4 +75,13 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn {
public Double getExplicitFactor() {
return explicitFactor;
}
/**
* Returns <code>false</code>, meaning that by default when aggregating total scores across a leaderboard, this race column's score
* is added to the aggregate of previous scores.
*/
@Override
public boolean isStartsWithZeroScore() {
return false;
}
}
@@ -198,7 +198,7 @@ public interface Leaderboard extends Named {
/**
* Sums up the {@link #getTotalPoints(Competitor, TrackedRace, TimePoint) total points} of <code>competitor</code>
* across all races tracked by this leaderboard.
* across all races tracked by this leaderboard, respecting the {@link RaceColumn#isStartsWithZeroScore()} property.
*/
Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException;
@@ -282,8 +282,6 @@ public interface Leaderboard extends Named {
SettableScoreCorrection getScoreCorrection();
ThresholdBasedResultDiscardingRule getResultDiscardingRule();
Competitor getCompetitorByName(String competitorName);
void setDisplayName(Competitor competitor, String displayName);
@@ -314,7 +312,19 @@ public interface Leaderboard extends Named {
*/
boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule);
/**
* Returns the result discarding rule for this leaderboard. This may be an explicit rule set for the entire leaderboard,
* or it may be an implicit rule that relies on other data such as a regatta's series-specific result discarding rules.
*/
ResultDiscardingRule getResultDiscardingRule();
/**
* Trying to set a cross-leaderboard result discarding rule may fail with an exception if the leaderboard obtains
* its result discarding rule in another way. For example, if the leaderboard is a {@link RegattaLeaderboard} and the
* regatta's series define their own result discarding rules, the regatta leaderboard will use a composite
* result discarding rule that considers all series-specific result discarding rules.
*/
public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule);
Competitor getCompetitorByIdAsString(String idAsString);
@@ -228,6 +228,11 @@ public class LeaderboardCacheManager {
removeFromCache(leaderboard);
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
removeFromCache(leaderboard);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
@@ -254,8 +259,7 @@ public class LeaderboardCacheManager {
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
removeFromCache(leaderboard);
}
@@ -61,6 +61,7 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoreCorrection.Result;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
@@ -100,7 +101,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
private final SettableScoreCorrection scoreCorrection;
private ThresholdBasedResultDiscardingRule resultDiscardingRule;
private ThresholdBasedResultDiscardingRule crossLeaderboardResultDiscardingRule;
/**
* The optional display name mappings for competitors. This allows a user to override the tracking-provided
@@ -330,7 +331,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
this.carriedPoints = new HashMap<Competitor, Double>();
this.scoreCorrection = scoreCorrection;
this.displayNames = new HashMap<Competitor, String>();
this.resultDiscardingRule = resultDiscardingRule;
this.crossLeaderboardResultDiscardingRule = resultDiscardingRule;
this.suppressedCompetitors = Collections.synchronizedSet(new HashSet<Competitor>());
this.raceColumnListeners = new RaceColumnListeners();
this.raceDetailsAtEndOfTrackingCache = new HashMap<Pair<TrackedRace, Competitor>, RunnableFuture<RaceDetails>>();
@@ -383,8 +384,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
}
@Override
public ThresholdBasedResultDiscardingRule getResultDiscardingRule() {
return resultDiscardingRule;
public ResultDiscardingRule getResultDiscardingRule() {
return crossLeaderboardResultDiscardingRule;
}
@Override
@@ -423,9 +424,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
}
@Override
public void setResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) {
ThresholdBasedResultDiscardingRule oldDiscardingRule = getResultDiscardingRule();
this.resultDiscardingRule = discardingRule;
public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) {
ResultDiscardingRule oldDiscardingRule = getResultDiscardingRule();
this.crossLeaderboardResultDiscardingRule = discardingRule;
getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, discardingRule);
}
@@ -480,11 +481,20 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override
public Double getTotalPoints(Competitor competitor, TimePoint timePoint) throws NoWindException {
// when a column with isStartsWithZeroScore() is found, only reset score if the competitor scored in any race from there on
boolean needToResetScoreUponNextNonEmptyEntry = false;
double result = getCarriedPoints(competitor);
for (RaceColumn r : getRaceColumns()) {
if (r.isStartsWithZeroScore()) {
needToResetScoreUponNextNonEmptyEntry = true;
}
if (getScoringScheme().isValidInTotalScore(this, r, timePoint)) {
final Double totalPoints = getTotalPoints(competitor, r, timePoint);
if (totalPoints != null) {
if (needToResetScoreUponNextNonEmptyEntry) {
result = 0;
needToResetScoreUponNextNonEmptyEntry = false;
}
result += totalPoints;
}
}
@@ -694,6 +704,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
@Override
public void raceColumnMoved(RaceColumn raceColumn, int newIndex) {
getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex);
@@ -735,8 +750,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
}
@@ -1021,7 +1035,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
result.setDelayToLiveInMillisForLatestRace(this.getDelayToLiveInMillis());
result.rows = new HashMap<CompetitorDTO, LeaderboardRowDTO>();
result.hasCarriedPoints = this.hasCarriedPoints();
result.discardThresholds = this.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces();
if (this.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) {
result.discardThresholds = ((ThresholdBasedResultDiscardingRule) this.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces();
} else {
result.discardThresholds = null;
}
// Computing the competitor leg ranks is expensive, especially in live mode, in case new events keep invalidating
// the ranks cache in TrackedLegImpl. Then problem then is that the sorting based on wind data is repeated for each
// competitor, leading to square effort. We therefore need to compute the leg ranks for those race where leg details
@@ -1052,6 +1070,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
row.competitor = competitorDTO;
row.fieldsByRaceColumnName = new HashMap<String, LeaderboardEntryDTO>();
row.carriedPoints = this.hasCarriedPoints(competitor) ? this.getCarriedPoints(competitor) : null;
row.totalPoints = this.getTotalPoints(competitor, timePoint);
addOverallDetailsToRow(timePoint, competitor, row);
result.competitors.add(competitorDTO);
Map<String, Future<LeaderboardEntryDTO>> futuresForColumnName = new HashMap<String, Future<LeaderboardEntryDTO>>();
@@ -99,17 +99,31 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
double o2ScoreSum = getLeaderboard().getCarriedPoints(o2);
Double o1MedalRaceScore = 0.0;
Double o2MedalRaceScore = 0.0;
// When a column has isStartsWithZeroScore, the competitor's score only need to be reset to zero if from there on
// the competitor scored in this or any subsequent columns
boolean needToResetO1ScoreUponNextValidResult = false;
boolean needToResetO2ScoreUponNextValidResult = false;
for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) {
needToResetO1ScoreUponNextValidResult = raceColumn.isStartsWithZeroScore();
needToResetO2ScoreUponNextValidResult = raceColumn.isStartsWithZeroScore();
if (getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, timePoint)) {
int preemptiveColumnResult = 0;
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
if (o1Score != null) {
o1Scores.add(new Pair<RaceColumn, Double>(raceColumn, o1Score));
if (needToResetO1ScoreUponNextValidResult) {
o1ScoreSum = 0;
needToResetO1ScoreUponNextValidResult = false;
}
o1ScoreSum += o1Score;
}
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
if (o2Score != null) {
o2Scores.add(new Pair<RaceColumn, Double>(raceColumn, o2Score));
if (needToResetO2ScoreUponNextValidResult) {
o2ScoreSum = 0;
needToResetO2ScoreUponNextValidResult = false;
}
o2ScoreSum += o2Score;
}
if (raceColumn.isMedalRace()) {
@@ -128,10 +142,8 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
}
}
// now count the races in which they scored; if they scored in a different number of races, prefer the
// competitor
// who scored more often; otherwise, prefer the competitor who has a better score sum; if score sums are equal,
// break
// tie by sorting scores and looking for the first score difference.
// competitor who scored more often; otherwise, prefer the competitor who has a better score sum; if score sums are equal,
// break tie by sorting scores and looking for the first score difference.
int result = compareByNumberOfRacesScored(o1Scores.size(), o2Scores.size());
if (result == 0) {
result = compareByScoreSum(o1ScoreSum, o2ScoreSum);
@@ -0,0 +1,55 @@
package com.sap.sailing.domain.leaderboard.impl;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
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.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
/**
* A result discarding rule that is used when one or more of a {@link Regatta}'s {@link Series} define their own
* result discarding rule. In this case, discards are decided per series and not per regatta or leaderboard.
*
* @author Axel Uhl (d043530)
*
*/
public class PerSeriesResultDiscardingRuleImpl implements ResultDiscardingRule {
private static final long serialVersionUID = 4596442640424592181L;
private final Regatta regatta;
public PerSeriesResultDiscardingRuleImpl(Regatta regatta) {
super();
this.regatta = regatta;
}
@Override
public Set<RaceColumn> getDiscardedRaceColumns(Competitor competitor, Leaderboard leaderboard,
Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) {
Set<RaceColumn> result = new HashSet<>();
for (Series s : regatta.getSeries()) {
if (s.getResultDiscardingRule() != null) {
Iterable<RaceColumn> columnsToConsiderInSeries = getColumnsToConsiderInSeries(s, raceColumnsToConsider);
result.addAll(s.getResultDiscardingRule().getDiscardedRaceColumns(competitor, leaderboard, columnsToConsiderInSeries, timePoint));
}
}
return result;
}
private Iterable<RaceColumn> getColumnsToConsiderInSeries(Series series, Iterable<RaceColumn> allRaceColumnsToConsider) {
Set<RaceColumn> result = new HashSet<>();
for (RaceColumn seriesColumn : series.getRaceColumns()) {
if (Util.contains(allRaceColumnsToConsider, seriesColumn)) {
result.add(seriesColumn);
}
}
return result;
}
}
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.RaceColumnInSeriesImpl;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
@@ -81,4 +82,21 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
return regatta.getDefaultCourseArea();
}
/**
* If the regatta' series {@link Regatta#definesSeriesDiscardThresholds() define} their own result discarding rules, this leaderboard uses
* a composite result discarding rule of type {@link PerSeriesResultDiscardingRuleImpl} that contains discards within each series according
* to the series' discarding rules. Otherwise, the default cross-leaderboard result discarding rule is obtained from the super class
* implementation.
*/
@Override
public ResultDiscardingRule getResultDiscardingRule() {
if (regatta.definesSeriesDiscardThresholds()) {
return new PerSeriesResultDiscardingRuleImpl(regatta);
} else {
return super.getResultDiscardingRule();
}
}
}
@@ -164,7 +164,7 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
*/
@Override
public Result getCorrectedScore(Callable<Integer> trackedRankProvider, final Competitor competitor,
final RaceColumn raceColumn, TimePoint timePoint,NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
final RaceColumn raceColumn, TimePoint timePoint, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
ScoringScheme scoringScheme) {
Double result;
final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn);
@@ -30,11 +30,11 @@ import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
* @author Axel Uhl (d043530)
*
*/
public class ResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingRule {
public class ThresholdBasedResultDiscardingRuleImpl implements ThresholdBasedResultDiscardingRule {
private static final long serialVersionUID = 386341628517357988L;
private final int[] discardIndexResultsStartingWithHowManyRaces;
public ResultDiscardingRuleImpl(int[] discardIndexResultsStartingWithHowManyRaces) {
public ThresholdBasedResultDiscardingRuleImpl(int[] discardIndexResultsStartingWithHowManyRaces) {
super();
this.discardIndexResultsStartingWithHowManyRaces = new int[discardIndexResultsStartingWithHowManyRaces.length];
System.arraycopy(discardIndexResultsStartingWithHowManyRaces, 0,
@@ -9,7 +9,7 @@ import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.base.impl.SimpleAbstractRaceColumn;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
@@ -121,6 +121,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace);
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn);
@@ -152,8 +157,7 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
}
@@ -166,4 +170,9 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R
public boolean isTransient() {
return false;
}
@Override
public RaceLogIdentifier getRaceLogIdentifier(Fleet fleet) {
return null;
}
}
@@ -12,6 +12,6 @@ public enum EmptyRaceLogStore implements RaceLogStore {
@Override
public RaceLog getRaceLog(RaceLogIdentifier identifier) {
return new RaceLogImpl(UUID.randomUUID().toString());
return new RaceLogImpl(UUID.randomUUID().toString(), identifier.getIdentifier());
}
}
@@ -88,4 +88,8 @@ public interface DynamicTrackedRace extends TrackedRace {
* the new course design to be published
*/
void onCourseDesignChangedByRaceCommittee(CourseBase courseDesign);
void invalidateStartTime();
void invalidateEndTime();
}
@@ -51,10 +51,12 @@ public interface TrackedRace extends Serializable {
/**
* Computes the estimated start time for this race (not to be confused with the {@link #getStartOfTracking()} time
* point which is expected to be before the race start time). When there are no {@link MarkPassing}s for the first
* mark, <code>null</code> is returned. If there are mark passings for the first mark and the start time is less
* than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark passing for the
* first mark. Otherwise, the first mark passing for the first mark minus
* point which is expected to be before the race start time). The highest precedence take the
* {@link #attachedRaceLogs race logs} and their start time events, followed by the field {@link #startTimeReceived}
* which can explicitly be set using {@link #setStartTimeReceived(TimePoint)}. When there are no {@link MarkPassing}s
* for the first mark, <code>null</code> is returned. If there are mark passings for the first mark and the start
* time is less than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark
* passing for the first mark. Otherwise, the first mark passing for the first mark minus
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time.
* <p>
*
@@ -482,7 +484,12 @@ public interface TrackedRace extends Serializable {
/**
* Detaches the race log associated with this {@link TrackedRace}.
*/
void detachRaceLog();
void detachRaceLog(Serializable identifier);
/**
* Detaches all {@link RaceLog} instances from this race
*/
void detachAllRaceLogs();
/**
* Attaches the passed race log with this {@link TrackedRace}.
@@ -494,7 +501,7 @@ public interface TrackedRace extends Serializable {
* Returns the attached race log event track for this race if any.
* Otherwise <code>null</code>.
*/
RaceLog getRaceLog();
RaceLog getRaceLog(Serializable identifier);
/**
* a setter for the listener on course design changes. The listener is mostly part of the tracking provider adapter.
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
@@ -665,15 +666,16 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
@Override
public void attachRaceLog(RaceLog raceLog) {
super.attachRaceLog(raceLog);
logListener.addTo(attachedRaceLog);
logListener.addTo(raceLog);
}
@Override
public void detachRaceLog() {
public void detachRaceLog(Serializable identifier) {
RaceLog attachedRaceLog = attachedRaceLogs.get(identifier);
if (attachedRaceLog != null) {
logListener.removeFrom(attachedRaceLog);
}
super.detachRaceLog();
super.detachRaceLog(identifier);
}
@Override
@@ -1,7 +1,6 @@
package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogCourseAreaChangedEvent;
import com.sap.sailing.domain.racelog.RaceLogCourseDesignChangedEvent;
@@ -17,15 +16,12 @@ import com.sap.sailing.domain.racelog.RaceLogStartProcedureChangedEvent;
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
import com.sap.sailing.domain.racelog.analyzing.impl.LastPublishedCourseDesignFinder;
import com.sap.sailing.domain.racelog.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.racelog.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
private DynamicTrackedRace trackedRace;
private StartTimeFinder startTimeFinder;
private RaceStatusAnalyzer statusAnalyzer;
private LastPublishedCourseDesignFinder courseDesignFinder;
@@ -36,7 +32,8 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
public void addTo(RaceLog raceLog) {
raceLog.addListener(this);
startTimeFinder = new StartTimeFinder(raceLog);
trackedRace.invalidateStartTime();
trackedRace.invalidateEndTime();
courseDesignFinder = new LastPublishedCourseDesignFinder(raceLog);
statusAnalyzer = new RaceStatusAnalyzer(raceLog);
analyze();
@@ -55,24 +52,10 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
}
private void analyze() {
analyzeStartTime();
analyzeStatus();
analyzeCourseDesign();
}
private void analyzeStartTime() {
TimePoint startTime = startTimeFinder.getStartTime();
// Because this code can be triggered by an obsolete (delayed) event or on an empty pass...
// ... the current pass's start time might be null
if (startTime != null) {
// ... or setStartTimeReceived(TimePoint) might be called more than once with the same start time.
trackedRace.setStartTimeReceived(startTime);
} else {
// TODO: Can we somehow withdraw a previously set start time?
}
}
private void analyzeStatus() {
/* RaceLogRaceStatus newStatus = */statusAnalyzer.getStatus();
@@ -92,13 +75,11 @@ public class DynamicTrackedRaceLogListener implements RaceLogEventVisitor {
@Override
public void visit(RaceLogPassChangeEvent event) {
analyzeStartTime();
analyzeStatus();
}
@Override
public void visit(RaceLogStartTimeEvent event) {
analyzeStartTime();
analyzeStatus();
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.tracking.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
@@ -71,6 +72,7 @@ import com.sap.sailing.domain.confidence.Weigher;
import com.sap.sailing.domain.confidence.impl.HyperbolicTimeDifferenceWeigher;
import com.sap.sailing.domain.confidence.impl.PositionAndTimePointWeigher;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -220,7 +222,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
private transient CombinedWindTrackImpl combinedWindTrack;
protected transient RaceLog attachedRaceLog;
protected transient HashMap<Serializable, RaceLog> attachedRaceLogs;
/**
* The time delay to the current point in time in milliseconds.
@@ -256,6 +258,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
long millisecondsOverWhichToAverageSpeed, long delayForWindEstimationCacheInvalidation) {
super();
locksForMarkPassings = new IdentityHashMap<>();
attachedRaceLogs = new HashMap<>();
this.status = new TrackedRaceStatusImpl(TrackedRaceStatusEnum.PREPARED, 0.0);
this.statusNotifier = new Object[0];
this.serializationLock = new NamedReentrantReadWriteLock("Serialization lock for tracked race "
@@ -365,6 +368,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
*/
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
attachedRaceLogs = new HashMap<>();
markPassingsTimes = new ArrayList<Pair<Waypoint, Pair<TimePoint, TimePoint>>>();
cacheInvalidationTimerLock = new Object();
windStore = EmptyWindStore.INSTANCE;
@@ -451,11 +455,11 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
return endOfTrackingReceived;
}
protected void invalidateStartTime() {
public void invalidateStartTime() {
startTime = null;
}
protected void invalidateEndTime() {
public void invalidateEndTime() {
endTime = null;
}
@@ -466,35 +470,46 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
}
/**
* Calculates the start time of the race from various sources
* Calculates the start time of the race from various sources. The highest precedence take the {@link #attachedRaceLogs race logs},
* followed by the field {@link #startTimeReceived} which can explicitly be set using {@link #setStartTimeReceived(TimePoint)}.
* If that does not provide any start time either, a start time is attempted to be inferred from the time points
* of the start mark passing events.
*/
@Override
public TimePoint getStartOfRace() {
if (startTime == null) {
startTime = startTimeReceived;
// If not null, check if the first mark passing for the start line is too much after the startTimeReceived;
// if so, return an adjusted, later start time.
// If no official start time was received, try to estimate the start time using the mark passings for the
// start line.
final Waypoint firstWaypoint = getRace().getCourse().getFirstWaypoint();
if (firstWaypoint != null) {
if (startTimeReceived != null) {
TimePoint timeOfFirstMarkPassing = getFirstPassingTime(firstWaypoint);
if (timeOfFirstMarkPassing != null) {
long startTimeReceived2timeOfFirstMarkPassingFirstMark = timeOfFirstMarkPassing.asMillis()
- startTimeReceived.asMillis();
if (startTimeReceived2timeOfFirstMarkPassingFirstMark > MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS) {
startTime = new MillisecondsTimePoint(timeOfFirstMarkPassing.asMillis()
- MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS);
} else {
startTime = startTimeReceived;
for (RaceLog raceLog : attachedRaceLogs.values()) {
startTime = new StartTimeFinder(raceLog).getStartTime();
if (startTime != null) {
break;
}
}
if (startTime == null) {
startTime = startTimeReceived;
// If not null, check if the first mark passing for the start line is too much after the
// startTimeReceived; if so, return an adjusted, later start time.
// If no official start time was received, try to estimate the start time using the mark passings for
// the start line.
final Waypoint firstWaypoint = getRace().getCourse().getFirstWaypoint();
if (firstWaypoint != null) {
if (startTimeReceived != null) {
TimePoint timeOfFirstMarkPassing = getFirstPassingTime(firstWaypoint);
if (timeOfFirstMarkPassing != null) {
long startTimeReceived2timeOfFirstMarkPassingFirstMark = timeOfFirstMarkPassing.asMillis()
- startTimeReceived.asMillis();
if (startTimeReceived2timeOfFirstMarkPassingFirstMark > MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS) {
startTime = new MillisecondsTimePoint(timeOfFirstMarkPassing.asMillis()
- MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS);
} else {
startTime = startTimeReceived;
}
}
} else {
final NavigableSet<MarkPassing> markPassingsForFirstWaypointInOrder = getMarkPassingsInOrderAsNavigableSet(firstWaypoint);
if (markPassingsForFirstWaypointInOrder != null) {
startTime = calculateStartOfRaceFromMarkPassings(markPassingsForFirstWaypointInOrder,
getRace().getCompetitors());
}
}
} else {
final NavigableSet<MarkPassing> markPassingsForFirstWaypointInOrder = getMarkPassingsInOrderAsNavigableSet(firstWaypoint);
if (markPassingsForFirstWaypointInOrder != null) {
startTime = calculateStartOfRaceFromMarkPassings(markPassingsForFirstWaypointInOrder, getRace()
.getCompetitors());
}
}
}
@@ -2331,17 +2346,22 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
@Override
public void attachRaceLog(RaceLog raceLog) {
this.attachedRaceLog = raceLog;
this.attachedRaceLogs.put(raceLog.getId(), raceLog);
}
@Override
public void detachRaceLog() {
this.attachedRaceLog = null;
public void detachRaceLog(Serializable identifier) {
this.attachedRaceLogs.remove(identifier);
}
@Override
public void detachAllRaceLogs() {
this.attachedRaceLogs.clear();
}
@Override
public RaceLog getRaceLog() {
return attachedRaceLog;
public RaceLog getRaceLog(Serializable identifier) {
return attachedRaceLogs.get(identifier);
}
@Override
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -85,6 +85,12 @@ public class RaceColumnListeners implements Serializable {
}
}
public void notifyListenersAboutIsStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.isStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore);
}
}
public void notifyListenersAboutRaceColumnAddedToContainer(RaceColumn raceColumn) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.raceColumnAddedToContainer(raceColumn);
@@ -126,8 +132,8 @@ public class RaceColumnListeners implements Serializable {
}
}
public void notifyListenersAboutResultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void notifyListenersAboutResultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule,
ResultDiscardingRule newDiscardingRule) {
for (RaceColumnListener listener : getRaceColumnListeners()) {
listener.resultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule);
}
@@ -406,6 +406,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
// TODO Auto-generated method stub
}
@Override
public boolean definesSeriesDiscardThresholds() {
// TODO Auto-generated method stub
return false;
}
};
}
@@ -742,7 +748,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public void detachRaceLog() {
public void detachRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
}
@@ -754,7 +760,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public RaceLog getRaceLog() {
public RaceLog getRaceLog(Serializable identifier) {
// TODO Auto-generated method stub
return null;
}
@@ -783,4 +789,22 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return null;
}
@Override
public void detachAllRaceLogs() {
// TODO Auto-generated method stub
}
@Override
public void invalidateStartTime() {
// TODO Auto-generated method stub
}
@Override
public void invalidateEndTime() {
// TODO Auto-generated method stub
}
}
@@ -1,10 +1,6 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.TextBox;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
@@ -17,73 +13,30 @@ public abstract class AbstractLeaderboardDialog extends DataEntryDialog<Leaderbo
protected TextBox displayNameTextBox;
protected LeaderboardDescriptor leaderboardDescriptor;
protected LongBox[] discardThresholdBoxes;
protected DiscardThresholdBoxes discardThresholdBoxes;
protected static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4;
public AbstractLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDescriptor, StringMessages stringConstants,
public AbstractLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDescriptor, StringMessages stringMessages,
Validator<LeaderboardDescriptor> validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, null, stringConstants.ok(), stringConstants.cancel(), validator, callback);
this.stringMessages = stringConstants;
super(title, null, stringMessages.ok(), stringMessages.cancel(), validator, callback);
this.stringMessages = stringMessages;
this.leaderboardDescriptor = leaderboardDescriptor;
}
@Override
protected LeaderboardDescriptor getResult() {
int[] discardThresholdsBoxContents = getDiscardThresholds(discardThresholdBoxes);
leaderboardDescriptor.setName(nameTextBox.getValue());
leaderboardDescriptor.setDisplayName(displayNameTextBox.getValue().trim().isEmpty() ? null : displayNameTextBox.getValue());
leaderboardDescriptor.setDiscardThresholds(discardThresholdsBoxContents);
leaderboardDescriptor.setDiscardThresholds(discardThresholdBoxes==null?null:discardThresholdBoxes.getDiscardThresholds());
return leaderboardDescriptor;
}
protected static int[] getDiscardThresholds(LongBox[] discardThresholdBoxes) {
List<Integer> discardThresholds = new ArrayList<Integer>();
// go backwards; starting from first non-zero element, add them; take over leading zeroes which validator shall discard
for (int i = discardThresholdBoxes.length-1; i>=0; i--) {
if ((discardThresholdBoxes[i].getValue() != null
&& discardThresholdBoxes[i].getValue().toString().length() > 0) || !discardThresholds.isEmpty()) {
if (discardThresholdBoxes[i].getValue() == null) {
discardThresholds.add(0, 0);
} else {
discardThresholds.add(0, discardThresholdBoxes[i].getValue().intValue());
}
}
}
int[] discardThresholdsBoxContents = new int[discardThresholds.size()];
for (int i = 0; i < discardThresholds.size(); i++) {
discardThresholdsBoxContents[i] = discardThresholds.get(i);
}
return discardThresholdsBoxContents;
}
@Override
public void show() {
super.show();
nameTextBox.setFocus(true);
}
protected static LongBox[] initEmptyDiscardThresholdBoxes(DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
result[i].setVisibleLength(2);
}
return result;
}
protected static LongBox[] initPrefilledDiscardThresholdBoxes(int[] valuesToShow, DataEntryDialog<?> dialog) {
LongBox[] result = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < result.length; i++) {
if (i < valuesToShow.length) {
result[i] = dialog.createLongBox(valuesToShow[i], 2);
} else {
result[i] = dialog.createLongBoxWithOptionalValue(null, 2);
}
result[i].setVisibleLength(2);
}
return result;
}
protected static ListBox createScoringSchemeListBox(DataEntryDialog<?> dialog, StringMessages stringMessages) {
ListBox scoringSchemeListBox2 = dialog.createListBox(false);
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
@@ -0,0 +1,116 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
/**
* For a result discarding rule based on thresholds that tell after how many races the next discard kicks in, an instance of
* this class offers the UI components and validation rules that help in composing a UI that, among other things, allows a user
* to configure the discarding thresholds.<p>
*
* TODO produce error messages during validation
*
* @author Axel Uhl (d043530)
*
*/
public class DiscardThresholdBoxes {
private static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4;
private final LongBox[] discardThresholdBoxes;
private final DataEntryDialog<?> parent;
/**
* The widget used to represent the UI
*/
private final Widget widget;
public DiscardThresholdBoxes(DataEntryDialog<?> parent, StringMessages stringMessages) {
this(parent, /* values to show */ new int[0], stringMessages);
}
public DiscardThresholdBoxes(DataEntryDialog<?> parent, int[] initialDiscardThresholds, StringMessages stringMessages) {
this.parent = parent;
discardThresholdBoxes = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS];
for (int i = 0; i < discardThresholdBoxes.length; i++) {
if (initialDiscardThresholds != null && i < initialDiscardThresholds.length) {
discardThresholdBoxes[i] = parent.createLongBox(initialDiscardThresholds[i], 2);
} else {
discardThresholdBoxes[i] = parent.createLongBoxWithOptionalValue(null, 2);
}
discardThresholdBoxes[i].setVisibleLength(2);
}
widget = createDiscardThresholdBoxesPanel(stringMessages);
}
/**
* @return the widget that can be added to the parent dialog passed to the constructor to visualize and edit the
* result discarding thresholds. The result returned from this object remains the same across this object's
* life time.
*/
public Widget getWidget() {
return widget;
}
public int[] getDiscardThresholds() {
List<Integer> discardThresholds = new ArrayList<Integer>();
// go backwards; starting from first non-zero element, add them; take over leading zeroes which validator shall discard
for (int i = discardThresholdBoxes.length-1; i>=0; i--) {
if ((discardThresholdBoxes[i].getValue() != null
&& discardThresholdBoxes[i].getValue().toString().length() > 0) || !discardThresholds.isEmpty()) {
if (discardThresholdBoxes[i].getValue() == null) {
discardThresholds.add(0, 0);
} else {
discardThresholds.add(0, discardThresholdBoxes[i].getValue().intValue());
}
}
}
int[] discardThresholdsBoxContents = new int[discardThresholds.size()];
for (int i = 0; i < discardThresholds.size(); i++) {
discardThresholdsBoxContents[i] = discardThresholds.get(i);
}
return discardThresholdsBoxContents;
}
private Widget createDiscardThresholdBoxesPanel(StringMessages stringMessages) {
assert discardThresholdBoxes != null && discardThresholdBoxes.length == MAX_NUMBER_OF_DISCARDED_RESULTS;
VerticalPanel vp = new VerticalPanel();
vp.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
vp.add(hp);
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
parent.alignAllPanelWidgetsVertically(hp, HasVerticalAlignment.ALIGN_MIDDLE);
return vp;
}
public static String getErrorMessage(int[] discardThresholds, StringMessages stringMessages) {
String errorMessage = null;
if (discardThresholds != null) {
boolean discardThresholdsAscending = true;
for (int i = 1; i < discardThresholds.length; i++) {
if (0 < discardThresholds.length) {
discardThresholdsAscending = discardThresholdsAscending
&& discardThresholds[i - 1] < discardThresholds[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (discardThresholds[i] == 0 || discardThresholds[i - 1] > 0);
}
}
if (!discardThresholdsAscending) {
errorMessage = stringMessages.discardThresholdsMustBeAscending();
}
}
return errorMessage;
}
}
@@ -11,7 +11,8 @@ public class FlexibleLeaderboardCreateDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, StringMessages stringMessages,
Collection<EventDTO> existingEvents, ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) {
super(stringMessages.createFlexibleLeaderboard(), new LeaderboardDescriptor(), stringMessages, existingEvents, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
super(stringMessages.createFlexibleLeaderboard(), new LeaderboardDescriptor(), stringMessages, existingEvents,
errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
nameTextBox = createTextBox(null);
nameTextBox.ensureDebugId("LeaderboardNameField");
displayNameTextBox = createTextBox(null);
@@ -20,6 +21,6 @@ public class FlexibleLeaderboardCreateDialog extends FlexibleLeaderboardDialog {
scoringSchemeListBox = createScoringSchemeListBox(this, stringMessages);
sailingEventsListBox = createSailingEventListBox();
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this);
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
}
}
@@ -6,8 +6,6 @@ import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
@@ -24,12 +22,12 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
protected ListBox courseAreaListBox;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants;
protected final StringMessages stringMessages;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards) {
super();
this.stringConstants = stringConstants;
this.stringMessages = stringConstants;
this.existingLeaderboards = existingLeaderboards;
}
@@ -37,33 +35,23 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage;
boolean nonEmpty = leaderboardToValidate.getName() != null && leaderboardToValidate.getName().length() > 0;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getName())){
unique = false;
}
}
if (!nonEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
errorMessage = stringMessages.pleaseEnterAName();
} else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
errorMessage = stringMessages.leaderboardWithThisNameAlreadyExists();
} else {
errorMessage = null;
String discardThresholdErrorMessage = DiscardThresholdBoxes.getErrorMessage(leaderboardToValidate.getDiscardThresholds(), stringMessages);
if (discardThresholdErrorMessage != null) {
errorMessage = discardThresholdErrorMessage;
} else {
errorMessage = null;
}
}
return errorMessage;
}
@@ -112,15 +100,7 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
formGrid.setWidget(4, 0, new Label(stringMessages.courseArea() + ":"));
formGrid.setWidget(4, 1, courseAreaListBox);
mainPanel.add(formGrid);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
}
alignAllPanelWidgetsVertically(hp, HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.add(hp);
mainPanel.add(discardThresholdBoxes.getWidget());
return mainPanel;
}
@@ -14,10 +14,10 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards,
LeaderboardDescriptor leaderboard, StringMessages stringConstants, List<EventDTO> existingEvents, ErrorReporter errorReporter,
LeaderboardDescriptor leaderboard, StringMessages stringMessages, List<EventDTO> existingEvents, ErrorReporter errorReporter,
DialogCallback<LeaderboardDescriptor> callback) {
super(stringConstants.editFlexibleLeaderboard(), leaderboard, stringConstants, existingEvents, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(
stringConstants, otherExistingLeaderboards), callback);
super(stringMessages.editFlexibleLeaderboard(), leaderboard, stringMessages, existingEvents, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(
stringMessages, otherExistingLeaderboards), callback);
nameTextBox = createTextBox(leaderboard.getName());
displayNameTextBox = createTextBox(leaderboard.getDisplayName());
@@ -27,7 +27,7 @@ public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
int j = 0;
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringConstants));
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages));
if(leaderboard.getScoringScheme() == scoringSchemeType) {
scoringSchemeListBox.setSelectedIndex(j);
}
@@ -48,6 +48,6 @@ public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
}
}
}
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboard.getDiscardThresholds(), this);
discardThresholdBoxes = new DiscardThresholdBoxes(this, leaderboard.getDiscardThresholds(), stringMessages);
}
}
@@ -269,8 +269,10 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
Window.alert("This is a meta leaderboard. It can't be changed here.");
} else {
if (leaderboardDTO.isRegattaLeaderboard) {
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name,
leaderboardDTO.displayName, null, leaderboardDTO.discardThresholds, leaderboardDTO.regattaName, leaderboardDTO.defaultCourseAreaIdAsString);
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name,
leaderboardDTO.displayName, /* scoring scheme provided by regatta */ null,
leaderboardDTO.discardThresholds, leaderboardDTO.regattaName,
leaderboardDTO.defaultCourseAreaIdAsString);
AbstractLeaderboardDialog dialog = new RegattaLeaderboardEditDialog(Collections
.unmodifiableCollection(otherExistingLeaderboard), Collections.unmodifiableCollection(allRegattas),
descriptor, stringMessages, errorReporter,
@@ -3,9 +3,10 @@ package com.sap.sailing.gwt.ui.adminconsole;
import com.sap.sailing.domain.common.ScoringSchemeType;
/**
* A descriptor class for creating and editing basic data of a leaderboard
* A descriptor class for creating and editing basic data of a leaderboard.
*
* @author Frank
*
*
*/
public class LeaderboardDescriptor {
private String name;
@@ -27,6 +28,9 @@ public class LeaderboardDescriptor {
this.courseAreaIdAsString = courseAreaIdAsString;
}
/**
* Leaves the {@link #regattaName} <code>null</code>, representing a flexible leaderboard, not a regatta leaderboard
*/
public LeaderboardDescriptor(String name, String displayName, ScoringSchemeType scoringScheme, int[] discardThresholds, String courseAreaIdAsString) {
this.name = name;
this.displayName = displayName;
@@ -51,6 +55,11 @@ public class LeaderboardDescriptor {
this.scoringScheme = scoringScheme;
}
/**
* @return <code>null</code>, in case the leaderboard does not explicitly define result discarding thresholds itself
* (e.g., a regatta leaderboard that obtains its result discarding rules from the underlying regatta's
* series definitions); a valid but possibly empty array otherwise
*/
public int[] getDiscardThresholds() {
return discardThresholds;
}
@@ -10,10 +10,8 @@ import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
@@ -32,7 +30,7 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
protected CheckBox displayLeaderboardsInReverseOrderCheckBox;
protected CheckBox useOverallLeaderboardCheckBox;
private Panel overallLeaderboardConfigPanel;
private LongBox[] overallLeaderboardDiscardThresholdFields;
private DiscardThresholdBoxes overallLeaderboardDiscardThresholdBoxes;
private ListBox overallLeaderboardScoringSchemeListBox;
public static class LeaderboardGroupDescriptor {
@@ -120,10 +118,6 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
return overallLeaderboardScoringSchemeListBox;
}
protected LongBox[] getOverallLeaderboardDiscardThresholdFields() {
return overallLeaderboardDiscardThresholdFields;
}
public LeaderboardGroupDialog(LeaderboardGroupDTO group, StringMessages stringMessages,
DialogCallback<LeaderboardGroupDescriptor> callback, Collection<LeaderboardGroupDTO> existingLeaderboardGroups) {
super(stringMessages.leaderboardGroup(), null, stringMessages.ok(), stringMessages.cancel(),
@@ -140,20 +134,12 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
group.getOverallLeaderboardScoringSchemeType()));
}
formGrid.setWidget(0, 1, overallLeaderboardScoringSchemeListBox);
formGrid.setWidget(1, 0, new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
if (group.hasOverallLeaderboard()) {
overallLeaderboardDiscardThresholdFields = AbstractLeaderboardDialog.initPrefilledDiscardThresholdBoxes(
group.getOverallLeaderboardDiscardThresholds(), this);
overallLeaderboardDiscardThresholdBoxes = new DiscardThresholdBoxes(this, group.getOverallLeaderboardDiscardThresholds(), stringMessages);
} else {
overallLeaderboardDiscardThresholdFields = AbstractLeaderboardDialog.initEmptyDiscardThresholdBoxes(this);
overallLeaderboardDiscardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
}
for (int i = 0; i < overallLeaderboardDiscardThresholdFields.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(overallLeaderboardDiscardThresholdFields[i]);
}
formGrid.setWidget(1, 1, hp);
formGrid.setWidget(1, 1, overallLeaderboardDiscardThresholdBoxes.getWidget());
overallLeaderboardConfigPanel = formGrid;
}
@@ -162,8 +148,7 @@ public class LeaderboardGroupDialog extends DataEntryDialog<LeaderboardGroupDial
return new LeaderboardGroupDescriptor(nameEntryField.getText(), descriptionEntryField.getText(),
displayLeaderboardsInReverseOrderCheckBox.getValue(),
useOverallLeaderboardCheckBox.getValue(),
useOverallLeaderboardCheckBox.getValue() ? AbstractLeaderboardDialog
.getDiscardThresholds(getOverallLeaderboardDiscardThresholdFields()) : null,
useOverallLeaderboardCheckBox.getValue() ? overallLeaderboardDiscardThresholdBoxes.getDiscardThresholds() : null,
useOverallLeaderboardCheckBox.getValue() ? AbstractLeaderboardDialog.getSelectedScoringSchemeType(
overallLeaderboardScoringSchemeListBox, stringMessages) : null);
}
@@ -1,6 +1,7 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.google.gwt.cell.client.FieldUpdater;
@@ -24,12 +25,11 @@ import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
@@ -62,32 +62,24 @@ public class RegattaDetailsComposite extends Composite {
this.regattaRefresher = regattaRefresher;
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
regatta = null;
mainPanel = new CaptionPanel(stringMessages.regatta());
VerticalPanel vPanel = new VerticalPanel();
mainPanel.add(vPanel);
Grid grid = new Grid(4, 2);
vPanel.add(grid);
regattaName = new Label();
grid.setWidget(0 , 0, new Label(stringMessages.regattaName() + ":"));
grid.setWidget(0 , 1, regattaName);
boatClassName = new Label();
grid.setWidget(1 , 0, new Label(stringMessages.boatClass() + ":"));
grid.setWidget(1 , 1, boatClassName);
defaultCourseArea = new Label();
grid.setWidget(2 , 0, new Label(stringMessages.courseArea() + ":"));
grid.setWidget(2 , 1, defaultCourseArea);
scoringSystem = new Label();
grid.setWidget(3 , 0, new Label(stringMessages.scoringSystem() + ":"));
grid.setWidget(3 , 1, scoringSystem);
seriesTable = createRegattaSeriesTable();
seriesSelectionModel = new SingleSelectionModel<SeriesDTO>();
seriesSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@@ -96,11 +88,9 @@ public class RegattaDetailsComposite extends Composite {
}
});
seriesTable.setSelectionModel(seriesSelectionModel);
seriesListDataProvider = new ListDataProvider<SeriesDTO>();
seriesListDataProvider.addDataDisplay(seriesTable);
vPanel.add(seriesTable);
initWidget(mainPanel);
}
@@ -122,6 +112,13 @@ public class RegattaDetailsComposite extends Composite {
}
};
TextColumn<SeriesDTO> startsWithZeroScoreColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
return series.isStartsWithZeroScore() ? stringMessages.yes() : stringMessages.no();
}
};
TextColumn<SeriesDTO> racesColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
@@ -161,6 +158,29 @@ public class RegattaDetailsComposite extends Composite {
}
};
TextColumn<SeriesDTO> discardsColumn = new TextColumn<SeriesDTO>() {
@Override
public String getValue(SeriesDTO series) {
final String result;
if (series.getDiscardThresholds() == null) {
result = stringMessages.no();
} else {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (int threshold : series.getDiscardThresholds()) {
if (first) {
first = false;
} else {
sb.append(", ");
}
sb.append(threshold);
}
result = sb.toString();
}
return result;
}
};
ImagesBarColumn<SeriesDTO, SeriesConfigImagesBarCell> seriesActionColumn = new ImagesBarColumn<SeriesDTO, SeriesConfigImagesBarCell>(
new SeriesConfigImagesBarCell(stringMessages));
seriesActionColumn.setFieldUpdater(new FieldUpdater<SeriesDTO, String>() {
@@ -179,6 +199,8 @@ public class RegattaDetailsComposite extends Composite {
table.addColumn(isMedalSeriesColumn, stringMessages.medalSeries());
table.addColumn(racesColumn, stringMessages.races());
table.addColumn(fleetsColumn, stringMessages.fleets());
table.addColumn(discardsColumn, stringMessages.discarding());
table.addColumn(startsWithZeroScoreColumn, stringMessages.startsWithZeroScore());
table.addColumn(seriesActionColumn, stringMessages.actions());
return table;
@@ -194,21 +216,27 @@ public class RegattaDetailsComposite extends Composite {
}
private void editRacesOfRegattaSeries(final RegattaDTO regatta, final SeriesDTO series) {
RaceColumnInRegattaSeriesDialog raceDialog = new RaceColumnInRegattaSeriesDialog(regatta, series, stringMessages,
new DialogCallback<Pair<SeriesDTO, List<RaceColumnDTO>>>() {
SeriesEditDialog raceDialog = new SeriesEditDialog(regatta, series, stringMessages,
new DialogCallback<SeriesDescriptor>() {
@Override
public void cancel() {
}
@Override
public void ok(final Pair<SeriesDTO, List<RaceColumnDTO>> result) {
updateRacesOfRegattaSeries(regatta, result.getA(), result.getB());
public void ok(final SeriesDescriptor result) {
updateRacesOfRegattaSeries(regatta, result);
}
});
raceDialog.show();
}
private void updateRacesOfRegattaSeries(final RegattaDTO regatta, final SeriesDTO series, List<RaceColumnDTO> newRaceColumns) {
private void updateRacesOfRegattaSeries(final RegattaDTO regatta, final SeriesDescriptor seriesDescriptor) {
final SeriesDTO series = seriesDescriptor.getSeries();
final List<RaceColumnDTO> newRaceColumns = seriesDescriptor.getRaces();
final boolean isMedalChanged = series.isMedal() != seriesDescriptor.isMedal();
final boolean isStartsWithZeroScoreChanged = series.isStartsWithZeroScore() != seriesDescriptor.isStartsWithZeroScore();
final boolean seriesResultDiscardingThresholdsChanged = !Arrays.equals(series.getDiscardThresholds(),
seriesDescriptor.getResultDiscardingThresholds());
final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.name);
List<RaceColumnDTO> existingRaceColumns = series.getRaceColumns();
final List<String> raceColumnsToAdd = new ArrayList<String>();
@@ -224,7 +252,6 @@ public class RegattaDetailsComposite extends Composite {
raceColumnsToRemove.add(existingRaceColumn.name);
}
}
sailingService.addRaceColumnsToSeries(regattaIdentifier, series.name, raceColumnsToAdd, new AsyncCallback<List<RaceColumnInSeriesDTO>>() {
@Override
public void onFailure(Throwable caught) {
@@ -246,7 +273,6 @@ public class RegattaDetailsComposite extends Composite {
errorReporter.reportError("Error trying to remove race columns "
+ raceColumnsToAdd + " from series " + series.name
+ ": " + caught.getMessage());
}
@Override
@@ -254,6 +280,23 @@ public class RegattaDetailsComposite extends Composite {
regattaRefresher.fillRegattas();
}
});
if (isMedalChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged) {
sailingService.updateSeries(regattaIdentifier, series.name, seriesDescriptor.isMedal(),
seriesDescriptor.getResultDiscardingThresholds(), seriesDescriptor.isStartsWithZeroScore(),
new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error trying to remove race columns "
+ raceColumnsToAdd + " from series " + series.name
+ ": " + caught.getMessage());
}
@Override
public void onSuccess(Void result) {
regattaRefresher.fillRegattas();
}
});
}
}
private void updateRegattaDetails() {
@@ -11,9 +11,9 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants,
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas, StringMessages stringMessages,
ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) {
super(stringConstants.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringConstants, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringConstants, existingLeaderboards), callback);
super(stringMessages.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringMessages, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
nameTextBox = createTextBox(null);
displayNameTextBox = createTextBox(null);
@@ -30,11 +30,10 @@ public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
if (selectedIndex > 0) {
nameTextBox.setText(regattaListBox.getValue(selectedIndex));
}
adjustVisibilityOfResultDiscardingRuleComponent();
}
});
discardThresholdBoxes = initEmptyDiscardThresholdBoxes(this);
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
}
}
@@ -6,8 +6,6 @@ import java.util.Collections;
import java.util.List;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.VerticalPanel;
@@ -21,58 +19,62 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog {
protected ListBox regattaListBox;
protected Collection<RegattaDTO> existingRegattas;
private Label regattaDefinesDiscardsLabel;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringConstants;
protected final StringMessages stringMessages;
protected final Collection<StrippedLeaderboardDTO> existingLeaderboards;
public LeaderboardParameterValidator(StringMessages stringConstants, Collection<StrippedLeaderboardDTO> existingLeaderboards){
public LeaderboardParameterValidator(StringMessages stringMessages, Collection<StrippedLeaderboardDTO> existingLeaderboards){
super();
this.stringConstants = stringConstants;
this.stringMessages = stringMessages;
this.existingLeaderboards = existingLeaderboards;
}
@Override
public String getErrorMessage(LeaderboardDescriptor leaderboardToValidate) {
String errorMessage;
boolean discardThresholdsAscending = true;
for (int i = 1; i < leaderboardToValidate.getDiscardThresholds().length; i++) {
// TODO what are correct values for discarding Thresholds?
if (0 < leaderboardToValidate.getDiscardThresholds().length){
discardThresholdsAscending = discardThresholdsAscending
&& leaderboardToValidate.getDiscardThresholds()[i - 1] < leaderboardToValidate.getDiscardThresholds()[i]
// and if one box is empty, all subsequent boxes need to be empty too
&& (leaderboardToValidate.getDiscardThresholds()[i] == 0 || leaderboardToValidate.getDiscardThresholds()[i-1] > 0);
}
}
boolean unique = true;
for (StrippedLeaderboardDTO dao : existingLeaderboards) {
if(dao.name.equals(leaderboardToValidate.getRegattaName())){
unique = false;
}
}
boolean regattaSelected = leaderboardToValidate.getRegattaName() != null ? true : false;
if(!regattaSelected){
errorMessage = stringConstants.pleaseSelectARegatta();
errorMessage = stringMessages.pleaseSelectARegatta();
} else if(!unique){
errorMessage = stringConstants.leaderboardWithThisNameAlreadyExists();
} else if (!discardThresholdsAscending) {
errorMessage = stringConstants.discardThresholdsMustBeAscending();
errorMessage = stringMessages.leaderboardWithThisNameAlreadyExists();
} else {
errorMessage = null;
String discardThresholdErrorMessage = DiscardThresholdBoxes.getErrorMessage(
leaderboardToValidate.getDiscardThresholds(), stringMessages);
if (discardThresholdErrorMessage != null) {
errorMessage = discardThresholdErrorMessage;
} else {
errorMessage = null;
}
}
return errorMessage;
}
}
public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection<RegattaDTO> existingRegattas, StringMessages stringConstants,
public RegattaLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDTO, Collection<RegattaDTO> existingRegattas, StringMessages stringMessages,
ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, leaderboardDTO, stringConstants, validator, callback);
super(title, leaderboardDTO, stringMessages, validator, callback);
this.existingRegattas = existingRegattas;
regattaDefinesDiscardsLabel = new Label(stringMessages.regattaDefinesResultDiscardingRules());
}
protected void adjustVisibilityOfResultDiscardingRuleComponent() {
if (getSelectedRegatta().definesSeriesDiscardThresholds()) {
discardThresholdBoxes.getWidget().setVisible(false);
regattaDefinesDiscardsLabel.setVisible(true);
} else {
discardThresholdBoxes.getWidget().setVisible(true);
regattaDefinesDiscardsLabel.setVisible(false);
}
}
protected ListBox createSortedRegattaListBox(Collection<RegattaDTO> regattas, String preSelectedRegattaName) {
@@ -107,7 +109,6 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
@Override
protected Widget getAdditionalWidget() {
VerticalPanel mainPanel = new VerticalPanel();
Grid formGrid = new Grid(3,3);
formGrid.setCellSpacing(3);
formGrid.setWidget(0, 0, createLabel(stringMessages.regatta()));
@@ -116,28 +117,22 @@ public abstract class RegattaLeaderboardDialog extends AbstractLeaderboardDialog
formGrid.setWidget(1, 1, nameTextBox);
formGrid.setWidget(2, 0, createLabel(stringMessages.displayName()));
formGrid.setWidget(2, 1, displayNameTextBox);
mainPanel.add(formGrid);
mainPanel.add(new Label(stringMessages.discardRacesFromHowManyStartedRacesOn()));
HorizontalPanel hp = new HorizontalPanel();
hp.setSpacing(3);
for (int i = 0; i < discardThresholdBoxes.length; i++) {
hp.add(new Label("" + (i + 1) + "."));
hp.add(discardThresholdBoxes[i]);
mainPanel.add(regattaDefinesDiscardsLabel);
if (discardThresholdBoxes != null) {
mainPanel.add(discardThresholdBoxes.getWidget());
regattaDefinesDiscardsLabel.setVisible(false);
}
alignAllPanelWidgetsVertically(hp, HasVerticalAlignment.ALIGN_MIDDLE);
mainPanel.add(hp);
return mainPanel;
}
public RegattaDTO getSelectedRegatta() {
RegattaDTO result = null;
int selIndex = regattaListBox.getSelectedIndex();
if(selIndex > 0) { // the zero index represents the 'no selection' text
if (selIndex > 0) { // the zero index represents the 'no selection' text
String itemText = regattaListBox.getItemText(selIndex);
for(RegattaDTO regattaDTO: existingRegattas) {
if(regattaDTO.name.equals(itemText)) {
for (RegattaDTO regattaDTO : existingRegattas) {
if (regattaDTO.name.equals(itemText)) {
result = regattaDTO;
break;
}
@@ -23,6 +23,9 @@ public class RegattaLeaderboardEditDialog extends RegattaLeaderboardDialog {
regattaListBox = createSortedRegattaListBox(existingRegattas, leaderboardDescriptor.getRegattaName());
regattaListBox.setEnabled(false);
discardThresholdBoxes = initPrefilledDiscardThresholdBoxes(leaderboardDescriptor.getDiscardThresholds(), this);
if (leaderboardDescriptor.getDiscardThresholds() != null) {
discardThresholdBoxes = new DiscardThresholdBoxes(this, leaderboardDescriptor.getDiscardThresholds(), stringMessages);
} // else, the regatta leaderboard obtains its result discarding rule implicitly from the underlying regatta
adjustVisibilityOfResultDiscardingRuleComponent();
}
}
@@ -118,15 +118,17 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
}
private void createNewRegatta(final RegattaDTO newRegatta) {
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesStructure =
new LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>>();
LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesStructure =
new LinkedHashMap<String, Triple<List<Triple<String,Integer,Color>>,Pair<Boolean, Boolean>,int[]>>();
for (SeriesDTO seriesDTO : newRegatta.series) {
List<Triple<String, Integer, Color>> fleets = new ArrayList<Triple<String, Integer, Color>>();
for(FleetDTO fleetDTO : seriesDTO.getFleets()) {
Triple<String, Integer, Color> fleetTriple = new Triple<String, Integer, Color>(fleetDTO.name, fleetDTO.getOrderNo(), fleetDTO.getColor());
fleets.add(fleetTriple);
}
Pair<List<Triple<String, Integer, Color>>, Boolean> seriesPair = new Pair<List<Triple<String, Integer, Color>>, Boolean>(fleets, seriesDTO.isMedal());
Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]> seriesPair =
new Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>(
fleets, new Pair<Boolean, Boolean>(seriesDTO.isMedal(), seriesDTO.isStartsWithZeroScore()), seriesDTO.getDiscardThresholds());
seriesStructure.put(seriesDTO.name, seriesPair);
}
sailingService.createRegatta(newRegatta.name, newRegatta.boatClass==null?null:newRegatta.boatClass.name, seriesStructure, true,
@@ -106,7 +106,7 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd
protected void setupAdditionalWidgetsOnPanel(final VerticalPanel panel) {
panel.add(createHeadlineLabel(stringMessages.series()));
panel.add(seriesGrid);
Button addSeriesButton = new Button("Add series");
Button addSeriesButton = new Button(stringMessages.addSeries());
addSeriesButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
@@ -17,7 +17,6 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl
nameEntryField.setEnabled(false);
boatClassEntryField.setEnabled(false);
scoringSchemeListBox.setEnabled(false);
sailingEventsListBox.setEnabled(true);
courseAreaListBox.setEnabled(true);
}
@@ -0,0 +1,49 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.List;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
/**
* 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.
*
* @author Axel Uhl (d043530)
*
*/
public class SeriesDescriptor {
private final SeriesDTO series;
private final List<RaceColumnDTO> races;
private final boolean isMedal;
private final boolean startsWithZeroScore;
private final int[] resultDiscardingThresholds;
public SeriesDescriptor(SeriesDTO series, List<RaceColumnDTO> races, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore) {
this.series = series;
this.races = races;
this.isMedal = isMedal;
this.startsWithZeroScore = startsWithZeroScore;
this.resultDiscardingThresholds = resultDiscardingThresholds;
}
public SeriesDTO getSeries() {
return series;
}
public List<RaceColumnDTO> getRaces() {
return races;
}
public boolean isMedal() {
return isMedal;
}
public int[] getResultDiscardingThresholds() {
return resultDiscardingThresholds;
}
public boolean isStartsWithZeroScore() {
return startsWithZeroScore;
}
}
@@ -7,8 +7,11 @@ import java.util.Set;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
@@ -17,15 +20,17 @@ import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<SeriesDTO, List<RaceColumnDTO>>> {
public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
private final ListBox addRacesListBox;
private Button addRacesBtn;
private CheckBox isMedalCheckbox;
private CheckBox startWithZeroScoreCheckbox;
private CheckBox useSeriesResultDiscardingThresholdsCheckbox;
private final List<TextBox> raceNameEntryFields;
private final List<Button> raceNameDeleteButtons;
private final StringMessages stringMessages;
@@ -33,22 +38,22 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
private Grid raceColumnsGrid;
private VerticalPanel additionalWidgetPanel;
private final SeriesDTO selectedSeries;
private final DiscardThresholdBoxes discardThresholdBoxes;
private static class RaceDialogValidator implements Validator<Pair<SeriesDTO, List<RaceColumnDTO>>> {
private StringMessages stringConstants;
private static class RaceDialogValidator implements Validator<SeriesDescriptor> {
private StringMessages stringMessages;
private RegattaDTO regatta;
public RaceDialogValidator(RegattaDTO regatta, StringMessages stringConstants) {
this.stringConstants = stringConstants;
public RaceDialogValidator(RegattaDTO regatta, StringMessages stringMessages) {
this.stringMessages = stringMessages;
this.regatta = regatta;
}
@Override
public String getErrorMessage(Pair<SeriesDTO, List<RaceColumnDTO>> valueToValidate) {
public String getErrorMessage(SeriesDescriptor valueToValidate) {
Set<String> raceColumnNamesOfOtherSeries = new HashSet<String>();
SeriesDTO seriesToValidate = valueToValidate.getA();
List<RaceColumnDTO> raceColumnsToValidate = valueToValidate.getB();
SeriesDTO seriesToValidate = valueToValidate.getSeries();
List<RaceColumnDTO> raceColumnsToValidate = valueToValidate.getRaces();
String errorMessage = null;
for (SeriesDTO seriesDTO: regatta.series) {
if (!seriesDTO.name.equals(seriesToValidate.name)) {
@@ -57,10 +62,8 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
}
}
}
boolean raceColumnNameNotEmpty = true;
RaceColumnDTO wrongRaceColumn = null;
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
raceColumnNameNotEmpty = raceColumn.name != null && raceColumn.name.length() > 0;
if (!raceColumnNameNotEmpty) {
@@ -68,10 +71,8 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
break;
}
}
boolean raceColumnUniqueInSeries = true;
boolean raceColumnUniqueInRegatta = true;
HashSet<String> setToFindDuplicates = new HashSet<String>();
for (RaceColumnDTO raceColumn : raceColumnsToValidate) {
if (!setToFindDuplicates.add(raceColumn.name)) {
@@ -84,34 +85,33 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
break;
}
}
if (!raceColumnNameNotEmpty) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.pleaseEnterAName();
errorMessage = stringMessages.race() + " " + wrongRaceColumn.name + ": "
+ stringMessages.pleaseEnterAName();
} else if (!raceColumnUniqueInSeries) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.raceWithThisNameAlreadyExists();
errorMessage = stringMessages.race() + " " + wrongRaceColumn.name + ": "
+ stringMessages.raceWithThisNameAlreadyExists();
} else if (!raceColumnUniqueInRegatta) {
errorMessage = stringConstants.race() + " " + wrongRaceColumn.name + ": "
+ stringConstants.raceWithThisNameAlreadyExistsInRegatta();
errorMessage = stringMessages.race() + " " + wrongRaceColumn.name + ": "
+ stringMessages.raceWithThisNameAlreadyExistsInRegatta();
} else {
errorMessage = DiscardThresholdBoxes.getErrorMessage(valueToValidate.getResultDiscardingThresholds(), stringMessages);
}
return errorMessage;
}
}
public RaceColumnInRegattaSeriesDialog(RegattaDTO regatta, SeriesDTO selectedSeries, StringMessages stringConstants,
DialogCallback<Pair<SeriesDTO, List<RaceColumnDTO>>> callback) {
super(stringConstants.actionEditRaces(), null, stringConstants.ok(), stringConstants.cancel(),
new RaceDialogValidator(regatta, stringConstants), callback);
public SeriesEditDialog(RegattaDTO regatta, SeriesDTO selectedSeries, StringMessages stringMessages,DialogCallback<SeriesDescriptor> callback) {
super(stringMessages.actionEditRaces(), null, stringMessages.ok(), stringMessages.cancel(),
new RaceDialogValidator(regatta, stringMessages), callback);
this.selectedSeries = selectedSeries;
this.stringMessages = stringConstants;
this.stringMessages = stringMessages;
addRacesListBox = createListBox(false);
raceNamePrefixTextBox = createTextBox(null);
raceNameEntryFields = new ArrayList<TextBox>();
raceNameDeleteButtons = new ArrayList<Button>();
raceColumnsGrid = new Grid(0, 0);
discardThresholdBoxes = new DiscardThresholdBoxes(this, selectedSeries.getDiscardThresholds(), stringMessages);
}
private Widget createRaceNameWidget(String defaultName, boolean enabled) {
@@ -146,7 +146,7 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
}
@Override
protected Pair<SeriesDTO, List<RaceColumnDTO>> getResult() {
protected SeriesDescriptor getResult() {
SeriesDTO selectedSeries = getSelectedSeries();
List<RaceColumnDTO> races = new ArrayList<RaceColumnDTO>();
int racesCount = raceNameEntryFields.size();
@@ -159,7 +159,9 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
}
races.add(raceColumnDTO);
}
return new Pair<SeriesDTO, List<RaceColumnDTO>>(selectedSeries, races);
return new SeriesDescriptor(selectedSeries, races, isMedalCheckbox.getValue(),
useSeriesResultDiscardingThresholdsCheckbox.getValue() ?
discardThresholdBoxes.getDiscardThresholds() : null, startWithZeroScoreCheckbox.getValue());
}
private RaceColumnDTO findRaceColumnInSeriesByName(SeriesDTO series, String raceColumnName) {
@@ -182,18 +184,28 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
if (additionalWidget != null) {
additionalWidgetPanel.add(additionalWidget);
}
HorizontalPanel seriesPanel = new HorizontalPanel();
seriesPanel.setSpacing(3);
String seriesName = getSelectedSeries().name;
seriesPanel.add(new Label(stringMessages.series() + ": " + seriesName));
if ("Default".equals(seriesName)) {
raceNamePrefixTextBox.setText("R");
} else {
raceNamePrefixTextBox.setText(seriesName.substring(0, 1).toUpperCase());
}
additionalWidgetPanel.add(seriesPanel);
isMedalCheckbox = createCheckbox(stringMessages.medalSeries());
isMedalCheckbox.setValue(selectedSeries.isMedal());
additionalWidgetPanel.add(isMedalCheckbox);
startWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
startWithZeroScoreCheckbox.setValue(selectedSeries.isStartsWithZeroScore());
additionalWidgetPanel.add(startWithZeroScoreCheckbox);
useSeriesResultDiscardingThresholdsCheckbox = createCheckbox(stringMessages.seriesDefinesResultDiscardingRule());
useSeriesResultDiscardingThresholdsCheckbox.setValue(selectedSeries.getDiscardThresholds() != null);
useSeriesResultDiscardingThresholdsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
discardThresholdBoxes.getWidget().setVisible(event.getValue());
}
});
additionalWidgetPanel.add(useSeriesResultDiscardingThresholdsCheckbox);
additionalWidgetPanel.add(discardThresholdBoxes.getWidget());
discardThresholdBoxes.getWidget().setVisible(useSeriesResultDiscardingThresholdsCheckbox.getValue());
// add races controls
HorizontalPanel addRacesPanel = new HorizontalPanel();
addRacesPanel.setSpacing(3);
@@ -203,6 +215,11 @@ public class RaceColumnInRegattaSeriesDialog extends DataEntryDialog<Pair<Series
addRacesListBox.addItem("" + i);
}
addRacesListBox.setSelectedIndex(0);
if ("Default".equals(seriesName)) {
raceNamePrefixTextBox.setText("R");
} else {
raceNamePrefixTextBox.setText(seriesName.substring(0, 1).toUpperCase());
}
raceNamePrefixTextBox.setWidth("20px");
addRacesPanel.add(raceNamePrefixTextBox);
addRacesBtn = new Button(stringMessages.add());
@@ -9,6 +9,8 @@ import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Grid;
@@ -26,11 +28,14 @@ import com.sap.sailing.gwt.ui.shared.SeriesDTO;
public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
private StringMessages stringConstants;
private StringMessages stringMessages;
private SeriesDTO series;
private TextBox nameEntryField;
private CheckBox isMedalSeriesCheckbox;
private CheckBox startsWithZeroScoreCheckbox;
private CheckBox useSeriesResultDiscardingThresholdsCheckbox;
private DiscardThresholdBoxes discardThresholdBoxes;
private List<TextBox> fleetNameEntryFields;
private List<ListBox> fleetColorEntryFields;
@@ -39,12 +44,11 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
private Grid fleetsGrid;
protected static class SeriesParameterValidator implements Validator<SeriesDTO> {
private StringMessages stringConstants;
private StringMessages stringMessages;
private ArrayList<SeriesDTO> existingSeries;
public SeriesParameterValidator(StringMessages stringConstants, Collection<SeriesDTO> existingSeries) {
this.stringConstants = stringConstants;
this.stringMessages = stringConstants;
this.existingSeries = new ArrayList<SeriesDTO>(existingSeries);
}
@@ -60,9 +64,9 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
errorMessage = stringMessages.pleaseEnterAName();
} else if (!unique) {
errorMessage = stringConstants.seriesWithThisNameAlreadyExists();
errorMessage = stringMessages.seriesWithThisNameAlreadyExists();
}
if (errorMessage == null) {
List<FleetDTO> fleetsToValidate = seriesToValidate.getFleets();
@@ -86,24 +90,36 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
index2++;
}
if (!fleetNameNotEmpty) {
errorMessage = stringConstants.fleet() + " " + (index + 1) + ": " + stringConstants.pleaseEnterAName();
errorMessage = stringMessages.fleet() + " " + (index + 1) + ": " + stringMessages.pleaseEnterAName();
} else if (!fleetUnique) {
errorMessage = stringConstants.fleet() + " " + (index2 + 1) + ": " + stringConstants.fleetWithThisNameAlreadyExists();
errorMessage = stringMessages.fleet() + " " + (index2 + 1) + ": " + stringMessages.fleetWithThisNameAlreadyExists();
} else {
errorMessage = DiscardThresholdBoxes.getErrorMessage(seriesToValidate.getDiscardThresholds(), stringMessages);
}
}
return errorMessage;
}
}
public SeriesWithFleetsCreateDialog(Collection<SeriesDTO> existingSeries, StringMessages stringConstants,
public SeriesWithFleetsCreateDialog(Collection<SeriesDTO> existingSeries, StringMessages stringMessages,
DialogCallback<SeriesDTO> callback) {
super(stringConstants.series(), null, stringConstants.ok(), stringConstants.cancel(),
new SeriesParameterValidator(stringConstants, existingSeries), callback);
this.stringConstants = stringConstants;
super(stringMessages.series(), null, stringMessages.ok(), stringMessages.cancel(),
new SeriesParameterValidator(stringMessages, existingSeries), callback);
this.stringMessages = stringMessages;
this.series = new SeriesDTO();
nameEntryField = createTextBox(null);
nameEntryField.setVisibleLength(40);
isMedalSeriesCheckbox = createCheckbox(stringConstants.medalSeries());
isMedalSeriesCheckbox = createCheckbox(stringMessages.medalSeries());
startsWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
useSeriesResultDiscardingThresholdsCheckbox = createCheckbox(stringMessages.seriesDefinesResultDiscardingRule());
useSeriesResultDiscardingThresholdsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
discardThresholdBoxes.getWidget().setVisible(event.getValue());
}
});
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
discardThresholdBoxes.getWidget().setVisible(false);
fleetNameEntryFields = new ArrayList<TextBox>();
fleetColorEntryFields = new ArrayList<ListBox>();
fleetOrderNoEntryFields = new ArrayList<IntegerBox>();
@@ -143,7 +159,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
validate();
}
});
listBox.addItem(stringConstants.noColor());
listBox.addItem(stringMessages.noColor());
for(FleetColors value: FleetColors.values()) {
listBox.addItem(value.name());
}
@@ -164,6 +180,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
protected SeriesDTO getResult() {
series.name = nameEntryField.getText();
series.setMedal(isMedalSeriesCheckbox.getValue());
series.setStartsWithZeroScore(startsWithZeroScoreCheckbox.getValue());
List<FleetDTO> fleets = new ArrayList<FleetDTO>();
int fleetsCount = fleetNameEntryFields.size();
for(int i = 0; i < fleetsCount; i++) {
@@ -178,6 +195,7 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
fleets.add(fleetDTO);
}
series.setFleets(fleets);
series.setDiscardThresholds(useSeriesResultDiscardingThresholdsCheckbox.getValue() ? discardThresholdBoxes.getDiscardThresholds() : null);
return series;
}
@@ -204,14 +222,17 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(2, 2);
Grid formGrid = new Grid(5, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(0, 1, nameEntryField);
formGrid.setWidget(1, 1, isMedalSeriesCheckbox);
panel.add(createHeadlineLabel(stringConstants.fleets()));
formGrid.setWidget(2, 1, startsWithZeroScoreCheckbox);
formGrid.setWidget(3, 1, useSeriesResultDiscardingThresholdsCheckbox);
formGrid.setWidget(4, 1, discardThresholdBoxes.getWidget());
panel.add(createHeadlineLabel(stringMessages.fleets()));
panel.add(fleetsGrid);
Button addFleetButton = new Button(stringConstants.addFleet());
Button addFleetButton = new Button(stringMessages.addFleet());
addFleetButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
@@ -236,9 +257,9 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
int fleetCount = fleetNameEntryFields.size();
fleetsGrid = new Grid(fleetCount + 1, 3);
fleetsGrid.setCellSpacing(4);
fleetsGrid.setHTML(0, 0, stringConstants.color());
fleetsGrid.setHTML(0, 1, stringConstants.name());
fleetsGrid.setHTML(0, 2, stringConstants.rank());
fleetsGrid.setHTML(0, 0, stringMessages.color());
fleetsGrid.setHTML(0, 1, stringMessages.name());
fleetsGrid.setHTML(0, 2, stringMessages.rank());
for(int i = 0; i < fleetCount; i++) {
fleetsGrid.setWidget(i+1, 0, fleetColorEntryFields.get(i));
fleetsGrid.setWidget(i+1, 1, fleetNameEntryFields.get(i));
@@ -384,7 +384,7 @@ public abstract class DataEntryDialog<T> {
}
public void alignAllPanelWidgetsVertically(HorizontalPanel panel, HasVerticalAlignment.VerticalAlignmentConstant alignment) {
for(int i = 0; i < panel.getWidgetCount(); i++) {
for (int i = 0; i < panel.getWidgetCount(); i++) {
panel.setCellVerticalAlignment(panel.getWidget(i), alignment);
}
}
@@ -144,7 +144,7 @@ public interface SailingService extends RemoteService {
void moveLeaderboardColumnDown(String leaderboardName, String columnName);
RegattaDTO createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId);
void removeRegatta(RegattaIdentifier regattaIdentifier);
@@ -153,6 +153,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);
RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
@@ -365,11 +365,14 @@ public interface SailingServiceAsync {
AsyncCallback<Void> callback);
void createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId, AsyncCallback<RegattaDTO> callback);
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, AsyncCallback<Void> callback);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
AsyncCallback<Void> callback);
@@ -649,4 +649,8 @@ public interface StringMessages extends Messages {
String showFollowingRegattas();
String showOnlyRacesOfSameDay();
String showOnlyCurrentlyRunningRaces();
String seriesDefinesResultDiscardingRule();
String addSeries();
String regattaDefinesResultDiscardingRules();
String startsWithZeroScore();
}
@@ -642,3 +642,7 @@ showFollowingCourseAreas=Show following course areas
showFollowingRegattas=Show following regattas
showOnlyRacesOfSameDay=Show only races of same day
showOnlyCurrentlyRunningRaces=Show currently running races
seriesDefinesResultDiscardingRule=Series defines result discarding rules
addSeries=Add series
regattaDefinesResultDiscardingRules=Regatta defines result discarding rules
startsWithZeroScore=Series starts with zero score
@@ -641,3 +641,7 @@ showFollowingCourseAreas=Zeige folgende Rennbahnen
showFollowingRegattas=Zeige folgende Regatten
showOnlyRacesOfSameDay=Zeige nur Rennen desselben Tags
showOnlyCurrentlyRunningRaces=Zeige nur gerade laufende Rennen
seriesDefinesResultDiscardingRule=Serie legt Streichregel fest
addSeries=Serie hinzufügen
regattaDefinesResultDiscardingRules=Streichergebnisse werden von der Regatta festgelegt
startsWithZeroScore=Serie beginnt mit auf 0 gesetzten Ergebnissen
@@ -1201,7 +1201,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
@Override
public String getValue(LeaderboardRowDTO object) {
Double totalPoints = getLeaderboard().getTotalPoints(object);
Double totalPoints = object.totalPoints;
return "" + (totalPoints == null ? "" : scoreFormat.format(totalPoints));
}
@@ -127,6 +127,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.MetaLeaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoFactory;
@@ -265,6 +266,7 @@ import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardMaxPoin
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrection;
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboardScoreCorrectionMetadata;
import com.sap.sailing.server.operationaltransformation.UpdateRaceDelayToLive;
import com.sap.sailing.server.operationaltransformation.UpdateSeries;
import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta;
import com.sap.sailing.server.replication.ReplicaDescriptor;
import com.sap.sailing.server.replication.ReplicationFactory;
@@ -491,7 +493,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
raceColumnDTO.setExplicitFactor(raceColumn.getExplicitFactor());
raceColumns.add(raceColumnDTO);
}
SeriesDTO result = new SeriesDTO(series.getName(), fleets, raceColumns, series.isMedal());
SeriesDTO result = new SeriesDTO(series.getName(), fleets, raceColumns, series.isMedal(),
series.getResultDiscardingRule() == null ? null : series.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(),
series.isStartsWithZeroScore());
return result;
}
@@ -1612,7 +1616,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
leaderboardDTO.setDelayToLiveInMillisForLatestRace(delayToLiveInMillisForLatestRace);
leaderboardDTO.hasCarriedPoints = leaderboard.hasCarriedPoints();
leaderboardDTO.discardThresholds = leaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces();
if (leaderboard.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) {
leaderboardDTO.discardThresholds = ((ThresholdBasedResultDiscardingRule) leaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces();
} else {
leaderboardDTO.discardThresholds = null;
}
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
for (Fleet fleet : raceColumn.getFleets()) {
TimePoint latestTimePointAfterQueryTimePointWhenATrackedRaceWasLive = null;
@@ -2261,7 +2269,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
Leaderboard overallLeaderboard = leaderboardGroup.getOverallLeaderboard();
if (overallLeaderboard != null) {
groupDTO.setOverallLeaderboardDiscardThresholds(overallLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces());
if (overallLeaderboard.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) {
groupDTO.setOverallLeaderboardDiscardThresholds(((ThresholdBasedResultDiscardingRule) overallLeaderboard
.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces());
}
groupDTO.setOverallLeaderboardScoringSchemeType(overallLeaderboard.getScoringScheme().getType());
}
return groupDTO;
@@ -2506,6 +2517,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
return result;
}
@Override
public void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore) {
getService().apply(new UpdateSeries(regattaIdentifier, seriesName, isMedal, resultDiscardingThresholds, startsWithZeroScore));
}
@Override
public RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
@@ -2541,7 +2558,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public RegattaDTO createRegatta(String regattaName, String boatClassName,
LinkedHashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
LinkedHashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, String defaultCourseAreaId) {
UUID courseAreaUuid = convertIdentifierStringToUuid(defaultCourseAreaId);
Regatta regatta = getService().apply(
@@ -53,6 +53,20 @@ public class RegattaDTO extends NamedDTO {
}
return tracked;
}
/**
* @return whether this regatta defines its local per-series result discarding rules; if so, any leaderboard based
* on the regatta has to respect this and has to use a result discarding rule implementation that
* keeps discards local to each series rather than spreading them across the entire leaderboard.
*/
public boolean definesSeriesDiscardThresholds() {
for (SeriesDTO s : series) {
if (s.definesSeriesDiscardThresholds()) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
@@ -11,14 +11,18 @@ public class SeriesDTO extends NamedDTO {
private List<FleetDTO> fleets;
private List<RaceColumnDTO> raceColumns;
private boolean isMedal;
private int[] discardThresholds;
private Boolean startsWithZeroScore;
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, int[] discardThresholds, boolean startsWithZeroScore) {
super(name);
this.fleets = fleets;
this.raceColumns = raceColumns;
this.isMedal = isMedal;
this.startsWithZeroScore = startsWithZeroScore;
this.discardThresholds = discardThresholds;
}
public boolean isMedal() {
@@ -44,4 +48,29 @@ public class SeriesDTO extends NamedDTO {
public void setRaceColumns(List<RaceColumnDTO> raceColumns) {
this.raceColumns = raceColumns;
}
/**
* @return whether this series defines its local result discarding rule; if so, any leaderboard based on the
* enclosing regatta has to respect this and has to use a result discarding rule implementation that keeps
* discards local to each series rather than spreading them across the entire leaderboard.
*/
public boolean definesSeriesDiscardThresholds() {
return getDiscardThresholds() != null;
}
public int[] getDiscardThresholds() {
return discardThresholds;
}
public void setDiscardThresholds(int[] discardThresholds) {
this.discardThresholds = discardThresholds;
}
public Boolean isStartsWithZeroScore() {
return startsWithZeroScore;
}
public void setStartsWithZeroScore(Boolean startsWithZeroScore) {
this.startsWithZeroScore = startsWithZeroScore;
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.mongodb.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
@@ -53,8 +54,10 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
@@ -208,10 +211,10 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
assertEquals(Util.size(regatta.getSeries()), Util.size(loadedRegatta.getSeries()));
Leaderboard loadedLeaderboard = resForLoading.getLeaderboardByName(loadedRegatta.getName());
assertNotNull(loadedLeaderboard);
assertEquals(regattaLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces().length,
loadedLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces().length);
assertTrue(Arrays.equals(regattaLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(),
loadedLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()));
assertEquals(((ThresholdBasedResultDiscardingRule) regattaLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces().length,
((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces().length);
assertTrue(Arrays.equals(((ThresholdBasedResultDiscardingRule) regattaLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces(),
((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
assertTrue(loadedLeaderboard instanceof RegattaLeaderboard);
RegattaLeaderboard loadedRegattaLeaderboard = (RegattaLeaderboard) loadedLeaderboard;
assertSame(loadedRegatta, loadedRegattaLeaderboard.getRegatta());
@@ -283,6 +286,42 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
assertEquals(1, Util.size(loadedMedalSeries.getRaceColumns()));
}
@Test
public void testLoadStoreSimpleRegattaWithSeriesScoringScheme() {
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, regattaBaseName,
boatClass, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
regatta.getSeriesByName("Qualifying").setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(new int[] { 1, 2, 3 }));
MongoObjectFactory mof = MongoFactory.INSTANCE.getMongoObjectFactory(getMongoService());
mof.storeRegatta(regatta);
DomainObjectFactory dof = MongoFactory.INSTANCE.getDomainObjectFactory(getMongoService());
Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null);
assertTrue(Arrays.equals(new int[] { 1, 2, 3 },
loadedRegatta.getSeriesByName("Qualifying").getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()));
}
@Test
public void testLoadStoreSimpleRegattaWithScoreForMedalStartingWithZero() {
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, regattaBaseName,
boatClass, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
regatta.getSeriesByName("Medal").setStartsWithZeroScore(true);
MongoObjectFactory mof = MongoFactory.INSTANCE.getMongoObjectFactory(getMongoService());
mof.storeRegatta(regatta);
DomainObjectFactory dof = MongoFactory.INSTANCE.getDomainObjectFactory(getMongoService());
Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null);
assertFalse(loadedRegatta.getSeriesByName("Qualifying").isStartsWithZeroScore());
assertTrue(loadedRegatta.getSeriesByName("Medal").isStartsWithZeroScore());
}
@Test
public void testLoadStoreSimpleRegattaWithHighPointScoringScheme() {
final int numberOfQualifyingRaces = 5;
@@ -39,7 +39,7 @@ import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
@@ -77,21 +77,21 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup,
new HighPointExtremeSailingSeriesOverall(), new ResultDiscardingRuleImpl(new int[0]));
new HighPointExtremeSailingSeriesOverall(), new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup);
@@ -125,18 +125,18 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription = "A leaderboard group";
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard);
leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false,
leaderboard.getFleet(null));
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
racingEventService.addLeaderboard(leaderboard);
LeaderboardGroup leaderboardGroup = racingEventService.addLeaderboardGroup(groupName, groupDescription, false, Arrays.asList(leaderboardNames), new int[0],
@@ -182,23 +182,23 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false,
leaderboard.getFleet(null));
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup,
new HighPointExtremeSailingSeriesOverall(), new ResultDiscardingRuleImpl(new int[0]));
new HighPointExtremeSailingSeriesOverall(), new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
overallLeaderboard.setSuppressed(wolfgang, true);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup);
@@ -239,23 +239,23 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard.addRace(raceWithTwoCompetitors, raceColumnName1, /* medalRace */ false,
leaderboard.getFleet(null));
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
final Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup,
new HighPointExtremeSailingSeriesOverall(), new ResultDiscardingRuleImpl(new int[0]));
new HighPointExtremeSailingSeriesOverall(), new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
overallLeaderboard.setSuppressed(wolfgang, true);
overallLeaderboard.setSuppressed(hasso, true);
@@ -287,13 +287,13 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription1 = "A leaderboard group 1";
final ArrayList<Leaderboard> leaderboards1 = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards1.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards1.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards1.add(leaderboard);
final LeaderboardGroup leaderboardGroup1 = new LeaderboardGroupImpl(groupName1, groupDescription1, false, leaderboards1);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup1);
@@ -302,10 +302,10 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription2= "A leaderboard group 2";
final ArrayList<Leaderboard> leaderboards2 = new ArrayList<>();
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards2.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards2.add(leaderboard);
final LeaderboardGroup leaderboardGroup2 = new LeaderboardGroupImpl(groupName2, groupDescription2, false, leaderboards2);
mongoObjectFactory.storeLeaderboardGroup(leaderboardGroup2);
@@ -332,16 +332,16 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[3], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
@@ -370,10 +370,10 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
Leaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
leaderboard = new FlexibleLeaderboardImpl(leaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboards.add(leaderboard);
final LeaderboardGroup leaderboardGroup = new LeaderboardGroupImpl(groupName, groupDescription, false, leaderboards);
@@ -383,11 +383,11 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final FlexibleLeaderboard[] ungroupedLeaderboards = {
new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[0], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null),
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null),
new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[1], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null),
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null),
new FlexibleLeaderboardImpl(ungroupedLeaderboardNames[2], new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null) };
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null) };
mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[0]);
mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[1]);
mongoObjectFactory.storeLeaderboard(ungroupedLeaderboards[2]);
@@ -424,7 +424,7 @@ public class TestStoringAndRetrievingLeaderboardGroups extends AbstractMongoDBTe
final String groupDescription = "A leaderboard group";
final ArrayList<Leaderboard> leaderboards = new ArrayList<>();
final FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
final FlexibleLeaderboard leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
final Fleet fleet = leaderboard.getFleet(null);
final RaceColumn race = leaderboard.addRaceColumn(columnName, false, fleet);
leaderboards.add(leaderboard);
@@ -27,10 +27,11 @@ import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.persistence.impl.DomainObjectFactoryImpl;
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
@@ -48,7 +49,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
leaderboard.addRaceColumn("R1", /* medalRace */ false);
RaceColumn r2 = leaderboard.addRaceColumn("R2", /* medalRace */ false);
r2.setFactor(1.5);
@@ -66,7 +67,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor wolfgang = createCompetitor();
Competitor hasso = new CompetitorImpl(234, "Hasso Plattner", new TeamImpl("STG", Collections.singleton(
new PersonImpl("Hasso Plattner", new NationalityImpl("GER"),
@@ -95,7 +96,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, scoreCorrection,
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
scoreCorrection.setComment("Humba");
MillisecondsTimePoint now = MillisecondsTimePoint.now();
scoreCorrection.setTimePointOfLastCorrectionsValidity(now);
@@ -111,7 +112,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
SettableScoreCorrection scoreCorrection = new ScoreCorrectionImpl();
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, scoreCorrection,
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
final String raceColumnName1 = "My First Race 1";
@@ -132,12 +133,12 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard);
Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName, /* regattaRegistry */ null);
assertEquals(leaderboardName, loadedLeaderboard.getName());
assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces, loadedLeaderboard.getResultDiscardingRule()
.getDiscardIndexResultsStartingWithHowManyRaces()));
assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces,
((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
}
@Test
@@ -145,7 +146,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final String leaderboardName = "TestLeaderboard";
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new HighPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new HighPoint(), null);
new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard);
Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db).loadLeaderboard(leaderboardName, /* regattaRegistry */ null);
assertSame(HighPoint.class, loadedLeaderboard.getScoringScheme().getClass());
@@ -159,7 +160,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final double carriedPointsForWolfgangHunger = 3.7;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
leaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace */ false, leaderboard.getFleet(null));
@@ -169,8 +170,8 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
// attach tracked race to leaderboard to ensure that competitor object is assigned properly
loadedLeaderboard.addRace(raceWithOneCompetitor, raceColumnName, /* medalRace, ignored */ false, leaderboard.getFleet(null));
assertEquals(leaderboardName, loadedLeaderboard.getName());
assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces, loadedLeaderboard.getResultDiscardingRule()
.getDiscardIndexResultsStartingWithHowManyRaces()));
assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces,
((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
assertEquals(1, Util.size(loadedLeaderboard.getCompetitors()));
assertEquals(competitor, loadedLeaderboard.getCompetitors().iterator().next());
assertEquals(carriedPointsForWolfgangHunger, loadedLeaderboard.getCarriedPoints(competitor), 0.000000001);
@@ -184,7 +185,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final double correctedPoints = 2.75;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -212,7 +213,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final double correctedPoints = 2.75;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -238,7 +239,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final MaxPointsReason maxPointsReason = MaxPointsReason.DNF;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -267,7 +268,7 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest {
final MaxPointsReason maxPointsReason = MaxPointsReason.DNF;
final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 };
FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Competitor competitor = createCompetitor();
TrackedRace raceWithOneCompetitor1 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 1, /* started */ true);
TrackedRace raceWithOneCompetitor2 = new MockedTrackedRaceWithFixedRank(competitor, /* rank */ 2, /* started */ true);
@@ -28,7 +28,7 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.persistence.MongoFactory;
import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory;
@@ -68,7 +68,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD
RaceLogStore raceLogStore = MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory);
leaderboard = new FlexibleLeaderboardImpl(raceLogStore, leaderboardName, new ScoreCorrectionImpl(),
new ResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), new LowPoint(), null);
Fleet defaultFleet = leaderboard.getFleet(null);
leaderboard.addRaceColumn(raceColumnName, /* medalRace */ false, defaultFleet);
@@ -1,5 +1,7 @@
package com.sap.sailing.server.gateway.deserialization.racelog.impl;
import java.io.Serializable;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
@@ -20,7 +22,8 @@ public class RaceLogDeserializer implements JsonDeserializer<RaceLog> {
}
public RaceLog deserialize(JSONObject object) throws JsonDeserializationException {
RaceLog result = new RaceLogImpl();
Serializable id = (String)object.get(RaceLogSerializer.FIELD_RACELOG_IDENTIFIER);
RaceLog result = new RaceLogImpl(id);
if (object.get(RaceLogSerializer.FIELD_EVENTS) == null) {
return result;
@@ -10,6 +10,7 @@ import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class RaceLogSerializer implements JsonSerializer<RaceLog> {
public static final String FIELD_EVENTS = "events";
public static final String FIELD_RACELOG_IDENTIFIER = "racelog_id";
private final JsonSerializer<RaceLogEvent> itemSerializer;
@@ -27,6 +28,7 @@ public class RaceLogSerializer implements JsonSerializer<RaceLog> {
events.add(itemSerializer.serialize(event));
}
result.put(FIELD_EVENTS, events);
result.put(FIELD_RACELOG_IDENTIFIER, object.getId());
} finally {
object.unlockAfterRead();
}
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.server.operationaltransformation.AddDefaultRegatta;
import com.sap.sailing.server.operationaltransformation.AddRaceDefinition;
@@ -55,8 +56,8 @@ public class ServerReplicationTest extends AbstractServerReplicationTest {
Thread.sleep(1000); // wait 1s for JMS to deliver the message and the message to be applied
final Leaderboard replicaLeaderboard = replica.getLeaderboardByName(leaderboardName);
assertNotNull(replicaLeaderboard);
assertTrue(Arrays.equals(masterLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(),
replicaLeaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()));
assertTrue(Arrays.equals(((ThresholdBasedResultDiscardingRule) masterLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces(),
((ThresholdBasedResultDiscardingRule) replicaLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces()));
}
@Test
@@ -20,9 +20,10 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.test.AbstractLeaderboardTest;
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithFixedRank;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -47,11 +48,11 @@ public class LeaderboardDiscardingRulesTest {
FlexibleLeaderboard leaderboard = (FlexibleLeaderboard) racingEventService.getLeaderboardByName(LEADERBOARDNAME);
assertNotNull(leaderboard);
int[] discardingRulesNew = new int[] { 1, 5 };
leaderboard.setResultDiscardingRule(new ResultDiscardingRuleImpl(discardingRulesNew));
leaderboard.setCrossLeaderboardResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(discardingRulesNew));
racingEventService.updateStoredLeaderboard(leaderboard);
Leaderboard leaderboardNew = racingEventService.getLeaderboardByName(LEADERBOARDNAME);
assertNotNull(leaderboardNew);
int[] result = leaderboardNew.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces();
int[] result = ((ThresholdBasedResultDiscardingRule) leaderboardNew.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces();
assertArrayEquals(discardingRulesNew, result);
}
@@ -4,7 +4,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnListener;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogIdentifierTemplateResolver;
@@ -68,6 +68,10 @@ public class RaceLogReplicator implements RaceColumnListener {
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
@@ -94,8 +98,7 @@ public class RaceLogReplicator implements RaceColumnListener {
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
}
@@ -14,7 +14,7 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogFinishPositioningConfirmedEvent;
@@ -50,6 +50,10 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) {
}
@Override
public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) {
}
@Override
public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) {
return true;
@@ -76,8 +80,7 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
}
@Override
public void resultDiscardingRuleChanged(ThresholdBasedResultDiscardingRule oldDiscardingRule,
ThresholdBasedResultDiscardingRule newDiscardingRule) {
public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) {
}
@Override
@@ -28,6 +28,10 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.CourseArea;
@@ -66,7 +70,7 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.impl.ScoreCorrectionImpl;
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
@@ -330,7 +334,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
mongoObjectFactory,
domainObjectFactory);
CourseArea courseArea = getCourseArea(courseAreaId);
FlexibleLeaderboard result = new FlexibleLeaderboardImpl(raceLogStore, leaderboardName, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(
FlexibleLeaderboard result = new FlexibleLeaderboardImpl(raceLogStore, leaderboardName, new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
discardThresholds), scoringScheme, courseArea);
result.setDisplayName(leaderboardDisplayName);
synchronized (leaderboardsByName) {
@@ -362,7 +366,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
+ (regatta == null ? "null" : (regatta.getName() + " (" + regatta.hashCode() + ")")) + " to " + this);
RegattaLeaderboard result = null;
if (regatta != null) {
result = new RegattaLeaderboardImpl(regatta, new ScoreCorrectionImpl(), new ResultDiscardingRuleImpl(
result = new RegattaLeaderboardImpl(regatta, new ScoreCorrectionImpl(), new ThresholdBasedResultDiscardingRuleImpl(
discardThresholds));
result.setDisplayName(leaderboardDisplayName);
synchronized (leaderboardsByName) {
@@ -824,15 +828,19 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
}
}
private Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> getSeriesWithoutRaceColumnsConstructionParametersAsMap(Regatta regatta) {
Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> result = new HashMap<String, Pair<List<Triple<String, Integer, Color>>, Boolean>>();
private Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> getSeriesWithoutRaceColumnsConstructionParametersAsMap(
Regatta regatta) {
Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> result =
new HashMap<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>>();
for (Series s : regatta.getSeries()) {
assert Util.isEmpty(s.getRaceColumns());
List<Triple<String, Integer, Color>> fleetNamesAndOrdering = new ArrayList<Triple<String, Integer, Color>>();
for (Fleet f : s.getFleets()) {
fleetNamesAndOrdering.add(new Triple<String, Integer, Color>(f.getName(), f.getOrdering(), f.getColor()));
}
result.put(s.getName(), new Pair<List<Triple<String, Integer, Color>>, Boolean>(fleetNamesAndOrdering, s.isMedal()));
result.put(s.getName(), new Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>(fleetNamesAndOrdering,
new Pair<Boolean, Boolean>(s.isMedal(), s.isStartsWithZeroScore()),
s.getResultDiscardingRule() == null ? null : s.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces()));
}
return result;
}
@@ -1513,7 +1521,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
removeLeaderboard(overallLeaderboard.getName());
} else {
// update existing overall leaderboard's discards settings; scoring scheme cannot be updated in-place
overallLeaderboard.setResultDiscardingRule(new ResultDiscardingRuleImpl(overallLeaderboardDiscardThresholds));
overallLeaderboard.setCrossLeaderboardResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(overallLeaderboardDiscardThresholds));
updateStoredLeaderboard(overallLeaderboard);
}
} else if (overallLeaderboard == null && overallLeaderboardScoringSchemeType != null) {
@@ -1527,7 +1535,7 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen
private void addOverallLeaderboardToLeaderboardGroup(LeaderboardGroup leaderboardGroup,
ScoringScheme scoringScheme, int[] discardThresholds) {
Leaderboard overallLeaderboard = new LeaderboardGroupMetaLeaderboard(leaderboardGroup, scoringScheme,
new ResultDiscardingRuleImpl(discardThresholds));
new ThresholdBasedResultDiscardingRuleImpl(discardThresholds));
leaderboardGroup.setOverallLeaderboard(overallLeaderboard);
addLeaderboard(overallLeaderboard);
updateStoredLeaderboard(overallLeaderboard);
@@ -1,15 +1,9 @@
package com.sap.sailing.server.operationaltransformation;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.server.RacingEventService;
public abstract class AbstractColumnInSeriesOperation<ResultType> extends AbstractSeriesOperation<ResultType> {
private static final long serialVersionUID = -8985474505327170991L;
private static final Logger logger = Logger.getLogger(AbstractColumnInSeriesOperation.class.getName());
private final String columnName;
public AbstractColumnInSeriesOperation(RegattaIdentifier regattaIdentifier, String seriesName, String columnName) {
@@ -20,18 +14,4 @@ public abstract class AbstractColumnInSeriesOperation<ResultType> extends Abstra
protected String getColumnName() {
return columnName;
}
protected Series getSeries(RacingEventService toState) {
Series series = null;
Regatta regatta = toState.getRegatta(getRegattaIdentifier());
if (regatta != null) {
series = regatta.getSeriesByName(getSeriesName());
if (series == null) {
logger.warning("series "+getSeriesName()+" not found in regatta "+regatta.getName());
}
} else {
logger.warning("regatta "+getRegattaIdentifier()+" not found");
}
return series;
}
}
@@ -1,9 +1,15 @@
package com.sap.sailing.server.operationaltransformation;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.server.RacingEventService;
public abstract class AbstractSeriesOperation<ResultType> extends AbstractRacingEventServiceOperation<ResultType> {
private static final long serialVersionUID = -7786133902346491275L;
private Logger logger = Logger.getLogger(AbstractSeriesOperation.class.getName());
private final RegattaIdentifier regattaIdentifier;
private final String seriesName;
@@ -21,4 +27,17 @@ public abstract class AbstractSeriesOperation<ResultType> extends AbstractRacing
return seriesName;
}
protected Series getSeries(RacingEventService toState) {
Series series = null;
Regatta regatta = toState.getRegatta(getRegattaIdentifier());
if (regatta != null) {
series = regatta.getSeriesByName(getSeriesName());
if (series == null) {
logger.warning("series "+getSeriesName()+" not found in regatta "+regatta.getName());
}
} else {
logger.warning("regatta "+getRegattaIdentifier()+" not found");
}
return series;
}
}
@@ -19,7 +19,7 @@ public class AddColumnToSeries extends AbstractColumnInSeriesOperation<RaceColum
Series series = getSeries(toState);
if (series != null) {
result = series.addRaceColumn(getColumnName(), toState);
if(result != null && series.getRegatta().isPersistent()) {
if (result != null && series.getRegatta().isPersistent()) {
toState.updateStoredRegatta(series.getRegatta());
}
}
@@ -15,22 +15,23 @@ import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
public class AddSpecificRegatta extends AbstractAddRegattaOperation {
private static final long serialVersionUID = -8018855620167669352L;
private final Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrderingAndMedal;
private final Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds;
private final boolean persistent;
private final ScoringScheme scoringScheme;
private final Serializable defaultCourseAreaId;
public AddSpecificRegatta(String regattaName, String boatClassName, Serializable id,
Map<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> seriesNamesWithFleetNamesAndFleetOrdering,
Map<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds,
boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId) {
super(regattaName, boatClassName, id);
this.seriesNamesWithFleetNamesAndFleetOrderingAndMedal = seriesNamesWithFleetNamesAndFleetOrdering;
this.seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds = seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds;
this.persistent = persistent;
this.scoringScheme = scoringScheme;
this.defaultCourseAreaId = defaultCourseAreaId;
@@ -44,10 +45,14 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation {
private Iterable<? extends Series> createSeries(TrackedRegattaRegistry trackedRegattaRegistry) {
List<Series> result = new ArrayList<Series>();
for (Map.Entry<String, Pair<List<Triple<String, Integer, Color>>, Boolean>> e : seriesNamesWithFleetNamesAndFleetOrderingAndMedal.entrySet()) {
for (Map.Entry<String, Triple<List<Triple<String, Integer, Color>>, Pair<Boolean, Boolean>, int[]>> e : seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds.entrySet()) {
final List<String> emptyRaceColumnNamesList = Collections.emptyList();
Series s = new SeriesImpl(e.getKey(), /* isMedal */e.getValue().getB(), createFleets(e.getValue().getA()),
Series s = new SeriesImpl(e.getKey(), /* isMedal */e.getValue().getB().getA(), createFleets(e.getValue().getA()),
emptyRaceColumnNamesList, trackedRegattaRegistry);
if (e.getValue().getC() != null) {
s.setResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(e.getValue().getC()));
}
s.setStartsWithZeroScore(e.getValue().getB().getB());
result.add(s);
}
return result;
@@ -6,7 +6,8 @@ import java.util.Arrays;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.impl.ResultDiscardingRuleImpl;
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
@@ -43,8 +44,15 @@ public class UpdateLeaderboard extends AbstractLeaderboardOperation<Leaderboard>
toState.renameLeaderboard(getLeaderboardName(), newLeaderboardName);
}
Leaderboard leaderboard = toState.getLeaderboardByName(newLeaderboardName);
if (!Arrays.equals(leaderboard.getResultDiscardingRule().getDiscardIndexResultsStartingWithHowManyRaces(), newDiscardingThresholds)) {
leaderboard.setResultDiscardingRule(new ResultDiscardingRuleImpl(newDiscardingThresholds));
// If the new thresholds are null this means that the leaderboard is expected to obtain its result discarding
// configuration from somewhere else implicitly, e.g., an underlying regatta, and we'll leave it alone;
// Otherwise, a new threshold-based result discarding rule will be set based on the newDiscardingThresholds
// unless the leaderboard already has an equal definition.
if (newDiscardingThresholds != null
&& (!(leaderboard.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) || !Arrays
.equals(((ThresholdBasedResultDiscardingRule) leaderboard.getResultDiscardingRule())
.getDiscardIndexResultsStartingWithHowManyRaces(), newDiscardingThresholds))) {
leaderboard.setCrossLeaderboardResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(newDiscardingThresholds));
}
leaderboard.setDisplayName(newLeaderboardDisplayName);
@@ -0,0 +1,47 @@
package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
public class UpdateSeries extends AbstractSeriesOperation<Void> {
private static final long serialVersionUID = 6356089749049112710L;
private final boolean isMedal;
private final int[] resultDiscardingThresholds;
private final boolean startsWithZeroScore;
public UpdateSeries(RegattaIdentifier regattaIdentifier, String seriesName, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore) {
super(regattaIdentifier, seriesName);
this.isMedal = isMedal;
this.resultDiscardingThresholds = resultDiscardingThresholds;
this.startsWithZeroScore = startsWithZeroScore;
}
@Override
public Void internalApplyTo(RacingEventService toState) throws Exception {
Series series = getSeries(toState);
series.setIsMedal(isMedal);
series.setResultDiscardingRule(resultDiscardingThresholds == null ?
null : new ThresholdBasedResultDiscardingRuleImpl(resultDiscardingThresholds));
series.setStartsWithZeroScore(startsWithZeroScore);
if (series.getRegatta().isPersistent()) {
toState.updateStoredRegatta(series.getRegatta());
}
return null;
}
@Override
public RacingEventServiceOperation<?> transformClientOp(RacingEventServiceOperation<?> serverOp) {
// TODO Auto-generated method stub
return null;
}
@Override
public RacingEventServiceOperation<?> transformServerOp(RacingEventServiceOperation<?> clientOp) {
// TODO Auto-generated method stub
return null;
}
}
+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
SERVER_NAME=MASTER
MEMORY="1024m"
REPLICATION_CHANNEL=sapsailinganalytics-master
TELNET_PORT=14885
SERVER_PORT=8885
MONGODB_PORT=27017
EXPEDITION_PORT=2010
if [[ "$JAVA_HOME" == "" ]]; then
JAVA_HOME=$HOME/jdk1.7.0_02
echo "Setting JAVA_HOME to $JAVA_HOME"
fi
+5 -5
View File
@@ -1,11 +1,11 @@
#!/bin/sh
export TELNET_PORT=14886
export SERVER_NAME=MASTER
# source variables
. `pwd`/env.sh
export JAVA_HOME=/home/trac/jdk1.7.0_02
scriptdir=`dirname "$0"`
LIB_DIR=`pwd`/native-libraries
echo scriptdir is $scriptdir and LIB_DIR is $LIB_DIR
nohup "$JAVA_HOME/bin/java" -XX:+UseMembar -D$SERVER_NAME -Dcom.sap.sailing.server.name=$SERVER_NAME -Djava.io.tmpdir=`pwd`/tmp -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx8192m -jar plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar -console $TELNET_PORT -configuration ./ -clean &
echo Script directory is $scriptdir and LIB_DIR is $LIB_DIR
nohup "$JAVA_HOME/bin/java" -XX:+UseMembar -D$SERVER_NAME -Dcom.sap.sailing.server.name=$SERVER_NAME -Djava.io.tmpdir=`pwd`/tmp -Dfile.encoding=cp1252 -Djetty.home=$scriptdir/configuration/jetty -Djava.util.logging.config.file=$scriptdir/configuration/logging.properties -Djava.library.path=$LIB_DIR -Xmx$MEMORY -jar plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar -console $TELNET_PORT -configuration ./ -clean &
echo "Started $SERVER_NAME on port $TELNET_PORT - use telnet localhost $TELNET_PORT to connect to the OSGi console"
+7 -6
View File
@@ -1,13 +1,14 @@
#!/bin/bash
OSGI_TELNET_PORT=14886
TELNET_ACTIVE=`netstat -tlnp 2>/dev/null | grep ":$OSGI_TELNET_PORT"`
if [[ $OSGI_TELNET_PORT == "" ]] || [[ $TELNET_ACTIVE == "" ]]; then
echo "Could not find server on telnet port $OSGI_TELNET_PORT. Perhaps server is not running?"
. `pwd`/env.sh
TELNET_ACTIVE=`netstat -tlnp 2>/dev/null | grep ":$TELNET_PORT"`
if [[ $TELNET_PORT == "" ]] || [[ $TELNET_ACTIVE == "" ]]; then
echo "Could not find server on telnet port $TELNET_PORT. Perhaps server is not running?"
exit
fi
NC_CMD="nc -t 127.0.0.1 $OSGI_TELNET_PORT"
NC_CMD="nc -t 127.0.0.1 $TELNET_PORT"
echo -n exit | $NC_CMD > /dev/null
echo "Stopped server MASTER"
echo "Stopped server $SERVER_NAME"
@@ -73,7 +73,7 @@ public class OfflineDataManager extends DataManager {
competitors.add(new CompetitorImpl(UUID.randomUUID(), "Realteam", null, null));
RaceLogEventFactory factory = new RaceLogEventFactoryImpl();
RaceLog log = new RaceLogImpl();
RaceLog log = new RaceLogImpl(UUID.randomUUID());
log.add(factory.createStartTimeEvent(
new MillisecondsTimePoint(new Date().getTime() - 2000),
1,
@@ -93,7 +93,7 @@ public class OfflineDataManager extends DataManager {
StartProcedureType.ESS,
log);
log = new RaceLogImpl();
log = new RaceLogImpl(UUID.randomUUID());
/*log.add(factory.createStartTimeEvent(
new MillisecondsTimePoint(new Date()),
1,
@@ -110,7 +110,7 @@ public class OfflineDataManager extends DataManager {
StartProcedureType.ESS,
log);
log = new RaceLogImpl();
log = new RaceLogImpl(UUID.randomUUID());
/*log.add(factory.createRaceStatusEvent(
new MillisecondsTimePoint(new Date()),
5,