Merge branch 'master' into bug4847

This commit is contained in:
Robert Benedikt committed 2020-05-14 10:16:57 +02:00
commit a5d412239c
186 files changed
+1552 -1261

No files matched your search

@@ -19,7 +19,8 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sse.common,
org.eclipse.osgi,
com.sap.sailing.datamining.provider,
com.sap.sse.security.common
com.sap.sse.security.common,
com.sap.sailing.domain.shared.android
Bundle-ClassPath: .
Bundle-Activator: com.sap.sailing.datamining.Activator
Bundle-ActivationPolicy: lazy
@@ -1,5 +1,6 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sse.datamining.annotations.Connector;
@@ -11,4 +12,6 @@ public interface HasLeaderboardGroupContext {
PolarDataService getPolarDataService();
DomainFactory getBaseDomainFactory();
}
@@ -30,7 +30,7 @@ public class LeaderboardGroupRetrievalProcessor extends AbstractRetrievalProcess
if (isAborted()) {
break;
}
data.add(new LeaderboardGroupWithContext(leaderboardGroup, polarDataService));
data.add(new LeaderboardGroupWithContext(leaderboardGroup, polarDataService, element.getBaseDomainFactory()));
}
return data;
}
@@ -1,16 +1,19 @@
package com.sap.sailing.datamining.impl.data;
import com.sap.sailing.datamining.data.HasLeaderboardGroupContext;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.polars.PolarDataService;
public class LeaderboardGroupWithContext implements HasLeaderboardGroupContext {
private final LeaderboardGroup leaderboardGroup;
private final PolarDataService polarDataService;
private final DomainFactory baseDomainFactory;
public LeaderboardGroupWithContext(LeaderboardGroup leaderboardGroup, PolarDataService polarDataService) {
public LeaderboardGroupWithContext(LeaderboardGroup leaderboardGroup, PolarDataService polarDataService, DomainFactory baseDomainFactory) {
this.leaderboardGroup = leaderboardGroup;
this.polarDataService = polarDataService;
this.baseDomainFactory = baseDomainFactory;
}
@Override
@@ -22,4 +25,9 @@ public class LeaderboardGroupWithContext implements HasLeaderboardGroupContext {
public PolarDataService getPolarDataService() {
return polarDataService;
}
@Override
public DomainFactory getBaseDomainFactory() {
return baseDomainFactory;
}
}
@@ -7,8 +7,12 @@ import java.util.stream.StreamSupport;
import com.sap.sailing.datamining.data.HasLeaderboardContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.ReadonlyRaceStateImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceDefinition;
@@ -59,10 +63,26 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
@Override
public CourseArea getCourseArea() {
if(getRegatta()!=null) {
return getRegatta().getDefaultCourseArea();
final CourseArea result;
if (getRegatta() != null) {
final Fleet fleetOfTrackedRace = raceColumn.getFleetOfTrackedRace(trackedRace);
if (fleetOfTrackedRace != null) {
final RaceLog raceLog = getRegatta().getRacelog(raceColumn.getName(), fleetOfTrackedRace.getName());
if (raceLog != null) {
final ReadonlyRaceState raceState = ReadonlyRaceStateImpl
.getOrCreate(getTrackedRace().getRaceLogResolver(), raceLog);
final DomainFactory baseDomainFactory = getLeaderboardContext().getLeaderboardGroupContext().getBaseDomainFactory();
result = baseDomainFactory.getExistingCourseAreaById(raceState.getCourseAreaId());
} else {
result = null; // no race log
}
} else {
result = null; // tracked race not found in race column
}
} else {
result = null; // no regatta, therefore no race log
}
return null;
return result;
}
@Override
@@ -6,7 +6,6 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.RaceIdentifier;
@@ -29,8 +28,7 @@ public abstract class AbstractLeaderboardDTO extends NamedDTO implements Seriali
*/
public String regattaName;
public String displayName;
public UUID defaultCourseAreaId;
public String defaultCourseAreaName;
public List<CourseAreaDTO> courseAreas;
public ScoringSchemeType scoringScheme;
public LeaderboardType type;
public boolean canBoatsOfCompetitorsChangePerRace;
@@ -1,14 +1,13 @@
package com.sap.sailing.gwt.ui.shared;
package com.sap.sailing.domain.common.dto;
import java.util.UUID;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sse.security.shared.dto.NamedDTO;
/**
* Equality and hash code are based on the course area's {@link #id}.
*/
public class CourseAreaDTO extends NamedDTO implements IsSerializable {
public class CourseAreaDTO extends NamedDTO {
private static final long serialVersionUID = -5279690838452265454L;
public UUID id;
@@ -18,6 +17,10 @@ public class CourseAreaDTO extends NamedDTO implements IsSerializable {
public CourseAreaDTO(String name) {
super(name);
}
public UUID getId() {
return id;
}
@Override
public int hashCode() {
@@ -56,8 +56,7 @@ public class IncrementalLeaderboardDTO extends LeaderboardDTO implements Increme
private boolean competitorDisplayNamesUnchanged;
private boolean regattaNameUnchanged;
private boolean displayNameUnchanged;
private boolean defaultCourseAreaIdAsStringUnchanged;
private boolean defaultCourseAreaNameUnchanged;
private boolean courseAreasUnchanged;
private boolean boatClassUnchanged;
/**
@@ -352,11 +351,8 @@ public class IncrementalLeaderboardDTO extends LeaderboardDTO implements Increme
if (this.commentUnchanged) {
this.setComment(previousVersion.getComment());
}
if (this.defaultCourseAreaIdAsStringUnchanged) {
this.defaultCourseAreaId = previousVersion.defaultCourseAreaId;
}
if (this.defaultCourseAreaNameUnchanged) {
this.defaultCourseAreaName = previousVersion.defaultCourseAreaName;
if (this.courseAreasUnchanged) {
this.courseAreas = previousVersion.courseAreas;
}
if (this.displayNameUnchanged) {
this.displayName = previousVersion.displayName;
@@ -519,13 +515,9 @@ public class IncrementalLeaderboardDTO extends LeaderboardDTO implements Increme
this.displayName = null;
this.displayNameUnchanged = true;
}
if (Util.equalsWithNull(this.defaultCourseAreaId, previousVersion.defaultCourseAreaId)) {
this.defaultCourseAreaId = null;
this.defaultCourseAreaIdAsStringUnchanged = true;
}
if (Util.equalsWithNull(this.defaultCourseAreaName, previousVersion.defaultCourseAreaName)) {
this.defaultCourseAreaName = null;
this.defaultCourseAreaNameUnchanged = true;
if (Util.equalsWithNull(this.courseAreas, previousVersion.courseAreas)) {
this.courseAreas = null;
this.courseAreasUnchanged = true;
}
if (Util.equalsWithNull(this.getBoatClass(), previousVersion.getBoatClass())) {
this.setBoatClass(null);
@@ -613,25 +613,16 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
result = null;
} else {
final ScoringScheme scoringScheme = loadScoringScheme(dbLeaderboard);
Serializable courseAreaId = (Serializable) dbLeaderboard.get(FieldNames.COURSE_AREA_ID.name());
CourseArea courseArea = null;
if (courseAreaId != null) {
UUID courseAreaUuid = UUID.fromString(courseAreaId.toString());
courseArea = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid);
}
final Iterable<CourseArea> courseAreas = loadCourseAreas(dbLeaderboard);
result = new FlexibleLeaderboardImpl(getRaceLogStore(), getRegattaLogStore(),
(String) dbLeaderboard.get(FieldNames.LEADERBOARD_NAME.name()), resultDiscardingRule, scoringScheme,
courseArea);
courseAreas);
// For a FlexibleLeaderboard, there should be only the default fleet for any race column
for (Object dbRaceColumnAsObject : dbRaceColumns) {
Document dbRaceColumn = (Document) dbRaceColumnAsObject;
String columnName = (String) dbRaceColumn.get(FieldNames.LEADERBOARD_COLUMN_NAME.name());
RaceColumn raceColumn = result.addRaceColumn(columnName,
(Boolean) dbRaceColumn.get(FieldNames.LEADERBOARD_IS_MEDAL_RACE_COLUMN.name()));
Map<String, RaceIdentifier> raceIdentifiers = loadRaceIdentifiers(dbRaceColumn);
RaceIdentifier defaultFleetRaceIdentifier = raceIdentifiers.get(result.getFleet(null).getName());
if (defaultFleetRaceIdentifier == null) {
@@ -641,7 +632,6 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
if (defaultFleetRaceIdentifier != null) {
Fleet defaultFleet = result.getFleet(null);
if (defaultFleet != null) {
raceColumn.setRaceIdentifier(defaultFleet, defaultFleetRaceIdentifier);
} else {
// leaderboard has no default fleet; don't know what to do with default RaceIdentifier
@@ -650,12 +640,43 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
+ " because no default fleet was found in leaderboard");
}
}
}
}
return result;
}
/**
* Loads no, one, or several course areas from the {@link FieldNames#COURSE_AREA_ID} and the
* {@link FieldNames#COURSE_AREA_IDS} fields of the document passed. For backward compatibility, {@code null} or a
* single course area ID are accepted in {@link FieldNames#COURSE_AREA_ID}. If a non-{@code null} value is found
* there, it is looked up as a course area and dded to the result. The default way of representing the course areas
* now is in the {@link FieldNames#COURSE_AREA_IDS} field where a list is expected.
*
* @return an always valid, non-{@code null} sequence which may be empty
*/
private Iterable<CourseArea> loadCourseAreas(Document documentContainingCourseAreaIds) {
final Set<CourseArea> courseAreas = new HashSet<>();
String courseAreaId = (String) documentContainingCourseAreaIds.get(FieldNames.COURSE_AREA_ID.name());
if (courseAreaId != null) {
lookupCourseAreaAndAddIfFound(courseAreas, courseAreaId);
}
List<?> courseAreaIds = (List<?>) documentContainingCourseAreaIds.get(FieldNames.COURSE_AREA_IDS.name());
if (courseAreaIds != null) {
for (final Object courseAreaIdAsString : courseAreaIds) {
lookupCourseAreaAndAddIfFound(courseAreas, courseAreaIdAsString.toString());
}
}
return courseAreas;
}
private void lookupCourseAreaAndAddIfFound(final Set<CourseArea> courseAreas, String courseAreaIdAsString) {
UUID courseAreaUuid = UUID.fromString(courseAreaIdAsString);
final CourseArea lookupResult = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid);
if (lookupResult != null) {
courseAreas.add(lookupResult);
}
}
private ScoringScheme loadScoringScheme(Document dbLeaderboard) {
ScoringSchemeType scoringSchemeType = getScoringSchemeType(dbLeaderboard);
final ScoringScheme scoringScheme = baseDomainFactory.createScoringScheme(scoringSchemeType);
@@ -1268,12 +1289,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
@SuppressWarnings("unchecked")
Iterable<Document> dbSeries = (Iterable<Document>) dbRegatta.get(FieldNames.REGATTA_SERIES.name());
Iterable<Series> series = loadSeries(dbSeries, trackedRegattaRegistry);
Serializable courseAreaId = (Serializable) dbRegatta.get(FieldNames.COURSE_AREA_ID.name());
CourseArea courseArea = null;
if (courseAreaId != null) {
UUID courseAreaUuid = UUID.fromString(courseAreaId.toString());
courseArea = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid);
}
final Iterable<CourseArea> courseAreas = loadCourseAreas(dbRegatta);
RegattaConfiguration configuration = null;
if (dbRegatta.containsKey(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())) {
final JsonWriterSettings writerSettings = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();
@@ -1309,7 +1325,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
if (createMigratableRegatta) {
result = new MigratableRegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass,
canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, startDate, endDate, series, /* persistent */true,
loadScoringScheme(dbRegatta), id, courseArea,
loadScoringScheme(dbRegatta), id, courseAreas,
buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
@@ -1318,7 +1334,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
} else {
result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass,
canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, startDate, endDate, series, /* persistent */true,
loadScoringScheme(dbRegatta), id, courseArea,
loadScoringScheme(dbRegatta), id, courseAreas,
buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS
: buoyZoneRadiusInHullLengths,
useStartTimeInference == null ? true : useStartTimeInference,
@@ -2023,10 +2039,17 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
private RaceLogStartTimeEvent loadRaceLogStartTimeEvent(TimePoint createdAt, AbstractLogEventAuthor author,
TimePoint logicalTimePoint, Serializable id, Integer passId, List<Competitor> competitors,
Document dbObject) {
TimePoint startTime = loadTimePoint(dbObject, FieldNames.RACE_LOG_EVENT_START_TIME);
RaceLogRaceStatus nextStatus = RaceLogRaceStatus
final TimePoint startTime = loadTimePoint(dbObject, FieldNames.RACE_LOG_EVENT_START_TIME);
final UUID courseAreaId = loadCourseAreaId(dbObject);
final RaceLogRaceStatus nextStatus = RaceLogRaceStatus
.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name()));
return new RaceLogStartTimeEventImpl(createdAt, logicalTimePoint, author, id, passId, startTime, nextStatus);
return new RaceLogStartTimeEventImpl(createdAt, logicalTimePoint, author, id, passId, startTime, nextStatus, courseAreaId);
}
private UUID loadCourseAreaId(Document dbObject) {
final String courseAreaIdAsString = (String) dbObject.get(FieldNames.RACE_LOG_EVENT_COURSE_AREA_ID_AS_STRING.name());
final UUID courseAreaId = courseAreaIdAsString == null ? null : UUID.fromString(courseAreaIdAsString);
return courseAreaId;
}
private RaceLogStartOfTrackingEvent loadRaceLogStartOfTrackingEvent(TimePoint createdAt,
@@ -2055,10 +2078,11 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final Object startTimeDifferenceObject = dbObject.get(FieldNames.RACE_LOG_START_TIME_DIFFERENCE_IN_MS.name());
final Duration startTimeDifference = startTimeDifferenceObject == null ? null
: new MillisecondsDurationImpl(((Number) startTimeDifferenceObject).longValue());
RaceLogRaceStatus nextStatus = RaceLogRaceStatus
final RaceLogRaceStatus nextStatus = RaceLogRaceStatus
.valueOf((String) dbObject.get(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name()));
final UUID courseAreaId = loadCourseAreaId(dbObject);
return new RaceLogDependentStartTimeEventImpl(createdAt, logicalTimePoint, author, id, passId, dependentRaceLog,
startTimeDifference, nextStatus);
startTimeDifference, nextStatus, courseAreaId);
}
private RaceLogRaceStatusEvent loadRaceLogRaceStatusEvent(TimePoint createdAt, AbstractLogEventAuthor author,
@@ -67,6 +67,7 @@ public enum FieldNames {
COURSE_AREAS,
COURSE_AREA_NAME,
COURSE_AREA_ID,
COURSE_AREA_IDS,
BOAT_CLASS_NAME,
BOAT_CLASS_TYPICALLY_STARTS_UPWIND,
@@ -100,7 +101,7 @@ public enum FieldNames {
// race log
RACE_LOG_IDENTIFIER, RACE_LOG_EVENT, RACE_LOG_EVENT_PASS_ID, RACE_LOG_EVENT_ID, RACE_LOG_EVENT_CREATED_AT, RACE_LOG_EVENT_AUTHOR_NAME,
RACE_LOG_EVENT_AUTHOR_PRIORITY, RACE_LOG_EVENT_INVOLVED_BOATS, RACE_LOG_EVENT_FLAG_UPPER, RACE_LOG_EVENT_FLAG_LOWER, RACE_LOG_EVENT_FLAG_DISPLAYED,
RACE_LOG_EVENT_START_TIME, RACE_LOG_EVENT_CLASS, RACE_LOG_EVENT_NEXT_STATUS, RACE_LOG_COURSE_DESIGN, RACE_LOG_COURSE_DESIGNER_MODE, WAYPOINT_ID, WAYPOINT_NAME,
RACE_LOG_EVENT_START_TIME, RACE_LOG_EVENT_COURSE_AREA_ID_AS_STRING, RACE_LOG_EVENT_CLASS, RACE_LOG_EVENT_NEXT_STATUS, RACE_LOG_COURSE_DESIGN, RACE_LOG_COURSE_DESIGNER_MODE, WAYPOINT_ID, WAYPOINT_NAME,
WAYPOINT_PASSINGINSTRUCTIONS, CONTROLPOINT, CONTROLPOINT_CLASS, MARK_ID, MARK_COLOR, MARK_NAME, MARK_SHORT_NAME, MARK_PATTERN, MARK_SHAPE, MARK_TYPE,
MARK_ORIGINATING_MARK_TEMPLATE_ID, MARK_ORIGINATING_MARK_PROPERTIES_ID, RACE_LOG_COURSE_ORIGINATING_TEMPLATE_ID, RACE_LOG_COURSE_ASSOCIATED_ROLES, RACE_LOG_COURSE_ASSOCIATED_ROLES_MARK_ID, RACE_LOG_COURSE_ASSOCIATED_ROLES_ROLE_ID,
CONTROLPOINT_VALUE, CONTROLPOINTWITHTWOMARKS_ID, CONTROLPOINTWITHTWOMARKS_NAME, CONTROLPOINTWITHTWOMARKS_SHORT_NAME, CONTROLPOINTWITHTWOMARKS_LEFT, CONTROLPOINTWITHTWOMARKS_RIGHT,
@@ -30,12 +30,12 @@ public class MigratableRegattaImpl extends RegattaImpl implements MigratableRega
public <S extends Series> MigratableRegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name,
BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType,
TimePoint startDate, TimePoint endDate,
Iterable<S> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea,
Iterable<S> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, Iterable<CourseArea> courseAreas,
Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange,
RankingMetricConstructor rankingMetricConstructor, MongoObjectFactory mongoObjectFactory, String registrationLinkSecret) {
super(raceLogStore, regattaLogStore, name, boatClass, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, startDate, endDate, series,
persistent, scoringScheme, id, courseArea, buoyZoneRadiusInHullLengths, useStartTimeInference,
persistent, scoringScheme, id, courseAreas, buoyZoneRadiusInHullLengths, useStartTimeInference,
controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange,
rankingMetricConstructor, registrationLinkSecret);
this.mongoObjectFactory = mongoObjectFactory;
@@ -37,6 +37,7 @@ import com.sap.sailing.domain.abstractlog.orc.RaceLogORCScratchBoatEvent;
import com.sap.sailing.domain.abstractlog.orc.RegattaLogORCCertificateAssignmentEvent;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseAreaSpecification;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
@@ -360,11 +361,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
// at least store the scoring scheme
dbLeaderboard.put(FieldNames.SCORING_SCHEME_TYPE.name(), leaderboard.getScoringScheme().getType().name());
}
if (leaderboard.getDefaultCourseArea() != null) {
dbLeaderboard.put(FieldNames.COURSE_AREA_ID.name(), leaderboard.getDefaultCourseArea().getId().toString());
} else {
dbLeaderboard.put(FieldNames.COURSE_AREA_ID.name(), null);
}
final BasicDBList courseAreaIdsAsStrings = new BasicDBList();
Util.addAll(Util.map(leaderboard.getCourseAreas(), ca->ca.getId().toString()), courseAreaIdsAsStrings);
dbLeaderboard.put(FieldNames.COURSE_AREA_IDS.name(), courseAreaIdsAsStrings);
storeColumnFactors(leaderboard, dbLeaderboard);
storeLeaderboardCorrectionsAndDiscards(leaderboard, dbLeaderboard);
}
@@ -714,12 +713,9 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
dbRegatta.put(FieldNames.BOAT_CLASS_TYPICALLY_STARTS_UPWIND.name(), regatta.getBoatClass().typicallyStartsUpwind());
}
dbRegatta.put(FieldNames.REGATTA_SERIES.name(), storeSeries(regatta.getSeries()));
if (regatta.getDefaultCourseArea() != null) {
dbRegatta.put(FieldNames.COURSE_AREA_ID.name(), regatta.getDefaultCourseArea().getId().toString());
} else {
dbRegatta.put(FieldNames.COURSE_AREA_ID.name(), null);
}
final BasicDBList courseAreaIdsAsStrings = new BasicDBList();
Util.addAll(Util.map(regatta.getCourseAreas(), ca->ca.getId().toString()), courseAreaIdsAsStrings);
dbRegatta.put(FieldNames.COURSE_AREA_IDS.name(), courseAreaIdsAsStrings);
if (regatta.getRegattaConfiguration() != null) {
JsonSerializer<RegattaConfiguration> serializer = RegattaConfigurationJsonSerializer.create();
JSONObject json = serializer.serialize(regatta.getRegattaConfiguration());
@@ -1232,9 +1228,15 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
storeRaceLogEventProperties(startTimeEvent, result);
result.put(FieldNames.RACE_LOG_EVENT_CLASS.name(), RaceLogStartTimeEvent.class.getSimpleName());
storeTimePoint(startTimeEvent.getStartTime(), result, FieldNames.RACE_LOG_EVENT_START_TIME);
storeCourseAreaToStartTimeEvent(startTimeEvent, result);
result.put(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name(), startTimeEvent.getNextStatus().name());
return result;
}
private void storeCourseAreaToStartTimeEvent(RaceLogCourseAreaSpecification startTimeEvent, Document result) {
result.put(FieldNames.RACE_LOG_EVENT_COURSE_AREA_ID_AS_STRING.name(),
startTimeEvent.getCourseAreaId()==null?null:startTimeEvent.getCourseAreaId().toString());
}
private void storeRaceLogEventProperties(RaceLogEvent event, Document result) {
// for compatibility reasons we reuse the field name of Timed
@@ -1270,6 +1272,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.RACE_LOG_DEPDENDENT_ON_RACECOLUMN.name(), dependentStartTimeEvent.getDependentOnRaceIdentifier().getRaceColumnName());
result.put(FieldNames.RACE_LOG_DEPDENDENT_ON_FLEET.name(), dependentStartTimeEvent.getDependentOnRaceIdentifier().getFleetName());
storeDuration(dependentStartTimeEvent.getStartTimeDifference(), result, FieldNames.RACE_LOG_START_TIME_DIFFERENCE_IN_MS);
storeCourseAreaToStartTimeEvent(dependentStartTimeEvent, result);
result.put(FieldNames.RACE_LOG_EVENT_NEXT_STATUS.name(), dependentStartTimeEvent.getNextStatus().name());
return result;
}
@@ -23,7 +23,7 @@ public abstract class RaceLogTrackingTestHelper {
}
protected void setStartAndEndOfRaceInRaceLog(TimePoint startTimeInRaceLog, TimePoint endTimeInRaceLog) {
raceLog.add(new RaceLogStartTimeEventImpl(startTimeInRaceLog, author, 0, startTimeInRaceLog));
raceLog.add(new RaceLogStartTimeEventImpl(startTimeInRaceLog, author, 0, startTimeInRaceLog, /* courseAreaId */ null));
raceLog.add(new RaceLogRaceStatusEventImpl(endTimeInRaceLog, endTimeInRaceLog, author, UUID.randomUUID(), 0, RaceLogRaceStatus.FINISHED));
}
@@ -101,7 +101,8 @@ public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser",
/* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED,
/* registrationLinkSecret */ null, /* startDate */null, /* endDate */null, UUID.randomUUID(),
Collections.<Series> singletonList(series), /* persistent */ true, new HighPoint(), UUID.randomUUID(),
Collections.<Series> singletonList(series), /* persistent */ true, new HighPoint(),
service.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Default").getId(),
/* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */true,
/* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new);
series.addRaceColumn(columnName, /* trackedRegattaRegistry */null);
@@ -741,7 +741,7 @@ public class SensorFixStoreAndLoadTest {
trackedRace.attachRegattaLog(regattaLog);
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 0);
raceLog.add(new RaceLogStartTimeEventImpl(new MillisecondsTimePoint(START_OF_TRACKING), author, 0,
new MillisecondsTimePoint(START_OF_TRACKING)));
new MillisecondsTimePoint(START_OF_TRACKING), /* courseAreaId */ null));
assertNotNull(trackedRace.getStartOfTracking());
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
@@ -174,7 +174,7 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
});
assertNull(trackedRace.getStartOfRace());
final TimePoint newStartOfRace = MillisecondsTimePoint.now();
raceLog.add(new RaceLogStartTimeEventImpl(newStartOfRace, author, 0, newStartOfRace));
raceLog.add(new RaceLogStartTimeEventImpl(newStartOfRace, author, 0, newStartOfRace, /* courseAreaId */ null));
assertNull(oldAndNewStartTimeNotifiedByRace[0]);
assertEquals(newStartOfRace, oldAndNewStartTimeNotifiedByRace[1]);
assertEquals(newStartOfRace, trackedRace.getStartOfRace());
@@ -381,7 +381,7 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
});
assertNull(trackedRace.getStartOfRace());
final TimePoint newStartOfRace = MillisecondsTimePoint.now();
raceLog.add(new RaceLogStartTimeEventImpl(newStartOfRace, author, 0, newStartOfRace));
raceLog.add(new RaceLogStartTimeEventImpl(newStartOfRace, author, 0, newStartOfRace, /* courseAreaId */ null));
assertNull(oldAndNewStartTimeNotifiedByRace[0]);
assertEquals(newStartOfRace, oldAndNewStartTimeNotifiedByRace[1]);
assertEquals(newStartOfRace, trackedRace.getStartOfRace());
@@ -66,7 +66,7 @@ public class CurrentRaceFilterImplTest {
_505Series.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, _505RaceRows));
final RaceGroup _505 = new RaceGroupImpl("505", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._5O5.getDisplayName(), BoatClassMasterdata._5O5), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, _505Series, /* regattaConfiguration */ null);
_505Series, /* regattaConfiguration */ null);
return _505;
}
@@ -76,7 +76,7 @@ public class CurrentRaceFilterImplTest {
yesSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, yesRaceRows));
final RaceGroup yes = new RaceGroupImpl("YES", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, yesSeries, /* regattaConfiguration */ null);
yesSeries, /* regattaConfiguration */ null);
return yes;
}
@@ -90,7 +90,7 @@ public class CurrentRaceFilterImplTest {
isafSeries.add(new SeriesWithRowsImpl("Medal", /* isMedal */ true, /* isFleetsCanRunInParallel */ true, isafMedalRaceRows));
final RaceGroup isaf = new RaceGroupImpl("ISAF", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata._470.getDisplayName(), BoatClassMasterdata._470), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, isafSeries, /* regattaConfiguration */ null);
isafSeries, /* regattaConfiguration */ null);
return isaf;
}
@@ -110,7 +110,7 @@ public class CurrentRaceFilterImplTest {
essSeries.add(new SeriesWithRowsImpl("Day 4", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, essDay4RaceRows));
final RaceGroup ess = new RaceGroupImpl("ESS", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata.EXTREME_40.getDisplayName(), BoatClassMasterdata.EXTREME_40), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, essSeries, /* regattaConfiguration */ null);
essSeries, /* regattaConfiguration */ null);
return ess;
}
@@ -120,7 +120,7 @@ public class CurrentRaceFilterImplTest {
leagueSeries.add(new SeriesWithRowsImpl("Default", /* isMedal */ false, /* isFleetsCanRunInParallel */ false, leagueRaceRows));
final RaceGroup league = new RaceGroupImpl("League", /* displayName */ null,
new BoatClassImpl(BoatClassMasterdata.J70.getDisplayName(), BoatClassMasterdata.J70), /* canBoatsOfCompetitorsChangePerRace */ false,
/* courseArea */ null, leagueSeries, /* regattaConfiguration */ null);
leagueSeries, /* regattaConfiguration */ null);
return league;
}
@@ -63,7 +63,7 @@ public class DependentStartTimeFinderTest {
public void testDependentStartTimeUpdate() {
final MillisecondsDurationImpl cAfterB = new MillisecondsDurationImpl(5000);
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB, RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
StartTimeFinder finder = new StartTimeFinder(raceLogResolver, raceLogC);
// Tests for correct behaviour, when race depending on has no start time set
@@ -71,7 +71,7 @@ public class DependentStartTimeFinderTest {
final MillisecondsDurationImpl bAfterA = new MillisecondsDurationImpl(6000);
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA, RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
finder = new StartTimeFinder(raceLogResolver, raceLogB);
StartTimeFinderResult result = finder.analyze();
@@ -97,7 +97,7 @@ public class DependentStartTimeFinderTest {
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(5000),
RaceLogRaceStatus.SCHEDULED));
RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
finder = new StartTimeFinder(raceLogResolver, raceLogB);
result = finder.analyze();
@@ -111,7 +111,7 @@ public class DependentStartTimeFinderTest {
// Test correct behaviour, when middle element changes
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(15000),
RaceLogRaceStatus.SCHEDULED));
RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
finder = new StartTimeFinder(raceLogResolver, raceLogA);
result = finder.analyze();
assertEquals(5000, result.getStartTime().asMillis());
@@ -132,15 +132,15 @@ public class DependentStartTimeFinderTest {
public void testDependentStartTimeCycle() {
final MillisecondsDurationImpl cAfterB = new MillisecondsDurationImpl(6000);
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB, RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("B", "", ""), cAfterB, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
final MillisecondsDurationImpl bAfterA = new MillisecondsDurationImpl(7000);
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA, RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("A", "", ""), bAfterA, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
final MillisecondsDurationImpl aAfterC = new MillisecondsDurationImpl(8000);
raceLogA.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("C", "", ""), aAfterC, RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("C", "", ""), aAfterC, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
// Check that all resolve to null in case of a cycle
StartTimeFinder finder = new StartTimeFinder(raceLogResolver, raceLogC);
@@ -158,7 +158,7 @@ public class DependentStartTimeFinderTest {
// Check that all resolve correctly after changing some element in cycle
TimePoint now = MillisecondsTimePoint.now();
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(5000),
RaceLogRaceStatus.SCHEDULED));
RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
// now A -> C -> B
finder = new StartTimeFinder(raceLogResolver, raceLogA);
@@ -213,7 +213,7 @@ public class RaceStatusAnalyzerTest extends PassAwareRaceLogAnalyzerTest<RaceSta
}
private static RaceLogStartTimeEvent createStartTimeEvent(TimePoint startTime) {
return new RaceLogStartTimeEventImpl(startTime, new LogEventAuthorImpl("Author", /* priority */ 0), /* pass */ 0, startTime);
return new RaceLogStartTimeEventImpl(startTime, new LogEventAuthorImpl("Author", /* priority */ 0), /* pass */ 0, startTime, /* courseAreaId */ null);
}
private static class StartTimeVisitorAnswer implements Answer<Void> {
@@ -29,7 +29,8 @@ public class StartTimeFinderTest extends PassAwareRaceLogAnalyzerTest<StartTimeF
protected TargetPair getTargetEventsAndResultForPassAwareTests(int passId, AbstractLogEventAuthor author) {
RaceLogStartTimeEvent event = createEvent(RaceLogStartTimeEvent.class, 1, passId, author);
when(event.getStartTime()).thenReturn(mock(TimePoint.class));
return new TargetPair(Arrays.asList(event), new StartTimeFinderResult(event.getStartTime(), null));
return new TargetPair(Arrays.asList(event), new StartTimeFinderResult(event.getStartTime(),
/* startTimeDiff */ null, /* courseAreaId */ null));
}
@Test
@@ -101,11 +101,11 @@ public class DependentRaceStateTest {
@Test
public void testCorrectAmountOfStartTimeChanges() {
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(5000), RaceLogRaceStatus.SCHEDULED));
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
verify(listenerC, times(3)).onStartTimeChanged(stateC);
verify(listenerB, times(2)).onStartTimeChanged(stateB);
verify(listenerA, times(1)).onStartTimeChanged(stateA);
@@ -117,11 +117,11 @@ public class DependentRaceStateTest {
@Test
public void testCyclicStartTimeDependencyDoesNotLeadToEndlessRecursion() {
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
raceLogA.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("C", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("C", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
assertEquals(ResolutionFailed.CYCLIC_DEPENDENCY, stateA.getStartTimeFinderResult().getResolutionFailed());
}
@@ -129,7 +129,7 @@ public class DependentRaceStateTest {
public void testInitialRegistrationOfRaceState() {
final MillisecondsDurationImpl delta = new MillisecondsDurationImpl(5000);
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), delta));
new SimpleRaceLogIdentifierImpl("A", "", ""), delta, /* courseAreaId */ null));
final RaceState stateB2 = new RaceStateImpl(raceLogResolver, raceLogB, author, new RacingProcedureFactoryImpl(
author, configuration));
final TimePoint[] bTime = new TimePoint[1];
@@ -140,19 +140,19 @@ public class DependentRaceStateTest {
}
});
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, 12, now));
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, 12, now, /* courseAreaId */ null));
assertEquals(now.plus(delta), bTime[0]);
}
@Test
public void testCorrectAmountOfStartTimeChanges2() {
raceLogC.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("B", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, nowMock, author, "12", 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(5000), RaceLogRaceStatus.SCHEDULED));
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED));
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, "12", 12, new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
verify(listenerA, times(1)).onStartTimeChanged(stateA);
verify(listenerB, times(3)).onStartTimeChanged(stateB);
verify(listenerC, times(4)).onStartTimeChanged(stateC);
@@ -166,14 +166,14 @@ public class DependentRaceStateTest {
@Test
public void testLateDeliveryOfDependentStartTimeForPriorPass() {
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "1", /* pass */ 0, new MillisecondsTimePoint( 5000), RaceLogRaceStatus.SCHEDULED));
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "2", /* pass */ 0, new MillisecondsTimePoint(10000), RaceLogRaceStatus.SCHEDULED));
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "1", /* pass */ 0, new MillisecondsTimePoint( 5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "2", /* pass */ 0, new MillisecondsTimePoint(10000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
// now B enters a new pass:
raceLogB.add(new RaceLogPassChangeEventImpl(now.plus(1000), author, /* pass */ 1));
// this is expected to reset B's start time:
assertNull(stateB.getStartTime());
// now B receives an absolute start time definition for pass 1 (the second pass):
raceLogB.add(new RaceLogStartTimeEventImpl(now.plus(1000), now.plus(1000), author, /* ID */ "3", /* pass */ 1, new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED));
raceLogB.add(new RaceLogStartTimeEventImpl(now.plus(1000), now.plus(1000), author, /* ID */ "3", /* pass */ 1, new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
assertEquals(new MillisecondsTimePoint( 5000), stateA.getStartTime());
assertEquals(new MillisecondsTimePoint(20000), stateB.getStartTime());
// here comes a late event for B's pass 0, setting a relative start time; it is expected to not
@@ -182,7 +182,7 @@ public class DependentRaceStateTest {
// be established from B to A; in particular, updating A's start time is expected to leave B's
// start time unmodified.
raceLogB.add(new RaceLogDependentStartTimeEventImpl(now.plus(500), now.plus(500), author, /* ID */ "4", /* pass */ 0,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
// verify that B's race state is *not* observing A's race state
assertNull(stateB.getRaceStateToObserve());
@@ -191,7 +191,7 @@ public class DependentRaceStateTest {
assertEquals(new MillisecondsTimePoint(20000), stateB.getStartTime());
// moving A's start time is expected to NOT move B's start time:
raceLogA.add(new RaceLogStartTimeEventImpl(now.plus(10000), now.plus(10000), author, /* ID */ "5", /* pass */ 0,
/* new start time */ new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED));
/* new start time */ new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
assertEquals(new MillisecondsTimePoint(20000), stateA.getStartTime());
assertEquals(new MillisecondsTimePoint(20000), stateB.getStartTime()); // no update to B's start time expected
}
@@ -204,32 +204,32 @@ public class DependentRaceStateTest {
@Test
public void testLateDeliveryOfAbsoluteStartTimeForPrioPass() {
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "1", /* pass */ 0, new MillisecondsTimePoint( 5000), RaceLogRaceStatus.SCHEDULED));
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "2", /* pass */ 0, new MillisecondsTimePoint(10000), RaceLogRaceStatus.SCHEDULED));
raceLogA.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "1", /* pass */ 0, new MillisecondsTimePoint( 5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
raceLogB.add(new RaceLogStartTimeEventImpl(now, now, author, /* ID */ "2", /* pass */ 0, new MillisecondsTimePoint(10000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
// now B enters a new pass:
raceLogB.add(new RaceLogPassChangeEventImpl(now.plus(1000), author, /* pass */ 1));
// this is expected to reset B's start time:
assertNull(stateB.getStartTime());
// now B receives a start time definition relative to that of A:
raceLogB.add(new RaceLogDependentStartTimeEventImpl(now.plus(2000), now.plus(2000), author, /* ID */ "3", /* pass */ 1,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED));
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
assertEquals(new MillisecondsTimePoint( 5000), stateA.getStartTime());
assertEquals(stateA.getStartTime().plus(5000), stateB.getStartTime()); // B is expected to have a start time 5s after that of A due to its relative start time
// moving A's start time is expected to move B's start time:
raceLogA.add(new RaceLogStartTimeEventImpl(now.plus(10000), now.plus(10000), author, /* ID */ "4", /* pass */ 0,
/* new start time */ new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED));
/* new start time */ new MillisecondsTimePoint(20000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
assertEquals(new MillisecondsTimePoint(20000), stateA.getStartTime());
assertEquals(stateA.getStartTime().plus(5000), stateB.getStartTime()); // B is expected to have a start time 5s after that of A due to its relative start time
// here comes a late event for B's pass 0, updating the absolute start time; it is expected to not
// have any effect on B's current start time because B is already in pass 1; furthermore, it is
// expected to not have an effect on the observing relationship: when updating A's start time
// later, B's relative start time is expected to still update.
raceLogB.add(new RaceLogStartTimeEventImpl(now.plus(10), now.plus(10), author, /* ID */ "5", /* pass */ 0, new MillisecondsTimePoint(11000), RaceLogRaceStatus.SCHEDULED));
raceLogB.add(new RaceLogStartTimeEventImpl(now.plus(10), now.plus(10), author, /* ID */ "5", /* pass */ 0, new MillisecondsTimePoint(11000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
assertEquals(stateA.getStartTime().plus(5000), stateB.getStartTime()); // B is expected to still have a start time 5s after that of A due to its relative start time
// move A's start time to then check that B's still relative start time has moved as well, testing the
// continued existence of the observer relationship:
raceLogA.add(new RaceLogStartTimeEventImpl(now.plus(11000), now.plus(11000), author, /* ID */ "6", /* pass */ 0,
/* new start time */ new MillisecondsTimePoint(30000), RaceLogRaceStatus.SCHEDULED));
/* new start time */ new MillisecondsTimePoint(30000), RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null));
assertEquals(new MillisecondsTimePoint(30000), stateA.getStartTime());
assertEquals(stateA.getStartTime().plus(5000), stateB.getStartTime()); // B is expected to still have a start time 5s after that of A due to its relative start time
}
@@ -117,10 +117,8 @@ public class RaceStateTest {
@Test
public void testStartTime() {
state.addChangedListener(listener);
TimePoint startTime = MillisecondsTimePoint.now().plus(60 * 60 * 1000);
state.forceNewStartTime(nowMock, startTime);
state.forceNewStartTime(nowMock, startTime, /* courseAreaId */ null);
assertEquals(startTime, state.getStartTime());
assertEquals(RaceLogRaceStatus.SCHEDULED, state.getStatus());
verify(listener).onStartTimeChanged(state);
@@ -168,7 +166,7 @@ public class RaceStateTest {
@Test
public void testInvalidateAfterAdvancePass() throws InterruptedException {
state.addChangedListener(listener);
state.forceNewStartTime(nowMock, new MillisecondsTimePoint(1));
state.forceNewStartTime(nowMock, new MillisecondsTimePoint(1), /* courseAreaId */ null);
Thread.sleep(100);
state.setFinishedTime(new MillisecondsTimePoint(10));
state.setCourseDesign(nowMock, mock(CourseBase.class), CourseDesignerMode.ADMIN_CONSOLE);
@@ -433,14 +433,14 @@ public class RaceLogTest {
UUID client1Id = UUID.randomUUID();
UUID client2Id = UUID.randomUUID();
final TimePoint now = MillisecondsTimePoint.now();
RaceLogStartTimeEvent startTimeEvent1 = new RaceLogStartTimeEventImpl(now, author, 1, now.plus(1));
RaceLogStartTimeEvent startTimeEvent1 = new RaceLogStartTimeEventImpl(now, author, 1, now.plus(1), /* courseAreaId */ null);
Iterable<RaceLogEvent> empty = raceLog.add(startTimeEvent1, client1Id);
assertTrue(Util.isEmpty(empty));
RaceLogStartTimeEvent startTimeEvent2 = new RaceLogStartTimeEventImpl(now.plus(2), author, 1, now.plus(3));
RaceLogStartTimeEvent startTimeEvent2 = new RaceLogStartTimeEventImpl(now.plus(2), author, 1, now.plus(3), /* courseAreaId */ null);
Iterable<RaceLogEvent> nonEmpty = raceLog.add(startTimeEvent2, client2Id);
assertEquals(1, Util.size(nonEmpty));
assertSame(startTimeEvent1, nonEmpty.iterator().next());
RaceLogStartTimeEvent startTimeEvent3 = new RaceLogStartTimeEventImpl(now.plus(4), author, 1, now.plus(5));
RaceLogStartTimeEvent startTimeEvent3 = new RaceLogStartTimeEventImpl(now.plus(4), author, 1, now.plus(5), /* courseAreaId */ null);
Iterable<RaceLogEvent> nonEmpty2 = raceLog.add(startTimeEvent3, client1Id);
assertEquals(1, Util.size(nonEmpty2));
assertSame(startTimeEvent2, nonEmpty2.iterator().next());
@@ -56,7 +56,7 @@ public class SerializeRaceLogEventsTest {
public void testRaceLogSerializationWithSingleEvent() throws IOException, ClassNotFoundException {
RaceLogStartTimeEvent startTimeEvent = new RaceLogStartTimeEventImpl(MillisecondsTimePoint.now(),
new LogEventAuthorImpl("Author Name", /* priority */0), /* passId */1, /* startTime */
MillisecondsTimePoint.now());
MillisecondsTimePoint.now(), /* courseAreaId */ null);
raceLog.add(startTimeEvent);
oos.writeObject(raceLog);
ObjectInputStream ois = getObjectInputStream();
@@ -0,0 +1,15 @@
package com.sap.sailing.domain.abstractlog.race;
import java.util.UUID;
import com.sap.sailing.domain.base.CourseArea;
/**
* Specifies a {@link CourseArea course area} by its UUID
*
* @author Axel Uhl (d043530)
*
*/
public interface RaceLogCourseAreaSpecification {
UUID getCourseAreaId();
}
@@ -3,7 +3,7 @@ package com.sap.sailing.domain.abstractlog.race;
import com.sap.sse.common.Duration;
public interface RaceLogDependentStartTimeEvent extends RaceLogRaceStatusEvent {
public interface RaceLogDependentStartTimeEvent extends RaceLogRaceStatusEvent, RaceLogCourseAreaSpecification {
Duration getStartTimeDifference();
@@ -4,12 +4,11 @@ import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sse.common.TimePoint;
/**
* A {@link RaceLogStartTimeEvent} denotes a new start time.
* A {@link RaceLogStartTimeEvent} denotes a new start time.<p>
*
* Each {@link RaceLogStartTimeEvent} is also a {@link RaceLogRaceStatusEvent} with
* {@link RaceLogRaceStatusEvent#getNextStatus()} returning {@link RaceLogRaceStatus#SCHEDULED}.
*/
public interface RaceLogStartTimeEvent extends RaceLogRaceStatusEvent {
public interface RaceLogStartTimeEvent extends RaceLogRaceStatusEvent, RaceLogCourseAreaSpecification {
TimePoint getStartTime();
}
@@ -29,16 +29,15 @@ public class DependentStartTimeResolver {
SimpleRaceLogIdentifier identifier = event.getDependentOnRaceIdentifier();
Duration startTimeDifference = event.getStartTimeDifference();
RaceLog raceLog = raceLogResolver.resolve(identifier);
final StartTimeFinderResult result;
if (raceLog == null) {
result = new StartTimeFinderResult(dependingOnRaces, startTimeDifference, ResolutionFailed.RACE_LOG_UNRESOLVED);
result = new StartTimeFinderResult(dependingOnRaces, startTimeDifference, ResolutionFailed.RACE_LOG_UNRESOLVED, /* courseAreaId */ null);
} else {
List<SimpleRaceLogIdentifier> extendedDependingOnRaces = new ArrayList<>();
Util.addAll(dependingOnRaces, extendedDependingOnRaces);
extendedDependingOnRaces.add(identifier);
if (containsCycle(extendedDependingOnRaces)) {
result = new StartTimeFinderResult(extendedDependingOnRaces, null, ResolutionFailed.CYCLIC_DEPENDENCY);
result = new StartTimeFinderResult(extendedDependingOnRaces, null, ResolutionFailed.CYCLIC_DEPENDENCY, /* courseAreaId */ null);
} else {
StartTimeFinder dependentStartTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
StartTimeFinderResult resultOfDependentRace = dependentStartTimeFinder.analyze(extendedDependingOnRaces);
@@ -47,7 +46,8 @@ public class DependentStartTimeResolver {
result = resultOfDependentRace;
} else {
result = new StartTimeFinderResult(resultOfDependentRace.getDependingOnRaces(),
resultOfDependentRace.getStartTime().plus(startTimeDifference), startTimeDifference);
resultOfDependentRace.getStartTime().plus(startTimeDifference), startTimeDifference,
event.getCourseAreaId());
}
}
}
@@ -29,14 +29,17 @@ public class StartTimeFinder extends RaceLogAnalyzer<StartTimeFinderResult> {
try {
for (RaceLogEvent event : getPassEventsDescending()) {
if (event instanceof RaceLogStartTimeEvent) {
TimePoint startTime = ((RaceLogStartTimeEvent) event).getStartTime();
return new StartTimeFinderResult(dependingOnRaces, startTime, null);
final RaceLogStartTimeEvent startTimeEvent = (RaceLogStartTimeEvent) event;
final TimePoint startTime = startTimeEvent.getStartTime();
return new StartTimeFinderResult(dependingOnRaces, startTime, null,
startTimeEvent.getCourseAreaId());
} else if (event instanceof RaceLogDependentStartTimeEvent) {
DependentStartTimeResolver dependentStartTimeResolver = new DependentStartTimeResolver(resolver);
final DependentStartTimeResolver dependentStartTimeResolver = new DependentStartTimeResolver(resolver);
return dependentStartTimeResolver.internalResolve((RaceLogDependentStartTimeEvent) event, dependingOnRaces);
}
}
return new StartTimeFinderResult(dependingOnRaces, null, null, ResolutionFailed.NO_START_TIME_SET);
return new StartTimeFinderResult(dependingOnRaces, null, null,
ResolutionFailed.NO_START_TIME_SET, /* courseAreaId */ null);
} finally {
log.unlockAfterRead();
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.abstractlog.race.analyzing.impl;
import java.util.Collections;
import java.util.UUID;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sse.common.Duration;
@@ -20,6 +21,7 @@ public class StartTimeFinderResult {
private final Iterable<SimpleRaceLogIdentifier> dependingOnRaces;
private final TimePoint startTime;
private final UUID courseAreaId;
/**
* <code>null</code> if resolution worked
@@ -28,24 +30,25 @@ public class StartTimeFinderResult {
private Duration startTimeDiff;
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime, Duration startTimeDiff) {
this(racesDependingOn, startTime, startTimeDiff, /* resolutionFailed */ null);
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime, Duration startTimeDiff, UUID courseAreaId) {
this(racesDependingOn, startTime, startTimeDiff, /* resolutionFailed */ null, courseAreaId);
}
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, Duration startTimeDiff, ResolutionFailed resolutionFailed) {
this(racesDependingOn, /* startTime */ null, startTimeDiff, resolutionFailed);
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, Duration startTimeDiff, ResolutionFailed resolutionFailed, UUID courseAreaId) {
this(racesDependingOn, /* startTime */ null, startTimeDiff, resolutionFailed, courseAreaId);
}
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> dependingOnRaces, TimePoint startTime, Duration startTimeDiff,
ResolutionFailed resolutionFailed) {
ResolutionFailed resolutionFailed, UUID courseAreaId) {
this.startTime = startTime;
this.startTimeDiff = startTimeDiff;
this.dependingOnRaces = dependingOnRaces;
this.resolutionFailed = resolutionFailed;
this.courseAreaId = courseAreaId;
}
public StartTimeFinderResult(TimePoint startTime, Duration startTimeDiff) {
this(/* racesDependingOn */ Collections.<SimpleRaceLogIdentifier>emptyList(), startTime, startTimeDiff);
public StartTimeFinderResult(TimePoint startTime, Duration startTimeDiff, UUID courseAreaId) {
this(/* racesDependingOn */ Collections.<SimpleRaceLogIdentifier>emptyList(), startTime, startTimeDiff, courseAreaId);
}
/**
@@ -79,6 +82,10 @@ public class StartTimeFinderResult {
return dependingOnRaces != null && !Util.isEmpty(dependingOnRaces);
}
public UUID getCourseAreaId() {
return courseAreaId;
}
@Override
public int hashCode() {
final int prime = 31;
@@ -87,6 +94,7 @@ public class StartTimeFinderResult {
result = prime * result + ((resolutionFailed == null) ? 0 : resolutionFailed.hashCode());
result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());
result = prime * result + ((startTimeDiff == null) ? 0 : startTimeDiff.hashCode());
result = prime * result + ((courseAreaId == null) ? 0 : courseAreaId.hashCode());
return result;
}
@@ -116,6 +124,11 @@ public class StartTimeFinderResult {
return false;
} else if (!startTimeDiff.equals(other.startTimeDiff))
return false;
if (courseAreaId == null) {
if (other.courseAreaId != null)
return false;
} else if (!courseAreaId.equals(other.courseAreaId))
return false;
return true;
}
}
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.abstractlog.race.impl;
import java.io.Serializable;
import java.util.UUID;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.Revokable;
@@ -16,19 +17,21 @@ public class RaceLogDependentStartTimeEventImpl extends RaceLogRaceStatusEventIm
private static final long serialVersionUID = -2555082771473210123L;
private final Duration startTimeDifference;
private SimpleRaceLogIdentifier dependentOnRace;
private final SimpleRaceLogIdentifier dependentOnRace;
private final UUID courseAreaId;
public RaceLogDependentStartTimeEventImpl(TimePoint createdAt, TimePoint pTimePoint, AbstractLogEventAuthor author,
Serializable pId, int pPassId, SimpleRaceLogIdentifier dependentOnRace,
Duration startTimeDifference, RaceLogRaceStatus nextStatus) {
Duration startTimeDifference, RaceLogRaceStatus nextStatus, UUID courseAreaId) {
super(createdAt, pTimePoint, author, pId, pPassId, nextStatus);
this.dependentOnRace = dependentOnRace;
this.startTimeDifference = startTimeDifference;
this.courseAreaId = courseAreaId;
}
public RaceLogDependentStartTimeEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author, int pPassId,
SimpleRaceLogIdentifier dependentOnRace, Duration startTimeDifference) {
this(now(), logicalTimePoint, author, randId(), pPassId, dependentOnRace, startTimeDifference, RaceLogRaceStatus.SCHEDULED);
SimpleRaceLogIdentifier dependentOnRace, Duration startTimeDifference, UUID courseAreaId) {
this(now(), logicalTimePoint, author, randId(), pPassId, dependentOnRace, startTimeDifference, RaceLogRaceStatus.SCHEDULED, courseAreaId);
}
@Override
@@ -36,6 +39,11 @@ public class RaceLogDependentStartTimeEventImpl extends RaceLogRaceStatusEventIm
return startTimeDifference;
}
@Override
public UUID getCourseAreaId() {
return courseAreaId;
}
@Override
public void accept(RaceLogEventVisitor visitor) {
visitor.visit((RaceLogDependentStartTimeEvent) this);
@@ -43,7 +51,8 @@ public class RaceLogDependentStartTimeEventImpl extends RaceLogRaceStatusEventIm
@Override
public String getShortInfo() {
return "dependentOnRace=" + dependentOnRace + "startTimeDifference=" + startTimeDifference;
return "dependentOnRace=" + dependentOnRace + "startTimeDifference=" + startTimeDifference
+(getCourseAreaId()==null?"":(", course area ID="+getCourseAreaId().toString()));
}
@Override
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.abstractlog.race.impl;
import java.io.Serializable;
import java.util.UUID;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
@@ -9,19 +10,20 @@ import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sse.common.TimePoint;
public class RaceLogStartTimeEventImpl extends RaceLogRaceStatusEventImpl implements RaceLogStartTimeEvent {
private static final long serialVersionUID = 8185811395997196162L;
private final TimePoint startTime;
private final UUID courseAreaId;
public RaceLogStartTimeEventImpl(TimePoint createdAt, TimePoint pTimePoint, AbstractLogEventAuthor author,
Serializable pId, int pPassId, TimePoint pStartTime, RaceLogRaceStatus nextStatus) {
Serializable pId, int pPassId, TimePoint pStartTime, RaceLogRaceStatus nextStatus, UUID courseAreaId) {
super(createdAt, pTimePoint, author, pId, pPassId, nextStatus);
this.startTime = pStartTime;
this.courseAreaId = courseAreaId;
}
public RaceLogStartTimeEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author, int pPassId,
TimePoint pStartTime) {
this(now(), logicalTimePoint, author, randId(), pPassId, pStartTime, RaceLogRaceStatus.SCHEDULED);
TimePoint pStartTime, UUID courseAreaId) {
this(now(), logicalTimePoint, author, randId(), pPassId, pStartTime, RaceLogRaceStatus.SCHEDULED, courseAreaId);
}
@Override
@@ -29,6 +31,11 @@ public class RaceLogStartTimeEventImpl extends RaceLogRaceStatusEventImpl implem
return startTime;
}
@Override
public UUID getCourseAreaId() {
return courseAreaId;
}
@Override
public void accept(RaceLogEventVisitor visitor) {
visitor.visit(this);
@@ -36,7 +43,7 @@ public class RaceLogStartTimeEventImpl extends RaceLogRaceStatusEventImpl implem
@Override
public String getShortInfo() {
return "startTime=" + startTime;
return "startTime=" + startTime+(getCourseAreaId()==null?"":(", course area ID="+getCourseAreaId().toString()));
}
}
@@ -5,13 +5,9 @@ import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.scoring.AdditionalScoringInformationType;
import com.sap.sailing.domain.abstractlog.race.scoring.RaceLogAdditionalScoringInformationEvent;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedurePrerequisite;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedurePrerequisite.Resolver;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.RacingProcedurePrerequisiteAutoResolver;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.Wind;
@@ -21,10 +17,12 @@ import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import java.util.UUID;
/**
* Extension to the {@link ReadonlyRaceState} allowing write-access to the state of a race.
*
* All methods changing the state of the race (e.g. {@link RaceState#forceNewStartTime(TimePoint,TimePoint)}) map to a
* All methods changing the state of the race (e.g. {@link RaceState#forceNewStartTime(TimePoint, TimePoint, UUID)}) map to a
* single add on the underlying {@link RaceLog}. You are in charge of advancing the pass identifier (
* {@link RaceState#setAdvancePass(TimePoint)} ) at appropriate times.
*
@@ -75,62 +73,25 @@ public interface RaceState extends ReadonlyRaceState {
* Forces a new start time without checking for any prerequisites to be fulfilled. Although there is no technical
* reason for the prerequisites to be fulfilled before setting a start time event, there might be clients of this
* {@link RaceState} that expect them to be. Therefore use this method with caution.
*
*
* @param now
* logical {@link TimePoint} the start time event should be attached to.
* @param courseAreaId
*/
void forceNewStartTime(TimePoint now, TimePoint startTime);
void forceNewStartTime(TimePoint now, TimePoint startTime, UUID courseAreaId);
/**
* Forces a new dependent start time without checking for any prerequisites to be fulfilled. Although there is no technical
* reason for the prerequisites to be fulfilled before setting a start time event, there might be clients of this
* {@link RaceState} that expect them to be. Therefore use this method with caution.
*
* @param now
* @param now
* logical {@link TimePoint} the start time event should be attached to.
* @param startTimeDifference
* difference in startTime to dependentRace
* @param dependentRace
* Identifier of the race the startTime requested to set depends on
* @param courseAreaId
*/
void forceNewDependentStartTime(TimePoint now, Duration startTimeDifference, SimpleRaceLogIdentifier dependentRace);
/**
* Starts a request to set a new start time. Before the start time is set the
* {@link RacingProcedurePrerequisite.Resolver} must fulfill all prerequisites set by the currently active
* {@link RacingProcedure}.
*
* @param now
* logical {@link TimePoint} the start time event (and all events created due to fulfilled prerequisites)
* should be attached to.
* @param startTime
* start time to be set
* @param resolver
* Object used to fulfill all {@link RacingProcedurePrerequisite}s. If you just want to use reasonable
* defaults pass a {@link RacingProcedurePrerequisiteAutoResolver}.
*/
void requestNewStartTime(TimePoint now, TimePoint startTime, RacingProcedurePrerequisite.Resolver resolver);
/**
* Starts a request to set a new dependent start time. Before the start time is set the
* {@link RacingProcedurePrerequisite.Resolver} must fulfill all prerequisites set by the currently active
* {@link RacingProcedure}.
*
* @param now
* logical {@link TimePoint} the start time event (and all events created due to fulfilled prerequisites)
* should be attached to.
* @param startTimeDifference
* difference in startTime to dependentRace
* @param dependentRace
* Identifier of the race the startTime requested to set depends on
* @param raceLogResolver
* Needed in order to fetch the RaceLog of the dependentRace
* @param resolver
* Object used to fulfill all {@link RacingProcedurePrerequisite}s. If you just want to use reasonable
* defaults pass a {@link RacingProcedurePrerequisiteAutoResolver}.
*/
void requestNewDependentStartTime(TimePoint now, Duration startTimeDifference, SimpleRaceLogIdentifier dependentRace, RaceLogResolver raceLogResolver,
Resolver resolver);
void forceNewDependentStartTime(TimePoint now, Duration startTimeDifference, SimpleRaceLogIdentifier dependentRace, UUID courseAreaId);
/**
* Sets the finishing time.
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.abstractlog.race.state;
import java.util.UUID;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
@@ -11,6 +13,7 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderRes
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEventComparator;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.ReadonlyRacingProcedure;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.common.Wind;
@@ -44,7 +47,6 @@ import com.sap.sse.common.TimeRange;
*
*/
public interface ReadonlyRaceState extends RaceStateEventProcessor {
/**
* Accesses the underlying {@link RaceLog}.
*/
@@ -153,4 +155,13 @@ public interface ReadonlyRaceState extends RaceStateEventProcessor {
* Returns an iterable of tag events which may be empty but never <code>null</code>.
*/
Iterable<RaceLogTagEvent> getTagEvents();
/**
* In the last valid start time event looks for a course area specification. If found,
* the course area ID is returned. It represents the {@link CourseArea#getId() ID} of the
* course area on which a start time was set last. If no start time event is present in
* the underlying race log, or none of those start time events specifies a course area ID,
* {@code null} is returned.
*/
UUID getCourseAreaId();
}
@@ -1,16 +1,15 @@
package com.sap.sailing.domain.abstractlog.race.state.impl;
import java.util.Collections;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.AdditionalScoringInformationFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.DependentStartTimeResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceStatusAnalyzer;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogCourseDesignChangedEventImpl;
@@ -31,7 +30,6 @@ import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedureFactory;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedurePrerequisite;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.RacingProcedureFactoryImpl;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.configuration.ConfigurationLoader;
@@ -115,46 +113,15 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
}
@Override
public void requestNewStartTime(final TimePoint now, final TimePoint startTime, RacingProcedurePrerequisite.Resolver resolver) {
RacingProcedurePrerequisite.FulfillmentFunction function = new RacingProcedurePrerequisite.FulfillmentFunction() {
@Override
public void execute() {
raceLog.add(new RaceLogStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), startTime));
}
};
getRacingProcedure().checkPrerequisitesForStart(now, startTime, function).resolve(resolver);
public void forceNewStartTime(TimePoint now, TimePoint startTime, UUID courseAreaId) {
raceLog.add(new RaceLogStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), startTime, courseAreaId));
}
@Override
public void forceNewStartTime(TimePoint now, TimePoint startTime) {
raceLog.add(new RaceLogStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), startTime));
public void forceNewDependentStartTime(TimePoint now, final Duration startTimeDifference, final SimpleRaceLogIdentifier dependentOnRace, UUID courseAreaId) {
raceLog.add(new RaceLogDependentStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), dependentOnRace,
startTimeDifference, courseAreaId));
}
@Override
public void requestNewDependentStartTime(final TimePoint now, final Duration startTimeDifference, final SimpleRaceLogIdentifier dependentRace, RaceLogResolver raceLogResolver, RacingProcedurePrerequisite.Resolver resolver) {
final RaceLogDependentStartTimeEvent dependentStartTimeEvent = new RaceLogDependentStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), dependentRace, startTimeDifference);
RacingProcedurePrerequisite.FulfillmentFunction function = new RacingProcedurePrerequisite.FulfillmentFunction() {
@Override
public void execute() {
raceLog.add(dependentStartTimeEvent);
}
};
TimePoint startTime = null;
DependentStartTimeResolver dependentStartTimeResolver = new DependentStartTimeResolver(raceLogResolver);
startTime = dependentStartTimeResolver.resolve(dependentStartTimeEvent).getStartTime();
getRacingProcedure().checkPrerequisitesForStart(now, startTime, function).resolve(resolver);
}
@Override
public void forceNewDependentStartTime(TimePoint now, final Duration startTimeDifference, final SimpleRaceLogIdentifier dependentOnRace) {
raceLog.add(new RaceLogDependentStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), dependentOnRace, startTimeDifference));
}
@Override
public void setFinishingTime(TimePoint timePoint) {
@@ -5,6 +5,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
@@ -634,4 +635,9 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
// let's do an update because status might have changed with new procedure
update(dependentRaceStates);
}
@Override
public UUID getCourseAreaId() {
return cachedStartTimeFinderResult == null ? null : cachedStartTimeFinderResult.getCourseAreaId();
}
}
@@ -58,7 +58,6 @@ public abstract class BaseRacingProcedurePrerequisite implements RacingProcedure
if (resolver == null) {
throw new IllegalArgumentException("resolver must not be null");
}
this.resolver = resolver;
resolveOn(resolver);
}
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.base.racegroup;
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProcedure;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sse.common.Named;
@@ -30,11 +29,6 @@ import com.sap.sse.common.Named;
*
*/
public interface RaceGroup extends Named {
/**
* Gets the {@link CourseArea} associated with the {@link RaceGroup}.
*/
public CourseArea getDefaultCourseArea();
/**
* Gets the associated {@link BoatClass} if any present, otherwise
* <code>null</code>.
@@ -1,7 +1,6 @@
package com.sap.sailing.domain.base.racegroup.impl;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.base.racegroup.RaceGroup;
import com.sap.sailing.domain.base.racegroup.SeriesWithRows;
@@ -13,7 +12,6 @@ public class RaceGroupImpl extends NamedImpl implements RaceGroup {
private final String displayName;
private final BoatClass boatClass;
private final CourseArea courseArea;
private final Iterable<SeriesWithRows> series;
private final RegattaConfiguration regattaConfiguration;
private final boolean canBoatsOfCompetitorsChangePerRace;
@@ -23,13 +21,12 @@ public class RaceGroupImpl extends NamedImpl implements RaceGroup {
* the series; for a regatta they are expected to be in the same order as {@code Regatta.getSeries()}
* would deliver them.
*/
public RaceGroupImpl(String name, String displayName, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CourseArea courseArea,
Iterable<SeriesWithRows> series, RegattaConfiguration regattaConfiguration) {
public RaceGroupImpl(String name, String displayName, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, Iterable<SeriesWithRows> series,
RegattaConfiguration regattaConfiguration) {
super(name);
this.displayName = displayName;
this.boatClass = boatClass;
this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace;
this.courseArea = courseArea;
this.series = series;
this.regattaConfiguration = regattaConfiguration;
}
@@ -39,11 +36,6 @@ public class RaceGroupImpl extends NamedImpl implements RaceGroup {
return displayName;
}
@Override
public CourseArea getDefaultCourseArea() {
return courseArea;
}
@Override
public Iterable<SeriesWithRows> getSeries() {
return series;
@@ -12,6 +12,7 @@ import org.junit.Test;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
@@ -68,7 +69,8 @@ public class RegattaLogAttachOnTrackedRaceTest {
@Before
public void setUp() {
leaderboard = new FlexibleLeaderboardImpl(EmptyRaceLogStore.INSTANCE, new MyMockedRegattaLogStore(), "", null, new LowPoint(), null);
leaderboard = new FlexibleLeaderboardImpl(EmptyRaceLogStore.INSTANCE, new MyMockedRegattaLogStore(), "", null, new LowPoint(),
/* no course area */ (CourseArea) null);
}
@Test
@@ -71,10 +71,10 @@ public class TrackedRaceStartTimeUpdateForDependentStartTimeTest extends TrackBa
final AbstractLogEventAuthor author = new LogEventAuthorImpl("Axel", 0);
final Duration startTimeDiff = Duration.ONE_MINUTE;
r2RaceLog.add(new RaceLogDependentStartTimeEventImpl(MillisecondsTimePoint.now(), author, /* pass */ 0,
new SimpleRaceLogIdentifierImpl(r1.getTrackedRegatta().getRegatta().getName(), "R1", "Default"), startTimeDiff));
new SimpleRaceLogIdentifierImpl(r1.getTrackedRegatta().getRegatta().getName(), "R1", "Default"), startTimeDiff, /* courseAreaId */ null));
assertNull(r2StartTime[0]);
final TimePoint r1StartTimeToSet = MillisecondsTimePoint.now();
r1RaceLog.add(new RaceLogStartTimeEventImpl(r1StartTimeToSet, author, /* pass */ 0, r1StartTimeToSet));
r1RaceLog.add(new RaceLogStartTimeEventImpl(r1StartTimeToSet, author, /* pass */ 0, r1StartTimeToSet, /* courseAreaId */ null));
assertNotNull(r1StartTime[0]);
assertEquals(r1StartTimeToSet, r1StartTime[0]);
assertNotNull(r2StartTime[0]);
@@ -414,12 +414,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public CourseArea getDefaultCourseArea() {
public Iterable<CourseArea> getCourseAreas() {
return null;
}
@Override
public void setDefaultCourseArea(CourseArea newCourseArea) {
public void setCourseAreas(Iterable<CourseArea> newCourseAreas) {
}
@Override
@@ -151,7 +151,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
getTracTracEvent().getId()+" does not contain the IControl with ID "+
cp.getId()+" which is used by race "+tractracRace.getId()+
". Adding control point adapter based on race data.");
ttControlPointsForAllOriginalEventControlPoints.put(cp, new ControlPointAdapter(cp));
ttControlPointsForAllOriginalEventControlPoints.put(cp, new ControlPointAdapter(cp));
}
ttControlPoints.add(new Pair<TracTracControlPoint, PassingInstruction>(ttControlPointsForAllOriginalEventControlPoints.get(cp), passingInstructions));
i++;
@@ -28,7 +28,6 @@ import com.tractrac.subscription.lib.api.SubscriberInitializationException;
import com.tractrac.util.lib.api.exceptions.TimeOutException;
public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTrackingConnectivityParameters {
private static final long serialVersionUID = 5088282956033068149L;
private static final Logger logger = Logger.getLogger(RaceTrackingConnectivityParametersImpl.class.getName());
public static final String TYPE = "TRAC_TRAC";
@@ -81,11 +80,11 @@ public class RaceTrackingConnectivityParametersImpl extends AbstractRaceTracking
final IRace tractracRace = getTractracRace();
if (preferReplayIfAvailable && isReplayRace(tractracRace) &&
(!Util.equalsWithNull(liveURI, tractracRace.getLiveURI()) || !Util.equalsWithNull(storedURI, tractracRace.getStoredURI()))) {
logger.info("Replay format available and preferred for race " + tractracRace.getName()
+ "; using storedURI " + tractracRace.getStoredURI() + " instead of " + storedURI
+ " and liveURI " + tractracRace.getLiveURI() + " instead of " + liveURI);
this.liveURI = tractracRace.getLiveURI();
this.storedURI = tractracRace.getStoredURI();
logger.info("Replay format available and preferred for race " + tractracRace.getName()
+ "; using storedURI " + tractracRace.getStoredURI() + " instead of " + storedURI
+ " and liveURI " + tractracRace.getLiveURI() + " instead of " + liveURI);
this.liveURI = tractracRace.getLiveURI();
this.storedURI = tractracRace.getStoredURI();
} else {
this.liveURI = liveURI;
this.storedURI = storedURI;
@@ -36,7 +36,7 @@ public interface LeaderboardSearchResult extends LeaderboardSearchResultBase {
* If there is a connection between an {@link Event} and the {@link #getLeaderboard() leaderboard}, e.g., because
* the {@link Event} has a {@link Event#getLeaderboardGroups() leaderboard group} that is part of {@link #getLeaderboardGroups()} or
* the event has a course area attached to its {@link Event#getVenue() venue} that is referenced by the leaderboard as
* its {@link Leaderboard#getDefaultCourseArea() default course area}, the event is part of the resulting iterable.
* its {@link Leaderboard#getCourseAreas() default course area}, the event is part of the resulting iterable.
*/
Iterable<Event> getEvents();
}
@@ -101,6 +101,19 @@ public interface RaceColumn extends Named {
*/
TrackedRace getTrackedRace(Fleet fleet);
/**
* Finds the {@link Fleet} from {@link #getFleets()} such that {@link #getTrackedRace(Fleet)} returns {@code trackedRace}
* for that fleet and returns it. If no such fleet can be found in this race column, {@code null} is returned.
*/
default Fleet getFleetOfTrackedRace(TrackedRace trackedRace) {
for (final Fleet fleet : getFleets()) {
if (getTrackedRace(fleet) == trackedRace) {
return fleet;
}
}
return null;
}
/**
* If a race is associated with this column for the <code>fleet</code>, the respective {@link RaceDefinition} is returned.
* Otherwise, <code>null</code> is returned.
@@ -60,19 +60,20 @@ public interface Regatta
void setEndDate(TimePoint startDate);
/**
* Gets the course area for all races of this {@link Regatta}.
* Gets the course areas that the races of this {@link Regatta} are expected to be run on. This can, e.g., be used
* to implement a filter when retrieving regattas from an event.
*
* @return the {@link CourseArea} object.
* @return the {@link CourseArea} objects on which races of this regatta may run; always valid, never
* {@code null}, but may be empty; callers need to {@code synchronize} on the object returned
* if they want to iterate.
*/
CourseArea getDefaultCourseArea();
Iterable<CourseArea> getCourseAreas();
/**
* Sets the course area for all races of this {@link Regatta}.
*
* @param newCourseArea
* {@link CourseArea} to be set.
* Sets the course areas, telling where races in this regatta can be sailed. Replaces the course areas set
* so far.
*/
void setDefaultCourseArea(CourseArea newCourseArea);
void setCourseAreas(Iterable<CourseArea> newCourseAreas);
/**
* Gets the {@link RegattaConfiguration} associated with this {@link Regatta}'s races.
@@ -122,7 +122,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
private final RankingMetricConstructor rankingMetricConstructor;
private Double buoyZoneRadiusInHullLengths;
private CourseArea defaultCourseArea;
/**
* A synchronized list; synchronize on the object monitor before iterating or modifying
*/
private List<CourseArea> courseAreas;
private RegattaConfiguration configuration;
private RaceExecutionOrderCache raceExecutionOrderCache;
@@ -231,6 +235,21 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
scoringScheme, id, courseArea, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, controlTrackingFromStartAndFinishTimes,
autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor, registrationLinkSecret);
}
public <S extends Series> RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name,
BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType,
TimePoint startDate, TimePoint endDate, Iterable<S> series, boolean persistent,
ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange,
RankingMetricConstructor rankingMetricConstructor, String registrationLinkSecret) {
this(raceLogStore, regattaLogStore, name,
boatClass, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType,
startDate, endDate, series, persistent,
scoringScheme, id, courseArea==null?Collections.emptySet():Collections.singleton(courseArea),
buoyZoneRadiusInHullLengths, useStartTimeInference,
controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange,
rankingMetricConstructor, registrationLinkSecret);
}
/**
* @param series
@@ -240,7 +259,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
public <S extends Series> RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name,
BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType,
TimePoint startDate, TimePoint endDate, Iterable<S> series, boolean persistent,
ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
ScoringScheme scoringScheme, Serializable id, Iterable<CourseArea> courseAreas, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange,
RankingMetricConstructor rankingMetricConstructor, String registrationLinkSecret) {
super(name);
@@ -268,7 +287,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
}
this.persistent = persistent;
this.scoringScheme = scoringScheme;
this.defaultCourseArea = courseArea;
this.courseAreas = Collections.synchronizedList(new ArrayList<>());
Util.addAll(courseAreas, this.courseAreas);
this.configuration = null;
this.buoyZoneRadiusInHullLengths = buoyZoneRadiusInHullLengths;
this.regattaLikeHelper = new BaseRegattaLikeImpl(new RegattaAsRegattaLikeIdentifier(this), regattaLogStore) {
@@ -662,13 +682,16 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
}
@Override
public CourseArea getDefaultCourseArea() {
return defaultCourseArea;
public Iterable<CourseArea> getCourseAreas() {
return courseAreas;
}
@Override
public void setDefaultCourseArea(CourseArea newCourseArea) {
this.defaultCourseArea = newCourseArea;
public void setCourseAreas(Iterable<CourseArea> newCourseAreas) {
synchronized (this.courseAreas) {
this.courseAreas.clear();
Util.addAll(newCourseAreas, this.courseAreas);
}
}
@Override
@@ -69,8 +69,9 @@ public interface FlexibleLeaderboard extends LeaderboardThatHasRegattaLike, Rena
void updateIsMedalRace(String raceName, boolean isMedalRace);
/**
* Sets the default {@link CourseArea} of this leaderboard.
* @param newCourseArea the {@link CourseArea} to be set.
* Sets the {@link CourseArea}s for this leaderboard, telling on which course areas races in this leaderboard may
* happen. Afterwards, {@link #getCourseAreas()} will return an {@link Iterable} that has exactly the elements
* contained in {@code newCourseAreas}.
*/
void setDefaultCourseArea(CourseArea newCourseArea);
void setCourseAreas(Iterable<CourseArea> newCourseAreas);
}
@@ -646,10 +646,13 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
TimePoint getNowMinusDelay();
/**
* Get the default {@link CourseArea} of this leaderboard.
* @return the default {@link CourseArea} for all races of this leaderboard.
* Gets the course areas that the races of this leaderboard are expected to be run on. This can, e.g., be used to
* implement a filter when retrieving leaderboards from an event.
*
* @return the {@link CourseArea} objects on which races of this leaderboard may run; always valid, never
* {@code null}, but may be empty
*/
CourseArea getDefaultCourseArea();
Iterable<CourseArea> getCourseAreas();
/**
* Must be called when the leaderboard is removed from its server, becoming inaccessible. This will give the leaderboard
@@ -748,16 +751,7 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
}
default boolean isPartOfEvent(EventBase event) {
boolean result = false;
if (getDefaultCourseArea() != null) {
for (CourseArea courseArea : event.getVenue().getCourseAreas()) {
if(courseArea.equals(getDefaultCourseArea())) {
result = true;
break;
}
}
}
return result;
return Util.containsAny(event.getVenue().getCourseAreas(), getCourseAreas());
}
/**
@@ -937,7 +937,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW
public String toString() {
return getName() + " "
+ (getDefaultCourseArea() != null ? getDefaultCourseArea().getName() : "<No course area defined>") + " "
+ Util.join(", ", getCourseAreas()) + " "
+ (getScoringScheme() != null ? getScoringScheme().getType().name() : "<No scoring scheme set>");
}
@@ -422,8 +422,8 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
return getFullLeaderboard().getNowMinusDelay();
}
public CourseArea getDefaultCourseArea() {
return getFullLeaderboard().getDefaultCourseArea();
public Iterable<CourseArea> getCourseAreas() {
return getFullLeaderboard().getCourseAreas();
}
public NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher() {
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util;
/**
* A leaderboard implementation that allows users to flexibly configure which columns exist. No constraints need to be
@@ -72,7 +73,11 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
private final ScoringScheme scoringScheme;
private String name;
private transient RaceLogStore raceLogStore;
private CourseArea courseArea;
/**
* A synchronized list; obtain the object monitor in order to iterate over the contents!
*/
private List<CourseArea> courseAreas;
private RaceExecutionOrderProvider raceExecutionOrderProvider;
/**
@@ -89,7 +94,15 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
public FlexibleLeaderboardImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String name, ThresholdBasedResultDiscardingRule resultDiscardingRule,
ScoringScheme scoringScheme, CourseArea courseArea) {
this(raceLogStore, regattaLogStore, name, resultDiscardingRule, scoringScheme,
courseArea == null ? Collections.emptySet() : Collections.singleton(courseArea));
}
public FlexibleLeaderboardImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String name, ThresholdBasedResultDiscardingRule resultDiscardingRule,
ScoringScheme scoringScheme, Iterable<CourseArea> courseAreas) {
super(resultDiscardingRule);
assert courseAreas != null;
this.scoringScheme = scoringScheme;
if (name == null) {
throw new IllegalArgumentException("A leaderboard's name must not be null");
@@ -97,7 +110,8 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
this.name = name;
this.races = new ArrayList<>();
this.raceLogStore = raceLogStore;
this.courseArea = courseArea;
this.courseAreas = Collections.synchronizedList(new ArrayList<>());
Util.addAll(courseAreas, this.courseAreas);
this.regattaLikeHelper = new BaseRegattaLikeImpl(new FlexibleLeaderboardAsRegattaLikeIdentifier(this), regattaLogStore) {
private static final long serialVersionUID = 4082392360832548953L;
@@ -307,14 +321,21 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
return scoringScheme;
}
/**
* Callers need to {@code synchronize} on the result when iterating the elements in order to
* be safe regarding concurrent modifications through {@link #setCourseAreas(Iterable)}.
*/
@Override
public CourseArea getDefaultCourseArea() {
return courseArea;
public Iterable<CourseArea> getCourseAreas() {
return courseAreas;
}
@Override
public void setDefaultCourseArea(CourseArea newCourseArea) {
this.courseArea = newCourseArea;
public void setCourseAreas(Iterable<CourseArea> newCourseAreas) {
synchronized (this.courseAreas) {
this.courseAreas.clear();
Util.addAll(newCourseAreas, this.courseAreas);
}
}
@Override
@@ -87,8 +87,8 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
}
@Override
public CourseArea getDefaultCourseArea() {
return regatta.getDefaultCourseArea();
public Iterable<CourseArea> getCourseAreas() {
return regatta.getCourseAreas();
}
/**
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.leaderboard.meta;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.sap.sailing.domain.base.CourseArea;
@@ -59,8 +60,8 @@ public class FlexibleMetaLeaderboard extends AbstractMetaLeaderboard {
}
@Override
public CourseArea getDefaultCourseArea() {
return null;
public Iterable<CourseArea> getCourseAreas() {
return Collections.emptySet();
}
@Override
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.leaderboard.meta;
import java.util.Collections;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
@@ -87,8 +89,8 @@ public class LeaderboardGroupMetaLeaderboard extends AbstractMetaLeaderboard imp
}
@Override
public CourseArea getDefaultCourseArea() {
return null;
public Iterable<CourseArea> getCourseAreas() {
return Collections.emptySet();
}
@Override
@@ -141,7 +141,7 @@ public class TopLevelMasterData implements Serializable {
/**
* Workaround to look for the events connected to RegattaLeadeboards. There should be a proper connection between
* regatta and event soon. TODO
* regatta and event soon. TODO See bugs 1896 and 1532
*/
private Map<LeaderboardGroup, Set<Event>> createEventMap(Set<LeaderboardGroup> groupsToExport,
Iterable<Event> allEvents) {
@@ -156,9 +156,8 @@ public class TopLevelMasterData implements Serializable {
HashSet<Event> eventSet = new HashSet<Event>();
eventsForLeaderboardGroup.put(leaderboardGroup, eventSet);
for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) {
CourseArea courseArea = leaderboard.getDefaultCourseArea();
if (courseArea != null) {
Event event = eventForCourseArea.get(courseArea);
for (final CourseArea courseArea : leaderboard.getCourseAreas()) {
final Event event = eventForCourseArea.get(courseArea);
if (event != null) {
eventSet.add(event);
}
@@ -10,7 +10,6 @@ public class SAPHeaderComponentSettings extends AbstractGenericSerializableSetti
@Override
protected void addChildSettings() {
super.addChildSettings();
title = new StringSetting("title", this,"");
}
@@ -46,7 +46,7 @@ public interface HasRegattaMetadata {
boolean isFlexibleLeaderboard();
String getDefaultCourseAreaId();
Iterable<String> getCourseAreaIdsAsStrings();
EventSeriesReferenceDTO getSeriesReference();
}
@@ -6,6 +6,7 @@ import java.util.Date;
import com.sap.sailing.gwt.home.communication.event.EventSeriesReferenceDTO;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Util;
import com.sap.sse.gwt.dispatch.shared.commands.DTO;
public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegattaMetadata {
@@ -14,7 +15,7 @@ public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegatt
private String boatClass;
private ArrayList<String> leaderboardGroupNames;
private String defaultCourseAreaName;
private String defaultCourseAreaId;
private ArrayList<String> courseAreaIdsAsStrings;
private Date startDate;
private Date endDate;
private RegattaState state;
@@ -111,12 +112,13 @@ public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegatt
this.flexibleLeaderboard = flexibleLeaderboard;
}
public String getDefaultCourseAreaId() {
return defaultCourseAreaId;
public Iterable<String> getCourseAreaIdsAsStrings() {
return courseAreaIdsAsStrings;
}
public void setDefaultCourseAreaId(String defaultCourseAreaId) {
this.defaultCourseAreaId = defaultCourseAreaId;
public void setCourseAreaIdsAsStrings(Iterable<String> courseAreaIdsAsStrings) {
this.courseAreaIdsAsStrings = new ArrayList<>();
Util.addAll(courseAreaIdsAsStrings, this.courseAreaIdsAsStrings);
}
public RaceDataInfo getRaceDataInfo() {
@@ -243,10 +243,9 @@ public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> ex
RegattaRaceStatesSettings regattaRaceStatesSettings = new RegattaRaceStatesSettings();
regattaRaceStatesSettings.setShowOnlyCurrentlyRunningRaces(false);
regattaRaceStatesSettings.setShowOnlyRaceOfSameDay(eventDTO.isRunning());
if(showRegattaMetadata()) {
if(getRegattaMetadata().isFlexibleLeaderboard()) {
String defaultCourseAreaId = getRegattaMetadata().getDefaultCourseAreaId();
if(defaultCourseAreaId != null && !defaultCourseAreaId.isEmpty()) {
if (showRegattaMetadata()) {
if (getRegattaMetadata().isFlexibleLeaderboard()) {
for (final String defaultCourseAreaId : getRegattaMetadata().getCourseAreaIdsAsStrings()) {
regattaRaceStatesSettings.getVisibleCourseAreaSettings().addValue(UUID.fromString(defaultCourseAreaId));
}
} else {
@@ -2,8 +2,86 @@
<html>
<body>
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sail InSight (Android)</h4>
<h4 class="articleHeadline">What's New - SAP Sail InSight</h4>
<div class="innerContent">
<h5 class="articleSubheadline">08.04.2020 - Version 2.3.8</h5>
<ul class="bulletList">
<li>Added MTCP communications module.</li>
<li>Fixed the bug that allowed you to get the waypoint name editing form field on start/finish waypoints.</li>
<li>Fixed issue with first character being capitalized on password input on Android devices.</li>
<li>Swap the order of first two steps in Event Overview page.</li>
<li>Changed mark tracking screen to make binding marks visually more comprehensible.</li>
<li>Added splash screen on Android.</li>
<li>Fixed navigation issue when quickly pressing sub-sections one by one on Account page.</li>
<li>Fixed issue with events become unresponsive after starting tracking for an invited event.</li>
<li>Fixed issue with anonymous users loses joined events after restarting the app.</li>
</ul>
<h5 class="articleSubheadline">18.03.2020 - Version 2.2.0</h5>
<ul class="bulletList">
<li>Regatta details and races are now refreshed when selecting an event.</li>
<li>Fixed an issue with updating competitor ACL on the settings specified server instead of event server when joining an event.</li>
<li>Fixed an issue that prevented anonymous users from joining events due to an unnecessary call to update competitor ACL.</li>
<li>Course creator is now updated showing the new device binding when coming out of the ‘Scan QR Code’ screen for mark.</li>
<li>Display time gaps in the format 'DD:HH:mm:ss' in the leaderboard. Layout fixes to leaderboard screen.</li>
<li>Fixed a layout issue in course creator to extend the mark background to the lower edge of the screen.</li>
<li>Complete refactor of app navigation code, fixing various navigation issues.</li>
<li>Fixed an issue that showed an empty boat class field for owners in handicap based events.</li>
<li>Registration form is now cleared after a successful register.</li>
<li>Fixed an issue that hides the 'New team' button in teams screen when scrolling.</li>
</ul>
<h5 class="articleSubheadline">06.03.2020 - Version 2.1.1</h5>
<ul class="bulletList">
<li>Fixed an issue that stopped tracking when tapping on tracking tab while being in leaderboard view.</li>
<li>Fixed an issue that made the Android back button not working properly in screens other than tracking.</li>
<li>Fixed an issue where the app didn't navigate properly after registering a new user.</li>
</ul>
<h5 class="articleSubheadline">03.03.2020 - Version 2.0.2</h5>
<ul class="bulletList">
<li>React Navigation dependency upgraded to the latest version.</li>
<li>Visual fixes in leaderboard screen.</li>
<li>Added localized text for undefined course or missing start times in race details.</li>
</ul>
<h5 class="articleSubheadline">02.03.2020 - Version 2.0.0</h5>
<ul class="bulletList">
<li>Fixed leaderboard layout issues on certain devices.</li>
<li>Competitors and boats are assigned ‘READ_ALL’ permissions when joining an event.</li>
<li>Removed deletion of locally stored gps fixes when tracking is started. This fixes scenarios related to tracking being interrupted and restarted.</li>
<li>Fixed an issue that prevented start tracking while offline.</li>
<li>Color code other competitors in the leaderboard based on whether they're ahead or behind my competitor.</li>
<li>Reduce the length of the mantissa to 1 when converting between handicap value types.</li>
<li>Fixed issue with Scan QR Code button overlapping the last item in Tracking screen.</li>
<li>Fixed issue with first save of a new course with mark position change of existing marks from inventory.</li>
<li>Fixed issue that sometimes prevented start time being set on races while tracking was active.</li>
</ul>
<h5 class="articleSubheadline">20.02.2020 - Version 1.0.64</h5>
<ul class="bulletList">
<li>Fixed visuals for boat handicap style selector in Team details.</li>
<li>Default generated event names are now only placeholders in Event Creation screen, applied only if the input is left empty.</li>
<li>Default generated event names are now only placeholders in Event Creation screen, applied only if the input is left empty.</li>
<li>Fixed issue with fixed gps position overriding a tracker binding when saving a course right after binding was successfully made.</li>
<li>Fixed issue with ‘Join As Competitor’ button not being removed after an organizer joins an event he created.</li>
<li>Fixed issue where ‘Scan QR Code’ button overlapped the last event card in events list, making it hard to tap on the event.</li>
<li>Fixed issue where app was crashing when clicking on leaderboard dropdown.</li>
<li>Fixed issue with competitors not being able to see each other in the leaderboard due to permissions.</li>
<li>Fixed issue with missing multidex support on Android, making certain devices crash on app start.</li>
<li>Replaced hard to read font in event organizer view.</li>
</ul>
<h5 class="articleSubheadline">19.02.2020 - Version 1.0.63</h5>
<ul class="bulletList">
<li>Fixed issue with tracking screen being closed when tapping multiple times on the tracking tab.</li>
<li>Added loading indicator on the button when creating a new event.</li>
<li>Added link to SAP race board for current tracked race from tracking screen.</li>
<li>Main tab bar hidden while device keyboard is shown.</li>
<li>Improved back button handling in Android devices.</li>
<li>Removed the modal popup presented to the user that blocked joining an event which is already added to the user's device.</li>
</ul>
<h5 class="articleSubheadline">March 2018</h5>
<ul class="bulletList">
<li>Made app robust against events that have no start/end dates set</li>
@@ -24,6 +102,15 @@
and the automatic overriding of user preferences has been removed.</li>
<li>In some rare situations the app crashed, if you want to scan a QR-Code.</li>
</ul>
<h5 class="articleSubheadline">October 2016 - SAP Sail InSight (iOS)</h5>
<ul class="bulletList">
<li>This release introduces an entirely new design.</li>
<li>The landscape mode is optimized for small devices.</li>
<li>Unsent GPS-fixes will be sent automatically after a sudden reboot of the device.</li>
<li>Fixed an issue where a login could be done twice.</li>
<li>Fixed an issue where an invalid login is handled as a successful one.</li>
</ul>
<h5 class="articleSubheadline">August 2016</h5>
@@ -43,17 +130,7 @@
start tracking for one buoy. It will track all buoys.</li>
</ul>
</div>
<h4 class="articleHeadline">What's New - SAP Sail InSight (iOS)</h4>
<div class="innerContent">
<h5 class="articleSubheadline">October 2016</h5>
<ul class="bulletList">
<li>This release introduces an entirely new design.</li>
<li>The landscape mode is optimized for small devices.</li>
<li>Unsent GPS-fixes will be sent automatically after a sudden reboot of the device.</li>
<li>Fixed an issue where a login could be done twice.</li>
<li>Fixed an issue where an invalid login is handled as a successful one.</li>
</ul>
</div>
</div>
</body>
</html>
@@ -4,6 +4,16 @@
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4>
<div class="innerContent">
<h5 class="articleSubheadline">May 2020</h5>
<ul class="bulletList">
<li>Leaderboards and regattas can now be configured to take place on more than one course area.
This way, users on different course areas can see the races from those regattas in their
SAP Sailing Race Manager app race inventory. A typical scenario is a regatta racing the opening
series in split fleets (Yellow/Blue during qualification, Gold/Silver during final) where different
fleets sail on different course areas at the same time. The course area you choose during log-in
is now recorded in the start time events which allows the web site to show which race runs
on which course area, even if the regatta races on multiple course areas.</li>
</ul>
<h5 class="articleSubheadline">April 2020</h5>
<ul class="bulletList">
<li>The date of the start time can now be chosen via a suitable date picker.
@@ -269,8 +269,8 @@ public class LeaderboardContext {
regattaDTO.setEndDate(getEndDateWithEventFallback());
}
regattaDTO.setState(calculateRegattaState());
regattaDTO.setDefaultCourseAreaName(HomeServiceUtil.getCourseAreaNameForRegattaIdThereIsMoreThanOne(event, leaderboard));
regattaDTO.setDefaultCourseAreaId(HomeServiceUtil.getCourseAreaIdForRegatta(event, leaderboard));
regattaDTO.setDefaultCourseAreaName(HomeServiceUtil.getCourseAreaNameForRegattaIfThereIsMoreThanOne(event, leaderboard));
regattaDTO.setCourseAreaIdsAsStrings(HomeServiceUtil.getCourseAreaIdsAsStringsForRegatta(event, leaderboard));
regattaDTO.setFlexibleLeaderboard(leaderboard instanceof FlexibleLeaderboard);
RegattaRaceDataInfoCalculator regattaRaceDataInfoCalculator = new RegattaRaceDataInfoCalculator();
@@ -255,7 +255,7 @@ public class RaceContext {
}
private String getCourseAreaOrNull() {
return HomeServiceUtil.getCourseAreaNameForRegattaIdThereIsMoreThanOne(event, leaderboard);
return HomeServiceUtil.getCourseAreaNameForRegattaIfThereIsMoreThanOne(event, leaderboard);
}
private RaceProgressDTO getProgressOrNull() {
@@ -144,75 +144,62 @@ public class RaceDetailPanel extends SimplePanel {
}
private void updateUi() {
if (data == null) {
return;
}
String statusText = flagInterpreter.getMeaningOfRaceStateAndFlags(data.raceInfo.lastStatus,
data.raceInfo.lastUpperFlag, data.raceInfo.lastLowerFlag, data.raceInfo.lastFlagsAreDisplayed);
String headerLabelText = data.regattaDisplayName + " " + data.raceInfo.fleetName + " " + data.raceInfo.raceName
+ " (" + statusText + ")";
headerLabel.setText(stringMessages.showingDetailsOfRace(headerLabelText));
String startTimeLabelText = data.raceInfo.startTime == null ? "-" : timeFormatter
.format(data.raceInfo.startTime);
startTimeLabel.setText(startTimeLabelText);
String startDateLabelText = data.raceInfo.startTime == null ? "" : dateFormatter
.format(data.raceInfo.startTime);
startDateLabel.setText(startDateLabelText);
String finishTimeText = data.raceInfo.finishedTime == null ? "-" : timeFormatter
.format(data.raceInfo.finishedTime);
finishTimeLabel.setText(finishTimeText);
String finishDurationText = "";
if (data.raceInfo.finishedTime != null && data.raceInfo.startTime != null) {
finishDurationText = "(" + durationFormatter.format(data.raceInfo.startTime, data.raceInfo.finishedTime)
+ ")";
}
finishDurationLabel.setText(finishDurationText);
String protestStartTimeText = data.raceInfo.protestStartTime == null ? "-" : timeFormatter
.format(data.raceInfo.protestStartTime);
protestStartTimeLabel.setText(protestStartTimeText);
String protestFinishTimeText = data.raceInfo.protestFinishTime == null ? "-" : timeFormatter
.format(data.raceInfo.protestFinishTime);
protestFinishTimeLabel.setText(protestFinishTimeText);
String protestStartDateText = data.raceInfo.protestStartTime == null ? "" : dateFormatter
.format(data.raceInfo.protestStartTime);
protestStartDateLabel.setText(protestStartDateText);
String protestFinishDateText = data.raceInfo.protestFinishTime == null ? "" : dateFormatter
.format(data.raceInfo.protestFinishTime);
protestFinishDateLabel.setText(protestFinishDateText);
String updateTimeText = data.raceInfo.lastUpdateTime == null ? "-" : timeFormatter
.format(data.raceInfo.lastUpdateTime);
updateTimeLabel.setText(updateTimeText);
String updateDateText = data.raceInfo.lastUpdateTime == null ? "" : dateFormatter
.format(data.raceInfo.lastUpdateTime);
updateDateLabel.setText(updateDateText);
String vesselPositionText = stringMessages.unknown();
String windText = stringMessages.unknown();
if (data.raceInfo.lastWind != null) {
if (data.raceInfo.lastWind.position != null)
{
vesselPositionText = new HTML(
getDegMinSecFormatForDecimalDegree(data.raceInfo.lastWind.position.getLatDeg()) + " "
+ getDegMinSecFormatForDecimalDegree(data.raceInfo.lastWind.position.getLngDeg()) + " ")
.getHTML();
if (data != null) {
String statusText = flagInterpreter.getMeaningOfRaceStateAndFlags(data.raceInfo.lastStatus,
data.raceInfo.lastUpperFlag, data.raceInfo.lastLowerFlag, data.raceInfo.lastFlagsAreDisplayed);
String headerLabelText = data.regattaDisplayName + " " + data.raceInfo.fleetName + " " + data.raceInfo.raceName
+ " (" + statusText + ")";
headerLabel.setText(stringMessages.showingDetailsOfRace(headerLabelText));
String startTimeLabelText = data.raceInfo.startTime == null ? "-" : timeFormatter
.format(data.raceInfo.startTime);
startTimeLabel.setText(startTimeLabelText);
String startDateLabelText = data.raceInfo.startTime == null ? "" : dateFormatter
.format(data.raceInfo.startTime);
startDateLabel.setText(startDateLabelText);
String finishTimeText = data.raceInfo.finishedTime == null ? "-" : timeFormatter
.format(data.raceInfo.finishedTime);
finishTimeLabel.setText(finishTimeText);
String finishDurationText = "";
if (data.raceInfo.finishedTime != null && data.raceInfo.startTime != null) {
finishDurationText = "(" + durationFormatter.format(data.raceInfo.startTime, data.raceInfo.finishedTime)
+ ")";
}
windText = new HTML(decimalFormat.format(data.raceInfo.lastWind.trueWindFromDeg) + "&deg; "
+ decimalFormat.format(data.raceInfo.lastWind.trueWindSpeedInKnots) + "knts").getHTML();
finishDurationLabel.setText(finishDurationText);
String protestStartTimeText = data.raceInfo.protestStartTime == null ? "-" : timeFormatter
.format(data.raceInfo.protestStartTime);
protestStartTimeLabel.setText(protestStartTimeText);
String protestFinishTimeText = data.raceInfo.protestFinishTime == null ? "-" : timeFormatter
.format(data.raceInfo.protestFinishTime);
protestFinishTimeLabel.setText(protestFinishTimeText);
String protestStartDateText = data.raceInfo.protestStartTime == null ? "" : dateFormatter
.format(data.raceInfo.protestStartTime);
protestStartDateLabel.setText(protestStartDateText);
String protestFinishDateText = data.raceInfo.protestFinishTime == null ? "" : dateFormatter
.format(data.raceInfo.protestFinishTime);
protestFinishDateLabel.setText(protestFinishDateText);
String updateTimeText = data.raceInfo.lastUpdateTime == null ? "-" : timeFormatter
.format(data.raceInfo.lastUpdateTime);
updateTimeLabel.setText(updateTimeText);
String updateDateText = data.raceInfo.lastUpdateTime == null ? "" : dateFormatter
.format(data.raceInfo.lastUpdateTime);
updateDateLabel.setText(updateDateText);
String vesselPositionText = stringMessages.unknown();
String windText = stringMessages.unknown();
if (data.raceInfo.lastWind != null) {
if (data.raceInfo.lastWind.position != null)
{
vesselPositionText = new HTML(
getDegMinSecFormatForDecimalDegree(data.raceInfo.lastWind.position.getLatDeg()) + " "
+ getDegMinSecFormatForDecimalDegree(data.raceInfo.lastWind.position.getLngDeg()) + " ")
.getHTML();
}
windText = new HTML(decimalFormat.format(data.raceInfo.lastWind.trueWindFromDeg) + "&deg; "
+ decimalFormat.format(data.raceInfo.lastWind.trueWindSpeedInKnots) + "knts").getHTML();
}
vesselPositionLabel.setText(vesselPositionText);
windLabel.setText(windText);
updateUiStartProcedureSpecifics();
}
vesselPositionLabel.setText(vesselPositionText);
windLabel.setText(windText);
updateUiStartProcedureSpecifics();
}
private void updateUiStartProcedureSpecifics() {
@@ -282,7 +269,6 @@ public class RaceDetailPanel extends SimplePanel {
if (left == null || right == null) {
return false;
}
return bothNullOrEquals(left.courseAreaIdAsString, right.courseAreaIdAsString)
&& bothNullOrEquals(left.leaderboardName, right.leaderboardName)
&& bothNullOrEquals(left.raceInfo.seriesName, right.raceInfo.seriesName)
@@ -81,7 +81,6 @@ public class RegattaOverviewPanel extends SimplePanel {
final ErrorReporter errorReporter,
final StringMessages stringMessages,
RegattaOverviewContextDefinition regattaOverviewContextDefinition) {
this.sailingService = sailingService;
this.stringMessages = stringMessages;
this.errorReporter = errorReporter;
@@ -91,29 +90,24 @@ public class RegattaOverviewPanel extends SimplePanel {
this.eventDTO = null;
this.raceGroupDTOs = new ArrayList<RaceGroupDTO>();
this.userService = userService;
mainPanel = new VerticalPanel();
setWidget(mainPanel);
mainPanel.setWidth("100%");
mainPanel.addStyleName(style.contentWrapper());
refreshNowButton = new Anchor(stringMessages.refreshNow());
refreshNowButton.setStyleName(RES.mainCss().button());
refreshNowButton.addStyleName(style.button());
refreshNowButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
regattaRaceStatesComponent.loadAndUpdateEventLog();
}
});
settingsButton = new Anchor("&nbsp;", true);
settingsButton.ensureDebugId("RegattaOverviewSettingsButton");
settingsButton.setStyleName(style.settingsButton());
settingsButton.addStyleName(RES.mainCss().button());
settingsButton.addStyleName(style.button());
settingsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
@@ -123,7 +117,6 @@ public class RegattaOverviewPanel extends SimplePanel {
IgnoreLocalSettings.getIgnoreLocalSettingsFromCurrentUrl())).show();
}
});
startStopUpdatingButton = new Anchor("&nbsp;", true);
startStopUpdatingButton.setStyleName(RES.mainCss().button());
startStopUpdatingButton.addStyleName(style.refreshButton());
@@ -139,10 +132,8 @@ public class RegattaOverviewPanel extends SimplePanel {
startStopUpdatingButton.addStyleName(style.refreshButton_live());
}
}
});
//FIXME The chained calls from here are independent from each other and should be implemented with Dispatching Framework
// FIXME The chained calls from here are independent from each other and should be implemented with Dispatching Framework
sailingService.getEventById(regattaOverviewContextDefinition.getEvent(), false, new MarkedAsyncCallback<EventDTO>(
new AsyncCallback<EventDTO>() {
@Override
@@ -158,7 +149,6 @@ public class RegattaOverviewPanel extends SimplePanel {
continueInitAfterEventRetrieved();
}
}));
}
private void continueInitAfterEventRetrieved() {
@@ -198,11 +188,8 @@ public class RegattaOverviewPanel extends SimplePanel {
regattaRaceStatesComponent = new RegattaRaceStatesComponent(null, componentContext, sailingService, errorReporter,
stringMessages,
regattaOverviewContextDefinition.getEvent(), eventDTO, raceGroupDTOs, defaultSettings, uiUpdateTimer);
regattaRaceStatesComponent.setEntryClickedHandler(entryClickedHandler);
regattaRaceStatesComponent.setWidth("100%");
this.serverUpdateTimer.addTimeListener(new TimeListener() {
@Override
public void timeChanged(Date newTime, Date oldTime) {
@@ -211,7 +198,6 @@ public class RegattaOverviewPanel extends SimplePanel {
});
this.serverUpdateTimer.play();
startStopUpdatingButton.addStyleName(style.refreshButton_live());
this.uiUpdateTimer.addTimeListener(new TimeListener() {
@Override
public void timeChanged(Date newTime, Date oldTime) {
@@ -219,29 +205,22 @@ public class RegattaOverviewPanel extends SimplePanel {
}
});
this.uiUpdateTimer.play();
FlowPanel flowPanelLeft = new FlowPanel();
flowPanelLeft.addStyleName(style.functionBarLeft());
flowPanelLeft.add(repeatedInfoLabel);
repeatedInfoLabel.setStyleName(style.repeatedInfoLabel());
regattaRaceStatesComponent.setRepeatedInfoLabel(repeatedInfoLabel);
FlowPanel flowPanelRight = new FlowPanel();
flowPanelRight.addStyleName(style.functionBar());
// flexTable.add(refreshNowButton);
flowPanelRight.add(startStopUpdatingButton);
flowPanelRight.add(settingsButton);
FlexTable leftRightToolbar = new FlexTable();
leftRightToolbar.setWidget(0, 1, flowPanelRight);
leftRightToolbar.setWidget(0, 0, flowPanelLeft);
leftRightToolbar.getElement().getStyle().setWidth(100, Unit.PCT);
mainPanel.add(leftRightToolbar);
mainPanel.add(regattaRaceStatesComponent);
checkToEnableSettingsButton();
}
@@ -726,10 +726,6 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent<Regat
});
}
public List<RegattaOverviewEntryDTO> getAllRaces() {
return allEntries;
}
@Override
public boolean hasSettings() {
return true;
@@ -2,9 +2,9 @@ package com.sap.sailing.gwt.regattaoverview.client;
import java.util.List;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.settings.client.regattaoverview.RegattaRaceStatesSettings;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
import com.sap.sse.gwt.client.shared.components.ComponentLifecycle;
@@ -16,9 +16,9 @@ import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.settings.client.regattaoverview.RegattaRaceStatesSettings;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
@@ -25,9 +25,7 @@ import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
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;
@@ -420,34 +418,20 @@ public final class HomeServiceUtil {
return result;
}
public static String getCourseAreaNameForRegattaIdThereIsMoreThanOne(EventBase event, Leaderboard leaderboard) {
public static String getCourseAreaNameForRegattaIfThereIsMoreThanOne(EventBase event, Leaderboard leaderboard) {
/** The course area will not be shown if there is only one course area defined for the event */
final String result;
if (Util.size(event.getVenue().getCourseAreas()) <= 1) {
return null;
result = null;
} else {
final Iterable<CourseArea> courseAreas = leaderboard.getCourseAreas();
result = Util.isEmpty(courseAreas) ? null : Util.join(", ", courseAreas);
}
CourseArea courseArea = null;
if (leaderboard instanceof FlexibleLeaderboard) {
courseArea = ((FlexibleLeaderboard) leaderboard).getDefaultCourseArea();
} else if(leaderboard instanceof RegattaLeaderboard) {
Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
if (regatta != null) {
courseArea = regatta.getDefaultCourseArea();
}
}
return courseArea == null ? null : courseArea.getName();
return result;
}
public static String getCourseAreaIdForRegatta(EventBase event, Leaderboard leaderboard) {
CourseArea courseArea = null;
if (leaderboard instanceof FlexibleLeaderboard) {
courseArea = ((FlexibleLeaderboard) leaderboard).getDefaultCourseArea();
} else if(leaderboard instanceof RegattaLeaderboard) {
Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
if (regatta != null) {
courseArea = regatta.getDefaultCourseArea();
}
}
return courseArea == null ? null : courseArea.getId().toString();
public static Iterable<String> getCourseAreaIdsAsStringsForRegatta(EventBase event, Leaderboard leaderboard) {
return Util.map(leaderboard.getCourseAreas(), ca->ca.getId().toString());
}
public static void forAllPublicEventsWithReadPermission(RacingEventService service, HttpServletRequest request,
@@ -609,13 +593,10 @@ public final class HomeServiceUtil {
}
private static Event getAssociatedEventForLeaderboardInSeries(Leaderboard leaderboard,
Iterable<Event> eventsInSeries) {
final CourseArea defaultCourseArea = leaderboard.getDefaultCourseArea();
if (defaultCourseArea != null) {
for (Event event : eventsInSeries) {
if (Util.contains(event.getVenue().getCourseAreas(), defaultCourseArea)) {
return event;
}
Iterable<Event> eventsInSeries) {
for (Event event : eventsInSeries) {
if (Util.containsAny(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas())) {
return event;
}
}
return null;
@@ -5,7 +5,7 @@ import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings;
import com.sap.sse.common.settings.generic.BooleanSetting;
@@ -97,7 +97,7 @@ public final class RegattaRaceStatesSettings extends AbstractGenericSerializable
}
public void setDefaultRegattas(Iterable<RaceGroupDTO> defaultRaceGroups) {
if(defaultRaceGroups == null) {
if (defaultRaceGroups == null) {
defaultRaceGroups = Collections.emptySet();
}
Set<String> regattaIds = new HashSet<>();
@@ -1,8 +1,8 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.OptionElement;
@@ -23,6 +23,7 @@ import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.CompetitorRegistrationType;
import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithDateTimeBox;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -30,7 +31,6 @@ import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
import com.sap.sailing.gwt.ui.common.client.RandomString;
import com.sap.sailing.gwt.ui.leaderboard.RankingMetricTypeFormatter;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
@@ -56,7 +56,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
protected final DateAndTimeInput startDateBox;
protected final DateAndTimeInput endDateBox;
protected final ListBox scoringSchemeListBox;
protected final ListBox courseAreaListBox;
protected final CourseAreaSelection courseAreaSelection;
protected final ListBox sailingEventsListBox;
protected final CheckBox useStartTimeInferenceCheckBox;
protected final CheckBox controlTrackingFromStartAndFinishTimesCheckBox;
@@ -112,9 +112,9 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
autoRestartTrackingUponCompetitorSetChangeCheckBox.setValue(regatta.autoRestartTrackingUponCompetitorSetChange);
buoyZoneRadiusInHullLengthsDoubleBox = createDoubleBox(regatta.buoyZoneRadiusInHullLengths, 10);
buoyZoneRadiusInHullLengthsDoubleBox.ensureDebugId("BuoyZoneRadiusInHullLengthsDoubleBox");
courseAreaListBox = createListBox(false);
courseAreaListBox.ensureDebugId("CourseAreaListBox");
courseAreaListBox.setEnabled(false);
courseAreaSelection = new CourseAreaSelection(stringMessages, this);
courseAreaSelection.ensureDebugId("CourseAreaListBox");
courseAreaSelection.setEnabled(false);
this.seriesEditor = createSeriesEditor(series);
setupEventAndCourseAreaListBoxes(stringMessages);
competitorRegistrationTypeListBox = createListBox(false);
@@ -236,7 +236,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
formGrid.setWidget(4, 0, new Label(stringMessages.event() + ":"));
formGrid.setWidget(4, 1, sailingEventsListBox);
formGrid.setWidget(5, 0, new Label(stringMessages.courseArea() + ":"));
formGrid.setWidget(5, 1, courseAreaListBox);
formGrid.setWidget(5, 1, courseAreaSelection);
formGrid.setWidget(6, 0, new Label(stringMessages.useStartTimeInference() + ":"));
formGrid.setWidget(6, 1, useStartTimeInferenceCheckBox);
formGrid.setWidget(7, 0, new Label(stringMessages.controlTrackingFromStartAndFinishTimes() + ":"));
@@ -254,13 +254,8 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
}
protected void setCourseAreaInRegatta(RegattaDTO regatta) {
CourseAreaDTO courseArea = getSelectedCourseArea();
if (courseArea == null) {
regatta.defaultCourseAreaUuid = null;
} else {
regatta.defaultCourseAreaUuid = courseArea.id;
regatta.defaultCourseAreaName = courseArea.getName();
}
regatta.courseAreas = new ArrayList<>();
Util.addAll(getSelectedCourseAreas(), regatta.courseAreas);
}
public ScoringSchemeType getSelectedScoringSchemeType() {
@@ -271,16 +266,14 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
return null;
}
private boolean isCourseAreaInEvent(EventDTO event, UUID courseAreaId) {
private boolean isAnyOfTheCourseAreasInEvent(EventDTO event, Iterable<CourseAreaDTO> courseAreas) {
final boolean result;
if (event.venue == null) {
return false;
result = false;
} else {
result = Util.containsAny(event.venue.getCourseAreas(), courseAreas);
}
for (CourseAreaDTO courseArea : event.venue.getCourseAreas()) {
if (courseArea.id.equals(courseAreaId)) {
return true;
}
}
return false;
return result;
}
private void setupEventAndCourseAreaListBoxes(StringMessages stringMessages) {
@@ -288,16 +281,16 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
for (EventDTO event : Util.sortNamedCollection(existingEvents)) {
sailingEventsListBox.addItem(event.getName());
if (defaultEvent != null) {
if (defaultEvent.getName().equals(event.getName())) {
if (defaultEvent.getId().equals(event.getId())) {
sailingEventsListBox.setSelectedIndex(sailingEventsListBox.getItemCount() - 1);
fillCourseAreaListBox(event);
//select default course area, 2 elements as first is please select course area string
if (courseAreaListBox.getItemCount() == 2){
courseAreaListBox.setSelectedIndex(1);
if (event.venue.getCourseAreas().size() == 1) {
// select the single course area as the default:
courseAreaSelection.setSelected(event.venue.getCourseAreas().iterator().next(), true);
}
}
} else {
if (isCourseAreaInEvent(event, regatta.defaultCourseAreaUuid)) {
if (isAnyOfTheCourseAreasInEvent(event, regatta.courseAreas)) {
sailingEventsListBox.setSelectedIndex(sailingEventsListBox.getItemCount() - 1);
fillCourseAreaListBox(event);
}
@@ -316,23 +309,22 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
}
protected void setCourseAreaSelection() {
EventDTO selectedEvent = getSelectedEvent();
courseAreaListBox.clear();
courseAreaListBox.setEnabled(false);
final EventDTO selectedEvent = getSelectedEvent();
courseAreaSelection.removeAll();
courseAreaSelection.setEnabled(false);
if (selectedEvent != null) {
fillCourseAreaListBox(selectedEvent);
}
}
protected void fillCourseAreaListBox(EventDTO selectedEvent) {
courseAreaListBox.addItem(stringMessages.selectCourseArea());
for (CourseAreaDTO courseArea : selectedEvent.venue.getCourseAreas()) {
courseAreaListBox.addItem(courseArea.getName(), courseArea.id.toString());
if (courseArea.id.equals(regatta.defaultCourseAreaUuid)) {
courseAreaListBox.setSelectedIndex(courseAreaListBox.getItemCount() - 1);
}
for (final CourseAreaDTO courseAreaInEvent : selectedEvent.venue.getCourseAreas()) {
courseAreaSelection.addCourseArea(courseAreaInEvent);
}
courseAreaListBox.setEnabled(true);
for (final CourseAreaDTO courseAreaForRegatta : regatta.courseAreas) {
courseAreaSelection.setSelected(courseAreaForRegatta, true);
}
courseAreaSelection.setEnabled(true);
}
public EventDTO getSelectedEvent() {
@@ -350,20 +342,8 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
return result;
}
public CourseAreaDTO getSelectedCourseArea() {
CourseAreaDTO result = null;
EventDTO event = getSelectedEvent();
int selIndex = courseAreaListBox.getSelectedIndex();
if (selIndex > 0 && event != null) { // the zero index represents the 'no selection' text
String selectedCourseAreaIdAsString = courseAreaListBox.getValue(selIndex);
for (CourseAreaDTO courseAreaDTO : event.venue.getCourseAreas()) {
if (courseAreaDTO.id.toString().equals(selectedCourseAreaIdAsString)) {
result = courseAreaDTO;
break;
}
}
}
return result;
public Iterable<CourseAreaDTO> getSelectedCourseAreas() {
return courseAreaSelection.getSelectedCourseAreas();
}
public RegattaDTO getRegattaDTO() {
@@ -1,7 +1,7 @@
package com.sap.sailing.gwt.ui.adminconsole;
import com.google.gwt.resources.client.ImageResource;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.StringMessages;
import com.sap.sse.gwt.client.controls.listedit.ExpandedListEditorUi;
@@ -0,0 +1,101 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import com.google.gwt.user.client.ui.CaptionPanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
/**
* Visualizes a number of {@link CourseAreaDTO}s, together with the possibility to select
* zero or more from those.
*
* @author Axel Uhl (D043530)
*
*/
public class CourseAreaSelection extends CaptionPanel {
private final VerticalPanel checkboxPanel;
private final Map<CourseAreaDTO, CheckBox> checkboxes;
private final Set<CourseAreaDTO> selectedSet;
private final Function<String, CheckBox> checkboxConstructor;
public CourseAreaSelection(StringMessages stringMessages) {
this(Collections.emptySet(), stringMessages, CheckBox::new);
}
public CourseAreaSelection(StringMessages stringMessages, DataEntryDialog<?> dialog) {
this(Collections.emptySet(), stringMessages, dialog::createCheckbox);
}
public CourseAreaSelection(Iterable<CourseAreaDTO> courseAreas, StringMessages stringMessages,
Function<String, CheckBox> checkboxConstructor) {
super(stringMessages.selectCourseArea());
checkboxPanel = new VerticalPanel();
checkboxes = new HashMap<>();
selectedSet = new HashSet<>();
this.checkboxConstructor = checkboxConstructor;
add(checkboxPanel);
for (final CourseAreaDTO courseArea : courseAreas) {
addCourseArea(courseArea);
}
}
public void addCourseArea(final CourseAreaDTO courseArea) {
final CheckBox checkbox = checkboxConstructor.apply(courseArea.getName());
checkboxes.put(courseArea, checkbox);
checkbox.addValueChangeHandler(e->{
setSelected(courseArea, e.getValue());
});
checkboxPanel.add(checkbox);
}
/**
* Only has an effect if {@code courseArea} has previously been {@link #addCourseArea(CourseAreaDTO) added} or has been passed
* to the constructor for addition.
*/
public void setSelected(CourseAreaDTO courseArea, boolean selected) {
final CheckBox checkbox = checkboxes.get(courseArea);
if (checkbox != null) {
if (selected) {
selectedSet.add(courseArea);
} else {
selectedSet.remove(courseArea);
}
checkbox.setValue(selected);
}
}
public Iterable<CourseAreaDTO> getSelectedCourseAreas() {
return selectedSet;
}
public void setEnabled(boolean b) {
for (final CheckBox checkbox : checkboxes.values()) {
checkbox.setEnabled(b);
}
}
public void removeAll() {
for (final CourseAreaDTO courseArea : new ArrayList<>(checkboxes.keySet())) {
final CheckBox checkbox = checkboxes.remove(courseArea);
checkboxPanel.remove(checkbox);
}
selectedSet.clear();
}
public void setSelectedSet(Iterable<CourseAreaDTO> selectedCourseAreas) {
selectedSet.clear();
for (final CourseAreaDTO selected : selectedCourseAreas) {
setSelected(selected, true);
}
}
}
@@ -3,11 +3,11 @@ package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
@@ -16,13 +16,13 @@ import com.sap.sailing.gwt.ui.client.EventsRefresher;
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.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTOWithSecurity;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTOWithSecurity;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
@@ -70,11 +70,12 @@ public class CreateRegattaCallback implements DialogCallback<RegattaDTO>{
sailingService.createRegatta(newRegatta.getName(),
newRegatta.boatClass == null ? null : newRegatta.boatClass.getName(),
newRegatta.canBoatsOfCompetitorsChangePerRace, newRegatta.competitorRegistrationType,
newRegatta.registrationLinkSecret, newRegatta.startDate, newRegatta.endDate,
new RegattaCreationParametersDTO(seriesStructure), true,
newRegatta.scoringScheme, newRegatta.defaultCourseAreaUuid, newRegatta.buoyZoneRadiusInHullLengths, newRegatta.useStartTimeInference,
newRegatta.controlTrackingFromStartAndFinishTimes,
newRegatta.autoRestartTrackingUponCompetitorSetChange, newRegatta.rankingMetricType, new AsyncCallback<RegattaDTO>() {
newRegatta.registrationLinkSecret, newRegatta.startDate, newRegatta.endDate,
new RegattaCreationParametersDTO(seriesStructure), true, newRegatta.scoringScheme,
Util.mapToArrayList(newRegatta.courseAreas, CourseAreaDTO::getId), newRegatta.buoyZoneRadiusInHullLengths,
newRegatta.useStartTimeInference, newRegatta.controlTrackingFromStartAndFinishTimes,
newRegatta.autoRestartTrackingUponCompetitorSetChange, newRegatta.rankingMetricType,
new AsyncCallback<RegattaDTO>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to create new regatta " + newRegatta.getName() + ": " + t.getMessage());
@@ -148,10 +149,10 @@ public class CreateRegattaCallback implements DialogCallback<RegattaDTO>{
}
@Override
public void onSuccess(StrippedLeaderboardDTOWithSecurity result) {
if (newRegatta.defaultCourseAreaUuid != null) {
public void onSuccess(StrippedLeaderboardDTOWithSecurity result) {
if (!newRegatta.courseAreas.isEmpty()) {
// Show the event's leaderboard groups and allow the user to pick one to assign the regatta leaderboard to
final EventDTO event = getEventForCourseArea(existingEvents, newRegatta.defaultCourseAreaUuid);
final EventDTO event = getEventForCourseArea(existingEvents, newRegatta.courseAreas);
if (!event.getLeaderboardGroups().isEmpty()) {
openRegattaLeaderboardToLeaderboardGroupOfEventLinkingDialog(result, event);
}
@@ -215,16 +216,14 @@ public class CreateRegattaCallback implements DialogCallback<RegattaDTO>{
dialog.show();
}
private EventDTO getEventForCourseArea(final List<EventDTO> existingEvents, final UUID courseAreaId) {
private EventDTO getEventForCourseArea(final List<EventDTO> existingEvents, final Iterable<CourseAreaDTO> courseAreas) {
EventDTO result = null;
eventLoop:
for (final EventDTO event : existingEvents) {
if (event.venue != null) {
for (CourseAreaDTO courseArea : event.venue.getCourseAreas()) {
if (courseArea.id.equals(courseAreaId)) {
result = event;
break eventLoop;
}
if (Util.containsAny(event.venue.getCourseAreas(), courseAreas)) {
result = event;
break eventLoop;
}
}
}
@@ -5,9 +5,9 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sse.gwt.client.controls.datetime.DateTimeInput.Accuracy;
@@ -11,11 +11,11 @@ import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.settings.client.EntryPointWithSettingsLinkFactory;
import com.sap.sailing.gwt.settings.client.regattaoverview.RegattaOverviewContextDefinition;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sse.gwt.client.ErrorReporter;
@@ -12,7 +12,6 @@ import java.util.UUID;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Grid;
@@ -23,12 +22,12 @@ import com.google.gwt.user.client.ui.TextArea;
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.CourseAreaDTO;
import com.sap.sailing.domain.common.windfinder.AvailableWindFinderSpotCollections;
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithDateTimeBox;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
@@ -60,14 +59,6 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox<EventDT
protected ExternalLinksComposite externalLinksComposite;
private final FileStorageServiceConnectionTestObservable storageServiceAvailable;
/**
* We expect only a subset of possible URLs, i.e.:
* https://<hostname>:<port>/<path>
*/
private static RegExp REGEXP_URL = RegExp.compile(
"^(https?)://((?:[\\w-]+\\.)+[a-z]{2,}|localhost)(?::\\d+)?(/[\\w~:@_./$!%&=+*'\",;()-]*)?$",
"i");
protected static class EventParameterValidator implements Validator<EventDTO> {
private StringMessages stringMessages;
@@ -78,13 +69,6 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox<EventDT
this.existingEvents = new ArrayList<EventDTO>(existingEvents);
}
private boolean isValidURL(String input) {
if (input == null || input.isEmpty())
return true;
else
return REGEXP_URL.test(input);
}
@Override
public String getErrorMessage(EventDTO eventToValidate) {
String errorMessage = null;
@@ -135,13 +119,9 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox<EventDT
errorMessage = stringMessages.pleaseEnterNonEmptyCourseArea();
} else if (!unique) {
errorMessage = stringMessages.eventWithThisNameAlreadyExists();
} else if (!isValidURL(eventToValidate.getBaseURL())) {
errorMessage = stringMessages.invalidURL();
}
return errorMessage;
}
}
/**
@@ -40,6 +40,7 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.ui.adminconsole.LeaderboardGroupDialog.LeaderboardGroupDescriptor;
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
import com.sap.sailing.gwt.ui.client.EventsRefresher;
@@ -49,7 +50,6 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.controls.MultipleLinkCell;
import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventBaseDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
@@ -16,11 +16,9 @@ public class FlexibleLeaderboardCreateDialog extends FlexibleLeaderboardDialog {
nameTextBox = createTextBox(null);
nameTextBox.ensureDebugId("NameTextBox");
nameTextBox.setVisibleLength(50);
displayNameTextBox = createTextBox(null);
displayNameTextBox.ensureDebugId("DisplayNameTextBox");
displayNameTextBox.setVisibleLength(50);
scoringSchemeListBox = createScoringSchemeListBox(this, stringMessages);
sailingEventsListBox = createSailingEventListBox();
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
@@ -9,8 +9,8 @@ import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sse.common.Util;
@@ -20,7 +20,7 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
protected ListBox scoringSchemeListBox;
protected ListBox sailingEventsListBox;
protected Collection<EventDTO> existingEvents;
protected ListBox courseAreaListBox;
protected CourseAreaSelection courseAreaSelection;
protected static class LeaderboardParameterValidator implements Validator<LeaderboardDescriptor> {
protected final StringMessages stringMessages;
@@ -63,8 +63,8 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback<LeaderboardDescriptor> callback) {
super(title, leaderboardDTO, stringMessages, validator, callback);
this.existingEvents = existingEvents;
courseAreaListBox = createListBox(false);
courseAreaListBox.setEnabled(false);
courseAreaSelection = new CourseAreaSelection(stringMessages, this);
courseAreaSelection.setEnabled(false);
}
@Override
@@ -77,12 +77,7 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
}
private void setCourseAreaInDescriptor(LeaderboardDescriptor leaderboard) {
CourseAreaDTO courseArea = getSelectedCourseArea();
if (courseArea == null) {
leaderboard.setCourseAreaId(null);
} else {
leaderboard.setCourseAreaId(getSelectedCourseArea().id);
}
leaderboard.setCourseAreaIds(Util.mapToArrayList(getSelectedCourseAreas(), CourseAreaDTO::getId));
}
@Override
@@ -99,7 +94,7 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
formGrid.setWidget(3, 0, new Label(stringMessages.event() + ":"));
formGrid.setWidget(3, 1, sailingEventsListBox);
formGrid.setWidget(4, 0, new Label(stringMessages.courseArea() + ":"));
formGrid.setWidget(4, 1, courseAreaListBox);
formGrid.setWidget(4, 1, courseAreaSelection);
mainPanel.add(formGrid);
mainPanel.add(discardThresholdBoxes.getWidget());
@@ -123,28 +118,27 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
protected void onEventSelectionChanged() {
EventDTO selectedEvent = getSelectedEvent();
courseAreaListBox.clear();
courseAreaListBox.setEnabled(false);
courseAreaSelection.removeAll();
courseAreaSelection.setEnabled(false);
if (selectedEvent != null) {
fillCourseAreaListBox(selectedEvent);
}
}
private void fillCourseAreaListBox(EventDTO selectedEvent) {
courseAreaListBox.addItem(stringMessages.pleaseSelectACourseArea());
for (CourseAreaDTO courseArea : selectedEvent.venue.getCourseAreas()) {
courseAreaListBox.addItem(courseArea.getName(), courseArea.id.toString());
courseAreaSelection.addCourseArea(courseArea);
}
courseAreaListBox.setEnabled(true);
courseAreaSelection.setEnabled(true);
}
public EventDTO getSelectedEvent() {
EventDTO result = null;
int selIndex = sailingEventsListBox.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 = sailingEventsListBox.getItemText(selIndex);
for(EventDTO eventDTO: existingEvents) {
if(eventDTO.getName().equals(itemText)) {
for (EventDTO eventDTO : existingEvents) {
if (eventDTO.getName().equals(itemText)) {
result = eventDTO;
break;
}
@@ -153,19 +147,7 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo
return result;
}
public CourseAreaDTO getSelectedCourseArea() {
CourseAreaDTO result = null;
EventDTO event = getSelectedEvent();
int selIndex = courseAreaListBox.getSelectedIndex();
if (selIndex > 0 && event != null) { // the zero index represents the 'no selection' text
String selectedCourseAreaId = courseAreaListBox.getValue(selIndex);
for (CourseAreaDTO courseAreaDTO : event.venue.getCourseAreas()) {
if (courseAreaDTO.id.toString().equals(selectedCourseAreaId)) {
result = courseAreaDTO;
break;
}
}
}
return result;
public Iterable<CourseAreaDTO> getSelectedCourseAreas() {
return courseAreaSelection.getSelectedCourseAreas();
}
}
@@ -4,27 +4,25 @@ import java.util.Collection;
import java.util.List;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.ErrorReporter;
public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
public FlexibleLeaderboardEditDialog(Collection<StrippedLeaderboardDTO> otherExistingLeaderboards,
LeaderboardDescriptor leaderboard, StringMessages stringMessages, List<EventDTO> existingEvents, ErrorReporter errorReporter,
DialogCallback<LeaderboardDescriptor> callback) {
super(stringMessages.editFlexibleLeaderboard(), leaderboard, stringMessages, existingEvents, errorReporter, new FlexibleLeaderboardDialog.LeaderboardParameterValidator(
stringMessages, otherExistingLeaderboards), callback);
nameTextBox = createTextBox(leaderboard.getName());
displayNameTextBox = createTextBox(leaderboard.getDisplayName());
scoringSchemeListBox = createListBox(false);
nameTextBox.setVisibleLength(50);
displayNameTextBox.setVisibleLength(50);
int j = 0;
for (ScoringSchemeType scoringSchemeType: ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages));
@@ -34,16 +32,15 @@ public class FlexibleLeaderboardEditDialog extends FlexibleLeaderboardDialog {
j++;
}
scoringSchemeListBox.setEnabled(false);
sailingEventsListBox = createSailingEventListBox();
for (EventDTO event : existingEvents) {
for (CourseAreaDTO courseArea : event.venue.getCourseAreas()) {
if (courseArea.id.equals(leaderboard.getCourseAreaId())) {
if (Util.contains(leaderboard.getCourseAreaIds(), courseArea.id)) {
int index = existingEvents.indexOf(event) + 1; // + 1 because of the "Please select... item"
sailingEventsListBox.setSelectedIndex(index);
onEventSelectionChanged();
int courseAreaIndex = event.venue.getCourseAreas().indexOf(courseArea) + 1; // + 1 because of the "Please select... item"
courseAreaListBox.setSelectedIndex(courseAreaIndex);
courseAreaSelection.setSelectedSet(Util.map(leaderboard.getCourseAreaIds(),
id->Util.first(Util.filter(event.venue.getCourseAreas(), ca->ca.id.equals(id)))));
break;
}
}
@@ -8,8 +8,8 @@ import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBar
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_REFRESH_RACELOG;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_REMOVE;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_SCRATCH_BOAT_SELECTION;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_SET_IMPLIED_WIND;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_SET_FINISHING_AND_FINISH_TIME;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_SET_IMPLIED_WIND;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_SET_STARTTIME;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_SHOW_RACELOG;
import static com.sap.sailing.gwt.ui.adminconsole.LeaderboardRaceConfigImagesBarCell.ACTION_UNLINK;
@@ -32,6 +32,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.GWT;
@@ -57,6 +58,7 @@ import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.PairingListTemplateDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
@@ -282,15 +284,16 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
String s2 = o2.scoringScheme == null ? null : o2.scoringScheme.toString();
return new NaturalComparator().compare(s1, s2);
});
TextColumn<StrippedLeaderboardDTOWithSecurity> courseAreaColumn = new TextColumn<StrippedLeaderboardDTOWithSecurity>() {
TextColumn<StrippedLeaderboardDTOWithSecurity> courseAreasColumn = new TextColumn<StrippedLeaderboardDTOWithSecurity>() {
@Override
public String getValue(StrippedLeaderboardDTOWithSecurity leaderboard) {
return leaderboard.defaultCourseAreaId == null ? "" : leaderboard.defaultCourseAreaName;
return Util.joinStrings(", ", Util.map(leaderboard.courseAreas, CourseAreaDTO::getName));
}
};
courseAreaColumn.setSortable(true);
leaderboardColumnListHandler.setComparator(courseAreaColumn,
(o1, o2) -> new NaturalComparator().compare(o1.defaultCourseAreaName, o2.defaultCourseAreaName));
courseAreasColumn.setSortable(true);
leaderboardColumnListHandler.setComparator(courseAreasColumn,
(o1, o2) -> new NaturalComparator().compare(Util.joinStrings(", ", Util.map(o1.courseAreas, CourseAreaDTO::getName)),
Util.join(", ", Util.map(o2.courseAreas, CourseAreaDTO::getName))));
final HasPermissions type = SecuredDomainType.LEADERBOARD;
final AccessControlledActionsColumn<StrippedLeaderboardDTOWithSecurity, LeaderboardConfigImagesBarCell> leaderboardActionColumn = create(
@@ -381,7 +384,7 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
leaderboardTable.addColumn(discardingOptionsColumn, stringMessages.discarding());
leaderboardTable.addColumn(leaderboardTypeColumn, stringMessages.type());
leaderboardTable.addColumn(scoringSystemColumn, stringMessages.scoringSystem());
leaderboardTable.addColumn(courseAreaColumn, stringMessages.courseArea());
leaderboardTable.addColumn(courseAreasColumn, stringMessages.courseAreas());
SecuredDTOOwnerColumn.configureOwnerColumns(leaderboardTable, leaderboardColumnListHandler, stringMessages);
leaderboardTable.addColumn(leaderboardActionColumn, stringMessages.actions());
leaderboardTable.addColumnSortHandler(leaderboardColumnListHandler);
@@ -465,7 +468,7 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
private LeaderboardDescriptor createLeaderboardDescriptor(StrippedLeaderboardDTO leaderboardDTO,
ScoringSchemeType scoringScheme) {
return new LeaderboardDescriptor(leaderboardDTO.getName(), leaderboardDTO.displayName, scoringScheme,
leaderboardDTO.discardThresholds, leaderboardDTO.regattaName, leaderboardDTO.defaultCourseAreaId);
leaderboardDTO.discardThresholds, leaderboardDTO.regattaName, Util.mapToArrayList(leaderboardDTO.courseAreas, CourseAreaDTO::getId));
}
@Override
@@ -903,7 +906,7 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
public void ok(final LeaderboardDescriptor newLeaderboard) {
sailingService.createFlexibleLeaderboard(newLeaderboard.getName(),
newLeaderboard.getDisplayName(), newLeaderboard.getDiscardThresholds(),
newLeaderboard.getScoringScheme(), newLeaderboard.getCourseAreaId(),
newLeaderboard.getScoringScheme(), newLeaderboard.getCourseAreaIds(),
new MarkedAsyncCallback<StrippedLeaderboardDTOWithSecurity>(
new AsyncCallback<StrippedLeaderboardDTOWithSecurity>() {
@Override
@@ -1013,7 +1016,7 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel
private void updateLeaderboard(final String oldLeaderboardName, final LeaderboardDescriptor leaderboardToUpdate) {
sailingService.updateLeaderboard(oldLeaderboardName, leaderboardToUpdate.getName(),
leaderboardToUpdate.getDisplayName(), leaderboardToUpdate.getDiscardThresholds(),
leaderboardToUpdate.getCourseAreaId(), new AsyncCallback<StrippedLeaderboardDTOWithSecurity>() {
leaderboardToUpdate.getCourseAreaIds(), new AsyncCallback<StrippedLeaderboardDTOWithSecurity>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError(
@@ -1,8 +1,11 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sse.common.Util;
/**
* A descriptor class for creating and editing basic data of a leaderboard.
@@ -16,29 +19,30 @@ public class LeaderboardDescriptor {
private ScoringSchemeType scoringScheme;
private int[] discardThresholds;
private String regattaName;
private UUID courseAreaId;
private List<UUID> courseAreaIds;
public LeaderboardDescriptor() {
}
public LeaderboardDescriptor(String name, String displayName, ScoringSchemeType scoringScheme, int[] discardThresholds, String regattaName, UUID courseAreaId) {
public LeaderboardDescriptor(String name, String displayName, ScoringSchemeType scoringScheme,
int[] discardThresholds, String regattaName, Iterable<UUID> courseAreaIds) {
this.name = name;
this.displayName = displayName;
this.scoringScheme = scoringScheme;
this.discardThresholds = discardThresholds;
this.regattaName = regattaName;
this.courseAreaId = courseAreaId;
setCourseAreaIds(courseAreaIds);
}
/**
* 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, UUID courseAreaId) {
public LeaderboardDescriptor(String name, String displayName, ScoringSchemeType scoringScheme, int[] discardThresholds, Iterable<UUID> courseAreaIds) {
this.name = name;
this.displayName = displayName;
this.scoringScheme = scoringScheme;
this.discardThresholds = discardThresholds;
this.courseAreaId = courseAreaId;
setCourseAreaIds(courseAreaIds);
}
public String getName() {
@@ -86,11 +90,12 @@ public class LeaderboardDescriptor {
this.displayName = displayName;
}
public UUID getCourseAreaId() {
return courseAreaId;
public Iterable<UUID> getCourseAreaIds() {
return courseAreaIds;
}
public void setCourseAreaId(UUID courseAreaId) {
this.courseAreaId = courseAreaId;
public void setCourseAreaIds(Iterable<UUID> courseAreaIds) {
this.courseAreaIds = new ArrayList<>();
Util.addAll(courseAreaIds, this.courseAreaIds);
}
}
@@ -8,7 +8,7 @@ public class LeaderboardDescriptorWithEliminations extends LeaderboardDescriptor
private final Set<CompetitorDTO> eliminatedCompetitors;
public LeaderboardDescriptorWithEliminations(LeaderboardDescriptor base, Set<CompetitorDTO> eliminatedCompetitors) {
super(base.getName(), base.getDisplayName(), base.getScoringScheme(), base.getDiscardThresholds(), base.getRegattaName(), base.getCourseAreaId());
super(base.getName(), base.getDisplayName(), base.getScoringScheme(), base.getDiscardThresholds(), base.getRegattaName(), base.getCourseAreaIds());
this.eliminatedCompetitors = eliminatedCompetitors;
}
@@ -31,6 +31,7 @@ import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
@@ -41,6 +42,7 @@ import com.sap.sailing.gwt.ui.leaderboard.RankingMetricTypeFormatter;
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.SeriesDTO;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.gwt.adminconsole.AdminConsoleTableResources;
import com.sap.sse.gwt.client.ErrorReporter;
@@ -69,7 +71,7 @@ public class RegattaDetailsComposite extends Composite {
private final Label boatClassName;
private final Label scoringSystem;
private final Label rankingMetric;
private final Label defaultCourseArea;
private final Label courseAreas;
private final Label useStartTimeInference;
private final Label controlTrackingFromStartAndFinishTimes;
private final Label autoRestartTrackingUponCompetitorSetChange;
@@ -105,7 +107,7 @@ public class RegattaDetailsComposite extends Composite {
startDate = createLabelAndValueWidget(grid, currentRow++, stringMessages.startDate(), "StartDateLabel");
endDate = createLabelAndValueWidget(grid, currentRow++, stringMessages.endDate(), "EndDateLabel");
boatClassName = createLabelAndValueWidget(grid, currentRow++, stringMessages.boatClass(), "BoatClassLabel");
defaultCourseArea = createLabelAndValueWidget(grid, currentRow++, stringMessages.courseArea(), "CourseAreaLabel");
courseAreas = createLabelAndValueWidget(grid, currentRow++, stringMessages.courseArea(), "CourseAreaLabel");
useStartTimeInference = createLabelAndValueWidget(grid, currentRow++, stringMessages.useStartTimeInference(), "UseStartTimeInferenceLabel");
controlTrackingFromStartAndFinishTimes = createLabelAndValueWidget(grid, currentRow++, stringMessages.controlTrackingFromStartAndFinishTimes(), "ControlTrackingFromStartAndFinishTimesLabel");
autoRestartTrackingUponCompetitorSetChange = createLabelAndValueWidget(grid, currentRow++, stringMessages.autoRestartTrackingUponCompetitorSetChange(), "AutoRestartTrackingUponCompetitorSetChange");
@@ -445,16 +447,14 @@ public class RegattaDetailsComposite extends Composite {
startDate.setText(regatta.startDate != null ? regatta.startDate.toString() : "");
endDate.setText(regatta.endDate != null ? regatta.endDate.toString() : "");
boatClassName.setText(regatta.boatClass != null ? regatta.boatClass.getName() : "");
defaultCourseArea.setText(regatta.defaultCourseAreaUuid == null ? "" : regatta.defaultCourseAreaName);
courseAreas.setText(Util.joinStrings(", ", Util.map(regatta.courseAreas, CourseAreaDTO::getName)));
useStartTimeInference.setText(regatta.useStartTimeInference ? stringMessages.yes() : stringMessages.no());
controlTrackingFromStartAndFinishTimes.setText(regatta.controlTrackingFromStartAndFinishTimes ? stringMessages.yes() : stringMessages.no());
autoRestartTrackingUponCompetitorSetChange.setText(regatta.autoRestartTrackingUponCompetitorSetChange ? stringMessages.yes() : stringMessages.no());
canBoatsOfCompetitorsChangePerRace.setText(regatta.canBoatsOfCompetitorsChangePerRace ? stringMessages.yes() : stringMessages.no());
competitorRegistrationType.setText(regatta.competitorRegistrationType.getLabel(stringMessages));
registrationLinkWithQRCodeOpenButton.setVisible(regatta.competitorRegistrationType.isOpen());
buoyZoneRadiusInHullLengths.setText(String.valueOf(regatta.buoyZoneRadiusInHullLengths));
if (regatta.configuration != null) {
configuration.setText(stringMessages.configured());
} else {
@@ -470,5 +470,4 @@ public class RegattaDetailsComposite extends Composite {
seriesListDataProvider.getList().addAll(regatta.series);
}
}
}
@@ -31,6 +31,7 @@ import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.RegattaName;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.security.SecuredDomainType;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
@@ -345,7 +346,8 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer
private void commitEditedRegatta(final RegattaDTO editedRegatta) {
final RegattaIdentifier regattaName = new RegattaName(editedRegatta.getName());
sailingService.updateRegatta(regattaName, editedRegatta.startDate, editedRegatta.endDate, editedRegatta.defaultCourseAreaUuid,
sailingService.updateRegatta(regattaName, editedRegatta.startDate, editedRegatta.endDate,
Util.mapToArrayList(editedRegatta.courseAreas, CourseAreaDTO::getId),
editedRegatta.configuration, editedRegatta.buoyZoneRadiusInHullLengths, editedRegatta.useStartTimeInference, editedRegatta.controlTrackingFromStartAndFinishTimes,
editedRegatta.autoRestartTrackingUponCompetitorSetChange, editedRegatta.registrationLinkSecret,
editedRegatta.competitorRegistrationType, new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@@ -44,7 +44,7 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl
canBoatsOfCompetitorsChangePerRaceCheckBox.setEnabled(false);
scoringSchemeListBox.setEnabled(false);
sailingEventsListBox.setEnabled(true);
courseAreaListBox.setEnabled(true);
courseAreaSelection.setEnabled(true);
regattaConfigurationCheckbox = createCheckbox(stringMessages.setRacingProcedureConfiguration());
regattaConfigurationCheckbox.ensureDebugId("RacingProcedureConfigurationCheckBox");
regattaConfigurationCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@@ -83,7 +83,7 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl
@Override
protected FocusWidget getInitialFocusWidget() {
return courseAreaListBox;
return this.nameEntryField;
}
@Override
@@ -1,6 +1,8 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -19,6 +21,7 @@ import com.google.gwt.user.client.ui.PushButton;
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.CourseAreaDTO;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithDateTimeBox;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -48,6 +51,7 @@ public class SetStartTimeDialog extends DataEntryDialogWithDateTimeBox<RaceLogSe
private com.sap.sse.gwt.client.controls.IntegerBox authorPriorityBox;
private ListBox racingProcedureSelection;
private CheckBox advancePassIdCheckbox;
private ListBox courseAreaSelection;
public SetStartTimeDialog(SailingServiceAsync service, ErrorReporter errorReporter, String leaderboardName,
String raceColumnName, String fleetName, StringMessages stringMessages,
@@ -80,12 +84,10 @@ public class SetStartTimeDialog extends DataEntryDialogWithDateTimeBox<RaceLogSe
currentStartTimeLabel = new Label("");
grid.setWidget(0, 0, createLabel(stringMessages.startTime()));
grid.setWidget(0, 1, currentStartTimeLabel);
currentPassIdBox = new Label("");
grid.setWidget(1, 0, createLabel(stringMessages.currentPass()));
grid.setWidget(1, 1, currentPassIdBox);
currentPanel.add(grid);
PushButton refreshButton = new PushButton(new Image(resources.reloadIcon()));
refreshButton.addClickHandler(new ClickHandler() {
@Override
@@ -94,19 +96,16 @@ public class SetStartTimeDialog extends DataEntryDialogWithDateTimeBox<RaceLogSe
}
});
currentPanel.add(refreshButton);
current.add(currentPanel);
return current;
}
private Widget createInputPanel() {
Grid content = new Grid(5, 2);
Grid content = new Grid(6, 2);
timeBox = createDateTimeBox(new Date(), Accuracy.SECONDS);
timeBox.ensureDebugId("StartTimeTimeBox");
content.setWidget(0, 0, createLabel(stringMessages.startTime()));
content.setWidget(0, 1, timeBox);
authorNameBox = createTextBox("Shore");
authorNameBox.ensureDebugId("AuthorNameTextBox");
content.setWidget(1, 0, createLabel(stringMessages.authorName()));
@@ -115,7 +114,6 @@ public class SetStartTimeDialog extends DataEntryDialogWithDateTimeBox<RaceLogSe
authorPriorityBox.ensureDebugId("AuthorPriorityIntegerBox");
content.setWidget(2, 0, createLabel(stringMessages.authorPriority()));
content.setWidget(2, 1, authorPriorityBox);
racingProcedureSelection = createListBox(false);
ListBoxUtils.setupRacingProcedureTypeListBox(racingProcedureSelection, RacingProcedureType.RRS26, stringMessages.no());
int racingCounter = 0;
@@ -125,14 +123,40 @@ public class SetStartTimeDialog extends DataEntryDialogWithDateTimeBox<RaceLogSe
racingProcedureSelection.ensureDebugId("RacingProcedureListBox");
content.setWidget(3, 0, createLabel(stringMessages.racingProcedure()));
content.setWidget(3, 1, racingProcedureSelection);
courseAreaSelection = createListBox(/* isMultipleSelect */ false);
courseAreaSelection.ensureDebugId("CourseAreaSelection");
fillCourseAreas();
content.setWidget(4, 0, createLabel(stringMessages.courseArea()));
content.setWidget(4, 1, courseAreaSelection);
advancePassIdCheckbox = createCheckbox(stringMessages.advancePassId());
advancePassIdCheckbox.setValue(false);
advancePassIdCheckbox.ensureDebugId("AnvancePassIdCheckBox");
content.setWidget(4, 1, advancePassIdCheckbox);
advancePassIdCheckbox.ensureDebugId("AdvancePassIdCheckBox");
content.setWidget(5, 1, advancePassIdCheckbox);
return content;
}
private void fillCourseAreas() {
service.getCourseAreas(leaderboardName, new AsyncCallback<List<CourseAreaDTO>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.unableToLoadCourseAreas(caught.getMessage()), /* silentMode */ true);
}
@Override
public void onSuccess(List<CourseAreaDTO> result) {
fillCourseAreaSelection(result);
}
});
}
private void fillCourseAreaSelection(List<CourseAreaDTO> courseAreas) {
courseAreaSelection.clear();
courseAreaSelection.addItem(stringMessages.pleaseSelectACourseArea(), "");
for (final CourseAreaDTO courseArea : courseAreas) {
courseAreaSelection.addItem(courseArea.getName(), courseArea.getId().toString());
}
}
@Override
protected RaceLogSetStartTimeAndProcedureDTO getResult() {
RaceLogSetStartTimeAndProcedureDTO dto = new RaceLogSetStartTimeAndProcedureDTO();
@@ -145,12 +169,17 @@ public class SetStartTimeDialog extends DataEntryDialogWithDateTimeBox<RaceLogSe
dto.startTime = timeBox.getValue();
dto.passId = advancePassIdCheckbox.getValue() ? currentPassId+1 : currentPassId;
dto.racingProcedure = RacingProcedureType.values()[racingProcedureSelection.getSelectedIndex()];
if (courseAreaSelection.getSelectedIndex() == 0) {
dto.courseAreaId = null;
} else {
dto.courseAreaId = UUID.fromString(courseAreaSelection.getSelectedValue());
}
return dto;
}
private void refreshCurrentStartTime() {
service.getStartTimeAndProcedure(leaderboardName, raceColumnName, fleetName, new AsyncCallback<com.sap.sse.common.Util.Triple<Date,Integer, RacingProcedureType>>() {
service.getStartTimeAndProcedure(leaderboardName, raceColumnName, fleetName,
new AsyncCallback<com.sap.sse.common.Util.Triple<Date, Integer, RacingProcedureType>>() {
@Override
public void onSuccess(com.sap.sse.common.Util.Triple<Date, Integer, RacingProcedureType> result) {
if (result == null) {
@@ -43,6 +43,7 @@ import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLo
import com.sap.sailing.domain.common.dto.BoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceDTO;
@@ -747,7 +748,7 @@ public class SmartphoneTrackingEventManagementPanel
DeviceConfigurationDTO.RegattaConfigurationDTO configuration) {
final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.getName());
sailingService.updateRegatta(regattaIdentifier, regatta.startDate, regatta.endDate,
regatta.defaultCourseAreaUuid, configuration, regatta.buoyZoneRadiusInHullLengths,
Util.mapToArrayList(regatta.courseAreas, CourseAreaDTO::getId), configuration, regatta.buoyZoneRadiusInHullLengths,
regatta.useStartTimeInference, regatta.controlTrackingFromStartAndFinishTimes,
regatta.autoRestartTrackingUponCompetitorSetChange, regatta.registrationLinkSecret,
regatta.competitorRegistrationType, new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@@ -34,12 +34,12 @@ import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.BoatClassDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.gwt.ui.adminconsole.StructureImportListComposite.RegattaStructureProvider;
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.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventAndRegattaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.common.dto.BoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorAndBoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO;
import com.sap.sailing.domain.common.dto.PairingListDTO;
@@ -281,6 +282,8 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
String previousLeaderboardId, boolean fillTotalPointsUncorrected) throws UnauthorizedException, Exception;
List<CourseAreaDTO> getCourseAreas(String leaderboardName);
IncrementalOrFullLeaderboardDTO getLeaderboardForRace(RegattaAndRaceIdentifier raceIdentifer,
String leaderboardName, Date date, Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails,
boolean addOverallDetails, String previousLeaderboardId, boolean fillTotalPointsUncorrected)
@@ -289,11 +292,11 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
List<StrippedLeaderboardDTOWithSecurity> getLeaderboardsWithSecurity() throws UnauthorizedException;
StrippedLeaderboardDTOWithSecurity updateLeaderboard(String leaderboardName, String newLeaderboardName,
String newLeaderboardDisplayName, int[] newDiscardingThreasholds, UUID newCourseAreaId)
String newLeaderboardDisplayName, int[] newDiscardingThreasholds, Iterable<UUID> newCourseAreaIds)
throws UnauthorizedException;
StrippedLeaderboardDTOWithSecurity createFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName,
int[] discardThresholds, ScoringSchemeType scoringSchemeType, UUID courseAreaId)
int[] discardThresholds, ScoringSchemeType scoringSchemeType, Iterable<UUID> courseAreaIds)
throws UnauthorizedException;
StrippedLeaderboardDTOWithSecurity createRegattaLeaderboard(RegattaName regattaIdentifier,
@@ -323,7 +326,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
RegattaDTO createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace,
CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, Date startDate,
Date endDate, RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId,
boolean persistent, ScoringSchemeType scoringSchemeType, Iterable<UUID> courseAreaIds,
Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetrics rankingMetricType)
throws UnauthorizedException;
@@ -334,7 +337,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
void removeRegattas(Collection<RegattaIdentifier> regattas) throws UnauthorizedException;
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, UUID defaultCourseAreaUuid,
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, Iterable<UUID> courseAreaUuids,
RegattaConfigurationDTO regattaConfiguration, Double buoyZoneRadiusInHullLengths,
boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes,
boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, CompetitorRegistrationType registrationType) throws UnauthorizedException;
@@ -557,9 +560,6 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
/** for backward compatibility with the regatta overview */
List<RaceGroupDTO> getRegattaStructureForEvent(UUID eventId) throws UnauthorizedException;
/** the replacement service for getRegattaStructureForEvent() */
List<RaceGroupDTO> getRegattaStructureOfEvent(UUID eventId) throws UnauthorizedException;
List<RegattaOverviewEntryDTO> getRaceStateEntriesForRaceGroup(UUID eventId, List<UUID> visibleCourseAreas,
List<String> visibleRegattas, boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay,
Duration clientTimeZoneOffset) throws UnauthorizedException, Exception;
@@ -38,6 +38,7 @@ import com.sap.sailing.domain.common.dto.BoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorAndBoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO;
import com.sap.sailing.domain.common.dto.PairingListDTO;
@@ -303,12 +304,12 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn
AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void updateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName,
int[] newDiscardingThreasholds, UUID newCourseAreaId,
int[] newDiscardingThreasholds, Iterable<UUID> newCourseAreaIds,
AsyncCallback<StrippedLeaderboardDTOWithSecurity> callback);
void createFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName,
int[] discardThresholds,
ScoringSchemeType scoringSchemeType, UUID courseAreaId,
ScoringSchemeType scoringSchemeType, Iterable<UUID> courseAreaIds,
AsyncCallback<StrippedLeaderboardDTOWithSecurity> asyncCallback);
void createRegattaLeaderboard(RegattaName regattaIdentifier,
@@ -498,7 +499,7 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn
void createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace,
CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, Date startDate, Date endDate,
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent,
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
ScoringSchemeType scoringSchemeType, Iterable<UUID> courseAreaIds, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange,
RankingMetrics rankingMetricType, AsyncCallback<RegattaDTO> callback);
@@ -564,6 +565,8 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn
void removeLeaderboardColumns(String leaderboardName, List<String> columnsToRemove, AsyncCallback<Void> callback);
void getCourseAreas(String leaderboardName, AsyncCallback<List<CourseAreaDTO>> callback);
void getLeaderboard(String leaderboardName, AsyncCallback<StrippedLeaderboardDTO> callback);
void getLeaderboardWithSecurity(String leaderboardName, AsyncCallback<StrippedLeaderboardDTOWithSecurity> callback);
@@ -606,10 +609,11 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn
void deleteSwissTimingArchiveConfigurations(Collection<SwissTimingArchiveConfigurationWithSecurityDTO> dtos,
AsyncCallback<Void> asyncCallback);
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, UUID defaultCourseAreaUuid,
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, Iterable<UUID> courseAreaUuids,
RegattaConfigurationDTO regattaConfiguration, Double buoyZoneRadiusInHullLengths,
boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes,
boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, CompetitorRegistrationType registrationType, AsyncCallback<Void> callback);
boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret,
CompetitorRegistrationType registrationType, AsyncCallback<Void> callback);
/**
* @param detailType
@@ -629,8 +633,6 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn
void getRegattaStructureForEvent(UUID eventId, AsyncCallback<List<RaceGroupDTO>> asyncCallback);
void getRegattaStructureOfEvent(UUID eventId, AsyncCallback<List<RaceGroupDTO>> callback);
void getRaceStateEntriesForRaceGroup(UUID eventId, List<UUID> visibleCourseAreas, List<String> visibleRegattas,
boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay,
Duration clientTimeZoneOffset, AsyncCallback<List<RegattaOverviewEntryDTO>> markedAsyncCallback);
@@ -42,12 +42,10 @@ public abstract class SailingServiceHelper {
public static SailingServiceAsync createSailingServiceInstance(boolean sameBundle, ServiceRoutingProvider routingProvider) {
final SailingServiceAsync service = GWT.create(SailingService.class);
final ServiceDefTarget serviceToRegister = (ServiceDefTarget) service;
final StringBuilder servicePath = new StringBuilder(RemoteServiceMappingConstants.sailingServiceRemotePath);
if (routingProvider != null) {
servicePath.append(routingProvider.routingSuffixPath());
}
final String servicePathWithRoutingSuffix = servicePath.toString();
if (sameBundle) {
EntryPointHelper.registerASyncService(serviceToRegister, servicePathWithRoutingSuffix);
@@ -2380,4 +2380,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String autoRestartTrackingUponCompetitorSetChange();
String whitelabelFooterLanguage();
String whitelabelSailing();
String unableToLoadCourseAreas(String message);
}
@@ -2384,3 +2384,4 @@ keepEmptyForDefaultUserAndPassword=If username and password are left empty the c
autoRestartTrackingUponCompetitorSetChange=Restart tracking of races automatically when competitor set changes
whitelabelFooterLanguage=Sailing in
whitelabelSailing=Sailing
unableToLoadCourseAreas=Unable to load course areas: {0}
@@ -2391,4 +2391,5 @@ errorTryingToUpdateMarkProperties=Fehler beim Ändern der Bahnmarkeneigenschafte
confirmUnsettingPositionForMarkProperties=Positionierungsinformation für Bahnmarkeneigenschaften {0} wirklich entfernen?
useOfficialResultsForAutomaticUpdates=Automatisch von "offiziellen" von TracTrac veröffentlichten Ergebnissen aktualisieren
keepEmptyForDefaultUserAndPassword=Wenn kein Benutzername und kein Kennwort eingetragen werden, wird der aktuelle Benutzer zum importieren verwendet. Wenn der aktuelle Nutzer die Berechtigung zum Exportieren der Daten auf dem Zielserver hat, können die Felder leer bleiben. Sollen jedoch die Daten eines anderen Benutzers importiert werden, sind Benutzername und Kennwort des Benutzers einzutragen.
autoRestartTrackingUponCompetitorSetChange=Tracking der Rennen automatisch neu starten, wenn sich die Teilnehmerliste ändert
autoRestartTrackingUponCompetitorSetChange=Tracking der Rennen automatisch neu starten, wenn sich die Teilnehmerliste ändert
unableToLoadCourseAreas=Liste der Regattabahnen kann nicht geladen werden: {0}
@@ -278,6 +278,7 @@ import com.sap.sailing.domain.common.dto.BoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorAndBoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO;
import com.sap.sailing.domain.common.dto.CourseAreaDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.FullLeaderboardDTO;
import com.sap.sailing.domain.common.dto.IncrementalLeaderboardDTO;
@@ -446,7 +447,6 @@ import com.sap.sailing.gwt.ui.shared.CompetitorProviderDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO.RegattaConfigurationDTO;
@@ -984,6 +984,17 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
addOverallDetails, previousLeaderboardId, fillTotalPointsUncorrected);
}
@Override
public List<CourseAreaDTO> getCourseAreas(String leaderboardName) {
final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
getSecurityService().checkCurrentUserReadPermission(leaderboard);
final List<CourseAreaDTO> result = new ArrayList<>();
for (final CourseArea courseArea : leaderboard.getCourseAreas()) {
result.add(convertToCourseAreaDTO(courseArea));
}
return result;
}
@Override
public IncrementalOrFullLeaderboardDTO getLeaderboardForRace(final RegattaAndRaceIdentifier race,
final String leaderboardName, final Date date,
@@ -1136,10 +1147,8 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
if (boatClass != null) {
regattaDTO.boatClass = convertToBoatClassDTO(boatClass);
}
if (regatta.getDefaultCourseArea() != null) {
regattaDTO.defaultCourseAreaUuid = regatta.getDefaultCourseArea().getId();
regattaDTO.defaultCourseAreaName = regatta.getDefaultCourseArea().getName();
}
regattaDTO.courseAreas = new ArrayList<>();
Util.addAll(Util.map(regatta.getCourseAreas(), this::convertToCourseAreaDTO), regattaDTO.courseAreas);
regattaDTO.buoyZoneRadiusInHullLengths = regatta.getBuoyZoneRadiusInHullLengths();
regattaDTO.useStartTimeInference = regatta.useStartTimeInference();
regattaDTO.controlTrackingFromStartAndFinishTimes = regatta.isControlTrackingFromStartAndFinishTimes();
@@ -1199,13 +1208,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
return raceColumnDTOs;
}
private RaceInfoDTO createRaceInfoDTO(String seriesName, RaceColumn raceColumn, Fleet fleet) {
private RaceInfoDTO createRaceInfoDTO(String seriesName, RaceColumn raceColumn, Fleet fleet,
RaceLog raceLog, ReadonlyRaceState state) {
RaceInfoDTO raceInfoDTO = new RaceInfoDTO();
RaceLog raceLog = raceColumn.getRaceLog(fleet);
final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
raceInfoDTO.isTracked = trackedRace != null ? true : false;
if (raceLog != null) {
ReadonlyRaceState state = ReadonlyRaceStateImpl.getOrCreate(getService(), raceLog);
TimePoint startTime = state.getStartTime();
if (startTime != null) {
raceInfoDTO.startTime = startTime.asDate();
@@ -1224,7 +1232,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
raceInfoDTO.finishedTime = endOfRace != null ? endOfRace.asDate() : null;
}
}
final TimePoint now = MillisecondsTimePoint.now();
if (startTime != null) {
FlagPoleState activeFlagState = state.getRacingProcedure().getActiveFlags(startTime, now);
@@ -1232,7 +1239,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
FlagPoleState previousFlagState = activeFlagState.getPreviousState(state.getRacingProcedure(), startTime);
List<FlagPole> previousFlags = previousFlagState.getCurrentState();
FlagPole mostInterestingFlagPole = FlagPoleState.getMostInterestingFlagPole(previousFlags, activeFlags);
// TODO: adapt the LastFlagFinder#getMostRecent method!
if (mostInterestingFlagPole != null) {
raceInfoDTO.lastUpperFlag = mostInterestingFlagPole.getUpperFlag();
@@ -1241,9 +1247,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
raceInfoDTO.lastFlagsDisplayedStateChanged = previousFlagState.hasPoleChanged(mostInterestingFlagPole);
}
}
AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog);
RaceLogFlagEvent abortingFlagEvent = abortingFlagFinder.analyze();
if (abortingFlagEvent != null) {
raceInfoDTO.isRaceAbortedInPassBefore = true;
@@ -1256,13 +1260,11 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
raceInfoDTO.lastFlagsDisplayedStateChanged = true;
}
}
CourseBase lastCourse = state.getCourseDesign();
if (lastCourse != null) {
raceInfoDTO.lastCourseDesign = convertToRaceCourseDTO(lastCourse, new TrackedRaceMarkPositionFinder(trackedRace), now);
raceInfoDTO.lastCourseName = lastCourse.getName();
}
if (raceInfoDTO.lastStatus.equals(RaceLogRaceStatus.FINISHED)) {
if (state.getProtestTime() != null) {
final TimePoint protestEndTime = state.getProtestTime().to();
@@ -1277,12 +1279,10 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
}
}
Wind wind = state.getWindFix();
if (wind != null) {
raceInfoDTO.lastWind = createWindDTOFromAlreadyAveraged(wind, now);
}
fillStartProcedureSpecifics(raceInfoDTO, state);
}
raceInfoDTO.seriesName = seriesName;
@@ -2832,7 +2832,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
@Override
public StrippedLeaderboardDTOWithSecurity createFlexibleLeaderboard(String leaderboardName,
String leaderboardDisplayName, int[] discardThresholds, ScoringSchemeType scoringSchemeType,
UUID courseAreaId) {
Iterable<UUID> courseAreaIds) {
return getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
SecuredDomainType.LEADERBOARD, Leaderboard.getTypeRelativeObjectIdentifier(leaderboardName),
leaderboardDisplayName, new Callable<StrippedLeaderboardDTOWithSecurity>() {
@@ -2841,7 +2841,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
return createStrippedLeaderboardDTOWithSecurity(
getService().apply(new CreateFlexibleLeaderboard(leaderboardName,
leaderboardDisplayName, discardThresholds,
baseDomainFactory.createScoringScheme(scoringSchemeType), courseAreaId)), false, false);
baseDomainFactory.createScoringScheme(scoringSchemeType), courseAreaIds)), false, false);
}
});
}
@@ -2997,10 +2997,8 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
leaderboardDTO.scoringScheme = leaderboard.getScoringScheme().getType();
leaderboardDTO.canBoatsOfCompetitorsChangePerRace = false;
}
if (leaderboard.getDefaultCourseArea() != null) {
leaderboardDTO.defaultCourseAreaId = leaderboard.getDefaultCourseArea().getId();
leaderboardDTO.defaultCourseAreaName = leaderboard.getDefaultCourseArea().getName();
}
leaderboardDTO.courseAreas = new ArrayList<>();
Util.addAll(Util.map(leaderboard.getCourseAreas(), this::convertToCourseAreaDTO), leaderboardDTO.courseAreas);
leaderboardDTO.setDelayToLiveInMillisForLatestRace(delayToLiveInMillisForLatestRace);
leaderboardDTO.hasCarriedPoints = leaderboard.hasCarriedPoints();
if (leaderboard.getResultDiscardingRule() instanceof ThresholdBasedResultDiscardingRule) {
@@ -3045,11 +3043,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
@Override
public StrippedLeaderboardDTOWithSecurity updateLeaderboard(String leaderboardName, String newLeaderboardName,
String newLeaderboardDisplayName, int[] newDiscardingThresholds, UUID newCourseAreaId) {
String newLeaderboardDisplayName, int[] newDiscardingThresholds, Iterable<UUID> newCourseAreaIds) {
SecurityUtils.getSubject().checkPermission(
SecuredDomainType.LEADERBOARD.getStringPermissionForTypeRelativeIdentifier(DefaultActions.UPDATE,
Leaderboard.getTypeRelativeObjectIdentifier(leaderboardName)));
Leaderboard updatedLeaderboard = getService().apply(new UpdateLeaderboard(leaderboardName, newLeaderboardName, newLeaderboardDisplayName, newDiscardingThresholds, newCourseAreaId));
Leaderboard updatedLeaderboard = getService().apply(
new UpdateLeaderboard(leaderboardName, newLeaderboardName, newLeaderboardDisplayName, newDiscardingThresholds, newCourseAreaIds));
return createStrippedLeaderboardDTOWithSecurity(updatedLeaderboard, false, false);
}
@@ -4882,17 +4881,16 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
}
if (event != null) {
final Set<Leaderboard> leaderboardsAlreadyAddedAsRaceGroup = new HashSet<>();
for (CourseArea courseArea : event.getVenue().getCourseAreas()) {
for (Leaderboard leaderboard : getService().getLeaderboards().values()) {
if (getSecurityService().hasCurrentUserReadPermission(leaderboard)) {
if (leaderboard.getDefaultCourseArea() != null
&& leaderboard.getDefaultCourseArea() == courseArea) {
if (!leaderboardsAlreadyAddedAsRaceGroup.contains(leaderboard)
&& getSecurityService().hasCurrentUserReadPermission(leaderboard)) {
if (Util.contains(leaderboard.getCourseAreas(), courseArea)) {
RaceGroupDTO raceGroup = new RaceGroupDTO(leaderboard.getName());
raceGroup.courseAreaIdAsString = courseArea.getId().toString();
raceGroup.displayName = getRegattaNameFromLeaderboard(leaderboard);
if (leaderboardWithLeaderboardGroups.containsKey(leaderboard)) {
raceGroup.leaderboardGroupName = leaderboardWithLeaderboardGroups.get(leaderboard)
.getName();
raceGroup.leaderboardGroupName = leaderboardWithLeaderboardGroups.get(leaderboard).getName();
}
if (leaderboard instanceof RegattaLeaderboard) {
RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard;
@@ -4916,6 +4914,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
.addAll(convertToRaceColumnDTOs(leaderboard.getRaceColumns()));
}
raceGroups.add(raceGroup);
leaderboardsAlreadyAddedAsRaceGroup.add(leaderboard);
}
}
}
@@ -4924,71 +4923,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
return raceGroups;
}
/**
* the replacement service for {@link #getRegattaStructureForEvent(UUID)}
*/
@Override
public List<RaceGroupDTO> getRegattaStructureOfEvent(UUID eventId) {
List<RaceGroupDTO> raceGroups = new ArrayList<RaceGroupDTO>();
Event event = getService().getEvent(eventId);
getSecurityService().checkCurrentUserReadPermission(event);
Map<Leaderboard, LeaderboardGroup> leaderboardWithLeaderboardGroups = new HashMap<Leaderboard, LeaderboardGroup>();
for(LeaderboardGroup leaderboardGroup: event.getLeaderboardGroups()) {
for(Leaderboard leaderboard: leaderboardGroup.getLeaderboards()) {
if (getSecurityService().hasCurrentUserReadPermission(leaderboard)) {
leaderboardWithLeaderboardGroups.put(leaderboard, leaderboardGroup);
}
}
}
if (event != null) {
for(LeaderboardGroup leaderboardGroup: event.getLeaderboardGroups()) {
for(Leaderboard leaderboard: leaderboardGroup.getLeaderboards()) {
if (getSecurityService().hasCurrentUserReadPermission(leaderboard)) {
RaceGroupDTO raceGroup = new RaceGroupDTO(leaderboard.getName());
for (CourseArea courseArea : event.getVenue().getCourseAreas()) {
if (leaderboard.getDefaultCourseArea() != null
&& leaderboard.getDefaultCourseArea() == courseArea) {
raceGroup.courseAreaIdAsString = courseArea.getId().toString();
break;
}
}
raceGroup.displayName = getRegattaNameFromLeaderboard(leaderboard);
if (leaderboardWithLeaderboardGroups.containsKey(leaderboard)) {
raceGroup.leaderboardGroupName = leaderboardWithLeaderboardGroups.get(leaderboard)
.getName();
}
if (leaderboard instanceof RegattaLeaderboard) {
RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard;
raceGroup.boatClass = regattaLeaderboard.getRegatta().getBoatClass().getDisplayName();
for (Series series : regattaLeaderboard.getRegatta().getSeries()) {
RaceGroupSeriesDTO seriesDTO = new RaceGroupSeriesDTO(series.getName());
raceGroup.getSeries().add(seriesDTO);
for (Fleet fleet : series.getFleets()) {
FleetDTO fleetDTO = new FleetDTO(fleet.getName(), fleet.getOrdering(),
fleet.getColor());
seriesDTO.getFleets().add(fleetDTO);
}
seriesDTO.getRaceColumns().addAll(convertToRaceColumnDTOs(series.getRaceColumns()));
}
} else {
RaceGroupSeriesDTO seriesDTO = new RaceGroupSeriesDTO(
LeaderboardNameConstants.DEFAULT_SERIES_NAME);
raceGroup.getSeries().add(seriesDTO);
FleetDTO fleetDTO = new FleetDTO(LeaderboardNameConstants.DEFAULT_FLEET_NAME, 0, null);
seriesDTO.getFleets().add(fleetDTO);
seriesDTO.getRaceColumns().addAll(convertToRaceColumnDTOs(leaderboard.getRaceColumns()));
BoatClass boatClass = leaderboard.getBoatClass();
raceGroup.boatClass = boatClass != null ? boatClass.getDisplayName() : null;
}
raceGroups.add(raceGroup);
}
}
}
}
return raceGroups;
}
/**
* The name of the regatta to be shown on the regatta overview webpage is retrieved from the name of the {@link Leaderboard}. Since regattas are
* not always represented by a {@link Regatta} object in the Sailing Suite but need to be shown on the regatta overview page, the leaderboard is
@@ -5074,7 +5008,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
@Override
public void updateRegatta(RegattaIdentifier regattaName, Date startDate, Date endDate, UUID defaultCourseAreaUuid,
public void updateRegatta(RegattaIdentifier regattaName, Date startDate, Date endDate, Iterable<UUID> courseAreaUuids,
RegattaConfigurationDTO configurationDTO, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes,
boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, CompetitorRegistrationType registrationType) {
Regatta regatta = getService().getRegatta(regattaName);
@@ -5083,7 +5017,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
TimePoint startTimePoint = startDate != null ? new MillisecondsTimePoint(startDate) : null;
TimePoint endTimePoint = endDate != null ? new MillisecondsTimePoint(endDate) : null;
getService().apply(new UpdateSpecificRegatta(regattaName, startTimePoint, endTimePoint, defaultCourseAreaUuid,
getService().apply(new UpdateSpecificRegatta(regattaName, startTimePoint, endTimePoint, courseAreaUuids,
convertToRegattaConfiguration(configurationDTO), buoyZoneRadiusInHullLengths, useStartTimeInference,
controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange, registrationLinkSecret, registrationType));
}
@@ -5155,7 +5089,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret,
Date startDate, Date endDate,
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal,
boolean persistent, ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean persistent, ScoringSchemeType scoringSchemeType, Iterable<UUID> courseAreaIds, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetrics rankingMetricType) {
return getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(
SecuredDomainType.REGATTA, Regatta.getTypeRelativeObjectIdentifier(regattaName),
@@ -5168,7 +5102,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, registrationLinkSecret,
startTimePoint, endTimePoint, UUID.randomUUID(),
seriesNamesWithFleetNamesAndFleetOrderingAndMedal, persistent,
baseDomainFactory.createScoringScheme(scoringSchemeType), defaultCourseAreaId,
baseDomainFactory.createScoringScheme(scoringSchemeType), courseAreaIds,
buoyZoneRadiusInHullLengths, useStartTimeInference,
controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange, rankingMetricType));
return convertToRegattaDTO(regatta);
@@ -5478,7 +5412,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
List<RegattaOverviewEntryDTO> result = new ArrayList<RegattaOverviewEntryDTO>();
Calendar dayToCheck = Calendar.getInstance();
dayToCheck.setTime(new Date());
CourseArea usedCourseArea = leaderboard.getDefaultCourseArea();
if (leaderboard != null) {
if (visibleRegattas != null && !visibleRegattas.contains(leaderboard.getName())) {
return result;
@@ -5493,13 +5426,13 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
Map<String, List<RegattaOverviewEntryDTO>> entriesPerFleet = new HashMap<String, List<RegattaOverviewEntryDTO>>();
for (RaceColumn raceColumn : series.getRaceColumns()) {
getRegattaOverviewEntries(showOnlyRacesOfSameDay, clientTimeZoneOffset, dayToCheck,
usedCourseArea, leaderboard, boatClass.getName(), regattaName, buyZoneRadius,
series.getName(), raceColumn, entriesPerFleet);
leaderboard, boatClass.getName(), regattaName, buyZoneRadius, series.getName(),
raceColumn, entriesPerFleet);
}
result.addAll(getRegattaOverviewEntriesToBeShown(showOnlyCurrentlyRunningRaces, entriesPerFleet));
}
} else if(leaderboard instanceof FlexibleLeaderboard) {
} else if (leaderboard instanceof FlexibleLeaderboard) {
BoatClass boatClass = null;
for (TrackedRace trackedRace : leaderboard.getTrackedRaces()) {
boatClass = trackedRace.getRace().getBoatClass();
@@ -5508,9 +5441,9 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
Distance buyZoneRadius = RegattaUtil.getCalculatedRegattaBuoyZoneRadius(null, boatClass);
Map<String, List<RegattaOverviewEntryDTO>> entriesPerFleet = new HashMap<String, List<RegattaOverviewEntryDTO>>();
for (RaceColumn raceColumn : leaderboard.getRaceColumns()) {
getRegattaOverviewEntries(showOnlyRacesOfSameDay, clientTimeZoneOffset, dayToCheck, usedCourseArea,
leaderboard, boatClass == null ? "" : boatClass.getName(), regattaName, buyZoneRadius,
LeaderboardNameConstants.DEFAULT_SERIES_NAME, raceColumn, entriesPerFleet);
getRegattaOverviewEntries(showOnlyRacesOfSameDay, clientTimeZoneOffset, dayToCheck, leaderboard,
boatClass == null ? "" : boatClass.getName(), regattaName, buyZoneRadius, LeaderboardNameConstants.DEFAULT_SERIES_NAME,
raceColumn, entriesPerFleet);
}
result.addAll(getRegattaOverviewEntriesToBeShown(showOnlyCurrentlyRunningRaces, entriesPerFleet));
}
@@ -5519,12 +5452,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
private void createRegattaFromRegattaDTO(RegattaDTO regatta) {
this.createRegatta(regatta.getName(), regatta.boatClass.getName(),
regatta.canBoatsOfCompetitorsChangePerRace, regatta.competitorRegistrationType, regatta.registrationLinkSecret,
regatta.startDate, regatta.endDate,
new RegattaCreationParametersDTO(getSeriesCreationParameters(regatta)),
true, regatta.scoringScheme, regatta.defaultCourseAreaUuid, regatta.buoyZoneRadiusInHullLengths, regatta.useStartTimeInference,
regatta.controlTrackingFromStartAndFinishTimes, regatta.autoRestartTrackingUponCompetitorSetChange, regatta.rankingMetricType);
this.createRegatta(regatta.getName(), regatta.boatClass.getName(), regatta.canBoatsOfCompetitorsChangePerRace,
regatta.competitorRegistrationType, regatta.registrationLinkSecret, regatta.startDate, regatta.endDate,
new RegattaCreationParametersDTO(getSeriesCreationParameters(regatta)), true, regatta.scoringScheme,
Util.map(regatta.courseAreas, CourseAreaDTO::getId), regatta.buoyZoneRadiusInHullLengths,
regatta.useStartTimeInference, regatta.controlTrackingFromStartAndFinishTimes,
regatta.autoRestartTrackingUponCompetitorSetChange, regatta.rankingMetricType);
}
private SeriesParameters getSeriesParameters(SeriesDTO seriesDTO) {
@@ -5608,7 +5541,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
}
String eventName = newEvent.getName();
List<UUID> eventLeaderboardGroupUUIDs = new ArrayList<>();
// create Leaderboard Group
LeaderboardGroup leaderboardGroup = getService().getLeaderboardGroupByName(eventName);
if (leaderboardGroup == null) {
@@ -5629,47 +5561,59 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
newEvent.getWindFinderReviewedSpotsCollectionIds());
}
private Triple<String, String, String> getLeaderboardSlotKey(RegattaOverviewEntryDTO entry) {
return new Triple<>(entry.leaderboardName, entry.raceInfo.raceName, entry.raceInfo.fleetName);
}
@Override
public List<RegattaOverviewEntryDTO> getRaceStateEntriesForRaceGroup(UUID eventId, List<UUID> visibleCourseAreaIds,
List<String> visibleRegattas, boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset)
throws NoWindException, InterruptedException, ExecutionException {
List<RegattaOverviewEntryDTO> result = new ArrayList<RegattaOverviewEntryDTO>();
Calendar dayToCheck = Calendar.getInstance();
dayToCheck.setTime(new Date());
Event event = getService().getEvent(eventId);
getSecurityService().checkCurrentUserReadPermission(event);
final Map<Triple<String, String, String>, RegattaOverviewEntryDTO> entriesByRaceIdentifier = new HashMap<>();
if (event != null) {
for (CourseArea courseArea : event.getVenue().getCourseAreas()) {
if (visibleCourseAreaIds.contains(courseArea.getId())) {
for (Leaderboard leaderboard : getService().getLeaderboards().values()) {
final CourseArea leaderboardDefaultCourseArea = leaderboard.getDefaultCourseArea();
if (getSecurityService().hasCurrentUserReadPermission(leaderboard)) {
if (leaderboardDefaultCourseArea != null
&& leaderboardDefaultCourseArea.equals(courseArea)) {
result.addAll(getRaceStateEntriesForLeaderboard(leaderboard.getName(),
showOnlyCurrentlyRunningRaces, showOnlyRacesOfSameDay, clientTimeZoneOffset,
visibleRegattas));
// leaderboard's course areas must intersect with those of event; if so, add those
// race entries that either have no course area explicitly defined or otherwise have
// a course area that is part of the visibleCourseAreaIds
if (Util.contains(leaderboard.getCourseAreas(), courseArea)) {
for (final RegattaOverviewEntryDTO entry : getRaceStateEntriesForLeaderboard(leaderboard.getName(),
showOnlyCurrentlyRunningRaces, showOnlyRacesOfSameDay, clientTimeZoneOffset,
visibleRegattas)) {
// use "leaderboard slot key" consisting of leaderboard name / race column name / fleet name
// because the raceInfo.raceIdentifier could be null as no TrackedRace has to be attached
if (!entriesByRaceIdentifier.containsKey(getLeaderboardSlotKey(entry)) &&
(entry.courseAreaIdAsString == null || visibleCourseAreaIds.contains(UUID.fromString(
entry.courseAreaIdAsString)))) {
entriesByRaceIdentifier.put(getLeaderboardSlotKey(entry), entry);
}
}
}
}
}
}
}
}
return result;
return new ArrayList<>(entriesByRaceIdentifier.values());
}
/**
* The client's day starts at <code>00:00:00Z - clientTimeZoneOffset</code> and ends at <code>23:59:59Z - clientTimeZoneOffset</code>.
*/
private void getRegattaOverviewEntries(boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset,
Calendar dayToCheck, CourseArea courseArea, Leaderboard leaderboard, String boatClassName,
String regattaName, Distance buyZoneRadius, String seriesName, RaceColumn raceColumn,
Map<String, List<RegattaOverviewEntryDTO>> entriesPerFleet) {
Calendar dayToCheck, Leaderboard leaderboard, String boatClassName, String regattaName,
Distance buyZoneRadius, String seriesName, RaceColumn raceColumn, Map<String, List<RegattaOverviewEntryDTO>> entriesPerFleet) {
if (!raceColumn.isCarryForward()) {
for (Fleet fleet : raceColumn.getFleets()) {
RegattaOverviewEntryDTO entry = createRegattaOverviewEntryDTO(courseArea,
leaderboard, boatClassName, regattaName, buyZoneRadius, seriesName, raceColumn, fleet,
showOnlyRacesOfSameDay, clientTimeZoneOffset, dayToCheck);
RegattaOverviewEntryDTO entry = createRegattaOverviewEntryDTO(leaderboard,
boatClassName, regattaName, buyZoneRadius, seriesName, raceColumn, fleet, showOnlyRacesOfSameDay,
clientTimeZoneOffset, dayToCheck);
if (entry != null) {
addRegattaOverviewEntryToEntriesPerFleet(entriesPerFleet, fleet, entry);
}
@@ -5689,11 +5633,10 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
if (entry.raceInfo.lastStatus.equals(RaceLogRaceStatus.FINISHED)) {
finishedEntries.add(entry);
} else if (entry.raceInfo.lastStatus.equals(RaceLogRaceStatus.UNSCHEDULED)) {
//don't filter when the race is unscheduled and aborted before
// don't filter when the race is unscheduled and aborted before
if (!entry.raceInfo.isRaceAbortedInPassBefore) {
result.remove(entry);
}
}
}
}
@@ -5701,7 +5644,6 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
//keep the last finished race in the list to be shown
int indexOfLastElement = finishedEntries.size() - 1;
finishedEntries.remove(indexOfLastElement);
//... and remove all other finished races
result.removeAll(finishedEntries);
}
@@ -5721,21 +5663,38 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
/**
* The client's day starts at <code>00:00:00Z - clientTimeZoneOffset</code> and ends at <code>23:59:59Z - clientTimeZoneOffset</code>.
*/
private RegattaOverviewEntryDTO createRegattaOverviewEntryDTO(CourseArea courseArea, Leaderboard leaderboard,
String boatClassName, String regattaName, Distance buyZoneRadius, String seriesName, RaceColumn raceColumn,
Fleet fleet, boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset, Calendar dayToCheck) {
private RegattaOverviewEntryDTO createRegattaOverviewEntryDTO(Leaderboard leaderboard, String boatClassName,
String regattaName, Distance buyZoneRadius, String seriesName, RaceColumn raceColumn, Fleet fleet,
boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset, Calendar dayToCheck) {
RegattaOverviewEntryDTO entry = new RegattaOverviewEntryDTO();
if (courseArea != null) {
entry.courseAreaName = courseArea.getName();
entry.courseAreaIdAsString = courseArea.getId().toString();
final RaceLog raceLog = raceColumn.getRaceLog(fleet);
final ReadonlyRaceState state;
if (raceLog != null) {
state = ReadonlyRaceStateImpl.getOrCreate(getService(), raceLog);
} else {
entry.courseAreaName = "Default";
entry.courseAreaIdAsString = "Default";
state = null;
}
// try to find course area information in the race log which may tell on which course are
// the latest / valid start attempt / pass has happened; if this information cannot be found,
// e.g., because the race log is empty, or it is a legacy race log that doesn't provide this
// information, and the leaderboard has exactly one course areas assigned, that one is used.
// If the leaderboard has multiple course areas assigned we assume that the choice must come
// from a race log start time event or else it hasn't been decided yet where that race will take place.
final UUID courseAreaIdFromRaceLog;
if (state != null && (courseAreaIdFromRaceLog=state.getCourseAreaId()) != null) {
// no race log; use default course area in any case:
final CourseArea courseAreaFromRaceLog = getBaseDomainFactory().getExistingCourseAreaById(courseAreaIdFromRaceLog);
entry.courseAreaName = courseAreaFromRaceLog.getName();
entry.courseAreaIdAsString = courseAreaFromRaceLog.getId().toString();
} else if (Util.size(leaderboard.getCourseAreas()) == 1) {
final CourseArea defaultCourseArea = leaderboard.getCourseAreas().iterator().next();
entry.courseAreaName = defaultCourseArea.getName();
entry.courseAreaIdAsString = defaultCourseArea.getId().toString();
}
entry.boatClassName = boatClassName;
entry.regattaDisplayName = regattaName;
entry.leaderboardName = leaderboard.getName();
entry.raceInfo = createRaceInfoDTO(seriesName, raceColumn, fleet);
entry.raceInfo = createRaceInfoDTO(seriesName, raceColumn, fleet, raceLog, state);
entry.currentServerTime = new Date();
entry.buyZoneRadius = buyZoneRadius;
if (showOnlyRacesOfSameDay) {
@@ -6415,7 +6374,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
TimePoint newStartTime = getService().setStartTimeAndProcedure(dto.leaderboardName, dto.raceColumnName,
dto.fleetName, dto.authorName, dto.authorPriority,
dto.passId, new MillisecondsTimePoint(dto.logicalTimePoint), new MillisecondsTimePoint(dto.startTime),
dto.racingProcedure);
dto.racingProcedure, dto.courseAreaId);
return new MillisecondsTimePoint(dto.startTime).equals(newStartTime);
}
@@ -8716,7 +8675,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
raceLog.add(new RaceLogDependentStartTimeEventImpl(event.getCreatedAt(),
event.getLogicalTimePoint(), event.getAuthor(), UUID.randomUUID(),
raceLog.getCurrentPassId(), event.getDependentOnRaceIdentifier(),
event.getStartTimeDifference(), event.getNextStatus()));
event.getStartTimeDifference(), event.getNextStatus(), event.getCourseAreaId()));
}
}
@@ -8725,7 +8684,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
if (!dependentStartTime && isLatestPass(event)) {
raceLog.add(new RaceLogStartTimeEventImpl(event.getCreatedAt(), event.getLogicalTimePoint(),
event.getAuthor(), UUID.randomUUID(), raceLog.getCurrentPassId(), event.getStartTime(),
event.getNextStatus()));
event.getNextStatus(), event.getCourseAreaId()));
}
}
@@ -9,7 +9,6 @@ import com.sap.sse.security.shared.dto.NamedDTO;
public class RaceGroupDTO extends NamedDTO implements IsSerializable {
private static final long serialVersionUID = 8432572954520767329L;
public String courseAreaIdAsString;
public String displayName;
public String leaderboardGroupName;
public String boatClass;
Loaded 100 of 186 files, more files were not shown because too many files have changed in this diff. Show more