diff --git a/java/com.sap.sailing.datamining/META-INF/MANIFEST.MF b/java/com.sap.sailing.datamining/META-INF/MANIFEST.MF index 0c799b7a01f..869d0e48456 100644 --- a/java/com.sap.sailing.datamining/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.datamining/META-INF/MANIFEST.MF @@ -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 diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasLeaderboardGroupContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasLeaderboardGroupContext.java index 33f928d51c2..0ef4bd7f391 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasLeaderboardGroupContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasLeaderboardGroupContext.java @@ -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(); + } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/LeaderboardGroupRetrievalProcessor.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/LeaderboardGroupRetrievalProcessor.java index 126f24dbe1d..bc221b7f014 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/LeaderboardGroupRetrievalProcessor.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/components/LeaderboardGroupRetrievalProcessor.java @@ -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; } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/LeaderboardGroupWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/LeaderboardGroupWithContext.java index 77db74eb221..3d77b2f2d40 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/LeaderboardGroupWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/LeaderboardGroupWithContext.java @@ -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; + } } diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedRaceWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedRaceWithContext.java index 62a64f48bbe..484a3a70770 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedRaceWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedRaceWithContext.java @@ -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 diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java index deb6db49c7a..060102e7388 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/AbstractLeaderboardDTO.java @@ -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 courseAreas; public ScoringSchemeType scoringScheme; public LeaderboardType type; public boolean canBoatsOfCompetitorsChangePerRace; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CourseAreaDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CourseAreaDTO.java similarity index 84% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CourseAreaDTO.java rename to java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CourseAreaDTO.java index caa1eb4993e..be4f2916df0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CourseAreaDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/CourseAreaDTO.java @@ -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() { diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/IncrementalLeaderboardDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/IncrementalLeaderboardDTO.java index c01adf81c1f..b3d32685c4c 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/IncrementalLeaderboardDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/IncrementalLeaderboardDTO.java @@ -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); diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index 33938635f67..78fbb75d3b2 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -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 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 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 loadCourseAreas(Document documentContainingCourseAreaIds) { + final Set 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 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 dbSeries = (Iterable) dbRegatta.get(FieldNames.REGATTA_SERIES.name()); Iterable 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 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 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, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java index 5467cb8de54..f7c40e4f485 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java @@ -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, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java index 9b06d52e891..299e7cf30c9 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MigratableRegattaImpl.java @@ -30,12 +30,12 @@ public class MigratableRegattaImpl extends RegattaImpl implements MigratableRega public MigratableRegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, TimePoint startDate, TimePoint endDate, - Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, + Iterable series, boolean persistent, ScoringScheme scoringScheme, Serializable id, Iterable 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; diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java index 1aabc2b38b0..f986b00ca84 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java @@ -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 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; } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/RaceLogTrackingTestHelper.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/RaceLogTrackingTestHelper.java index c3e1ede31aa..ff41fefefbf 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/RaceLogTrackingTestHelper.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/RaceLogTrackingTestHelper.java @@ -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)); } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java index 7cf78cf058c..fe2a1d5bbff 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/CreateAndTrackWithRaceLogTest.java @@ -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. singletonList(series), /* persistent */ true, new HighPoint(), UUID.randomUUID(), + Collections. 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); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java index 49f7536337f..90915e063b4 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java @@ -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); diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java index d8736ef0c28..2123fb0a8c1 100755 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/TrackedRaceStartTimeInferenceTest.java @@ -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()); diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java index d8aa8e0a8e3..1e9d9a598a8 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/base/racegroup/impl/CurrentRaceFilterImplTest.java @@ -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; } diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/DependentStartTimeFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/DependentStartTimeFinderTest.java index 98191960711..39720e7ef11 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/DependentStartTimeFinderTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/DependentStartTimeFinderTest.java @@ -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); diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/RaceStatusAnalyzerTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/RaceStatusAnalyzerTest.java index b564f94ac9c..7a03c68717a 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/RaceStatusAnalyzerTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/RaceStatusAnalyzerTest.java @@ -213,7 +213,7 @@ public class RaceStatusAnalyzerTest extends PassAwareRaceLogAnalyzerTest { diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/StartTimeFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/StartTimeFinderTest.java index 9cc75cd6a06..a380c43f61f 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/StartTimeFinderTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/StartTimeFinderTest.java @@ -29,7 +29,8 @@ public class StartTimeFinderTest extends PassAwareRaceLogAnalyzerTest 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 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 nonEmpty2 = raceLog.add(startTimeEvent3, client1Id); assertEquals(1, Util.size(nonEmpty2)); assertSame(startTimeEvent2, nonEmpty2.iterator().next()); diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/test/SerializeRaceLogEventsTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/test/SerializeRaceLogEventsTest.java index 11c5f87fe94..4e6b423bb11 100755 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/test/SerializeRaceLogEventsTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/test/SerializeRaceLogEventsTest.java @@ -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(); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogCourseAreaSpecification.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogCourseAreaSpecification.java new file mode 100644 index 00000000000..88a832c565d --- /dev/null +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogCourseAreaSpecification.java @@ -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(); +} diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogDependentStartTimeEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogDependentStartTimeEvent.java index 5af62970560..312c9e2ddb3 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogDependentStartTimeEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogDependentStartTimeEvent.java @@ -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(); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartTimeEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartTimeEvent.java index a55d96ffbfe..d539df496b1 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartTimeEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/RaceLogStartTimeEvent.java @@ -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.

* * 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(); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/DependentStartTimeResolver.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/DependentStartTimeResolver.java index 4bf0402490a..247d48b749b 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/DependentStartTimeResolver.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/DependentStartTimeResolver.java @@ -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 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()); } } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinder.java index d4c9079f0f6..7ed98c8d0f3 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinder.java @@ -29,14 +29,17 @@ public class StartTimeFinder extends RaceLogAnalyzer { 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(); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinderResult.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinderResult.java index ace9266f26c..16e37cec5cc 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinderResult.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/StartTimeFinderResult.java @@ -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 dependingOnRaces; private final TimePoint startTime; + private final UUID courseAreaId; /** * null if resolution worked @@ -28,24 +30,25 @@ public class StartTimeFinderResult { private Duration startTimeDiff; - public StartTimeFinderResult(Iterable racesDependingOn, TimePoint startTime, Duration startTimeDiff) { - this(racesDependingOn, startTime, startTimeDiff, /* resolutionFailed */ null); + public StartTimeFinderResult(Iterable racesDependingOn, TimePoint startTime, Duration startTimeDiff, UUID courseAreaId) { + this(racesDependingOn, startTime, startTimeDiff, /* resolutionFailed */ null, courseAreaId); } - public StartTimeFinderResult(Iterable racesDependingOn, Duration startTimeDiff, ResolutionFailed resolutionFailed) { - this(racesDependingOn, /* startTime */ null, startTimeDiff, resolutionFailed); + public StartTimeFinderResult(Iterable racesDependingOn, Duration startTimeDiff, ResolutionFailed resolutionFailed, UUID courseAreaId) { + this(racesDependingOn, /* startTime */ null, startTimeDiff, resolutionFailed, courseAreaId); } public StartTimeFinderResult(Iterable 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.emptyList(), startTime, startTimeDiff); + public StartTimeFinderResult(TimePoint startTime, Duration startTimeDiff, UUID courseAreaId) { + this(/* racesDependingOn */ Collections.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; } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogDependentStartTimeEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogDependentStartTimeEventImpl.java index 64167beebf5..ff95a542d41 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogDependentStartTimeEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogDependentStartTimeEventImpl.java @@ -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 diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogStartTimeEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogStartTimeEventImpl.java index dd36cb324e9..717b475afe2 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogStartTimeEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/impl/RaceLogStartTimeEventImpl.java @@ -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())); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java index 710cafe3700..d38448c1b17 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/RaceState.java @@ -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. diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java index 67a67913bab..9cd979abd0a 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/ReadonlyRaceState.java @@ -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 null. */ Iterable 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(); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java index 5fee951c675..1fab6dffd8c 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/RaceStateImpl.java @@ -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) { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java index da7bdb00ec5..7ef2f5ecdde 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/impl/ReadonlyRaceStateImpl.java @@ -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(); + } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedurePrerequisite.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedurePrerequisite.java index b5113d147d4..939b90db7a8 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedurePrerequisite.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/state/racingprocedure/impl/BaseRacingProcedurePrerequisite.java @@ -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); } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java index e274d79a78e..c8d641bfe51 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/RaceGroup.java @@ -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 * null. diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java index 55399b6f23f..dcaa11f1b0b 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/racegroup/impl/RaceGroupImpl.java @@ -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 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 series, RegattaConfiguration regattaConfiguration) { + public RaceGroupImpl(String name, String displayName, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, Iterable 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 getSeries() { return series; diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogAttachOnTrackedRaceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogAttachOnTrackedRaceTest.java index f380b2f2410..93778a8a448 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogAttachOnTrackedRaceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/RegattaLogAttachOnTrackedRaceTest.java @@ -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 diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java index a7b9e0ffb32..a1f8241b26f 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackedRaceStartTimeUpdateForDependentStartTimeTest.java @@ -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]); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index b0757e95608..c29f55d2306 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -414,12 +414,12 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public CourseArea getDefaultCourseArea() { + public Iterable getCourseAreas() { return null; } @Override - public void setDefaultCourseArea(CourseArea newCourseArea) { + public void setCourseAreas(Iterable newCourseAreas) { } @Override diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java index cbc4ada5dee..366cb2236bd 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceCourseReceiver.java @@ -151,7 +151,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue(ttControlPointsForAllOriginalEventControlPoints.get(cp), passingInstructions)); i++; diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java index 8307fb03296..a09f79120fd 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/RaceTrackingConnectivityParametersImpl.java @@ -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; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/LeaderboardSearchResult.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/LeaderboardSearchResult.java index 8beb6d53e52..f64ebe7fe63 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/LeaderboardSearchResult.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/LeaderboardSearchResult.java @@ -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 getEvents(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java index 253ab268a12..0db1b5f6e63 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java @@ -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 fleet, the respective {@link RaceDefinition} is returned. * Otherwise, null is returned. diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java index 60997a38154..7a9c171f253 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Regatta.java @@ -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 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 newCourseAreas); /** * Gets the {@link RegattaConfiguration} associated with this {@link Regatta}'s races. diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java index 4cb2b71e4f4..856a046c93d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java @@ -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 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 RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name, + BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, + TimePoint startDate, TimePoint endDate, Iterable 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 RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name, BoatClass boatClass, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, TimePoint startDate, TimePoint endDate, Iterable series, boolean persistent, - ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, + ScoringScheme scoringScheme, Serializable id, Iterable 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 getCourseAreas() { + return courseAreas; } @Override - public void setDefaultCourseArea(CourseArea newCourseArea) { - this.defaultCourseArea = newCourseArea; + public void setCourseAreas(Iterable newCourseAreas) { + synchronized (this.courseAreas) { + this.courseAreas.clear(); + Util.addAll(newCourseAreas, this.courseAreas); + } } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java index ad673bbb966..f169ebbdf9b 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java @@ -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 newCourseAreas); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java index 2a94b95acca..30f6c333250 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java @@ -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 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()); } /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 10abda4cc60..4b7266b58c4 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -937,7 +937,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW public String toString() { return getName() + " " - + (getDefaultCourseArea() != null ? getDefaultCourseArea().getName() : "") + " " + + Util.join(", ", getCourseAreas()) + " " + (getScoringScheme() != null ? getScoringScheme().getType().name() : ""); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java index 3bc3bc656e1..bfd8582f184 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java @@ -422,8 +422,8 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr return getFullLeaderboard().getNowMinusDelay(); } - public CourseArea getDefaultCourseArea() { - return getFullLeaderboard().getDefaultCourseArea(); + public Iterable getCourseAreas() { + return getFullLeaderboard().getCourseAreas(); } public NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher() { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java index e05d0d11b69..8f1b851d203 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/FlexibleLeaderboardImpl.java @@ -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 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 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 getCourseAreas() { + return courseAreas; } @Override - public void setDefaultCourseArea(CourseArea newCourseArea) { - this.courseArea = newCourseArea; + public void setCourseAreas(Iterable newCourseAreas) { + synchronized (this.courseAreas) { + this.courseAreas.clear(); + Util.addAll(newCourseAreas, this.courseAreas); + } } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java index b2ee3dc3d1d..3b10e05599c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RegattaLeaderboardImpl.java @@ -87,8 +87,8 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R } @Override - public CourseArea getDefaultCourseArea() { - return regatta.getDefaultCourseArea(); + public Iterable getCourseAreas() { + return regatta.getCourseAreas(); } /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/FlexibleMetaLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/FlexibleMetaLeaderboard.java index ce998df8a04..5610a1020fd 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/FlexibleMetaLeaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/FlexibleMetaLeaderboard.java @@ -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 getCourseAreas() { + return Collections.emptySet(); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/LeaderboardGroupMetaLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/LeaderboardGroupMetaLeaderboard.java index 835c0293dd0..92b299809c3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/LeaderboardGroupMetaLeaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/LeaderboardGroupMetaLeaderboard.java @@ -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 getCourseAreas() { + return Collections.emptySet(); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/masterdataimport/TopLevelMasterData.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/masterdataimport/TopLevelMasterData.java index 793ca1e090f..eb8153395eb 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/masterdataimport/TopLevelMasterData.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/masterdataimport/TopLevelMasterData.java @@ -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> createEventMap(Set groupsToExport, Iterable allEvents) { @@ -156,9 +156,8 @@ public class TopLevelMasterData implements Serializable { HashSet eventSet = new HashSet(); 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); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponentSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponentSettings.java index 8ef186982f9..22e97504136 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponentSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/shared/header/SAPHeaderComponentSettings.java @@ -10,7 +10,6 @@ public class SAPHeaderComponentSettings extends AbstractGenericSerializableSetti @Override protected void addChildSettings() { - super.addChildSettings(); title = new StringSetting("title", this,""); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/HasRegattaMetadata.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/HasRegattaMetadata.java index c74e9a44ebe..1b77eacb149 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/HasRegattaMetadata.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/HasRegattaMetadata.java @@ -46,7 +46,7 @@ public interface HasRegattaMetadata { boolean isFlexibleLeaderboard(); - String getDefaultCourseAreaId(); + Iterable getCourseAreaIdsAsStrings(); EventSeriesReferenceDTO getSeriesReference(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/RegattaMetadataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/RegattaMetadataDTO.java index 5deb2484e6f..08411cb73da 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/RegattaMetadataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/eventview/RegattaMetadataDTO.java @@ -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 leaderboardGroupNames; private String defaultCourseAreaName; - private String defaultCourseAreaId; + private ArrayList 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 getCourseAreaIdsAsStrings() { + return courseAreaIdsAsStrings; } - public void setDefaultCourseAreaId(String defaultCourseAreaId) { - this.defaultCourseAreaId = defaultCourseAreaId; + public void setCourseAreaIdsAsStrings(Iterable courseAreaIdsAsStrings) { + this.courseAreaIdsAsStrings = new ArrayList<>(); + Util.addAll(courseAreaIdsAsStrings, this.courseAreaIdsAsStrings); } public RaceDataInfo getRaceDataInfo() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java index bc8cb1b7b52..3ce9059e9fa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java @@ -243,10 +243,9 @@ public abstract class AbstractEventActivity 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 { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/InSightAppNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/InSightAppNotes.html index d83307a398b..f94fbb8b769 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/InSightAppNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/InSightAppNotes.html @@ -2,8 +2,86 @@

-

What's New - SAP Sail InSight (Android)

+

What's New - SAP Sail InSight

+
08.04.2020 - Version 2.3.8
+
    +
  • Added MTCP communications module.
  • +
  • Fixed the bug that allowed you to get the waypoint name editing form field on start/finish waypoints.
  • +
  • Fixed issue with first character being capitalized on password input on Android devices.
  • +
  • Swap the order of first two steps in Event Overview page.
  • +
  • Changed mark tracking screen to make binding marks visually more comprehensible.
  • +
  • Added splash screen on Android.
  • +
  • Fixed navigation issue when quickly pressing sub-sections one by one on Account page.
  • +
  • Fixed issue with events become unresponsive after starting tracking for an invited event.
  • +
  • Fixed issue with anonymous users loses joined events after restarting the app.
  • +
+ +
18.03.2020 - Version 2.2.0
+
    +
  • Regatta details and races are now refreshed when selecting an event.
  • +
  • Fixed an issue with updating competitor ACL on the settings specified server instead of event server when joining an event.
  • +
  • Fixed an issue that prevented anonymous users from joining events due to an unnecessary call to update competitor ACL.
  • +
  • Course creator is now updated showing the new device binding when coming out of the ‘Scan QR Code’ screen for mark.
  • +
  • Display time gaps in the format 'DD:HH:mm:ss' in the leaderboard. Layout fixes to leaderboard screen.
  • +
  • Fixed a layout issue in course creator to extend the mark background to the lower edge of the screen.
  • +
  • Complete refactor of app navigation code, fixing various navigation issues.
  • +
  • Fixed an issue that showed an empty boat class field for owners in handicap based events.
  • +
  • Registration form is now cleared after a successful register.
  • +
  • Fixed an issue that hides the 'New team' button in teams screen when scrolling.
  • +
+ +
06.03.2020 - Version 2.1.1
+
    +
  • Fixed an issue that stopped tracking when tapping on tracking tab while being in leaderboard view.
  • +
  • Fixed an issue that made the Android back button not working properly in screens other than tracking.
  • +
  • Fixed an issue where the app didn't navigate properly after registering a new user.
  • +
+ +
03.03.2020 - Version 2.0.2
+
    +
  • React Navigation dependency upgraded to the latest version.
  • +
  • Visual fixes in leaderboard screen.
  • +
  • Added localized text for undefined course or missing start times in race details.
  • +
+ +
02.03.2020 - Version 2.0.0
+
    +
  • Fixed leaderboard layout issues on certain devices.
  • +
  • Competitors and boats are assigned ‘READ_ALL’ permissions when joining an event.
  • +
  • Removed deletion of locally stored gps fixes when tracking is started. This fixes scenarios related to tracking being interrupted and restarted.
  • +
  • Fixed an issue that prevented start tracking while offline.
  • +
  • Color code other competitors in the leaderboard based on whether they're ahead or behind my competitor.
  • +
  • Reduce the length of the mantissa to 1 when converting between handicap value types.
  • +
  • Fixed issue with Scan QR Code button overlapping the last item in Tracking screen.
  • +
  • Fixed issue with first save of a new course with mark position change of existing marks from inventory.
  • +
  • Fixed issue that sometimes prevented start time being set on races while tracking was active.
  • +
+ +
20.02.2020 - Version 1.0.64
+
    +
  • Fixed visuals for boat handicap style selector in Team details.
  • +
  • Default generated event names are now only placeholders in Event Creation screen, applied only if the input is left empty.
  • +
  • Default generated event names are now only placeholders in Event Creation screen, applied only if the input is left empty.
  • +
  • Fixed issue with fixed gps position overriding a tracker binding when saving a course right after binding was successfully made.
  • +
  • Fixed issue with ‘Join As Competitor’ button not being removed after an organizer joins an event he created.
  • +
  • Fixed issue where ‘Scan QR Code’ button overlapped the last event card in events list, making it hard to tap on the event.
  • +
  • Fixed issue where app was crashing when clicking on leaderboard dropdown.
  • +
  • Fixed issue with competitors not being able to see each other in the leaderboard due to permissions.
  • +
  • Fixed issue with missing multidex support on Android, making certain devices crash on app start.
  • +
  • Replaced hard to read font in event organizer view.
  • +
+ +
19.02.2020 - Version 1.0.63
+
    +
  • Fixed issue with tracking screen being closed when tapping multiple times on the tracking tab.
  • +
  • Added loading indicator on the button when creating a new event.
  • +
  • Added link to SAP race board for current tracked race from tracking screen.
  • +
  • Main tab bar hidden while device keyboard is shown.
  • +
  • Improved back button handling in Android devices.
  • +
  • Removed the modal popup presented to the user that blocked joining an event which is already added to the user's device.
  • +
+
March 2018
  • Made app robust against events that have no start/end dates set
  • @@ -24,6 +102,15 @@ and the automatic overriding of user preferences has been removed.
  • In some rare situations the app crashed, if you want to scan a QR-Code.
+ +
October 2016 - SAP Sail InSight (iOS)
+
    +
  • This release introduces an entirely new design.
  • +
  • The landscape mode is optimized for small devices.
  • +
  • Unsent GPS-fixes will be sent automatically after a sudden reboot of the device.
  • +
  • Fixed an issue where a login could be done twice.
  • +
  • Fixed an issue where an invalid login is handled as a successful one.
  • +
August 2016
@@ -43,17 +130,7 @@ start tracking for one buoy. It will track all buoys.
-

What's New - SAP Sail InSight (iOS)

-
-
October 2016
-
    -
  • This release introduces an entirely new design.
  • -
  • The landscape mode is optimized for small devices.
  • -
  • Unsent GPS-fixes will be sent automatically after a sudden reboot of the device.
  • -
  • Fixed an issue where a login could be done twice.
  • -
  • Fixed an issue where an invalid login is handled as a successful one.
  • -
-
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html index 4fcbd3fe2ac..4ca916ae44c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html @@ -4,6 +4,16 @@

What's New - SAP Sailing Race Manager

+
May 2020
+
    +
  • 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.
  • +
April 2020
  • The date of the start time can now be chosen via a suitable date picker. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/LeaderboardContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/LeaderboardContext.java index 7470d1f3f39..0978d7a5702 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/LeaderboardContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/LeaderboardContext.java @@ -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(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java index e3341f02d87..ac73d2e4f02 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/RaceContext.java @@ -255,7 +255,7 @@ public class RaceContext { } private String getCourseAreaOrNull() { - return HomeServiceUtil.getCourseAreaNameForRegattaIdThereIsMoreThanOne(event, leaderboard); + return HomeServiceUtil.getCourseAreaNameForRegattaIfThereIsMoreThanOne(event, leaderboard); } private RaceProgressDTO getProgressOrNull() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RaceDetailPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RaceDetailPanel.java index b08f9abbae8..5f1727fcd24 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RaceDetailPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RaceDetailPanel.java @@ -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) + "° " - + 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) + "° " + + 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) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java index 1068719a991..32f164ec8cd 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java @@ -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(); 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(" ", 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(" ", 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( new AsyncCallback() { @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(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java index e97e5e98c80..85a5fd502aa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java @@ -726,10 +726,6 @@ public class RegattaRaceStatesComponent extends AbstractCompositeComponent getAllRaces() { - return allEntries; - } - @Override public boolean hasSettings() { return true; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponentLifecycle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponentLifecycle.java index 7e7ea0ed24b..994d77e3136 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponentLifecycle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponentLifecycle.java @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesSettingsDialogComponent.java index 5531cde7450..355210f7207 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesSettingsDialogComponent.java @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java index a744844a4fb..59ec6cbf193 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java @@ -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 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 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 eventsInSeries) { - final CourseArea defaultCourseArea = leaderboard.getDefaultCourseArea(); - if (defaultCourseArea != null) { - for (Event event : eventsInSeries) { - if (Util.contains(event.getVenue().getCourseAreas(), defaultCourseArea)) { - return event; - } + Iterable eventsInSeries) { + for (Event event : eventsInSeries) { + if (Util.containsAny(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas())) { + return event; } } return null; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/regattaoverview/RegattaRaceStatesSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/regattaoverview/RegattaRaceStatesSettings.java index 3792b45cae6..00a47282216 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/regattaoverview/RegattaRaceStatesSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/settings/client/regattaoverview/RegattaRaceStatesSettings.java @@ -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 defaultRaceGroups) { - if(defaultRaceGroups == null) { + if (defaultRaceGroups == null) { defaultRaceGroups = Collections.emptySet(); } Set regattaIds = new HashSet<>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java index 1130e5c5de0..8372f923428 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractRegattaWithSeriesAndFleetsDialog.java @@ -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 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 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 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 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 extends DataEn return null; } - private boolean isCourseAreaInEvent(EventDTO event, UUID courseAreaId) { + private boolean isAnyOfTheCourseAreasInEvent(EventDTO event, Iterable 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 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 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 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 getSelectedCourseAreas() { + return courseAreaSelection.getSelectedCourseAreas(); } public RegattaDTO getRegattaDTO() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseAreaListInlineEditorComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseAreaListInlineEditorComposite.java index e84848ad860..4e3e9998646 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseAreaListInlineEditorComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseAreaListInlineEditorComposite.java @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseAreaSelection.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseAreaSelection.java new file mode 100755 index 00000000000..674b1b507ed --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CourseAreaSelection.java @@ -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 checkboxes; + private final Set selectedSet; + private final Function 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 courseAreas, StringMessages stringMessages, + Function 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 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 selectedCourseAreas) { + selectedSet.clear(); + for (final CourseAreaDTO selected : selectedCourseAreas) { + setSelected(selected, true); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java index 3e6fcd52e80..cc9cddaa879 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateRegattaCallback.java @@ -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{ 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() { + 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() { @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{ } @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{ dialog.show(); } - private EventDTO getEventForCourseArea(final List existingEvents, final UUID courseAreaId) { + private EventDTO getEventForCourseArea(final List existingEvents, final Iterable 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; } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventCreateDialog.java index 02b00bdec7a..e758b208828 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventCreateDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventCreateDialog.java @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java index 52b8689f287..8212d8ff687 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java index 27a347a9518..09e4a469c50 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDialog.java @@ -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:/ - */ - private static RegExp REGEXP_URL = RegExp.compile( - "^(https?)://((?:[\\w-]+\\.)+[a-z]{2,}|localhost)(?::\\d+)?(/[\\w~:@_./$!%&=+*'\",;()-]*)?$", - "i"); - protected static class EventParameterValidator implements Validator { private StringMessages stringMessages; @@ -78,13 +69,6 @@ public abstract class EventDialog extends DataEntryDialogWithDateTimeBox(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 existingEvents; - protected ListBox courseAreaListBox; + protected CourseAreaSelection courseAreaSelection; protected static class LeaderboardParameterValidator implements Validator { protected final StringMessages stringMessages; @@ -63,8 +63,8 @@ public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialo ErrorReporter errorReporter, LeaderboardParameterValidator validator, DialogCallback 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 getSelectedCourseAreas() { + return courseAreaSelection.getSelectedCourseAreas(); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/FlexibleLeaderboardEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/FlexibleLeaderboardEditDialog.java index 70374d750d5..643f20dfab5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/FlexibleLeaderboardEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/FlexibleLeaderboardEditDialog.java @@ -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 otherExistingLeaderboards, LeaderboardDescriptor leaderboard, StringMessages stringMessages, List existingEvents, ErrorReporter errorReporter, DialogCallback 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; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java index 65c1fccb287..6c62ee95e39 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java @@ -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 courseAreaColumn = new TextColumn() { + TextColumn courseAreasColumn = new TextColumn() { @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 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( new AsyncCallback() { @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() { + leaderboardToUpdate.getCourseAreaIds(), new AsyncCallback() { @Override public void onFailure(Throwable t) { errorReporter.reportError( diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptor.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptor.java index 12739592187..5eea1d1b060 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptor.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptor.java @@ -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 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 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} null, 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 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 getCourseAreaIds() { + return courseAreaIds; } - public void setCourseAreaId(UUID courseAreaId) { - this.courseAreaId = courseAreaId; + public void setCourseAreaIds(Iterable courseAreaIds) { + this.courseAreaIds = new ArrayList<>(); + Util.addAll(courseAreaIds, this.courseAreaIds); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithEliminations.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithEliminations.java index ea659cd13f5..8525dbda247 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithEliminations.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDescriptorWithEliminations.java @@ -8,7 +8,7 @@ public class LeaderboardDescriptorWithEliminations extends LeaderboardDescriptor private final Set eliminatedCompetitors; public LeaderboardDescriptorWithEliminations(LeaderboardDescriptor base, Set 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; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java index f91f2fb56ac..f72b4a85d27 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java @@ -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); } } - } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java index 7d70b40cfb9..000ef268338 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java @@ -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(new AsyncCallback() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java index a51f98d5cf7..b7d5548da83 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaWithSeriesAndFleetsEditDialog.java @@ -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() { @@ -83,7 +83,7 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl @Override protected FocusWidget getInitialFocusWidget() { - return courseAreaListBox; + return this.nameEntryField; } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeDialog.java index c8beeb561be..3a23e07fe6a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SetStartTimeDialog.java @@ -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>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError(stringMessages.unableToLoadCourseAreas(caught.getMessage()), /* silentMode */ true); + } + + @Override + public void onSuccess(List result) { + fillCourseAreaSelection(result); + } + }); + } + + private void fillCourseAreaSelection(List 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>() { - + service.getStartTimeAndProcedure(leaderboardName, raceColumnName, fleetName, + new AsyncCallback>() { @Override public void onSuccess(com.sap.sse.common.Util.Triple result) { if (result == null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java index 2e4ec1fbc1c..4da4b3411f0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java @@ -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(new AsyncCallback() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java index ee8149f72fa..707b2e832d6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index 565fbee0ba8..53445d2e06c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -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 namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails, String previousLeaderboardId, boolean fillTotalPointsUncorrected) throws UnauthorizedException, Exception; + List getCourseAreas(String leaderboardName); + IncrementalOrFullLeaderboardDTO getLeaderboardForRace(RegattaAndRaceIdentifier raceIdentifer, String leaderboardName, Date date, Collection namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails, String previousLeaderboardId, boolean fillTotalPointsUncorrected) @@ -289,11 +292,11 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS List getLeaderboardsWithSecurity() throws UnauthorizedException; StrippedLeaderboardDTOWithSecurity updateLeaderboard(String leaderboardName, String newLeaderboardName, - String newLeaderboardDisplayName, int[] newDiscardingThreasholds, UUID newCourseAreaId) + String newLeaderboardDisplayName, int[] newDiscardingThreasholds, Iterable newCourseAreaIds) throws UnauthorizedException; StrippedLeaderboardDTOWithSecurity createFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, - int[] discardThresholds, ScoringSchemeType scoringSchemeType, UUID courseAreaId) + int[] discardThresholds, ScoringSchemeType scoringSchemeType, Iterable 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 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 regattas) throws UnauthorizedException; - void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, UUID defaultCourseAreaUuid, + void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, Iterable 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 getRegattaStructureForEvent(UUID eventId) throws UnauthorizedException; - /** the replacement service for getRegattaStructureForEvent() */ - List getRegattaStructureOfEvent(UUID eventId) throws UnauthorizedException; - List getRaceStateEntriesForRaceGroup(UUID eventId, List visibleCourseAreas, List visibleRegattas, boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset) throws UnauthorizedException, Exception; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index a46b36921b4..773a9869d87 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -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> callback); void updateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName, - int[] newDiscardingThreasholds, UUID newCourseAreaId, + int[] newDiscardingThreasholds, Iterable newCourseAreaIds, AsyncCallback callback); void createFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, int[] discardThresholds, - ScoringSchemeType scoringSchemeType, UUID courseAreaId, + ScoringSchemeType scoringSchemeType, Iterable courseAreaIds, AsyncCallback 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 courseAreaIds, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetrics rankingMetricType, AsyncCallback callback); @@ -564,6 +565,8 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn void removeLeaderboardColumns(String leaderboardName, List columnsToRemove, AsyncCallback callback); + void getCourseAreas(String leaderboardName, AsyncCallback> callback); + void getLeaderboard(String leaderboardName, AsyncCallback callback); void getLeaderboardWithSecurity(String leaderboardName, AsyncCallback callback); @@ -606,10 +609,11 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn void deleteSwissTimingArchiveConfigurations(Collection dtos, AsyncCallback asyncCallback); - void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, UUID defaultCourseAreaUuid, + void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, Iterable courseAreaUuids, RegattaConfigurationDTO regattaConfiguration, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, - boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, CompetitorRegistrationType registrationType, AsyncCallback callback); + boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, + CompetitorRegistrationType registrationType, AsyncCallback callback); /** * @param detailType @@ -629,8 +633,6 @@ public interface SailingServiceAsync extends FileStorageManagementGwtServiceAsyn void getRegattaStructureForEvent(UUID eventId, AsyncCallback> asyncCallback); - void getRegattaStructureOfEvent(UUID eventId, AsyncCallback> callback); - void getRaceStateEntriesForRaceGroup(UUID eventId, List visibleCourseAreas, List visibleRegattas, boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset, AsyncCallback> markedAsyncCallback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceHelper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceHelper.java index c4400b452d4..5a3598bb1b9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceHelper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceHelper.java @@ -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); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index a94a995693f..fd30727f640 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2380,4 +2380,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String autoRestartTrackingUponCompetitorSetChange(); String whitelabelFooterLanguage(); String whitelabelSailing(); + String unableToLoadCourseAreas(String message); } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index f4cc9432ef9..3f43738511a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -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} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 301f1878761..7b249e5a36d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -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 \ No newline at end of file +autoRestartTrackingUponCompetitorSetChange=Tracking der Rennen automatisch neu starten, wenn sich die Teilnehmerliste ändert +unableToLoadCourseAreas=Liste der Regattabahnen kann nicht geladen werden: {0} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 6257a7453d5..0756debdfb0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -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 getCourseAreas(String leaderboardName) { + final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); + getSecurityService().checkCurrentUserReadPermission(leaderboard); + final List 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 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 courseAreaIds) { return getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError( SecuredDomainType.LEADERBOARD, Leaderboard.getTypeRelativeObjectIdentifier(leaderboardName), leaderboardDisplayName, new Callable() { @@ -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 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 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 getRegattaStructureOfEvent(UUID eventId) { - List raceGroups = new ArrayList(); - Event event = getService().getEvent(eventId); - getSecurityService().checkCurrentUserReadPermission(event); - Map leaderboardWithLeaderboardGroups = new HashMap(); - 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 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 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 result = new ArrayList(); 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> entriesPerFleet = new HashMap>(); 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> entriesPerFleet = new HashMap>(); 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 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 getLeaderboardSlotKey(RegattaOverviewEntryDTO entry) { + return new Triple<>(entry.leaderboardName, entry.raceInfo.raceName, entry.raceInfo.fleetName); + } + @Override public List getRaceStateEntriesForRaceGroup(UUID eventId, List visibleCourseAreaIds, List visibleRegattas, boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset) throws NoWindException, InterruptedException, ExecutionException { - List result = new ArrayList(); Calendar dayToCheck = Calendar.getInstance(); dayToCheck.setTime(new Date()); Event event = getService().getEvent(eventId); getSecurityService().checkCurrentUserReadPermission(event); + final Map, 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 00:00:00Z - clientTimeZoneOffset and ends at 23:59:59Z - clientTimeZoneOffset. */ private void getRegattaOverviewEntries(boolean showOnlyRacesOfSameDay, Duration clientTimeZoneOffset, - Calendar dayToCheck, CourseArea courseArea, Leaderboard leaderboard, String boatClassName, - String regattaName, Distance buyZoneRadius, String seriesName, RaceColumn raceColumn, - Map> entriesPerFleet) { + Calendar dayToCheck, Leaderboard leaderboard, String boatClassName, String regattaName, + Distance buyZoneRadius, String seriesName, RaceColumn raceColumn, Map> 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 00:00:00Z - clientTimeZoneOffset and ends at 23:59:59Z - clientTimeZoneOffset. */ - 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())); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceGroupDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceGroupDTO.java index a96b7484f15..9975804ab56 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceGroupDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceGroupDTO.java @@ -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; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java index 722ed03a2df..931e0ad1b8a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceInfoDTO.java @@ -10,7 +10,6 @@ import com.sap.sailing.domain.common.racelog.RacingProcedureType; public class RaceInfoDTO implements IsSerializable { public interface RaceInfoExtensionDTO extends IsSerializable { - } public static class GateStartInfoDTO implements RaceInfoExtensionDTO { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceLogSetStartTimeAndProcedureDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceLogSetStartTimeAndProcedureDTO.java index e5d24730394..447e9473ea5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceLogSetStartTimeAndProcedureDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceLogSetStartTimeAndProcedureDTO.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.shared; import java.util.Date; +import java.util.UUID; import com.google.gwt.user.client.rpc.IsSerializable; import com.sap.sailing.domain.common.racelog.RacingProcedureType; @@ -18,4 +19,5 @@ public class RaceLogSetStartTimeAndProcedureDTO implements IsSerializable { public Date logicalTimePoint; public Date startTime; public RacingProcedureType racingProcedure; + public UUID courseAreaId; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java index 25dc3338ea0..4387ab34e43 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RegattaDTO.java @@ -3,7 +3,6 @@ package com.sap.sailing.gwt.ui.shared; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.UUID; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.CompetitorRegistrationType; @@ -13,6 +12,7 @@ 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.BoatClassDTO; +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; @@ -37,8 +37,7 @@ public class RegattaDTO extends NamedSecuredObjectDTO implements SecuredDTO { public List races; public List series; public ScoringSchemeType scoringScheme; - public UUID defaultCourseAreaUuid; - public String defaultCourseAreaName; + public List courseAreas = new ArrayList<>(); public DeviceConfigurationDTO.RegattaConfigurationDTO configuration; public boolean useStartTimeInference = true; public boolean controlTrackingFromStartAndFinishTimes = false; @@ -72,8 +71,7 @@ public class RegattaDTO extends NamedSecuredObjectDTO implements SecuredDTO { this.series.add(new SeriesDTO(otherSeries)); // clone the series; clients may replace / alter their fields } this.scoringScheme = other.scoringScheme; - this.defaultCourseAreaUuid = other.defaultCourseAreaUuid; - this.defaultCourseAreaName = other.defaultCourseAreaName; + this.courseAreas = other.courseAreas; this.rankingMetricType = other.rankingMetricType; this.configuration = other.configuration; this.useStartTimeInference = other.useStartTimeInference; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/VenueDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/VenueDTO.java index 19ca659a4a7..200e251251a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/VenueDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/VenueDTO.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.shared; import java.util.ArrayList; import java.util.List; +import com.sap.sailing.domain.common.dto.CourseAreaDTO; import com.sap.sse.security.shared.dto.NamedDTO; public class VenueDTO extends NamedDTO { diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java index 2e505eaa35d..8a06284fa1f 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/RaceLogConnectivityParamsLoadAndStoreTest.java @@ -20,7 +20,6 @@ import com.mongodb.MongoException; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; -import com.sap.sailing.domain.base.impl.CourseAreaImpl; import com.sap.sailing.domain.base.impl.FleetImpl; import com.sap.sailing.domain.base.impl.SeriesImpl; import com.sap.sailing.domain.common.CompetitorRegistrationType; @@ -53,7 +52,8 @@ public class RaceLogConnectivityParamsLoadAndStoreTest extends AbstractConnectiv final Regatta regatta = racingEventService.createRegatta("My Regatta", "12mR", true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, MillisecondsTimePoint.now(), MillisecondsTimePoint.now().plus(Duration.ONE_DAY), UUID.randomUUID(), series, /* persistent */ true, - new LowPoint(), new CourseAreaImpl("Default", UUID.randomUUID()), /* buoyZoneRadiusInHullLengths */ 2.0, + new LowPoint(), racingEventService.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Default").getId(), + /* buoyZoneRadiusInHullLengths */ 2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); theSeries.addRaceColumn("R1", racingEventService); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java index bd7b0d6039e..1a636c2da3e 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreAndLoadRaceLogEventsTest.java @@ -150,7 +150,7 @@ public class StoreAndLoadRaceLogEventsTest extends AbstractMongoDBTest { public void testStoreAndLoadStartTimeEvent() { TimePoint startTime = new MillisecondsTimePoint(1337); RaceLogStartTimeEvent expectedEvent = new RaceLogStartTimeEventImpl(MillisecondsTimePoint.now(), - expectedEventTime, author, expectedId, expectedPassId, startTime, RaceLogRaceStatus.SCHEDULED); + expectedEventTime, author, expectedId, expectedPassId, startTime, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null); Document dbObject = mongoFactory.storeRaceLogEntry(logIdentifier, expectedEvent); RaceLogStartTimeEvent actualEvent = loadEvent(dbObject); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestRaceLogRemovalInRegatta.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestRaceLogRemovalInRegatta.java index 965f1e5bf81..ad4406320a6 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestRaceLogRemovalInRegatta.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestRaceLogRemovalInRegatta.java @@ -57,7 +57,7 @@ public class TestRaceLogRemovalInRegatta extends AbstractTestStoringAndRetrievin TimeRange protestTime = new TimeRangeImpl(MillisecondsTimePoint.now(), MillisecondsTimePoint.now().plus(Duration.ONE_MINUTE.times(90))); RaceLogProtestStartTimeEvent expectedEventYellow = new RaceLogProtestStartTimeEventImpl(now, author, 0, protestTime); yellowLog.add(expectedEventYellow); - RaceLogStartTimeEvent expectedEventBlue = new RaceLogStartTimeEventImpl(now, author, 0, MillisecondsTimePoint.now()); + RaceLogStartTimeEvent expectedEventBlue = new RaceLogStartTimeEventImpl(now, author, 0, MillisecondsTimePoint.now(), /* courseAreaId */ null); blueLog.add(expectedEventBlue); // these events are now expected to be stored persistently in the race log store //getLastError() is now deprecated - seems to run fine without (at least locally) diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java index 4bd1b2de649..6fd5b6cb0e6 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndLoadingEventsAndRegattas.java @@ -219,26 +219,23 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest final Venue venue = new VenueImpl(venueName); CourseArea courseArea = DomainFactory.INSTANCE.getOrCreateCourseArea(UUID.randomUUID(), courseAreaName); venue.addCourseArea(courseArea); - MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService()); Event event = new EventImpl(eventName, eventStartDate, eventEndDate, venue, /*isPublic*/ true, UUID.randomUUID()); mof.storeEvent(event); - final String regattaBaseName = "Kieler Woche"; BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); Regatta regatta = createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, regattaStartDate, regattaEndDate, /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), courseArea, OneDesignRankingMetric::new); mof.storeRegatta(regatta); - DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE); Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null); assertNotNull(loadedRegatta); assertEquals(regatta.getName(), loadedRegatta.getName()); assertEquals(Util.size(regatta.getSeries()), Util.size(loadedRegatta.getSeries())); - assertNotNull(loadedRegatta.getDefaultCourseArea()); - assertEquals(loadedRegatta.getDefaultCourseArea().getId(), courseArea.getId()); - assertEquals(loadedRegatta.getDefaultCourseArea().getName(), courseArea.getName()); + assertFalse(Util.isEmpty(loadedRegatta.getCourseAreas())); + assertTrue(Util.contains(Util.map(loadedRegatta.getCourseAreas(), CourseArea::getId), courseArea.getId())); + assertTrue(Util.contains(Util.map(loadedRegatta.getCourseAreas(), CourseArea::getName), courseArea.getName())); assertEquals(loadedRegatta.getStartDate(), regattaStartDate); assertEquals(loadedRegatta.getEndDate(), regattaEndDate); assertTrue(loadedRegatta.getCompetitorRegistrationType() == CompetitorRegistrationType.CLOSED); @@ -424,7 +421,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest /* canBoatsOfCompetitorsChangePerRace */ false, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, regattaStartDate, regattaEndDate, "123", regattaProxy.getSeries(), regattaProxy.isPersistent(), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), - /* defaultCourseAreaId */ null, /* buoyZoneRadiusInHullLengths */ 2.0, /* useStartTimeInference */ true, + /* courseAreaIds */ Collections.emptySet(), /* buoyZoneRadiusInHullLengths */ 2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); DynamicCompetitorWithBoat competitorWithBoat1 = AbstractLeaderboardTest.createCompetitorWithBoat("Humba1"); DynamicCompetitorWithBoat competitorWithBoat2 = AbstractLeaderboardTest.createCompetitorWithBoat("Humba2"); @@ -497,7 +494,7 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest regattaProxy.canBoatsOfCompetitorsChangePerRace(), regattaProxy.getCompetitorRegistrationType(), /* registrationLinkSecret */ null, regattaProxy.getStartDate(), regattaProxy.getEndDate(), "123", regattaProxy.getSeries(), regattaProxy.isPersistent(), - DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* courseAreaIds */ Collections.emptySet(), /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); trackedRegatta[0] = new DynamicTrackedRegattaImpl(regatta); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java index f1e581962b8..0852e2303ec 100755 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingLeaderboards.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; +import java.util.UUID; import org.junit.Before; import org.junit.Test; @@ -15,6 +16,7 @@ import org.junit.Test; import com.mongodb.MongoException; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorWithBoat; +import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.impl.BoatClassImpl; @@ -143,13 +145,17 @@ public class TestStoringAndRetrievingLeaderboards extends AbstractMongoDBTest { public void testStoreAndRetrieveSimpleLeaderboard() { final String leaderboardName = "TestLeaderboard"; final int[] discardIndexResultsStartingWithHowManyRaces = new int[] { 5, 8 }; + final CourseArea courseArea = DomainFactory.INSTANCE.getOrCreateCourseArea(UUID.randomUUID(), "My Course Area"); FlexibleLeaderboardImpl leaderboard = new FlexibleLeaderboardImpl(leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), - new LowPoint(), null); + new LowPoint(), courseArea); new MongoObjectFactoryImpl(db).storeLeaderboard(leaderboard); Leaderboard loadedLeaderboard = new DomainObjectFactoryImpl(db, DomainFactory.INSTANCE).loadLeaderboard(leaderboardName, /* regattaRegistry */ null, /* leaderboardRegistry */ null); assertEquals(leaderboardName, loadedLeaderboard.getName()); assertTrue(Arrays.equals(discardIndexResultsStartingWithHowManyRaces, ((ThresholdBasedResultDiscardingRule) loadedLeaderboard.getResultDiscardingRule()).getDiscardIndexResultsStartingWithHowManyRaces())); + assertEquals(1, Util.size(loadedLeaderboard.getCourseAreas())); + assertEquals(courseArea.getName(), loadedLeaderboard.getCourseAreas().iterator().next().getName()); + assertEquals(courseArea.getId(), loadedLeaderboard.getCourseAreas().iterator().next().getId()); } @Test diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java index ba3ef536328..2d4f2deddfd 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInLeaderboards.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.net.UnknownHostException; +import java.util.Collections; import java.util.UUID; import org.bson.Document; @@ -105,7 +106,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD DomainFactory.INSTANCE); leaderboard = new FlexibleLeaderboardImpl(getRaceLogStore(), getRegattaLogStore(), leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardIndexResultsStartingWithHowManyRaces), - new LowPoint(), null); + new LowPoint(), /* courseAreaIds */ Collections.emptySet()); leaderboard.addRaceColumn(raceColumnName, /* medalRace */false); } @@ -462,7 +463,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD @Test public void testStoreAndRetrieveSimpleLeaderboardWithRaceLogStartTimeEvent() { - RaceLogStartTimeEvent event = new RaceLogStartTimeEventImpl(now, author, 0, now); + RaceLogStartTimeEvent event = new RaceLogStartTimeEventImpl(now, author, 0, now, /* courseAreaId */ null); addAndStoreRaceLogEvent(leaderboard, raceColumnName, event); @@ -558,7 +559,7 @@ public class TestStoringAndRetrievingRaceLogInLeaderboards extends RaceLogMongoD final String FLEET = "fleet"; final SimpleRaceLogIdentifier srli = new SimpleRaceLogIdentifierImpl(PARENT, COLUMN, FLEET); RaceLogDependentStartTimeEvent event = new RaceLogDependentStartTimeEventImpl( - now, author, 0, srli, Duration.ONE_MINUTE); + now, author, 0, srli, Duration.ONE_MINUTE, /* courseAreaId */ null); addAndStoreRaceLogEvent(leaderboard, raceColumnName, event); RaceLog loadedRaceLog = retrieveRaceLog(); loadedRaceLog.lockForRead(); diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java index aaaa326825b..dc34c1626dc 100644 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java +++ b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/TestStoringAndRetrievingRaceLogInRegatta.java @@ -330,7 +330,7 @@ public class TestStoringAndRetrievingRaceLogInRegatta extends AbstractTestStorin @Test public void testStoreAndRetrieveRegattaWithRaceLogStartTimeEvent() { - RaceLogStartTimeEvent event = new RaceLogStartTimeEventImpl(now, author, 0, now); + RaceLogStartTimeEvent event = new RaceLogStartTimeEventImpl(now, author, 0, now, /* courseAreaId */ null); addAndStoreRaceLogEvent(regatta, raceColumnName, event); diff --git a/java/com.sap.sailing.news/src/com/sap/sailing/news/impl/LeaderboardUpdateEventNewsProvider.java b/java/com.sap.sailing.news/src/com/sap/sailing/news/impl/LeaderboardUpdateEventNewsProvider.java index 0c55fbdde67..2f297b46d10 100644 --- a/java/com.sap.sailing.news/src/com/sap/sailing/news/impl/LeaderboardUpdateEventNewsProvider.java +++ b/java/com.sap.sailing.news/src/com/sap/sailing/news/impl/LeaderboardUpdateEventNewsProvider.java @@ -7,7 +7,6 @@ import java.util.LinkedList; import java.util.List; import com.sap.sailing.domain.base.BoatClass; -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.leaderboard.Leaderboard; @@ -17,6 +16,7 @@ import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; import com.sap.sailing.news.EventNewsItem; import com.sap.sailing.news.EventNewsProvider; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; /** * An event news provider looking at leaderboard updates triggered by changes of timePointOfLatestModification @@ -29,20 +29,21 @@ public class LeaderboardUpdateEventNewsProvider implements EventNewsProvider { public Collection getNews(Event event) { List result = new LinkedList<>(); Iterable leaderboardGroups = event.getLeaderboardGroups(); - for(LeaderboardGroup leaderboardGroup: leaderboardGroups) { - for(Leaderboard leaderboard: leaderboardGroup.getLeaderboards()) { - if(!isPartOfEvent(event, leaderboard)) { + for (LeaderboardGroup leaderboardGroup : leaderboardGroups) { + for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { + if (!isPartOfEvent(event, leaderboard)) { continue; } SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection(); - if(scoreCorrection != null) { + if (scoreCorrection != null) { TimePoint timePointOfLatestModification = scoreCorrection.getTimePointOfLastCorrectionsValidity(); - if(timePointOfLatestModification != null) { - String displayName = leaderboard.getDisplayName() != null ?leaderboard.getDisplayName() :leaderboard.getName(); - String boatClassName= null; - if(leaderboard instanceof RegattaLeaderboard) { + if (timePointOfLatestModification != null) { + String displayName = leaderboard.getDisplayName() != null ? leaderboard.getDisplayName() + : leaderboard.getName(); + String boatClassName = null; + if (leaderboard instanceof RegattaLeaderboard) { BoatClass boatClass = ((RegattaLeaderboard) leaderboard).getRegatta().getBoatClass(); - if(boatClass != null) { + if (boatClass != null) { boatClassName = boatClass.getDisplayName(); } } @@ -51,10 +52,8 @@ public class LeaderboardUpdateEventNewsProvider implements EventNewsProvider { } } } - Collections.sort(result); - - if(result.size() <= 10) { + if (result.size() <= 10) { return result; } return result.subList(0, 11); @@ -62,16 +61,8 @@ public class LeaderboardUpdateEventNewsProvider implements EventNewsProvider { // TODO duplicate code taken from HomeServiceUtil private boolean isPartOfEvent(EventBase event, Leaderboard regattaEntity) { - final CourseArea defaultCourseArea = regattaEntity.getDefaultCourseArea(); - if(defaultCourseArea == null) { - return true; - } - for (CourseArea courseArea : event.getVenue().getCourseAreas()) { - if(courseArea.equals(defaultCourseArea)) { - return true; - } - } - return false; + return Util.isEmpty(regattaEntity.getCourseAreas()) + || Util.containsAny(event.getVenue().getCourseAreas(), regattaEntity.getCourseAreas()); } @Override diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/Regatta.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/Regatta.java index 09315986759..192bd3a2fdb 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/Regatta.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/Regatta.java @@ -1,5 +1,6 @@ package com.sap.sailing.selenium.api.regatta; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; import com.sap.sailing.domain.common.CompetitorRegistrationType; @@ -32,8 +33,8 @@ public class Regatta extends JsonWrapper { return get("scoringSystem"); } - public String getCourseAreaId() { - return get("courseAreaId"); + public JSONArray getCourseAreaId() { + return get("courseAreaIds"); } public Boolean canBoatsOfCompetitorsChangePerRace() { diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java index b9380f2a431..7fa69ced790 100755 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java @@ -70,28 +70,24 @@ public class RegattaApiTest extends AbstractSeleniumTest { @Test public void testGetRegattaForCreatedEvent() { final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT); - eventApi.createEvent(ctx, EVENT_NAME, BOAT_CLASS, CLOSED, "default"); Regatta regatta = regattaApi.getRegatta(ctx, EVENT_NAME); JSONArray series = (JSONArray) regatta.get("series"); JSONObject serie = (JSONObject) series.get(0); JSONArray fleets = (JSONArray) serie.get("fleets"); JSONObject trackedRaces = (JSONObject) serie.get("trackedRaces"); - assertEquals("read: regatta.name is different", EVENT_NAME, regatta.getName()); assertNull("read: regatta.startDate should be null", regatta.getStartDate()); assertNull("read: regatta.endDate should be null", regatta.getEndDate()); assertEquals("read: regatta.scoringSystem is different", "LOW_POINT", regatta.getScoringSystem()); assertEquals("read: regatta.boeatclass is different", BOAT_CLASS, regatta.getBoatClass()); - assertNotNull("read: regatta.courseAreaId is missing", regatta.getCourseAreaId()); + assertFalse("read: regatta.courseAreaId is missing", regatta.getCourseAreaId().isEmpty()); assertEquals("read: regatta.canBoatsOfCompetitorsChangePerRace should be false", false, regatta.canBoatsOfCompetitorsChangePerRace()); assertEquals("read: regatta.competitorRegistrationType is different", CLOSED, regatta.getCompetitorRegistrationType()); - assertTrue(regatta.isUseStartTimeInference()); assertFalse(regatta.isControlTrackingFromStartAndFinishTimes()); - assertEquals("read: reagtta.series should have 1 entry", 1, series.size()); assertEquals("read: reagtta.fleets should have 1 entry", 1, fleets.size()); assertNotNull("read: reagtta.trackedRaces is missing", trackedRaces); diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/CourseAreaSelectionPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/CourseAreaSelectionPO.java new file mode 100755 index 00000000000..35050bfa79f --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/CourseAreaSelectionPO.java @@ -0,0 +1,45 @@ +package com.sap.sailing.selenium.pages.adminconsole; + +import java.util.ArrayList; +import java.util.List; + +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import com.sap.sailing.selenium.pages.PageArea; +import com.sap.sailing.selenium.pages.gwt.CheckBoxPO; + +public class CourseAreaSelectionPO extends PageArea { + private final List courseAreaCheckBoxes; + + public static CourseAreaSelectionPO create(WebDriver driver, WebElement element) { + return new CourseAreaSelectionPO(driver, element); + } + + public CourseAreaSelectionPO(WebDriver driver, WebElement element) { + super(driver, element); + courseAreaCheckBoxes = new ArrayList<>(); + for (final WebElement checkbox : element.findElements(By.className("gwt-CheckBox"))) { + courseAreaCheckBoxes.add(CheckBoxPO.create(driver, checkbox)); + } + } + + public Iterable getSelectedCourseAreaNames() { + final List result = new ArrayList<>(); + for (final CheckBoxPO checkbox : courseAreaCheckBoxes) { + if (checkbox.isSelected()) { + result.add(checkbox.getLabel()); + } + } + return result; + } + + public void selectCourseAreaByLabel(String courseAreaName) { + for (final CheckBoxPO checkbox : courseAreaCheckBoxes) { + if (checkbox.getLabel().equals(courseAreaName)) { + checkbox.setSelected(true); + } + } + } +} diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventConfigurationPanelPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventConfigurationPanelPO.java index 110aabc37c6..9a0dd073c20 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventConfigurationPanelPO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventConfigurationPanelPO.java @@ -78,14 +78,14 @@ public class EventConfigurationPanelPO extends PageArea { public void createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(String eventName, String eventDesc, String venue, Date eventStartDate, Date eventEndDate, boolean isPublic, String regattaName, String boatClass, - Date regattaStartDate, Date regattaEndDate, boolean useOverallLeaderboard, String...courseAreaNames) { + Date regattaStartDate, Date regattaEndDate, boolean useOverallLeaderboard, String... courseAreaNames) { createEvent(eventName, eventDesc, venue, eventStartDate, eventEndDate, isPublic, courseAreaNames).pressYes(); LeaderboardGroupCreateDialogPO leaderboardGroupCreateDialogPO = getPO(LeaderboardGroupCreateDialogPO::new, ID_CREATE_DEFAULT_LEADERBOARD_GROUP_DIALOG); leaderboardGroupCreateDialogPO.setUseOverallLeaderboard(useOverallLeaderboard); leaderboardGroupCreateDialogPO.pressOk(); waitForPO(ConfirmDialogPO::new, ID_CREATE_DEFAULT_REGATTA_CONFIRM_DIALOG, 5).pressYes(); - if(courseAreaNames.length > 0) { - createRegatta(regattaName, boatClass, regattaStartDate, regattaEndDate, eventName, "Default").pressOk(); + if (courseAreaNames.length > 0) { + createRegatta(regattaName, boatClass, regattaStartDate, regattaEndDate, eventName, courseAreaNames).pressOk(); } else { createRegatta(regattaName, boatClass, regattaStartDate, regattaEndDate).pressOk(); } @@ -131,11 +131,12 @@ public class EventConfigurationPanelPO extends PageArea { return waitForPO(ConfirmDialogPO::new, ID_CREATE_DEFAULT_LEADERBOARD_GROUP_CONFIRM_DIALOG, 5); } - private ConfirmDialogPO createRegatta(String name, String boatClass, Date start, Date end, String eventToLink, String courseAreaToLink) { + private ConfirmDialogPO createRegatta(String name, String boatClass, Date start, Date end, String eventToLink, + String[] courseAreasToLink) { RegattaCreateDialogPO createDialog = getPO(RegattaCreateDialogPO::new, ID_REGATTA_CREATE_DIALOG); createDialog.setValues(name, boatClass, start, end); - if(eventToLink != null && courseAreaToLink != null) { - createDialog.setEventAndCourseArea(eventToLink, courseAreaToLink); + if (eventToLink != null && courseAreasToLink != null) { + createDialog.setEventAndCourseArea(eventToLink, courseAreasToLink); } createDialog.pressOk(); return waitForPO(ConfirmDialogPO::new, ID_CREATE_DEFAULT_REGATTA_LEADERBOARD_CONFIRM_DIALOG, 30); diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java index 121540b0097..a94bcc9f91a 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java @@ -9,6 +9,7 @@ import org.openqa.selenium.support.ui.Select; import com.sap.sailing.domain.common.CompetitorRegistrationType; import com.sap.sailing.selenium.core.BySeleniumId; import com.sap.sailing.selenium.core.FindBy; +import com.sap.sailing.selenium.pages.adminconsole.CourseAreaSelectionPO; import com.sap.sailing.selenium.pages.common.DataEntryDialogPO; import com.sap.sailing.selenium.pages.gwt.DateAndTimeInputPO; import com.sap.sailing.selenium.pages.gwt.ListBoxPO; @@ -27,12 +28,11 @@ public class RegattaCreateDialogPO extends DataEntryDialogPO { @FindBy(how = BySeleniumId.class, using = "EndDateTimeBox") private WebElement endDateTimeBox; -// @FindBy(how = BySeleniumId.class, using = "ScoringSchemeListBox") -// private WebElement scoringSystemDropDown; @FindBy(how = BySeleniumId.class, using = "EventListBox") private WebElement eventDropDown; + @FindBy(how = BySeleniumId.class, using = "CourseAreaListBox") - private WebElement courseAreaDropDown; + private WebElement courseAreaSelection; @FindBy(how = BySeleniumId.class, using = "AddSeriesButton") private WebElement addSeriesButton; @@ -59,9 +59,14 @@ public class RegattaCreateDialogPO extends DataEntryDialogPO { } } - public void setEventAndCourseArea(String event, String courseArea) { + public void setEventAndCourseArea(String event, String[] courseAreaNames) { ListBoxPO.create(driver, eventDropDown).selectOptionByLabel(event); - ListBoxPO.create(driver, courseAreaDropDown).selectOptionByLabel(courseArea); + if (courseAreaNames != null) { + final CourseAreaSelectionPO courseAreaSelectionPO = CourseAreaSelectionPO.create(driver, courseAreaSelection); + for (final String courseAreaName : courseAreaNames) { + courseAreaSelectionPO.selectCourseAreaByLabel(courseAreaName); + } + } } public void setValues(String name, String boatClass, Date startDate, Date endDate) { diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaEditDialogPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaEditDialogPO.java index 3eb2dcb7032..882036f652d 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaEditDialogPO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaEditDialogPO.java @@ -25,7 +25,7 @@ public class RegattaEditDialogPO extends DataEntryDialogPO { private WebElement canBoatsOfCompetitorsChangePerRaceCheckbox; @FindBy(how = BySeleniumId.class, using = "CourseAreaListBox") - private WebElement courseAreaListBox; + private WebElement courseAreaSelection; @FindBy(how = BySeleniumId.class, using = "AddSeriesButton") private WebElement addSeriesButton; @@ -49,9 +49,7 @@ public class RegattaEditDialogPO extends DataEntryDialogPO { public SeriesCreateDialogPO addSeries() { this.addSeriesButton.click(); - WebElement dialog = findElementBySeleniumId(this.driver, "SeriesCreateDialog"); - return new SeriesCreateDialogPO(this.driver, dialog); } diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaStructureManagementPanelPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaStructureManagementPanelPO.java index c2d672d09e0..c237820d9a5 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaStructureManagementPanelPO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaStructureManagementPanelPO.java @@ -57,12 +57,12 @@ public class RegattaStructureManagementPanelPO extends PageArea { createDefaultRegattaLeaderboardDialog.pressCancel(); } - public void createRegattaAndAddToEvent(RegattaDescriptor regatta, String event, String courseArea) { + public void createRegattaAndAddToEvent(RegattaDescriptor regatta, String event, String[] courseAreaNames) { RegattaCreateDialogPO createRegattaDialog = startRegattaCreation(); createRegattaDialog.setRegattaName(regatta.getName()+" ("+regatta.getBoatClass()+")"); createRegattaDialog.setBoatClass(regatta.getBoatClass()); createRegattaDialog.setCompetitorRegistrationType(regatta.getCompetitorRegistrationType()); - createRegattaDialog.setEventAndCourseArea(event, courseArea); + createRegattaDialog.setEventAndCourseArea(event, courseAreaNames); createRegattaDialog.pressOk(); createDefaultRegattaLeaderboard().pressOk(); waitForPO(ConfirmDialogPO::new, EventConfigurationPanelPO.ID_LINK_LEADERBORAD_TO_GROUP_DIALOG, 5).pressOk(); diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/raceboard/SettingsTest.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/raceboard/SettingsTest.java index 7633aae929a..64d2c054e84 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/raceboard/SettingsTest.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/raceboard/SettingsTest.java @@ -654,7 +654,7 @@ public class SettingsTest extends AbstractSeleniumTest { // add a second regatta to the event and link its leaderboard to the event leaderboard RegattaStructureManagementPanelPO regattas = adminConsole.goToRegattaStructure(); regattas.createRegattaAndAddToEvent(new RegattaDescriptor(AUDI_CUP_REGATTA, AUDI_CUP_BOAT_CLASS), BMW_CUP_EVENT, - CUSTOM_COURSE_AREA); + new String[] { CUSTOM_COURSE_AREA }); // open regatta overview for the created event events = adminConsole.goToEvents(); RegattaOverviewPage regattaOverviewPage = events.goToRegattaOverviewOfEvent(BMW_CUP_EVENT); @@ -718,7 +718,7 @@ public class SettingsTest extends AbstractSeleniumTest { // add a second regatta to the event and link its leaderboard to the event leaderboard regattas = adminConsole.goToRegattaStructure(); regattas.createRegattaAndAddToEvent(new RegattaDescriptor(BMW_CUP_REGATTA, BMW_CUP_BOAT_CLASS), AUDI_CUP_EVENT, - CUSTOM_COURSE_AREA); + new String[] { CUSTOM_COURSE_AREA }); // open regatta overview without ignoreLocalSettings flag for the recently created event events = adminConsole.goToEvents(); String audiCupRegattaOverviewUrl = events.getRegattaOverviewUrlOfEvent(AUDI_CUP_EVENT); diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java index 2a1b33415d7..2d3475959d4 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racegroup/impl/RaceGroupDeserializer.java @@ -6,7 +6,6 @@ import java.util.Collection; import org.json.simple.JSONObject; import com.sap.sailing.domain.base.BoatClass; -import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.SharedDomainFactory; import com.sap.sailing.domain.base.configuration.RegattaConfiguration; import com.sap.sailing.domain.base.racegroup.RaceGroup; @@ -54,12 +53,7 @@ public class RaceGroupDeserializer implements JsonDeserializer { BoatClass boatClass = null; String displayName = null; Boolean canBoatsOfCompetitorsChangePerRace = false; - CourseArea courseArea = null; RegattaConfiguration configuration = null; - if (object.containsKey(RaceGroupJsonSerializer.FIELD_COURSE_AREA)) { - // TODO: deserialize CourseArea ... - // WHY should I? - } if (object.containsKey(RaceGroupJsonSerializer.FIELD_BOAT_CLASS)) { boatClass = boatClassDeserializer.deserialize(Helpers.getNestedObjectSafe(object, RaceGroupJsonSerializer.FIELD_BOAT_CLASS)); @@ -81,6 +75,6 @@ public class RaceGroupDeserializer implements JsonDeserializer { final Object canBoatsOfCompetitorsChangePerRaceJson = object.get(RaceGroupJsonSerializer.FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE); canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRaceJson == null ? null : (Boolean) canBoatsOfCompetitorsChangePerRaceJson; } - return new RaceGroupImpl(name, displayName, boatClass, canBoatsOfCompetitorsChangePerRace, courseArea, series, configuration); + return new RaceGroupImpl(name, displayName, boatClass, canBoatsOfCompetitorsChangePerRace, series, configuration); } } \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDependentStartTimeEventDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDependentStartTimeEventDeserializer.java index 712a10e134b..d6d6a7f7f11 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDependentStartTimeEventDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogDependentStartTimeEventDeserializer.java @@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.deserialization.racelog.impl; import java.io.Serializable; import java.util.List; +import java.util.UUID; import org.json.simple.JSONObject; @@ -37,7 +38,14 @@ public class RaceLogDependentStartTimeEventDeserializer extends RaceLogRaceStatu Duration startTimeDifference = new MillisecondsDurationImpl(startTimeDifferenceInMs); RaceLogRaceStatusEvent event = (RaceLogRaceStatusEvent) super.deserialize(object, id, createdAt, author, timePoint, passId, competitors); SimpleRaceLogIdentifier dependentOnRace = new SimpleRaceLogIdentifierImpl(regattaLikeParentName, raceColumnName, fleetName); + final String courseAreaIdAsString = (String) object.get(RaceLogDependentStartTimeEventSerializer.FIELD_COURSE_AREA_ID_AS_STRING); + final UUID courseAreaId; + if (courseAreaIdAsString != null) { + courseAreaId = UUID.fromString(courseAreaIdAsString); + } else { + courseAreaId = null; + } return new RaceLogDependentStartTimeEventImpl(event.getCreatedAt(), event.getLogicalTimePoint(), author, event.getId(), - event.getPassId(), dependentOnRace, startTimeDifference, event.getNextStatus()); + event.getPassId(), dependentOnRace, startTimeDifference, event.getNextStatus(), courseAreaId); } } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogStartTimeEventDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogStartTimeEventDeserializer.java index f0680342f18..872cc3252e3 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogStartTimeEventDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/racelog/impl/RaceLogStartTimeEventDeserializer.java @@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.deserialization.racelog.impl; import java.io.Serializable; import java.util.List; +import java.util.UUID; import org.json.simple.JSONObject; @@ -26,9 +27,16 @@ public class RaceLogStartTimeEventDeserializer extends RaceLogRaceStatusEventDes @Override protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt, AbstractLogEventAuthor author, TimePoint timePoint, int passId, List competitors) throws JsonDeserializationException { - long startTime = (Long) object.get(RaceLogStartTimeEventSerializer.FIELD_START_TIME); + final long startTime = (Long) object.get(RaceLogStartTimeEventSerializer.FIELD_START_TIME); + final String courseAreaIdAsString = (String) object.get(RaceLogStartTimeEventSerializer.FIELD_COURSE_AREA_ID_AS_STRING); + final UUID courseAreaId; + if (courseAreaIdAsString != null) { + courseAreaId = UUID.fromString(courseAreaIdAsString); + } else { + courseAreaId = null; + } RaceLogRaceStatusEvent event = (RaceLogRaceStatusEvent) super.deserialize(object, id, createdAt, author, timePoint, passId, competitors); - return new RaceLogStartTimeEventImpl(event.getCreatedAt(), event.getLogicalTimePoint(), author, event.getId(), event.getPassId(), new MillisecondsTimePoint(startTime), event.getNextStatus()); + return new RaceLogStartTimeEventImpl(event.getCreatedAt(), event.getLogicalTimePoint(), author, event.getId(), event.getPassId(), new MillisecondsTimePoint(startTime), event.getNextStatus(), courseAreaId); } } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java index 2ecc544e4d8..d2fdbfb7e70 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racegroup/impl/RaceGroupJsonSerializer.java @@ -3,7 +3,6 @@ package com.sap.sailing.server.gateway.serialization.racegroup.impl; import org.json.simple.JSONObject; 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.server.gateway.serialization.ExtendableJsonSerializer; @@ -12,23 +11,19 @@ import com.sap.sailing.server.gateway.serialization.JsonSerializer; public class RaceGroupJsonSerializer extends ExtendableJsonSerializer { public static final String FIELD_NAME = "name"; - public static final String FIELD_COURSE_AREA = "courseArea"; public static final String FIELD_BOAT_CLASS = "boatClass"; public static final String FIELD_REGATTA_CONFIGURATION = "procedures"; public static final String FIELD_DISPLAY_NAME = "displayName"; public static final String FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE = "canBoatsOfCompetitorsChangePerRace"; private final JsonSerializer boatClassSerializer; - private final JsonSerializer courseAreaSerializer; private final JsonSerializer configurationSerializer; public RaceGroupJsonSerializer( JsonSerializer boatClassSerializer, - JsonSerializer courseAreaSerializer, JsonSerializer configurationSerializer, ExtensionJsonSerializer extensionSerializer) { super(extensionSerializer); - this.courseAreaSerializer = courseAreaSerializer; this.boatClassSerializer = boatClassSerializer; this.configurationSerializer = configurationSerializer; } @@ -37,9 +32,6 @@ public class RaceGroupJsonSerializer extends ExtendableJsonSerializer protected JSONObject serializeFields(RaceGroup object) { JSONObject result = new JSONObject(); result.put(FIELD_NAME, object.getName()); - if (object.getDefaultCourseArea() != null) { - result.put(FIELD_COURSE_AREA, courseAreaSerializer.serialize(object.getDefaultCourseArea())); - } if (object.getBoatClass() != null) { result.put(FIELD_BOAT_CLASS, boatClassSerializer.serialize(object.getBoatClass())); } diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogDependentStartTimeEventSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogDependentStartTimeEventSerializer.java index ccc94024c3c..ad25ed119f4 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogDependentStartTimeEventSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogDependentStartTimeEventSerializer.java @@ -19,6 +19,7 @@ public class RaceLogDependentStartTimeEventSerializer extends RaceLogRaceStatusE public static final String FIELD_DEPDENDENT_ON_RACECOLUMN = "dependentOnRaceColumn"; public static final String FIELD_DEPDENDENT_ON_FLEET = "dependentOnFleet"; public static final String FIELD_START_TIME_DIFFERENCE = "startTimeDifference"; + public static final Object FIELD_COURSE_AREA_ID_AS_STRING = "courseAreaIdAsString"; @Override protected String getClassFieldValue() { diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogStartTimeEventSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogStartTimeEventSerializer.java index 41d1e42f8dd..d5432734cb8 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogStartTimeEventSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/racelog/impl/RaceLogStartTimeEventSerializer.java @@ -11,6 +11,7 @@ public class RaceLogStartTimeEventSerializer extends RaceLogRaceStatusEventSeria public static final String VALUE_CLASS = RaceLogStartTimeEvent.class.getSimpleName(); public static final String FIELD_START_TIME = "startTime"; + public static final String FIELD_COURSE_AREA_ID_AS_STRING = "courseAreaIdAsString"; public RaceLogStartTimeEventSerializer(JsonSerializer competitorSerializer) { super(competitorSerializer); @@ -26,6 +27,8 @@ public class RaceLogStartTimeEventSerializer extends RaceLogRaceStatusEventSeria RaceLogStartTimeEvent startTimeEvent = (RaceLogStartTimeEvent) object; JSONObject result = super.serialize(startTimeEvent); result.put(FIELD_START_TIME, startTimeEvent.getStartTime().asMillis()); + result.put(FIELD_COURSE_AREA_ID_AS_STRING, startTimeEvent.getCourseAreaId()==null?null: + startTimeEvent.getCourseAreaId().toString()); return result; } } diff --git a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java index 5c14803685c..7486e4d9a9b 100644 --- a/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java +++ b/java/com.sap.sailing.server.gateway.serialization.test/src/com/sap/sailing/server/gateway/serialization/test/racelog/RaceLogEventSerializerTest.java @@ -137,7 +137,7 @@ public class RaceLogEventSerializerTest { @Test public void testChoosesStartTimeSerializer() { // we use the real event type here because we do not want to re-implement the dispatching. - RaceLogEvent event = new RaceLogStartTimeEventImpl(null, author, 0, null); + RaceLogEvent event = new RaceLogStartTimeEventImpl(null, author, 0, null, /* courseAreaId */ null); serializer.serialize(event); verify(startTimeSerializer).serialize(event); } diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/EventRaceStatesSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/EventRaceStatesSerializer.java index 7133b5d3d95..e0ba4ac8e22 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/EventRaceStatesSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/EventRaceStatesSerializer.java @@ -14,6 +14,7 @@ import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.server.gateway.serialization.JsonSerializer; import com.sap.sse.common.Duration; +import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; public class EventRaceStatesSerializer implements JsonSerializer>> { @@ -64,7 +65,7 @@ public class EventRaceStatesSerializer implements JsonSerializer { public static final String FIELD_COMPETITORS = "competitors"; public static final String FIELD_BOATS = "boats"; public static final String FIELD_TRACKED_RACES = "trackedRaces"; - public static final String FIELD_COURSE_AREA_ID = "courseAreaId"; + public static final String FIELD_COURSE_AREA_IDS = "courseAreaIds"; public static final String FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE = "canBoatsOfCompetitorsChangePerRace"; public static final String FIELD_COMPETITOR_REGISTRATION_TYPE = "competitorRegistrationType"; public static final String FIELD_USE_START_TIME_INFERENCE = "useStartTimeInference"; @@ -55,8 +56,9 @@ public class RegattaJsonSerializer implements JsonSerializer { result.put(FIELD_SCORINGSYSTEM, regatta.getScoringScheme().getType().name()); result.put(FIELD_RANKINGMETRIC, regatta.getRankingMetricType().name()); result.put(FIELD_BOATCLASS, regatta.getBoatClass() != null ? regatta.getBoatClass().getName() : null); - result.put(FIELD_COURSE_AREA_ID, - regatta.getDefaultCourseArea() != null ? regatta.getDefaultCourseArea().getId().toString() : null); + final JSONArray courseAreaIds = new JSONArray(); + Util.addAll(Util.map(regatta.getCourseAreas(), ca->ca.getId().toString()), courseAreaIds); + result.put(FIELD_COURSE_AREA_IDS, courseAreaIds); result.put(FIELD_CAN_BOATS_OF_COMPETITORS_CHANGE_PER_RACE, regatta.canBoatsOfCompetitorsChangePerRace()); result.put(FIELD_COMPETITOR_REGISTRATION_TYPE, regatta.getCompetitorRegistrationType().name()); result.put(FIELD_USE_START_TIME_INFERENCE, regatta.useStartTimeInference()); diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/EventResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/EventResourceTest.java index 09cc79dd29d..ef0381e8773 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/EventResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/EventResourceTest.java @@ -79,7 +79,7 @@ public class EventResourceTest extends AbstractJaxRsApiTest { assertTrue(hasAtLeastOneCourseArea(objEvent)); JSONObject objRegatta = getRegatta(randomName); String strRegattaName = (String) objRegatta.get("name"); - String strRegattaCourseAreaId = (String) objRegatta.get("courseAreaId"); + String strRegattaCourseAreaId = (String) ((JSONArray) objRegatta.get("courseAreaIds")).get(0); assertTrue(strRegattaCourseAreaId.equals(getDefaultCourseAreaId(objEvent))); leaderBoardWithNameExists(strRegattaName); JSONArray leaderboardGroups = getLeaderboardGroups(objEvent); @@ -98,7 +98,7 @@ public class EventResourceTest extends AbstractJaxRsApiTest { assertTrue(isValidCreateEventResponse(eventResponse)); String strEventId = getIdFromCreateEventResponse(eventResponse); JSONObject regatta = getRegatta(eventName); - String strRegattaCourseAreaId = (String) regatta.get("courseAreaId"); + String strRegattaCourseAreaId = (String) ((JSONArray) regatta.get("courseAreaIds")).get(0); JSONArray arrCourseAreas = getCourseAreasOfEvent(strEventId); assertTrue(arrCourseAreas.size() == 1); JSONObject objCourseArea = (JSONObject) arrCourseAreas.get(0); @@ -210,7 +210,6 @@ public class EventResourceTest extends AbstractJaxRsApiTest { private String getDefaultCourseAreaId(JSONObject objEvent) { JSONArray arrCourseAreas = getCourseAreas(objEvent); assertTrue(arrCourseAreas.size() == 1); - JSONObject courseArea = (JSONObject) arrCourseAreas.get(0); String strCourseAreaId = (String) courseArea.get("id"); return strCourseAreaId; diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java index 787fd3d842e..2bfa9991db0 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardGroupResourceTest.java @@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.test.jaxrs; import static org.junit.Assert.assertTrue; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -52,7 +53,7 @@ public class LeaderboardGroupResourceTest extends AbstractJaxRsApiTest { regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java index 9a720ff21f2..080e67e44a4 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/LeaderboardsResourceTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -65,7 +66,7 @@ public class LeaderboardsResourceTest extends AbstractJaxRsApiTest { regatta = racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaBaseName, boatClassName), boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null); diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java index 17067d6b832..53ecaf51100 100644 --- a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/test/jaxrs/RegattasResourceTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; +import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -94,7 +95,8 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest { /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, startDate, endDate, closedRegattaUuid, series, /* persistent */ true, - DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), + /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); } @@ -107,7 +109,8 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest { /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.OPEN_UNMODERATED, secret, startDate, endDate, UUID.randomUUID(), series, /* persistent */ true, - DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), + /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java index 3d719143ebe..c644bb393c9 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupFactory.java @@ -9,7 +9,6 @@ import java.util.Map; import java.util.logging.Logger; 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.RaceColumn; import com.sap.sailing.domain.base.RaceColumnInSeries; @@ -50,11 +49,10 @@ public class RaceGroupFactory { */ public RaceGroup convert(RegattaLeaderboard leaderboard) { String name = leaderboard.getName(); - CourseArea courseArea = leaderboard.getDefaultCourseArea(); Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta(); Iterable series = getSeries(leaderboard); return new RaceGroupImpl(name, leaderboard.getDisplayName(), regatta.getBoatClass(), regatta.canBoatsOfCompetitorsChangePerRace(), - courseArea, series, regatta.getRegattaConfiguration()); + series, regatta.getRegattaConfiguration()); } /** @@ -64,11 +62,10 @@ public class RaceGroupFactory { */ public RaceGroup convert(FlexibleLeaderboard leaderboard) { String name = leaderboard.getName(); - CourseArea courseArea = leaderboard.getDefaultCourseArea(); BoatClass boatClass = null; RegattaConfiguration configuration = null; Iterable series = getSeries(leaderboard); - return new RaceGroupImpl(name, leaderboard.getDisplayName(), boatClass, false, courseArea, series, configuration); + return new RaceGroupImpl(name, leaderboard.getDisplayName(), boatClass, false, series, configuration); } private Iterable getSeries(Leaderboard leaderboard) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java index da6d0625456..ee0eee5e9ae 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java @@ -26,7 +26,6 @@ import com.sap.sailing.server.gateway.serialization.JsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.BoatClassJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.ColorJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.CompetitorJsonSerializer; -import com.sap.sailing.server.gateway.serialization.impl.CourseAreaJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FleetJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.RegattaConfigurationJsonSerializer; @@ -40,6 +39,7 @@ import com.sap.sailing.server.gateway.serialization.racegroup.impl.SeriesWithRow import com.sap.sailing.server.gateway.serialization.racegroup.impl.SeriesWithRowsOfRaceGroupSerializer; import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogEventSerializer; import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogSerializer; +import com.sap.sse.common.Util; import com.sap.sse.security.shared.HasPermissions.DefaultActions; public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { @@ -74,7 +74,7 @@ public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { RaceGroupFactory raceGroupFactory = new RaceGroupFactory(); final Set regattasForWhichRegattaLeaderboardsWereAdded = new HashSet<>(); for (Leaderboard leaderboard : getService().getLeaderboards().values()) { - if (filterCourseArea.equals(leaderboard.getDefaultCourseArea())) { + if (Util.contains(leaderboard.getCourseAreas(), filterCourseArea)) { SecurityUtils.getSubject() .checkPermission(leaderboard.getIdentifier().getStringPermission(DefaultActions.READ)); if (leaderboard instanceof RegattaLeaderboard && !(leaderboard instanceof RegattaLeaderboardWithEliminations)) { @@ -83,7 +83,6 @@ public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { final Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta(); SecurityUtils.getSubject() .checkPermission(regatta.getIdentifier().getStringPermission(DefaultActions.READ)); - regattasForWhichRegattaLeaderboardsWereAdded.add(regatta); } else if (leaderboard instanceof FlexibleLeaderboard) { result.add(serializer.serialize(raceGroupFactory.convert((FlexibleLeaderboard) leaderboard))); @@ -93,7 +92,7 @@ public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { // now add only those RegattaLeaderboardWithEliminations for which the original RegattaLeaderboard hasn't been added for (Leaderboard leaderboard : getService().getLeaderboards().values()) { if (leaderboard instanceof RegattaLeaderboardWithEliminations && - filterCourseArea.equals(leaderboard.getDefaultCourseArea()) && + Util.contains(leaderboard.getCourseAreas(), filterCourseArea) && !regattasForWhichRegattaLeaderboardsWereAdded.contains(((RegattaLeaderboardWithEliminations) leaderboard).getRegatta())) { result.add(serializer.serialize(raceGroupFactory.convert((RegattaLeaderboardWithEliminations) leaderboard))); } @@ -111,8 +110,7 @@ public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { } private static JsonSerializer createSerializer(UUID clientUuid) { - return new RaceGroupJsonSerializer(new BoatClassJsonSerializer(), new CourseAreaJsonSerializer(), - RegattaConfigurationJsonSerializer.create(), + return new RaceGroupJsonSerializer(new BoatClassJsonSerializer(), RegattaConfigurationJsonSerializer.create(), new SeriesWithRowsOfRaceGroupSerializer(new SeriesWithRowsJsonSerializer( new RaceRowsOfSeriesWithRowsSerializer(new RaceRowJsonSerializer(new FleetJsonSerializer( new ColorJsonSerializer()), new RaceCellJsonSerializer(createRaceLogSerializer(clientUuid), diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java index d98568051c2..2c5db14dc54 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/EventsResource.java @@ -423,7 +423,8 @@ public class EventsResource extends AbstractSailingServerResource { public Regatta call() throws Exception { return getService().apply(new AddSpecificRegatta(regattaName, boatClassName, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, competitorRegistrationSecret, null, null, regattaId, regattaCreationParametersDTO, - /* isPersistent */ true, scoringScheme, courseAreaId, buoyZoneRadiusInHullLengths, + /* isPersistent */ true, scoringScheme, + courseAreaId==null?Collections.emptySet():Collections.singleton(courseAreaId), buoyZoneRadiusInHullLengths, useStartTimeInterference, controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange, rankingMetric)); } }); @@ -654,7 +655,7 @@ public class EventsResource extends AbstractSailingServerResource { for (Event event : getService().getAllEvents()) { Iterable courseAreas = event.getVenue().getCourseAreas(); for (CourseArea courseArea : courseAreas) { - if (courseArea.getId().equals(leaderboard.getRegatta().getDefaultCourseArea().getId())) { + if (Util.contains(leaderboard.getRegatta().getCourseAreas(), courseArea.getId())) { for (LeaderboardGroup lg : event.getLeaderboardGroups()) { // if leaderboard group is default leaderboard group, then add leaderboard if (lg.getName().equals(event.getName())) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java index cccf3e69b02..8b36f2ed604 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardGroupsResource.java @@ -199,7 +199,7 @@ public class LeaderboardGroupsResource extends AbstractSailingServerResource { jsonRaceColumn.put("trackedRaceName", trackedRace.getRace().getName()); final JSONObject raceBoardURLsByEventID = new JSONObject(); for (Event event : eventsReferencingLeaderboardGroup) { - if (Util.contains(event.getVenue().getCourseAreas(), leaderboard.getDefaultCourseArea())) { + if (Util.contains(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas())) { raceBoardURLsByEventID.put(event.getId().toString(), RaceBoardLinkFactory.createRaceBoardLink(trackedRace, leaderboard, event, leaderboardGroup, "PLAYER", /* locale */ null)); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java index 31f3eff21d0..0f6d5af34ae 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java @@ -64,6 +64,7 @@ import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.ControlPoint; import com.sap.sailing.domain.base.Course; +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.Mark; @@ -313,7 +314,7 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { getService().apply(new UpdateLeaderboard(/* leaderboardName */ leaderboard.getName(), /* newLeaderboardName */ leaderboard.getName(), newLeaderboardDisplayName, resultDiscardingThresholds, - leaderboard.getDefaultCourseArea() != null ? leaderboard.getDefaultCourseArea().getId() : null)); + Util.map(leaderboard.getCourseAreas(), CourseArea::getId))); } else { return Response.status(Status.NOT_FOUND).entity( "Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") @@ -1219,7 +1220,8 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { @QueryParam(BaseRaceLogEventSerializer.FIELD_AUTHOR_PRIORITY) Integer authorPriority, @QueryParam(BaseRaceLogEventSerializer.FIELD_PASS_ID) Integer passId, @QueryParam(RaceLogStartTimeEventSerializer.FIELD_START_TIME) Long startTime, - @QueryParam(RaceLogStartProcedureChangedEventSerializer.FIELD_START_PROCEDURE_TYPE) String racingProcedure) { + @QueryParam(RaceLogStartProcedureChangedEventSerializer.FIELD_START_PROCEDURE_TYPE) String racingProcedure, + @QueryParam(RaceLogStartTimeEventSerializer.FIELD_COURSE_AREA_ID_AS_STRING) String courseAreaIdAsString) { Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); if (leaderboard == null) { return Response.status(Status.NOT_FOUND) @@ -1244,7 +1246,8 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource { final TimePoint effectiveStartTime = getService().setStartTimeAndProcedure( leaderboardName, raceColumnName, fleetName, authorName, authorPriority, passId, MillisecondsTimePoint.now(), new MillisecondsTimePoint(startTime), - racingProcedure==null?null:RacingProcedureType.valueOf(racingProcedure)); + racingProcedure==null?null:RacingProcedureType.valueOf(racingProcedure), + courseAreaIdAsString==null?null:UUID.fromString(courseAreaIdAsString)); final JSONObject responseJson = new JSONObject(); responseJson.put("startTimeAsMillis", effectiveStartTime.asMillis()); return Response.ok(streamingOutput(responseJson)).build(); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java index 34496677bdb..9886c388b04 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/trackfiles/impl/ExpeditionAllInOneImporter.java @@ -572,7 +572,7 @@ public class ExpeditionAllInOneImporter { startOfTracking, endOfTracking, regatta, regattaLeaderboard, raceColumn, fleet); if (startTime != null) { final RaceLog raceLog = raceColumn.getRaceLog(raceColumn.getFleets().iterator().next()); - raceLog.add(new RaceLogStartTimeEventImpl(startTime, service.getServerAuthor(), /* priority */ 0, startTime)); + raceLog.add(new RaceLogStartTimeEventImpl(startTime, service.getServerAuthor(), /* priority */ 0, startTime, /* courseAreaId */ null)); } return new Triple<>(trackedRace, raceColumnName, fleetName); } @@ -762,7 +762,9 @@ public class ExpeditionAllInOneImporter { /* can boats of competitors change */ false, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ UUID.randomUUID().toString(), /* start date */ null, /* end date */ null, UUID.randomUUID(), - regattaCreationParameters, true, scoringScheme, courseAreaId, buoyZoneRadiusInHullLengths, /* use start time inference */ true, + regattaCreationParameters, true, scoringScheme, + courseAreaId==null?Collections.emptySet():Collections.singleton(courseAreaId), + buoyZoneRadiusInHullLengths, /* use start time inference */ true, /* control tracking from start and finish times */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, rankingMetric)); this.ensureBoatClassDetermination(regatta); diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceSetStartTimeAndRacingProcedure.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceSetStartTimeAndRacingProcedure.html index 388c2638b43..85f06b32f48 100755 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceSetStartTimeAndRacingProcedure.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceSetStartTimeAndRacingProcedure.html @@ -37,6 +37,12 @@
    startProcedureType any of UNKNOWN, RRS26, SWC, GateStart, ESS, BASIC, or LEAGUE
    + + Optional parameters: + +
    courseAreaIdAsString the UUID of the course area on which the race is being started
    + + Request method: PUT diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java index 133b25073b0..9afbb8b2ab6 100644 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/interfaces/RacingEventService.java @@ -6,6 +6,7 @@ import java.net.MalformedURLException; import java.net.SocketException; import java.net.URI; import java.net.URL; +import java.util.Collections; import java.util.ConcurrentModificationException; import java.util.List; import java.util.Locale; @@ -226,7 +227,8 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch * * @return the leaderboard created */ - FlexibleLeaderboard addFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, int[] discardThresholds, ScoringScheme scoringScheme, Serializable courseAreaId); + FlexibleLeaderboard addFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, + int[] discardThresholds, ScoringScheme scoringScheme, Iterable courseAreaIds); RegattaLeaderboard addRegattaLeaderboard(RegattaIdentifier regattaIdentifier, String leaderboardDisplayName, int[] discardThresholds); @@ -337,20 +339,29 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch * the series must not have any {@link RaceColumn}s yet * @param controlTrackingFromStartAndFinishTimes * cannot be {@code true} if {@link useStartTimeInference} is also {@code true} - * @param autoRestartTrackingUponCompetitorSetChange TODO */ + default Regatta createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, + CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, + boolean persistent, ScoringScheme scoringScheme, Serializable courseAreaId, Double buoyZoneRadiusInHullLengths, + boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetricConstructor rankingMetricConstructor) { + return createRegatta(regattaName, boatClassName, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, + registrationLinkSecret, startDate, endDate, id, series, persistent, scoringScheme, + courseAreaId==null?Collections.emptySet():Collections.singleton(courseAreaId), buoyZoneRadiusInHullLengths, useStartTimeInference, + controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange, + rankingMetricConstructor); + } + Regatta createRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, - boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, + boolean persistent, ScoringScheme scoringScheme, Iterable courseAreaIds, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetricConstructor rankingMetricConstructor); - + /** * @param controlTrackingFromStartAndFinishTimes * cannot be {@code true} if {@link useStartTimeInference} is also {@code true} - * @param autoRestartTrackingUponCompetitorSetChange TODO */ Regatta updateRegatta(RegattaIdentifier regattaIdentifier, TimePoint startDate, TimePoint endDate, - Serializable newDefaultCourseAreaId, RegattaConfiguration regattaConfiguration, + Iterable newCourseAreaIds, RegattaConfiguration regattaConfiguration, Iterable series, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, CompetitorRegistrationType registrationType); @@ -505,7 +516,6 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch Map getCompetitorToBoatMappingsForRace(String leaderboardName, String raceColumnName, String fleetName); /** - * @param autoRestartTrackingUponCompetitorSetChange TODO * @return a pair with the found or created regatta, and a boolean that tells whether the regatta was created during * the call */ @@ -513,8 +523,9 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, boolean persistent, ScoringScheme scoringScheme, - Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, - boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetricConstructor rankingMetricConstructor); + Iterable courseAreaIds, Double buoyZoneRadiusInHullLengths, + boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, + boolean autoRestartTrackingUponCompetitorSetChange, RankingMetricConstructor rankingMetricConstructor); /** * @return map where keys are the toString() representation of the {@link RaceDefinition#getId() IDs} of races passed to @@ -573,10 +584,11 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch * @param passId Pass identifier of the new start time event. * @param logicalTimePoint logical {@link TimePoint} of the new event. * @param startTime the new Start-Time + * @param courseAreaId the ID of the course area on which the start is happening, or {@code null} if not known * @return */ TimePoint setStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName, String authorName, - int authorPriority, int passId, TimePoint logicalTimePoint, TimePoint startTime, RacingProcedureType racingProcedure); + int authorPriority, int passId, TimePoint logicalTimePoint, TimePoint startTime, RacingProcedureType racingProcedure, UUID courseAreaId); /** * Forces a new end time identified by the passed parameters. diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java index f996873547c..42535f436ea 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/AddSpecificRegatta.java @@ -23,13 +23,14 @@ import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sailing.server.interfaces.RacingEventServiceOperation; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; public class AddSpecificRegatta extends AbstractAddRegattaOperation { private static final long serialVersionUID = -8018855620167669352L; private final RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds; private final boolean persistent; private final ScoringScheme scoringScheme; - private final Serializable defaultCourseAreaId; + private final List courseAreaIds; private final Double buoyZoneRadiusInHullLengths; private final boolean useStartTimeInference; private final boolean controlTrackingFromStartAndFinishTimes; @@ -39,10 +40,16 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation { private final RankingMetrics rankingMetricType; private final String registrationLinkSecret; + /** + * @param courseAreaIds + * may be {@code null}, meaning an empty set of course areas; no worries about serializability; a local + * serializable collection will be used, and the elements of this parameter, if not {@code null}, will be + * copied into it + */ public AddSpecificRegatta(String regattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, TimePoint startDate, TimePoint endDate, Serializable id, RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds, - boolean persistent, ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, + boolean persistent, ScoringScheme scoringScheme, Iterable courseAreaIds, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetrics rankingMetricType) { super(regattaName, boatClassName, startDate, endDate, id); this.canBoatsOfCompetitorsChangePerRace = canBoatsOfCompetitorsChangePerRace; @@ -50,7 +57,10 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation { this.seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndStartsWithZeroScoreAndDiscardingThresholds = seriesNamesWithFleetNamesAndFleetOrderingAndMedalAndDiscardingThresholds; this.persistent = persistent; this.scoringScheme = scoringScheme; - this.defaultCourseAreaId = defaultCourseAreaId; + this.courseAreaIds = new ArrayList<>(); + if (courseAreaIds != null) { + Util.addAll(courseAreaIds, this.courseAreaIds); + } this.useStartTimeInference = useStartTimeInference; this.controlTrackingFromStartAndFinishTimes = controlTrackingFromStartAndFinishTimes; this.autoRestartTrackingUponCompetitorSetChange = autoRestartTrackingUponCompetitorSetChange; @@ -63,7 +73,7 @@ public class AddSpecificRegatta extends AbstractAddRegattaOperation { public Regatta internalApplyTo(RacingEventService toState) throws Exception { Regatta regatta = toState.createRegatta(getRegattaName(), getBoatClassName(), canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, registrationLinkSecret, getStartDate(), - getEndDate(), getId(), createSeries(toState), persistent, scoringScheme, defaultCourseAreaId, + getEndDate(), getId(), createSeries(toState), persistent, scoringScheme, courseAreaIds, buoyZoneRadiusInHullLengths, useStartTimeInference, controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange, RankingMetricsFactory.getRankingMetricConstructor(rankingMetricType)); return regatta; diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateFlexibleLeaderboard.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateFlexibleLeaderboard.java index f628a76d9ba..7d65a9e0082 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateFlexibleLeaderboard.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/CreateFlexibleLeaderboard.java @@ -1,12 +1,15 @@ package com.sap.sailing.server.operationaltransformation; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import java.util.logging.Logger; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sailing.server.interfaces.RacingEventServiceOperation; +import com.sap.sse.common.Util; public class CreateFlexibleLeaderboard extends AbstractLeaderboardOperation { private static final Logger logger = Logger.getLogger(CreateFlexibleLeaderboard.class.getName()); @@ -14,21 +17,31 @@ public class CreateFlexibleLeaderboard extends AbstractLeaderboardOperation courseAreaIds; - public CreateFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, int[] discardThresholds, ScoringScheme scoringScheme, Serializable courseAreaId) { + /** + * @param courseAreaIds + * if {@code null}, an empty set of course areas will be used instead; you don't need to worry about + * the serializability as a local, serializable copy with all the elements in the iterable will + * be produced here. + */ + public CreateFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, int[] discardThresholds, + ScoringScheme scoringScheme, Iterable courseAreaIds) { super(leaderboardName); this.leaderboardDisplayName = leaderboardDisplayName; this.discardThresholds = discardThresholds; this.scoringScheme = scoringScheme; - this.courseAreaId = courseAreaId; + this.courseAreaIds = new ArrayList<>(); + if (courseAreaIds != null) { + Util.addAll(courseAreaIds, this.courseAreaIds); + } } @Override public FlexibleLeaderboard internalApplyTo(RacingEventService toState) { FlexibleLeaderboard result = null; if (toState.getLeaderboardByName(getLeaderboardName()) == null) { - result = toState.addFlexibleLeaderboard(getLeaderboardName(), leaderboardDisplayName, discardThresholds, scoringScheme, courseAreaId); + result = toState.addFlexibleLeaderboard(getLeaderboardName(), leaderboardDisplayName, discardThresholds, scoringScheme, courseAreaIds); } else { logger.warning("Cannot replicate creation of flexible leaderboard "+getLeaderboardName()+" because it already exists in the replica"); } diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateLeaderboard.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateLeaderboard.java index 4b41602ee47..6be9f9785ca 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateLeaderboard.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateLeaderboard.java @@ -3,27 +3,28 @@ package com.sap.sailing.server.operationaltransformation; import java.io.Serializable; import java.util.Arrays; -import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule; import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sailing.server.interfaces.RacingEventServiceOperation; +import com.sap.sse.common.Util; public class UpdateLeaderboard extends AbstractLeaderboardOperation { private static final long serialVersionUID = -8040361040050151768L; private final String newLeaderboardName; private final String newLeaderboardDisplayName; private final int[] newDiscardingThresholds; - private final Serializable newCourseAreaId; + private final Iterable newCourseAreaIds; - public UpdateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName, int[] newDiscardingThresholds, Serializable newCourseAreaId) { + public UpdateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName, + int[] newDiscardingThresholds, Iterable newCourseAreaIds) { super(leaderboardName); this.newLeaderboardName = newLeaderboardName; this.newLeaderboardDisplayName = newLeaderboardDisplayName; this.newDiscardingThresholds = newDiscardingThresholds; - this.newCourseAreaId = newCourseAreaId; + this.newCourseAreaIds = newCourseAreaIds; } @Override @@ -58,10 +59,7 @@ public class UpdateLeaderboard extends AbstractLeaderboardOperation if (leaderboard instanceof FlexibleLeaderboard) { FlexibleLeaderboard flexibleLeaderboard = (FlexibleLeaderboard) leaderboard; - CourseArea newCourseArea = toState.getCourseArea(newCourseAreaId); - if (newCourseArea != flexibleLeaderboard.getDefaultCourseArea()) { - flexibleLeaderboard.setDefaultCourseArea(newCourseArea); - } + flexibleLeaderboard.setCourseAreas(Util.map(newCourseAreaIds, toState::getCourseArea)); } updateStoredLeaderboard(toState, leaderboard); return leaderboard; diff --git a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSpecificRegatta.java b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSpecificRegatta.java index 326959b042c..4e6f1cf4cff 100755 --- a/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSpecificRegatta.java +++ b/java/com.sap.sailing.server.interface/src/com/sap/sailing/server/operationaltransformation/UpdateSpecificRegatta.java @@ -1,5 +1,6 @@ package com.sap.sailing.server.operationaltransformation; +import java.util.Collections; import java.util.UUID; import com.sap.sailing.domain.base.Regatta; @@ -14,7 +15,7 @@ public class UpdateSpecificRegatta extends AbstractRacingEventServiceOperation newCourseAreaIds; private final RegattaConfiguration newConfiguration; private final Double buoyZoneRadiusInHullLengths; private final boolean useStartTimeInference; @@ -24,15 +25,30 @@ public class UpdateSpecificRegatta extends AbstractRacingEventServiceOperation newCourseAreaIds, RegattaConfiguration newConfiguration, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, CompetitorRegistrationType registrationType) { this.regattaIdentifier = regattaIdentifier; this.startDate = startDate; this.endDate = endDate; - this.newDefaultCourseAreaId = newDefaultCourseAreaId; + this.newCourseAreaIds = newCourseAreaIds; this.newConfiguration = newConfiguration; this.useStartTimeInference = useStartTimeInference; this.controlTrackingFromStartAndFinishTimes = controlTrackingFromStartAndFinishTimes; @@ -44,7 +60,7 @@ public class UpdateSpecificRegatta extends AbstractRacingEventServiceOperation emptyWaypointList = Collections.emptyList(); @@ -101,7 +101,7 @@ public class BoatReplicationTest extends AbstractServerReplicationTest { assertNotNull(replicatedRegatta); assertTrue(replicatedRegatta.isPersistent()); assertTrue(Util.isEmpty((replicatedRegatta.getSeries()))); - assertNull(replicatedRegatta.getDefaultCourseArea()); + assertTrue(Util.isEmpty(replicatedRegatta.getCourseAreas())); assertTrue(regattaId.equals(replicatedRegatta.getId())); RaceDefinition replicatedRace = replicatedRegatta.getRaceByName(raceName); assertNotNull(replicatedRace); @@ -114,19 +114,15 @@ public class BoatReplicationTest extends AbstractServerReplicationTest { assertEquals(boat.getBoatClass().getName(), replicatedBoat.getBoatClass().getName()); assertEquals(boat.getSailID(), replicatedBoat.getSailID()); assertEquals(boat.getColor(), replicatedBoat.getColor()); - // now update the boat on master using replicating operation final String newBoatName = "Der Vogel, der mit dem Kiel zieht"; master.apply(new UpdateBoat(boat.getId().toString(), newBoatName, boat.getColor(), boat.getSailID())); Thread.sleep(1000); assertEquals(newBoatName, replicatedBoat.getName()); // expect in-place update of existing boat in replica - // now allow for resetting to default through some event, such as receiving a GPS position master.apply(new AllowBoatResetToDefaults(Collections.singleton(boat.getId().toString()))); // modify the boat on the master "from below" without an UpdateBoat operation, only locally: master.getBaseDomainFactory().getCompetitorAndBoatStore().updateBoat(boat.getId().toString(), boatName, boat.getColor(), boat.getSailID()); - - final RegattaAndRaceIdentifier raceIdentifier = masterRegatta.getRaceIdentifier(raceDefinition); DynamicTrackedRace trackedRace = (DynamicTrackedRace) master.apply(new CreateTrackedRace(raceIdentifier, EmptyWindStore.INSTANCE, /* delayToLiveInMillis */ 3000, @@ -138,14 +134,12 @@ public class BoatReplicationTest extends AbstractServerReplicationTest { assertNotNull(replicatedTrackedRace); Boat replicatedBoat2 = replicatedTrackedRace.getBoatOfCompetitor(replicatedCompetitor); assertNotNull(replicatedBoat2); - assertEquals(boatName, replicatedBoat2.getName()); } @Test public void testBoatCreationReplication() throws InterruptedException, URISyntaxException { final String boatName = "Kielzugvogel 123"; - BoatClass boatClass = new BoatClassImpl("Kielzugvogel", true); Boat boat = master.getBaseDomainFactory().getOrCreateBoat(123, boatName, boatClass, "GER 123", null, /* storePersistently */ true); Thread.sleep(1000); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java index 1a9b5a89687..1b35d9cb9da 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/CompetitorReplicationTest.java @@ -3,9 +3,9 @@ package com.sap.sailing.server.replication.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.Serializable; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; @@ -85,7 +85,7 @@ public class CompetitorReplicationTest extends AbstractServerReplicationTest { Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, regattaId, series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); Iterable emptyWaypointList = Collections.emptyList(); @@ -111,7 +111,7 @@ public class CompetitorReplicationTest extends AbstractServerReplicationTest { assertNotNull(replicatedRegatta); assertTrue(replicatedRegatta.isPersistent()); assertTrue(Util.isEmpty((replicatedRegatta.getSeries()))); - assertNull(replicatedRegatta.getDefaultCourseArea()); + assertTrue(Util.isEmpty(replicatedRegatta.getCourseAreas())); assertTrue(regattaId.equals(replicatedRegatta.getId())); RaceDefinition replicatedRace = replicatedRegatta.getRaceByName(raceName); assertNotNull(replicatedRace); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/EventReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/EventReplicationTest.java index 8ef42d4eccd..28672fc997c 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/EventReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/EventReplicationTest.java @@ -101,13 +101,11 @@ public class EventReplicationTest extends AbstractServerReplicationTest { final TimePoint eventEndDate = new MillisecondsTimePoint(new Date()); Event masterEvent = master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID()); CourseArea masterCourseArea = master.addCourseAreas(masterEvent.getId(), new String[] {courseArea}, new UUID[] {UUID.randomUUID()})[0]; - Thread.sleep(1000); Event replicatedEvent = replica.getEvent(masterEvent.getId()); assertNotNull(replicatedEvent); assertEquals(replicatedEvent.getName(), eventName); assertEquals(Util.size(replicatedEvent.getVenue().getCourseAreas()), 1); - CourseArea replicatedCourseArea = Util.get(replicatedEvent.getVenue().getCourseAreas(), 0); assertEquals(replicatedCourseArea.getId(), masterCourseArea.getId()); assertEquals(replicatedCourseArea.getName(), courseArea); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java index 3cda3089f89..a5aba3f7aa8 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -162,7 +163,8 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), + /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PostingOperationFromReplicaToMasterTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PostingOperationFromReplicaToMasterTest.java index 94e1cd31dd8..952243dcf4a 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PostingOperationFromReplicaToMasterTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/PostingOperationFromReplicaToMasterTest.java @@ -3,6 +3,7 @@ package com.sap.sailing.server.replication.test; import static org.junit.Assert.assertTrue; import java.net.URISyntaxException; +import java.util.Collections; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -11,6 +12,7 @@ import org.junit.Test; import org.junit.rules.Timeout; import com.sap.sailing.domain.base.CompetitorAndBoatStore; +import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.impl.LowPoint; import com.sap.sailing.server.operationaltransformation.AllowBoatResetToDefaults; @@ -39,9 +41,13 @@ public class PostingOperationFromReplicaToMasterTest extends AbstractServerRepli @Test public void testPostOperationToMaster() throws InterruptedException, URISyntaxException { final String leaderboardName = "My Leaderboard"; + final CourseArea courseArea = replica.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Course Area"); + // in production, backward replication of a course area would happen by a backward + // replication of an event with that course area; here, we have to "emulate" this explicitly + master.getBaseDomainFactory().getOrCreateCourseArea(courseArea.getId(), courseArea.getName()); final CreateFlexibleLeaderboard operation = new CreateFlexibleLeaderboard(/* leaderboardName */ leaderboardName, /* leaderboardDisplayName */ null, /* discardThresholds */ new int[0], /* scoringScheme */ new LowPoint(), - /* courseAreaId */ UUID.randomUUID()); + /* courseAreaId */ Collections.singleton(courseArea.getId())); replica.apply(operation); Thread.sleep(1000); assertTrue(master.getLeaderboards().containsKey(leaderboardName)); diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java index d6d0f0065c8..441e5152cec 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.Date; @@ -48,17 +49,16 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClassName), boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, regattaId, series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), + /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); - Thread.sleep(1000); - Regatta replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); assertNotNull(replicatedRegatta); assertTrue(replicatedRegatta.isPersistent()); assertTrue(Util.isEmpty((replicatedRegatta.getSeries()))); - assertNull(replicatedRegatta.getDefaultCourseArea()); + assertTrue(Util.isEmpty(replicatedRegatta.getCourseAreas())); assertTrue(regattaId.equals(replicatedRegatta.getId())); assertNull(replicatedRegatta.getRegattaConfiguration()); } @@ -76,13 +76,15 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Regatta masterRegatta1 = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClass1Name), boatClass1Name, canBoatsOfCompetitorsChangePerRaceRegatta1, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, regattaId1, series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), + /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); Regatta masterRegatta2 = master.createRegatta(RegattaImpl.getDefaultName(baseEventName, boatClass2Name), boatClass2Name, canBoatsOfCompetitorsChangePerRaceRegatta2, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, regattaId2, series, - /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + /* persistent */ true, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), + /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); @@ -122,7 +124,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Thread.sleep(1000); replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); assertNotNull(replicatedRegatta); - assertNull(replicatedRegatta.getDefaultCourseArea()); + assertTrue(Util.isEmpty(replicatedRegatta.getCourseAreas())); // Test for 'alpha' currentCourseAreaId = alphaCourseAreaId; master.apply(new UpdateSpecificRegatta(masterRegatta.getRegattaIdentifier(), /*startDate*/ null, /*endDate*/ null, @@ -131,7 +133,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Thread.sleep(1000); replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); assertNotNull(replicatedRegatta); - assertEquals(currentCourseAreaId, replicatedRegatta.getDefaultCourseArea().getId()); + assertEquals(1, Util.size(replicatedRegatta.getCourseAreas())); + assertEquals(currentCourseAreaId, replicatedRegatta.getCourseAreas().iterator().next().getId()); // Test for 'tv' currentCourseAreaId = tvCourseAreaId; master.apply(new UpdateSpecificRegatta(masterRegatta.getRegattaIdentifier(), /*startDate*/ null, /*endDate*/ null, @@ -140,7 +143,8 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Thread.sleep(1000); replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); assertNotNull(replicatedRegatta); - assertEquals(currentCourseAreaId, replicatedRegatta.getDefaultCourseArea().getId()); + assertEquals(1, Util.size(replicatedRegatta.getCourseAreas())); + assertEquals(currentCourseAreaId, replicatedRegatta.getCourseAreas().iterator().next().getId()); // Test back to 'null' currentCourseAreaId = null; master.apply(new UpdateSpecificRegatta(masterRegatta.getRegattaIdentifier(), /*startDate*/ null, /*endDate*/ null, currentCourseAreaId, null, @@ -149,7 +153,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { Thread.sleep(1000); replicatedRegatta = replica.getRegatta(new RegattaName(masterRegatta.getName())); assertNotNull(replicatedRegatta); - assertNull(replicatedRegatta.getDefaultCourseArea()); + assertTrue(Util.isEmpty(replicatedRegatta.getCourseAreas())); } public void testUpdateSpecificRegattaReplicationForProcedureAndCourseDesignerAndConfig() throws InterruptedException { @@ -215,7 +219,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), Arrays.asList(new Series[] { qualification, finals, medal }), /* persistent */ true, - DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); Thread.sleep(1000); @@ -241,7 +245,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { assertEquals("Medal", replicatedMedal.getName()); assertEquals(1, Util.size(replicatedMedal.getFleets())); assertNotNull(replicatedMedal.getFleetByName("Medal")); - assertNull(replicatedRegatta.getDefaultCourseArea()); + assertTrue(Util.isEmpty(replicatedRegatta.getCourseAreas())); } @Test @@ -255,7 +259,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, - DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); Thread.sleep(1000); @@ -303,7 +307,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), Arrays.asList(new Series[] { qualification }), /* persistent */ true, - DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, + DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), /* course area ID */ (Serializable) null, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); Thread.sleep(1000); @@ -354,8 +358,9 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { assertNotNull(replicatedRegatta); assertTrue(replicatedRegatta.isPersistent()); assertTrue(Util.isEmpty((replicatedRegatta.getSeries()))); - assertNotNull(replicatedRegatta.getDefaultCourseArea()); - assertEquals(masterCourseArea.getId(), replicatedRegatta.getDefaultCourseArea().getId()); - assertEquals(masterCourseArea.getName(), replicatedRegatta.getDefaultCourseArea().getName()); + assertNotNull(replicatedRegatta.getCourseAreas()); + assertEquals(1, Util.size(replicatedRegatta.getCourseAreas())); + assertEquals(masterCourseArea.getId(), replicatedRegatta.getCourseAreas().iterator().next().getId()); + assertEquals(masterCourseArea.getName(), replicatedRegatta.getCourseAreas().iterator().next().getName()); } } diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java index 467ab3cd33b..a302e4ff8a2 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceBoatCompetitorMetadataReplicationTest.java @@ -18,6 +18,7 @@ import org.junit.Test; import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.CompetitorRegistrationType; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; @@ -86,7 +87,8 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ startOfTracking, endOfTracking, /* delayToLiveInMillis */ 0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", "", /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false, - /* preferReplayIfAvailable */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, /* useOfficialEventsToUpdateRaceLog */ false); + /* preferReplayIfAvailable */ false, /* timeoutInMillis */ (int) RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, + /* useOfficialEventsToUpdateRaceLog */ false); } private void startTracking() throws Exception, InterruptedException { @@ -97,12 +99,17 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ } private void startTrackingOnMaster() throws Exception { + final CourseArea courseArea = master.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Course Area"); + // in production, a course area creation based on an event's venue creation would be + // replicated; in test set-ups, the course area needs to be "replicated" manually: + replica.getBaseDomainFactory().getOrCreateCourseArea(courseArea.getId(), courseArea.getName()); final Regatta regatta = master.createRegatta("Test regatta", "J/70", /* canBoatsOfCompetitorsChangePerRace==true because it's a league race we're using for this test */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), /* start with no series */ Collections.emptySet(), - /* persistent */ true, new LowPoint(), /* defaultCourseAreaId */ UUID.randomUUID(), + /* persistent */ true, new LowPoint(), + /* defaultCourseAreaId */ courseArea.getId(), /* buoyZoneRadiusInHullLengths */ 2., /* useStartTimeInference */ false, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, /* rankingMetricConstructor */ OneDesignRankingMetric::new); final RegattaName regattaIdentifier = new RegattaName(regatta.getName()); @@ -128,28 +135,22 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ final String boat1CompetitorName = "CYC"; final String boat1Name = "Boot 1"; final String boat1Color = "#141414"; - final String boat2CompetitorName = "SVI"; final String boat2Name = "Boot 2"; final String boat2Color = "#606060"; - final String boat3CompetitorName = "BYCÜ"; final String boat3Name = "Boot 3"; final String boat3Color = "#0169EF"; - startTracking(); Thread.sleep(5000); TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); assertNotNull(replicaTrackedRace); - Iterable masterCompetitors = masterTrackedRace.getRace().getCompetitors(); Iterable replicaCompetitors = replicaTrackedRace.getRace().getCompetitors(); - assertNotSame(masterTrackedRace, replicaTrackedRace); assertNotSame(masterTrackedRace.getRace(), replicaTrackedRace.getRace()); assertEquals(Util.size(masterCompetitors), 6); assertEquals(Util.size(masterCompetitors), Util.size(replicaCompetitors)); - for (Competitor competitor : masterCompetitors) { Competitor replicaCompetitor = findCompetitor(replicaCompetitors, competitor); Boat competitorBoat = masterTrackedRace.getBoatOfCompetitor(competitor); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java index 3a4536e4350..cb7cf6b4e21 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/AutomaticRetrackUponCompetitorSetChangeTest.java @@ -129,7 +129,9 @@ public class AutomaticRetrackUponCompetitorSetChangeTest { CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */ null, /* endDate */ null, UUID.randomUUID(), /* start with no series */ Collections.emptySet(), - /* persistent */ true, new LowPoint(), /* defaultCourseAreaId */ UUID.randomUUID(), + /* persistent */ true, new LowPoint(), + /* defaultCourseAreaId */ service.getBaseDomainFactory() + .getOrCreateCourseArea(UUID.randomUUID(), "Course Area").getId(), /* buoyZoneRadiusInHullLengths */ 2., /* useStartTimeInference */ false, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ true, /* rankingMetricConstructor */ OneDesignRankingMetric::new); regattaIdentifier = new RegattaName(regatta.getName()); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java index 69674893aa3..e2126162931 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardDiscardingRulesTest.java @@ -4,6 +4,8 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.util.Collections; + import org.junit.Before; import org.junit.Test; @@ -41,7 +43,7 @@ public class LeaderboardDiscardingRulesTest { @Test public void testDiscardingRules() { racingEventService.removeLeaderboard(LEADERBOARDNAME); - racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 4 }, new LowPoint(), null); + racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 4 }, new LowPoint(), Collections.emptySet()); FlexibleLeaderboard leaderboard = (FlexibleLeaderboard) racingEventService.getLeaderboardByName(LEADERBOARDNAME); assertNotNull(leaderboard); int[] discardingRulesNew = new int[] { 1, 5 }; @@ -61,7 +63,7 @@ public class LeaderboardDiscardingRulesTest { @Test public void testDiscardingRulesForMultipleEquallyBadRaces() throws NoWindException { racingEventService.removeLeaderboard(LEADERBOARDNAME); - racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 2 }, new LowPoint(), null); + racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 2 }, new LowPoint(), Collections.emptySet()); FlexibleLeaderboard leaderboard = (FlexibleLeaderboard) racingEventService.getLeaderboardByName(LEADERBOARDNAME); assertNotNull(leaderboard); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); @@ -100,7 +102,7 @@ public class LeaderboardDiscardingRulesTest { @Test public void testDiscardingRulesForMultipleEquallyBadRacesWithHighPointScoringScheme() throws NoWindException { racingEventService.removeLeaderboard(LEADERBOARDNAME); - racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 2 }, new HighPoint(), null); + racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 2 }, new HighPoint(), Collections.emptySet()); FlexibleLeaderboard leaderboard = (FlexibleLeaderboard) racingEventService.getLeaderboardByName(LEADERBOARDNAME); assertNotNull(leaderboard); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("ESS40", /* typicallyStartsUpwind */ true); @@ -144,7 +146,7 @@ public class LeaderboardDiscardingRulesTest { @Test public void testDiscardingRulesForMultipleEquallyBadRacesWithNonDiscardableDisqualification() throws NoWindException { racingEventService.removeLeaderboard(LEADERBOARDNAME); - racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 2 }, new LowPoint(), null); + racingEventService.addFlexibleLeaderboard(LEADERBOARDNAME, null, new int[] { 1, 2 }, new LowPoint(), Collections.emptySet()); FlexibleLeaderboard leaderboard = (FlexibleLeaderboard) racingEventService.getLeaderboardByName(LEADERBOARDNAME); assertNotNull(leaderboard); BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java index 5c6c493bee0..dedb512b99f 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/LeaderboardStorageTest.java @@ -2,7 +2,9 @@ package com.sap.sailing.server.test; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.UUID; import org.bson.Document; import org.junit.Test; @@ -50,19 +52,16 @@ public class LeaderboardStorageTest extends TestCase { RacingEventService service = new RacingEventServiceImpl(); int[] dicardingThresholds = {}; Leaderboard leaderboard = service.addFlexibleLeaderboard(LEADERBOARD_NAME, "testIt", dicardingThresholds, - new LowPoint(), "maaap"); - + new LowPoint(), Collections.singleton(service.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "maaap").getId())); List sailorList = new ArrayList(); sailorList.add(new PersonImpl("sailor", new NationalityImpl("GER"), null, "")); DynamicTeam team = new TeamImpl("team", sailorList, null); String competitorId = "testC"; Competitor competitor = service.getBaseDomainFactory().getOrCreateCompetitor(competitorId, "Test C", null, null, null, null, team, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null, /* storePersistently */ true); - double carriedPoints = 2.0; leaderboard.setCarriedPoints(competitor, carriedPoints); service.getMongoObjectFactory().storeLeaderboard(leaderboard); - // Test in db MongoCollection leaderboardCollection = MongoDBService.INSTANCE.getDB().getCollection("LEADERBOARDS"); BasicDBObject query = new BasicDBObject(); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index 71f2d233c1b..fbe20927823 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -76,7 +76,6 @@ import com.sap.sailing.domain.base.configuration.impl.RegattaConfigurationImpl; import com.sap.sailing.domain.base.impl.BoatClassImpl; import com.sap.sailing.domain.base.impl.BoatImpl; import com.sap.sailing.domain.base.impl.CompetitorImpl; -import com.sap.sailing.domain.base.impl.CourseAreaImpl; import com.sap.sailing.domain.base.impl.DomainFactoryImpl; import com.sap.sailing.domain.base.impl.DynamicBoat; import com.sap.sailing.domain.base.impl.DynamicPerson; @@ -262,7 +261,7 @@ public class MasterDataImportTest { Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); event.getVenue().addCourseArea(courseArea); List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; @@ -328,7 +327,7 @@ public class MasterDataImportTest { TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); TimePoint logTimePoint2 = new MillisecondsTimePoint(1372489201000L); RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, logTimePoint, author, 1, - 0, logTimePoint, RaceLogRaceStatus.SCHEDULED); + 0, logTimePoint, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null); raceColumn.getRaceLog(testFleet1).add(logEvent); Position p = new DegreePosition(3, 3); Wind wind = new WindImpl(p, logTimePoint2, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(12))); @@ -470,7 +469,7 @@ public class MasterDataImportTest { TimePoint logTimePoint5 = new MillisecondsTimePoint(1372489310000L); UUID postImportEventId = UUID.randomUUID(); RaceLogStartTimeEventImpl postImportLogEvent = new RaceLogStartTimeEventImpl(logTimePoint5, logTimePoint5, - author, postImportEventId, 3, logTimePoint5, RaceLogRaceStatus.SCHEDULED); + author, postImportEventId, 3, logTimePoint5, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null); raceColumnOnTarget.getRaceLog(fleet1OnTarget).add(postImportLogEvent); // Check for regatta log event RegattaLogRegisterCompetitorEvent registerEventOnTarget = (RegattaLogRegisterCompetitorEvent) regattaOnTarget @@ -546,16 +545,14 @@ public class MasterDataImportTest { // Setup source service Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); - UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + final UUID courseAreaUUID = UUID.randomUUID(); + final CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); event.getVenue().addCourseArea(courseArea); - List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; raceColumnNames.add(raceColumnName); raceColumnNames.add("T2"); final List emptyRaceColumnNamesList = Collections.emptyList(); - List series = new ArrayList(); List fleets = new ArrayList(); FleetImpl testFleet1 = new FleetImpl("testFleet1"); @@ -611,7 +608,7 @@ public class MasterDataImportTest { // Set log event TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); - RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint); + RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint, /* courseAreaId */ null); raceColumn.getRaceLog(testFleet1).add(logEvent); storedLogUUIDs.add(logEvent.getId()); @@ -702,12 +699,10 @@ public class MasterDataImportTest { private RacingEventService getDestService(UUID randomUUID) { RacingEventServiceImplMock destService = new RacingEventServiceImplMock( new DataImportProgressImpl(randomUUID)) { - @Override public SecurityService getSecurityService() { return MasterDataImportTest.this.securityService; } - }; return destService; } @@ -733,14 +728,12 @@ public class MasterDataImportTest { Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); event.getVenue().addCourseArea(courseArea); - List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; raceColumnNames.add(raceColumnName); final List emptyRaceColumnNamesList = Collections.emptyList(); - List series = new ArrayList(); List fleets = new ArrayList(); FleetImpl testFleet1 = new FleetImpl("testFleet1"); @@ -882,8 +875,7 @@ public class MasterDataImportTest { InterruptedException, ClassNotFoundException { Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); - UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + final CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "testArea"); event.getVenue().addCourseArea(courseArea); List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; @@ -901,7 +893,7 @@ public class MasterDataImportTest { RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ null, /* startDate */null, /* endDate */null, regattaUUID, series, true, - new LowPoint(), courseAreaUUID, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, + new LowPoint(), courseArea.getId(), /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); for (String name : raceColumnNames) { series.get(0).addRaceColumn(name, sourceService); @@ -941,7 +933,7 @@ public class MasterDataImportTest { raceColumn.setTrackedRace(testFleet1, trackedRace); // Set log event TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); - RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint); + RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint, /* courseAreaId */ null); raceColumn.getRaceLog(testFleet1).add(logEvent); storedLogUUIDs.add(logEvent.getId()); // Add a competitor-related race log event to ensure that no competitor resolution is attempted while receiving @@ -998,7 +990,7 @@ public class MasterDataImportTest { Assert.assertNotNull(leaderboardOnTarget); Regatta regattaOnTarget = destService.getRegattaByName(TEST_LEADERBOARD_NAME); Assert.assertNotNull(regattaOnTarget); - Assert.assertEquals(courseAreaUUID, eventOnTarget.getVenue().getCourseAreas().iterator().next().getId()); + Assert.assertEquals(courseArea.getId(), eventOnTarget.getVenue().getCourseAreas().iterator().next().getId()); RaceColumn raceColumnOnTarget = leaderboardOnTarget.getRaceColumnByName(raceColumnName); Assert.assertNotNull(raceColumnOnTarget); Assert.assertTrue(leaderboardOnTarget.getScoreCorrection().hasCorrectionFor(raceColumnOnTarget)); @@ -1027,8 +1019,8 @@ public class MasterDataImportTest { RacingEventService sourceService = new RacingEventServiceImpl(); Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); - UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + final UUID courseAreaUUID = UUID.randomUUID(); + final CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); event.getVenue().addCourseArea(courseArea); List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; @@ -1086,7 +1078,7 @@ public class MasterDataImportTest { raceColumn.setTrackedRace(testFleet1, trackedRace); // Set log event TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); - RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint); + RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint, /* courseAreaId */ null); raceColumn.getRaceLog(testFleet1).add(logEvent); storedLogUUIDs.add(logEvent.getId()); // Set score correction @@ -1112,7 +1104,7 @@ public class MasterDataImportTest { ByteArrayOutputStream os = new ByteArrayOutputStream(); UUID randomUUID = UUID.randomUUID(); String venueNameNotToOverride; - CourseAreaImpl courseAreaNotToOverride; + CourseArea courseAreaNotToOverride; String raceColumnNameNotToOveride; RegattaLeaderboard leaderboardNotToOverride; LeaderboardGroup groupNotToOverride; @@ -1129,7 +1121,7 @@ public class MasterDataImportTest { venueNameNotToOverride = "doNotOverride"; Event eventNotToOverride = destService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, venueNameNotToOverride, false, eventUUID); - courseAreaNotToOverride = new CourseAreaImpl("testAreaNotToOverride", courseAreaUUID); + courseAreaNotToOverride = destService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testAreaNotToOverride"); eventNotToOverride.getVenue().addCourseArea(courseAreaNotToOverride); List raceColumnNamesNotToOverride = new ArrayList(); raceColumnNameNotToOveride = "T1nottooverride"; @@ -1208,18 +1200,15 @@ public class MasterDataImportTest { @Test public void testMasterDataImportWithOverrideWithoutHttpStack() throws MalformedURLException, IOException, InterruptedException, ClassNotFoundException { - Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, - "testVenue", false, eventUUID); - UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); + final UUID courseAreaUUID = UUID.randomUUID(); + final CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); event.getVenue().addCourseArea(courseArea); - List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; raceColumnNames.add(raceColumnName); raceColumnNames.add("T2"); List emptyRaceColumnNamesList = Collections.emptyList(); - List series = new ArrayList(); List fleets = new ArrayList(); FleetImpl testFleet1 = new FleetImpl("testFleet1"); @@ -1233,19 +1222,16 @@ public class MasterDataImportTest { /* registrationLinkSecret */ null, /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); - for (String name : raceColumnNames) { series.get(0).addRaceColumn(name, sourceService); } - int[] discardRule = { 1, 2, 3, 4 }; Leaderboard leaderboard = sourceService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "testDisplayName", discardRule); List leaderboardNames = new ArrayList(); leaderboardNames.add(leaderboard.getName()); LeaderboardGroup group = sourceService.addLeaderboardGroup(UUID.randomUUID(), TEST_GROUP_NAME, "testGroupDesc", - /* displayName */null, false, leaderboardNames, null, null); - + /* displayName */null, false, leaderboardNames, null, null); // Set tracked Race with competitors Set competitors = new HashSet(); UUID competitorUUID = UUID.randomUUID(); @@ -1271,36 +1257,28 @@ public class MasterDataImportTest { competitors.add(competitorToSuppress); RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); TrackedRace trackedRace = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClass, competitors), regatta, null, sourceService.getWindStore()); - raceColumn.setTrackedRace(testFleet1, trackedRace); - // Set log event TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); - RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint); + RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint, /* courseAreaId */ null); raceColumn.getRaceLog(testFleet1).add(logEvent); storedLogUUIDs.add(logEvent.getId()); - // Set score correction double scoreCorrection = 12.0; leaderboard.getScoreCorrection().correctScore(competitor, raceColumn, scoreCorrection); MaxPointsReason maxPointsReason = MaxPointsReason.DNS; leaderboard.getScoreCorrection().setMaxPointsReason(competitor, raceColumn, maxPointsReason); - // Set carried Points double carriedPoints = 2.0; leaderboard.setCarriedPoints(competitor, carriedPoints); - // Set suppressed competitor leaderboard.setSuppressed(competitorToSuppress, true); - // Set display name String nickName = "Angie"; leaderboard.setDisplayName(competitorToSuppress, nickName); - // Serialize List groupNamesToExport = new ArrayList(); groupNamesToExport.add(group.getName()); - RacingEventService destService; DomainFactory domainFactory; Response response = masterDataResource.getMasterDataByLeaderboardGroups(groupNamesToExport, false, true, false, false); @@ -1308,6 +1286,7 @@ public class MasterDataImportTest { ByteArrayOutputStream os = new ByteArrayOutputStream(); UUID randomUUID = UUID.randomUUID(); RegattaAndRaceIdentifier identifierOfRegattaTrackedRace; + CourseArea courseAreaNotToOverride; try { streamingOutput.write(os); os.flush(); @@ -1320,14 +1299,12 @@ public class MasterDataImportTest { String venueNameToOverride = "Override"; Event eventToOverride = destService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, venueNameToOverride, false, eventUUID); - CourseArea courseAreaToOverride = new CourseAreaImpl("testAreaToOverride", courseAreaUUID); - eventToOverride.getVenue().addCourseArea(courseAreaToOverride); - + courseAreaNotToOverride = destService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testAreaNotToOverride"); + eventToOverride.getVenue().addCourseArea(courseAreaNotToOverride); List raceColumnNamesToOverride = new ArrayList(); String raceColumnNameToOveride = raceColumnName; raceColumnNamesToOverride.add(raceColumnNameToOveride); emptyRaceColumnNamesList = Collections.emptyList(); - List seriesToOverride = new ArrayList(); List fleetsToOverride = new ArrayList(); FleetImpl testFleet1ToOverride = new FleetImpl("testFleet1"); @@ -1345,7 +1322,6 @@ public class MasterDataImportTest { for (String name : raceColumnNamesToOverride) { seriesToOverride.get(0).addRaceColumn(name, destService); } - // Create competitor with same ID and other details Set competitorsToOverride = new HashSet(); Set sailorsToOverride = new HashSet(); @@ -1360,7 +1336,6 @@ public class MasterDataImportTest { Color.BLUE, "noone@nowhere.de", null, teamToOverride, /* timeOnTimeFactor */null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null, /* storePersistently */ true); competitorsToOverride.add(competitorToOverride); - Leaderboard leaderboardToOverride = destService.addRegattaLeaderboard( regattaToOverride.getRegattaIdentifier(), "testDisplayNameNotToOverride", discardRule); TrackedRace trackedRace2 = new DummyTrackedRace(raceId, createCompetitorsAndBoatsMap(boatClassToOverride, competitorsToOverride), @@ -1369,23 +1344,18 @@ public class MasterDataImportTest { columnToOverride.setTrackedRace(testFleet1ToOverride, trackedRace2); identifierOfRegattaTrackedRace = regattaToOverride.getRaceIdentifier(columnToOverride .getRaceDefinition(testFleet1ToOverride)); - destService.addLeaderboardGroup(UUID.randomUUID(), TEST_GROUP_NAME, "testGroupDescToOverride", /* displayName */ null, false, new ArrayList(), null, null); destService.getLeaderboardGroupByName(TEST_GROUP_NAME).addLeaderboard(leaderboardToOverride); destService.addLeaderboard(leaderboardToOverride); ByteArrayInputStream inputStream = new ByteArrayInputStream(os.toByteArray()); - MasterDataImporter importer = new MasterDataImporter(domainFactory, destService, securityService.getCurrentUser(), securityService.getDefaultTenantForCurrentUser()); importer.importFromStream(inputStream, randomUUID, true); } finally { os.close(); } - - MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID) - .getResult(); - + MasterDataImportObjectCreationCount creationCount = destService.getDataImportLock().getProgress(randomUUID).getResult(); // ---Asserts--- // Test correct number of creations Assert.assertNotNull(creationCount); @@ -1393,15 +1363,12 @@ public class MasterDataImportTest { Assert.assertEquals(1, creationCount.getRegattaCount()); Assert.assertEquals(1, creationCount.getLeaderboardCount()); Assert.assertEquals(1, creationCount.getLeaderboardGroupCount()); - Event eventOnTarget = destService.getEvent(eventUUID); Assert.assertNotNull(eventOnTarget); - // Check if existing event didn't survive import Assert.assertEquals(event.getVenue().getName(), eventOnTarget.getVenue().getName()); - // Check if existing course area survive import - Assert.assertEquals(courseArea.getName(), eventOnTarget.getVenue().getCourseAreas().iterator().next().getName()); + Assert.assertEquals(courseAreaNotToOverride.getName(), eventOnTarget.getVenue().getCourseAreas().iterator().next().getName()); LeaderboardGroup leaderboardGroupOnTarget = destService.getLeaderboardGroupByName(TEST_GROUP_NAME); Assert.assertNotNull(leaderboardGroupOnTarget); // Check if existing leaderboard group didn't survive import @@ -1410,27 +1377,21 @@ public class MasterDataImportTest { Assert.assertNotNull(leaderboardOnTarget); Regatta regattaOnTarget = destService.getRegattaByName(TEST_REGATTA_NAME + " (" + TEST_BOAT_CLASS_NAME + ")"); Assert.assertNotNull(regattaOnTarget); - RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboardOnTarget; Regatta regattaInLeaderboard = regattaLeaderboard.getRegatta(); Assert.assertSame(regattaOnTarget, regattaInLeaderboard); - Assert.assertEquals(courseAreaUUID, eventOnTarget.getVenue().getCourseAreas().iterator().next().getId()); - RaceColumn raceColumnOnTarget = leaderboardOnTarget.getRaceColumnByName(raceColumnName); Assert.assertNotNull(raceColumnOnTarget); // Check if existing leaderboard didn't survive import Assert.assertEquals(leaderboard.getDisplayName(), leaderboardOnTarget.getDisplayName()); Assert.assertTrue(leaderboardOnTarget.getScoreCorrection().hasCorrectionFor(raceColumnOnTarget)); - // Check that tracked race of regatta leaderboard has been removed Assert.assertNull(destService.getTrackedRace(identifierOfRegattaTrackedRace)); - // Assert that competitor details were overridden Competitor competitorOnTarget = destService.getBaseDomainFactory().getExistingCompetitorById(competitorUUID); Assert.assertEquals(competitor.getName(), competitorOnTarget.getName()); Assert.assertEquals(competitor.getColor(), competitorOnTarget.getColor()); - } @Test @@ -1573,7 +1534,7 @@ public class MasterDataImportTest { // Set log event TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); - RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint); + RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint, /* courseAreaId */ null); raceColumn.getRaceLog(testFleet1).add(logEvent); storedLogUUIDs.add(logEvent.getId()); @@ -1637,17 +1598,15 @@ public class MasterDataImportTest { IOException, InterruptedException, ClassNotFoundException { Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); - UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + final UUID courseAreaUUID = UUID.randomUUID(); + final CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); event.getVenue().addCourseArea(courseArea); - List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; raceColumnNames.add(raceColumnName); String raceColumnName2 = "T2"; raceColumnNames.add(raceColumnName2); final List emptyRaceColumnNamesList = Collections.emptyList(); - List series = new ArrayList(); List fleets = new ArrayList(); FleetImpl testFleet1 = new FleetImpl("testFleet1"); @@ -1706,7 +1665,7 @@ public class MasterDataImportTest { // Set log event TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); - RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint); + RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint, /* courseAreaId */ null); raceColumn.getRaceLog(testFleet1).add(logEvent); storedLogUUIDs.add(logEvent.getId()); @@ -1885,16 +1844,14 @@ public class MasterDataImportTest { // Setup source service Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); - UUID courseAreaUUID = UUID.randomUUID(); - CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); + final UUID courseAreaUUID = UUID.randomUUID(); + final CourseArea courseArea = sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); event.getVenue().addCourseArea(courseArea); - List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; raceColumnNames.add(raceColumnName); raceColumnNames.add("T2"); final List emptyRaceColumnNamesList = Collections.emptyList(); - List series = new ArrayList(); List fleets = new ArrayList(); FleetImpl testFleet1 = new FleetImpl("testFleet1"); @@ -1908,11 +1865,9 @@ public class MasterDataImportTest { /* registrationLinkSecret */ null, /* startDate */null, /* endDate */null, regattaUUID, series, true, new LowPoint(), courseAreaUUID, /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, OneDesignRankingMetric::new); - for (String name : raceColumnNames) { series.get(0).addRaceColumn(name, sourceService); } - int[] discardRule = { 1, 2, 3, 4 }; Leaderboard leaderboard = sourceService.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "testDisplayName", discardRule); @@ -2106,14 +2061,13 @@ public class MasterDataImportTest { @Test public void testMasterDataImportWithFlexibleLeaderboard() throws MalformedURLException, IOException, InterruptedException, ClassNotFoundException { - UUID courseAreaUUID = UUID.randomUUID(); - + final UUID courseAreaUUID = UUID.randomUUID(); + sourceService.getBaseDomainFactory().getOrCreateCourseArea(courseAreaUUID, "testArea"); List raceColumnNames = new ArrayList(); String raceColumnName = "T1"; raceColumnNames.add(raceColumnName); raceColumnNames.add("T2"); final List emptyRaceColumnNamesList = Collections.emptyList(); - List seriesOnSource = new ArrayList<>(); List seriesOnTarget = new ArrayList<>(); List fleets = new ArrayList(); @@ -2136,12 +2090,11 @@ public class MasterDataImportTest { } double factor = 3.0; seriesOnSource.get(0).getRaceColumnByName(raceColumnName).setFactor(factor); - int[] discardRule = { 1, 2, 3, 4 }; ScoringScheme scoringScheme = new LowPoint(); String flexLeaderboardName = "FlexName"; FlexibleLeaderboard leaderboard = sourceService.addFlexibleLeaderboard(flexLeaderboardName, "TestFlex", - discardRule, scoringScheme, courseAreaUUID); + discardRule, scoringScheme, Collections.singleton(courseAreaUUID)); List leaderboardNames = new ArrayList(); leaderboardNames.add(leaderboard.getName()); LeaderboardGroup group = sourceService.addLeaderboardGroup(UUID.randomUUID(), TEST_GROUP_NAME, "testGroupDesc", @@ -2179,7 +2132,7 @@ public class MasterDataImportTest { // Set log event TimePoint logTimePoint = new MillisecondsTimePoint(1372489200000L); TimePoint logTimePoint2 = new MillisecondsTimePoint(1372489201000L); - RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint); + RaceLogStartTimeEvent logEvent = new RaceLogStartTimeEventImpl(logTimePoint, author, 1, logTimePoint, /* courseAreaId */ null); Fleet defaultFleet = leaderboard.getFleet(null); raceColumnOnLeaderboard.getRaceLog(defaultFleet).add(logEvent); Position p = new DegreePosition(3, 3); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java index 984413a4446..10a2a810a97 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceColumnReloadTest.java @@ -84,7 +84,6 @@ public class RaceColumnReloadTest { // FIXME use master DomainFactory; see bug 592 final DomainFactory masterDomainFactory = service.getBaseDomainFactory(); final MongoObjectFactory objectFactory = new MongoObjectFactoryImpl(MongoDBService.INSTANCE.getDB()); - final String leaderboardName = "Test Leaderboard", boatClassName = "49er", raceName = "Test Race"; BoatClass boatClass = masterDomainFactory.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */true); PersonImpl sailor = new PersonImpl("Sailor", DomainFactory.INSTANCE.getOrCreateNationality("GER"), null, null); @@ -95,7 +94,6 @@ public class RaceColumnReloadTest { /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null, (DynamicBoat) boat, /* storePersistently */ true); service.apply(new CreateFlexibleLeaderboard(leaderboardName, "Test", new int[] { 1, 2 }, new LowPoint(), null)); raceColumn = service.apply(new AddColumnToLeaderboard("R1", leaderboardName, false)); - Regatta regatta = service.apply(new AddDefaultRegatta(RegattaImpl.getDefaultName("Test Event", boatClassName), boatClassName, /* startDate */ null, /* endDate */ null, UUID.randomUUID())); final CourseImpl masterCourse = new CourseImpl("Test Course", new ArrayList()); @@ -112,20 +110,15 @@ public class RaceColumnReloadTest { /* millisecondsOverWhichToAverageSpeed */10000, null)); trackedRace.setStartOfTrackingReceived(MillisecondsTimePoint.now()); defaultFleet = Util.get(raceColumn.getFleets(), 0); - final RaceLogIdentifier raceLogIdentifier = raceColumn.getRaceLogIdentifier(defaultFleet); raceLog = raceColumn.getRaceLog(defaultFleet); - trackedRace.attachRaceLog(raceLog); - mongoStoreVisitor = new MongoRaceLogStoreVisitor(raceLogIdentifier, objectFactory); - final TimePoint t1 = MillisecondsTimePoint.now(), t2 = MillisecondsTimePoint.now().plus(1000); final SpeedWithBearing s1 = new KnotSpeedWithBearingImpl(1, new DegreeBearingImpl(10)); final SpeedWithBearing s2 = new KnotSpeedWithBearingImpl(2, new DegreeBearingImpl(20)); final Position p1 = new DegreePosition(1, 1), p2 = new DegreePosition(2, 2); final AbstractLogEventAuthor author = new LogEventAuthorImpl("Test Author", 1); - testWindEvent1 = new RaceLogWindFixEventImpl(t1, author, 0, new WindImpl(p1, t1, s1), false); testWindEvent2 = new RaceLogWindFixEventImpl(t2, author, 0, new WindImpl(p2, t2, s2), false); } @@ -134,12 +127,10 @@ public class RaceColumnReloadTest { public void testWindAddedOnlyViaDB() throws InterruptedException { final WindFixLoggingRaceLogVisitor raceLogVisitor = new WindFixLoggingRaceLogVisitor(raceLog); final WindFixLoggingRaceChangeListener trackedRaceListener = new WindFixLoggingRaceChangeListener(trackedRace); - mongoStoreVisitor.visit(testWindEvent1); mongoStoreVisitor.visit(testWindEvent2); raceLogVisitor.assertWindFixCount(0); trackedRaceListener.assertWindFixCount(0); - raceColumn.reloadRaceLog(defaultFleet); raceLogVisitor.assertWindFixCount(2); trackedRaceListener.assertWindFixCount(2); @@ -149,15 +140,12 @@ public class RaceColumnReloadTest { public void testWindAddedAndDbWithDifferentAddedReloadWithTrackedRace() throws InterruptedException { final WindFixLoggingRaceLogVisitor raceLogVisitor = new WindFixLoggingRaceLogVisitor(raceLog); final WindFixLoggingRaceChangeListener trackedRaceListener = new WindFixLoggingRaceChangeListener(trackedRace); - raceLog.add(testWindEvent1); raceLogVisitor.assertWindFixCount(1); trackedRaceListener.assertWindFixCount(1); - mongoStoreVisitor.visit(testWindEvent2); raceLogVisitor.assertWindFixCount(1); trackedRaceListener.assertWindFixCount(1); - raceColumn.reloadRaceLog(defaultFleet); raceLogVisitor.assertWindFixCount(2); trackedRaceListener.assertWindFixCount(2); @@ -166,7 +154,6 @@ public class RaceColumnReloadTest { @Test public void testAddedWind() throws InterruptedException { final WindFixLoggingRaceLogVisitor raceLogVisitor = new WindFixLoggingRaceLogVisitor(raceLog); - raceLog.add(testWindEvent1); raceLogVisitor.assertWindFixCount(1); } @@ -174,7 +161,6 @@ public class RaceColumnReloadTest { @Test public void testAddedWindAndReloadAndTheSameWindMerged() throws InterruptedException { final WindFixLoggingRaceLogVisitor raceLogVisitor = new WindFixLoggingRaceLogVisitor(raceLog); - raceLog.add(testWindEvent1); raceColumn.reloadRaceLog(defaultFleet); raceLog.add(testWindEvent1); @@ -185,7 +171,6 @@ public class RaceColumnReloadTest { @Test public void testAddedWindAndReloadAndAddAnotherAndReloadAgain() throws InterruptedException { final WindFixLoggingRaceLogVisitor raceLogVisitor = new WindFixLoggingRaceLogVisitor(raceLog); - raceLog.add(testWindEvent1); raceColumn.reloadRaceLog(defaultFleet); raceLog.add(testWindEvent2); @@ -196,10 +181,8 @@ public class RaceColumnReloadTest { @Test public void testWindAddedAndDbWithSameAndReload() throws InterruptedException { final WindFixLoggingRaceLogVisitor raceLogVisitor = new WindFixLoggingRaceLogVisitor(raceLog); - raceLog.add(testWindEvent1); raceLogVisitor.assertWindFixCount(1); - mongoStoreVisitor.visit(testWindEvent1); raceColumn.reloadRaceLog(defaultFleet); raceLogVisitor.assertWindFixCount(1); @@ -208,13 +191,10 @@ public class RaceColumnReloadTest { @Test public void testWindAddedAndDbWithDifferentAndReload() throws InterruptedException { final WindFixLoggingRaceLogVisitor raceLogVisitor = new WindFixLoggingRaceLogVisitor(raceLog); - raceLog.add(testWindEvent1); raceLogVisitor.assertWindFixCount(1); - mongoStoreVisitor.visit(testWindEvent2); raceLogVisitor.assertWindFixCount(1); - raceColumn.reloadRaceLog(defaultFleet); raceLogVisitor.assertWindFixCount(2); } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java index 25430a3b5d0..7f603f9dcff 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceLogOperationalTransformationTest.java @@ -95,7 +95,7 @@ public class RaceLogOperationalTransformationTest { public void testSimpleApply() { Calendar c = new GregorianCalendar(2013, 6, 7, 13, 59, 33); final MillisecondsTimePoint startTime = new MillisecondsTimePoint(c.getTime()); - RaceLogStartTimeEvent startTimeEvent = new RaceLogStartTimeEventImpl(startTime, author, /* pass ID */ 1, startTime); + RaceLogStartTimeEvent startTimeEvent = new RaceLogStartTimeEventImpl(startTime, author, /* pass ID */ 1, startTime, /* courseAreaId */ null); RaceLogStartTimeEventWithTransformationSupport e = new RaceLogStartTimeEventWithTransformationSupport(startTimeEvent); server.apply(e); serverLatch.countDown(); @@ -140,7 +140,7 @@ public class RaceLogOperationalTransformationTest { { RaceLogStartTimeEvent startTimeEventServer = new RaceLogStartTimeEventImpl(createdAt, startTimeServer, author, UUID.randomUUID(), /* pass ID */1, - startTimeServer, RaceLogRaceStatus.SCHEDULED); + startTimeServer, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null); RaceLogStartTimeEventWithTransformationSupport eServer = new RaceLogStartTimeEventWithTransformationSupport( startTimeEventServer); server.apply(eServer); @@ -150,7 +150,7 @@ public class RaceLogOperationalTransformationTest { final MillisecondsTimePoint startTimeClient1 = new MillisecondsTimePoint(cClient1.getTime()); RaceLogStartTimeEvent startTimeEventClient1 = new RaceLogStartTimeEventImpl(createdAt, startTimeClient1, author, UUID.randomUUID(), /* pass ID */1, - startTimeClient1, RaceLogRaceStatus.SCHEDULED); + startTimeClient1, RaceLogRaceStatus.SCHEDULED, /* courseAreaId */ null); final RaceLogStartTimeEventWithTransformationSupport eClient1 = new RaceLogStartTimeEventWithTransformationSupport( startTimeEventClient1); client1.apply(eClient1); // this call won't block because server calls apply(Peer, O, int) which first waits for the latch before synchronizing diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java index 13a1c7c4fbf..15828f45e62 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.mockito.Mockito; import com.sap.sailing.domain.base.CompetitorWithBoat; +import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.LeaderboardGroupBase; @@ -33,7 +34,6 @@ import com.sap.sailing.domain.base.LeaderboardSearchResultBase; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Venue; import com.sap.sailing.domain.base.Waypoint; -import com.sap.sailing.domain.base.impl.CourseAreaImpl; import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RegattaImpl; @@ -159,9 +159,9 @@ public class SearchServiceTest { "Kiel", /* isPublic */ true, UUID.randomUUID(), /* officialWebsiteURLAsString */ null, /*baseURL*/null, /* sailorsInfoWebsiteURLAsString */ null, /* images */Collections. emptyList(), /* videos */Collections. emptyList(), /* leaderboardGroupIds */ Collections. emptyList())); kiel = pfingstbusch.getVenue(); - final CourseAreaImpl kielAlpha = new CourseAreaImpl("Alpha", UUID.randomUUID()); + final CourseArea kielAlpha = server.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Alpha"); kiel.addCourseArea(kielAlpha); - final CourseAreaImpl kielBravo = new CourseAreaImpl("Bravo", UUID.randomUUID()); + final CourseArea kielBravo = server.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Bravo"); kiel.addCourseArea(kielBravo); final LinkedHashMap seriesCreationParams = new LinkedHashMap(); seriesCreationParams.put("Default", @@ -173,7 +173,7 @@ public class SearchServiceTest { /* registrationLinkSecret */ UUID.randomUUID().toString(), /* startDate */ null, /* endDate */ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ - true, new LowPoint(), kielAlpha.getId(), /* buoyZoneRadiusInHullLengths */2.0, + true, new LowPoint(), Collections.singleton(kielAlpha.getId()), /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, RankingMetrics.ONE_DESIGN)); server.apply(new AddColumnToSeries(pfingstbusch29er.getRegattaIdentifier(), "Default", "R1")); @@ -186,7 +186,7 @@ public class SearchServiceTest { /* registrationLinkSecret */ UUID.randomUUID().toString(), /* startDate */ null, /* endDate */ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ - true, new LowPoint(), kielBravo.getId(), /* buoyZoneRadiusInHullLengths */2.0, + true, new LowPoint(), Collections.singleton(kielBravo.getId()), /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, RankingMetrics.ONE_DESIGN)); server.apply(new AddColumnToSeries(pfingstbusch470.getRegattaIdentifier(), "Default", "R1")); @@ -208,14 +208,14 @@ public class SearchServiceTest { "Flensburg", /* isPublic */ true, UUID.randomUUID(), /* officialWebsiteURLAsString */ null, /*baseURL*/null, /*sailorsInfoWebsiteURLAsString */ null, /* images */Collections. emptyList(), /* videos */Collections. emptyList(), /* leaderboardGroupIds */ Collections. emptyList())); flensburg = aalEvent.getVenue(); - final CourseAreaImpl flensburgStandard = new CourseAreaImpl("Standard", UUID.randomUUID()); + final CourseArea flensburgStandard = server.getBaseDomainFactory().getOrCreateCourseArea(UUID.randomUUID(), "Standard"); flensburg.addCourseArea(flensburgStandard); aalRegatta = server.apply(new AddSpecificRegatta(RegattaImpl.getDefaultName("Aalregatta", "ORC"), "ORC", /* canBoatsOfCompetitorsChangePerRace */ true, CompetitorRegistrationType.CLOSED, /* registrationLinkSecret */ UUID.randomUUID().toString(), /* startDate */ null, /* endDate */ null, UUID.randomUUID(), new RegattaCreationParametersDTO(seriesCreationParams), /* persistent */ - true, new LowPoint(), flensburgStandard.getId(), /* buoyZoneRadiusInHullLengths */2.0, + true, new LowPoint(), Collections.singleton(flensburgStandard.getId()), /* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, /* autoRestartTrackingUponCompetitorSetChange */ false, RankingMetrics.ONE_DESIGN)); server.apply(new AddColumnToSeries(aalRegatta.getRegattaIdentifier(), "Default", "R1")); diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch index 576879a8311..4dece4acff9 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch @@ -160,6 +160,7 @@ + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java index 470fa2a6fda..2700ad00eb3 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java @@ -158,6 +158,7 @@ public class Activator implements BundleActivator { */ protected void registerPreferenceConvertersForUserStore(BundleContext context) { Dictionary properties = new Hashtable(); + // it's okay to re-use the properties Dictionary for several registrations because the registry clones its contents properties.put(PreferenceConverter.KEY_PARAMETER_NAME, CompetitorNotificationPreferences.PREF_NAME); registrations.add(context.registerService(PreferenceConverter.class, new GenericJSONPreferenceConverter<>(() -> new CompetitorNotificationPreferences(racingEventService)), diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index e4dad872964..ae4c8d923b9 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -1239,12 +1239,11 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public FlexibleLeaderboard addFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, - int[] discardThresholds, ScoringScheme scoringScheme, Serializable courseAreaId) { + int[] discardThresholds, ScoringScheme scoringScheme, Iterable courseAreaIds) { logger.info("adding flexible leaderboard " + leaderboardName); - CourseArea courseArea = getCourseArea(courseAreaId); FlexibleLeaderboard result = new FlexibleLeaderboardImpl(getRaceLogStore(), getRegattaLogStore(), leaderboardName, new ThresholdBasedResultDiscardingRuleImpl(discardThresholds), scoringScheme, - courseArea); + Util.map(courseAreaIds, courseAreaId->getBaseDomainFactory().getExistingCourseAreaById(courseAreaId))); result.setDisplayName(leaderboardDisplayName); if (getLeaderboardByName(leaderboardName) != null) { throw new IllegalArgumentException("Leaderboard with name " + leaderboardName + " already exists"); @@ -1696,14 +1695,14 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes public Regatta createRegatta(String fullRegattaName, String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, boolean persistent, ScoringScheme scoringScheme, - Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, + Iterable courseAreaIds, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetricConstructor rankingMetricConstructor) { if (useStartTimeInference && controlTrackingFromStartAndFinishTimes) { throw new IllegalArgumentException("Cannot set both of useStartTimeInference and controlTrackingFromStartAndFinishTimes to true"); } com.sap.sse.common.Util.Pair regattaWithCreatedFlag = getOrCreateRegattaWithoutReplication( fullRegattaName, boatClassName, canBoatsOfCompetitorsChangePerRace, competitorRegistrationType, - registrationLinkSecret, startDate, endDate, id, series, persistent, scoringScheme, defaultCourseAreaId, + registrationLinkSecret, startDate, endDate, id, series, persistent, scoringScheme, courseAreaIds, buoyZoneRadiusInHullLengths, useStartTimeInference, controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor); Regatta regatta = regattaWithCreatedFlag.getA(); @@ -1716,11 +1715,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public void addRegattaWithoutReplication(Regatta regatta) { - UUID defaultCourseAreaId = null; - if (regatta.getDefaultCourseArea() != null) { - defaultCourseAreaId = regatta.getDefaultCourseArea().getId(); - } - boolean wasAdded = addAndConnectRegatta(regatta.isPersistent(), defaultCourseAreaId, regatta); + boolean wasAdded = addAndConnectRegatta(regatta.isPersistent(), regatta); if (!wasAdded) { logger.info("Regatta with name " + regatta.getName() + " already existed, so it hasn't been added."); } @@ -1739,23 +1734,23 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes String boatClassName, boolean canBoatsOfCompetitorsChangePerRace, CompetitorRegistrationType competitorRegistrationType, String registrationLinkSecret, TimePoint startDate, TimePoint endDate, Serializable id, Iterable series, boolean persistent, - ScoringScheme scoringScheme, Serializable defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, + ScoringScheme scoringScheme, Iterable courseAreaIds, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, RankingMetricConstructor rankingMetricConstructor) { - CourseArea courseArea = getCourseArea(defaultCourseAreaId); Regatta regatta = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), fullRegattaName, getBaseDomainFactory().getOrCreateBoatClass(boatClassName), canBoatsOfCompetitorsChangePerRace, - competitorRegistrationType, startDate, endDate, series, persistent, scoringScheme, id, courseArea, + competitorRegistrationType, startDate, endDate, series, persistent, scoringScheme, id, + Util.map(courseAreaIds, courseAreaId->getBaseDomainFactory().getExistingCourseAreaById(courseAreaId)), buoyZoneRadiusInHullLengths, useStartTimeInference, controlTrackingFromStartAndFinishTimes, autoRestartTrackingUponCompetitorSetChange, rankingMetricConstructor, registrationLinkSecret); - boolean wasCreated = addAndConnectRegatta(persistent, defaultCourseAreaId, regatta); + boolean wasCreated = addAndConnectRegatta(persistent, regatta); if (wasCreated) { logger.info("Created regatta " + regatta.getName() + " (" + hashCode() + ") on " + this); } return new com.sap.sse.common.Util.Pair(regatta, wasCreated); } - private boolean addAndConnectRegatta(boolean persistent, Serializable defaultCourseAreaId, Regatta regatta) { + private boolean addAndConnectRegatta(boolean persistent, Regatta regatta) { boolean wasCreated = false; // try a quick read protected by the concurrent hash map implementation if (!regattasByName.containsKey(regatta.getName())) { @@ -1955,17 +1950,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes * (yet)}. */ private void replicateSpecificRegattaWithoutRaceColumns(Regatta regatta) { - Serializable courseAreaId = null; - if (regatta.getDefaultCourseArea() != null) { - courseAreaId = regatta.getDefaultCourseArea().getId(); - } replicate(new AddSpecificRegatta(regatta.getName(), regatta.getBoatClass() == null ? null : regatta.getBoatClass().getName(), regatta.canBoatsOfCompetitorsChangePerRace(), regatta.getCompetitorRegistrationType(), /* registrationLinkSecret */ regatta.getRegistrationLinkSecret(), regatta.getStartDate(), regatta.getEndDate(), regatta.getId(), getSeriesWithoutRaceColumnsConstructionParametersAsMap(regatta), regatta.isPersistent(), - regatta.getScoringScheme(), courseAreaId, regatta.getBuoyZoneRadiusInHullLengths(), + regatta.getScoringScheme(), Util.mapToArrayList(regatta.getCourseAreas(), CourseArea::getId), regatta.getBuoyZoneRadiusInHullLengths(), regatta.useStartTimeInference(), regatta.isControlTrackingFromStartAndFinishTimes(), regatta.isAutoRestartTrackingUponCompetitorSetChange(), regatta.getRankingMetricType())); RegattaIdentifier regattaIdentifier = regatta.getRegattaIdentifier(); @@ -2696,7 +2687,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public Regatta updateRegatta(RegattaIdentifier regattaIdentifier, TimePoint startDate, TimePoint endDate, - Serializable newDefaultCourseAreaId, RegattaConfiguration newRegattaConfiguration, + Iterable newCourseAreaIds, RegattaConfiguration newRegattaConfiguration, Iterable series, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes, boolean autoRestartTrackingUponCompetitorSetChange, String registrationLinkSecret, CompetitorRegistrationType registrationType) { if (useStartTimeInference && controlTrackingFromStartAndFinishTimes) { @@ -2704,10 +2695,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } // We're not doing any renaming of the regatta itself, therefore we don't have to sync on the maps. Regatta regatta = getRegatta(regattaIdentifier); - CourseArea newCourseArea = getCourseArea(newDefaultCourseAreaId); - if (newCourseArea != regatta.getDefaultCourseArea()) { - regatta.setDefaultCourseArea(newCourseArea); - } + regatta.setCourseAreas(Util.map(newCourseAreaIds, this::getCourseArea)); regatta.setStartDate(startDate); regatta.setEndDate(endDate); regatta.setBuoyZoneRadiusInHullLengths(buoyZoneRadiusInHullLengths); @@ -3973,7 +3961,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes @Override public TimePoint setStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName, String authorName, int authorPriority, int passId, TimePoint logicalTimePoint, TimePoint startTime, - RacingProcedureType racingProcedure) { + RacingProcedureType racingProcedure, UUID courseAreaId) { RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName); Leaderboard leaderboard = getLeaderboardByName(leaderboardName); final TimePoint result; @@ -3983,7 +3971,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes state.setAdvancePass(logicalTimePoint); } state.setRacingProcedure(logicalTimePoint, racingProcedure); - state.forceNewStartTime(logicalTimePoint, startTime); + state.forceNewStartTime(logicalTimePoint, startTime, courseAreaId); result = state.getStartTime(); } else { result = null; @@ -4647,35 +4635,60 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes DetailedRaceInfo bestMatch = null; boolean matchesName = false; boolean matchesCourseArea = false; - // start from the top; while there are more efficient ways to look up the TrackedRace by its - // race identifier, this wouldn't tell a valid event and leaderboard combination through which - // to navigate to it - for (Event event : this.getAllEvents()) { - final EventType eventType = EventUtil.getEventType(event); - for (LeaderboardGroup group : event.getLeaderboardGroups()) { - for (Leaderboard leaderboard : group.getLeaderboards()) { - for (RaceColumn race : leaderboard.getRaceColumns()) { - for (Fleet fleet : race.getFleets()) { - TrackedRace trackedRace = race.getTrackedRace(fleet); - if (trackedRace != null) { - RegattaAndRaceIdentifier trackedRaceIdentifier = trackedRace.getRaceIdentifier(); - // check if the race matches the RegattaAndRaceIdentifier - if (trackedRaceIdentifier.equals(raceIdentifier) - && trackedRace.getStartOfRace() != null) { - final CourseArea defaultCourseArea = leaderboard.getDefaultCourseArea(); - boolean leaderboardLinkedToEventThroughCourseArea = (defaultCourseArea != null - && Util.contains(event.getVenue().getCourseAreas(), defaultCourseArea)); - boolean nameOfRegattaAndLeaderboardMatch = leaderboard.getName().equals(trackedRaceIdentifier.getRegattaName()); - // check if the match is a best match -> we keep the previous match otherwise - if (bestMatch == null - || (leaderboardLinkedToEventThroughCourseArea && !matchesCourseArea) - || (leaderboardLinkedToEventThroughCourseArea == matchesCourseArea - && nameOfRegattaAndLeaderboardMatch && !matchesName)) { - bestMatch = new DetailedRaceInfo(trackedRaceIdentifier, leaderboard.getName(), - leaderboard.getDisplayName(), trackedRace.getStartOfRace(), - event.getId(), event.getName(), eventType, null); - matchesName = nameOfRegattaAndLeaderboardMatch; - matchesCourseArea = leaderboardLinkedToEventThroughCourseArea; + // A perfect match with a leaderboard name matching the regatta name, and an event that has a course area also specified + // by the leaderboard can be found in O(n) with n being the number of events, by scanning the events for one with a matching + // course area. The leaderboard can be found in O(1). + // Only if this lookup/search does not provide perfect match, we will have to start searching in all leaderboards because + // there could be a FlexibleLeaderboard with a name different from the regatta name that has a matching course area. + // So, first try to identify the leaderboard by the regatta name; if found, we already have a name match there. We then + // only need to find an event that contains the leaderboard through any leaderboard group + final Leaderboard leaderboardByRegattaName = getLeaderboardByName(raceIdentifier.getRegattaName()); + if (leaderboardByRegattaName != null) { + final TrackedRace trackedRace = Util.first(Util.filter(leaderboardByRegattaName.getTrackedRaces(), tr->tr.getRaceIdentifier().equals(raceIdentifier))); + if (trackedRace != null && leaderboardByRegattaName != null && !Util.isEmpty(leaderboardByRegattaName.getCourseAreas())) { + final Event eventMatchingAtLeastOneCourseArea = findEventContainingLeaderboardAndMatchingAtLeastOneCourseArea(leaderboardByRegattaName); + if (eventMatchingAtLeastOneCourseArea != null) { + // that's the best match + bestMatch = new DetailedRaceInfo(raceIdentifier, leaderboardByRegattaName.getName(), + leaderboardByRegattaName.getDisplayName(), trackedRace.getStartOfRace(), + eventMatchingAtLeastOneCourseArea.getId(), eventMatchingAtLeastOneCourseArea.getName(), + EventUtil.getEventType(eventMatchingAtLeastOneCourseArea), null); + } + } + } + if (bestMatch == null) { // do the more expensive search now: + for (Event event : this.getAllEvents()) { + final EventType eventType = EventUtil.getEventType(event); + for (LeaderboardGroup group : event.getLeaderboardGroups()) { + for (Leaderboard leaderboard : group.getLeaderboards()) { + for (RaceColumn race : leaderboard.getRaceColumns()) { + for (Fleet fleet : race.getFleets()) { + TrackedRace trackedRace = race.getTrackedRace(fleet); + if (trackedRace != null) { + RegattaAndRaceIdentifier trackedRaceIdentifier = trackedRace.getRaceIdentifier(); + // check if the race matches the RegattaAndRaceIdentifier and has a valid start time + if (trackedRaceIdentifier.equals(raceIdentifier) && trackedRace.getStartOfRace() != null) { + boolean leaderboardLinkedToEventThroughCourseArea = Util.containsAny(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas()); + boolean nameOfRegattaAndLeaderboardMatch = leaderboard.getName().equals(trackedRaceIdentifier.getRegattaName()); + // check if the match is a better match than bestMatch -> we keep the previous match otherwise; + // "better" means that we either have no match at all, or + // the new match now matches the event's course area (regardless whether the previous best match has a matching leaderboard name), or + // the new match now matches the leaderboard name while not "getting worse" regarding a previous course area match. + // In other words, we will prefer a course area match with a leaderboard name mismatch over a match that has + // no course area match but a matching leaderboard name. + if (bestMatch == null + || (leaderboardLinkedToEventThroughCourseArea && !matchesCourseArea) + || (leaderboardLinkedToEventThroughCourseArea == matchesCourseArea + && nameOfRegattaAndLeaderboardMatch && !matchesName)) { + bestMatch = new DetailedRaceInfo(trackedRaceIdentifier, leaderboard.getName(), + leaderboard.getDisplayName(), trackedRace.getStartOfRace(), + event.getId(), event.getName(), eventType, null); + matchesName = nameOfRegattaAndLeaderboardMatch; + matchesCourseArea = leaderboardLinkedToEventThroughCourseArea; + if (matchesName && matchesCourseArea) { + return bestMatch; // it won't get any better than this + } + } } } } @@ -4687,6 +4700,20 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes return bestMatch; } + private Event findEventContainingLeaderboardAndMatchingAtLeastOneCourseArea(Leaderboard leaderboard) { + assert !Util.isEmpty(leaderboard.getCourseAreas()); + for (final Event event : getAllEvents()) { + if (Util.containsAny(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas())) { + for (final LeaderboardGroup leaderboardGroup : event.getLeaderboardGroups()) { + if (leaderboardGroup.getIndexOf(leaderboard) >= 0) { + return event; + } + } + } + } + return null; + } + @Override public DetailedRaceInfo getFullDetailsForRaceCascading(RegattaAndRaceIdentifier raceIdentifier) { DetailedRaceInfo bestMatch = getFullDetailsForRaceLocal(raceIdentifier); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java index cbe646989f7..053bdcf1a70 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -127,10 +128,11 @@ public class RegattaByKeywordSearchService { } } } - final Event eventByDefaultCourseArea = eventForCourseArea.get(leaderboard.getDefaultCourseArea()); - if (eventByDefaultCourseArea != null) { - leaderboardStrings.addAll(stringsForEvent.get(eventByDefaultCourseArea)); - } + final Optional eventByDefaultCourseArea = StreamSupport + .stream(leaderboard.getCourseAreas().spliterator(), /* parallel */ false) + .filter(ca -> eventForCourseArea.containsKey(ca)).findFirst() + .map(ca -> eventForCourseArea.get(ca)); + eventByDefaultCourseArea.ifPresent(e -> leaderboardStrings.addAll(stringsForEvent.get(e))); return leaderboardStrings; } }; @@ -153,30 +155,34 @@ public class RegattaByKeywordSearchService { * series. In bug3348 a change was made to show all events associated to a leaderboard in the search results. This * lead to an "explosion of results" as there were potentially n results referencing n events instead of each result * only referencing the associated event. + *

    * * This filters the events to be associated to a leaderboard. If the leaderboardGroup has a OverallLeaderboard (in * case of a series), there is a special matching to find the right event. If a leaderboard has a defaultCourseArea, * the event hosting this CourseArea is the right one. If this reference isn't given or the CourseArea doesn't * belong to an event of the series, the fallback behavior is causing all events to be returned. + *

    * * This doesn't affect any leaderboard's event set if the leaderboard isn't part of a series. + *

    * - * @param leaderboard the leaderboard to get the matching events for - * @param leaderboardGroup the LeaderboardGroup hosting the leaderboard - * @param events all events hosting the LeaderboardGroup; may be {@code null}; if {@code null}, result will be {@code null} + * @param leaderboard + * the leaderboard to get the matching events for + * @param leaderboardGroup + * the LeaderboardGroup hosting the leaderboard + * @param events + * all events hosting the LeaderboardGroup; may be {@code null}; if {@code null}, result will be + * {@code null} * @return the best matching events for the given Leaderboard/LeaderboardGroup */ private Set filterEventsForLeaderboard(Leaderboard leaderboard, LeaderboardGroup leaderboardGroup, Set events) { final Set result; if (events != null && leaderboardGroup.hasOverallLeaderboard()) { - CourseArea defaultCourseArea = leaderboard.getDefaultCourseArea(); Set preResult = null; - if (defaultCourseArea != null) { - for (Event event : events) { - if (Util.contains(event.getVenue().getCourseAreas(), defaultCourseArea)) { - preResult = Collections.singleton(event); - break; - } + for (Event event : events) { + if (Util.containsAny(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas())) { + preResult = Collections.singleton(event); + break; } } result = preResult; @@ -199,10 +205,11 @@ public class RegattaByKeywordSearchService { } } } - final Event eventByCourseArea = eventForCourseArea.get(matchingLeaderboard.getDefaultCourseArea()); - if (eventByCourseArea != null) { - result.add(eventByCourseArea); - } + final Optional eventByDefaultCourseArea = StreamSupport + .stream(matchingLeaderboard.getCourseAreas().spliterator(), /* parallel */ false) + .filter(ca -> eventForCourseArea.containsKey(ca)).findFirst() + .map(ca -> eventForCourseArea.get(ca)); + eventByDefaultCourseArea.ifPresent(e -> result.add(e)); return result; } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java index 52797073dda..2490b2828af 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/notification/impl/SailingNotificationServiceImpl.java @@ -14,7 +14,6 @@ import org.osgi.framework.BundleContext; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; @@ -113,12 +112,9 @@ public class SailingNotificationServiceImpl implements SailingNotificationServic // could be a series for (final LeaderboardGroup leaderboardGroup : foundLeaderboardGroups) { if (leaderboardGroup.hasOverallLeaderboard()) { - CourseArea defaultCourseArea = leaderboard.getDefaultCourseArea(); - if (defaultCourseArea != null) { - for (Event event : foundEvents) { - if (Util.contains(event.getVenue().getCourseAreas(), defaultCourseArea)) { - return new Pair<>(event, leaderboardGroup); - } + for (Event event : foundEvents) { + if (Util.containsAny(event.getVenue().getCourseAreas(), leaderboard.getCourseAreas())) { + return new Pair<>(event, leaderboardGroup); } } } diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index e4759947928..b26a1c60c89 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -32,6 +32,32 @@ Particularly attributes such as QueuedTasks and CompletedTaskCount can help JMX clients such as JConsole to understand the state of the server.

  • +
  • Leaderboards and regattas used to have a single "default" course area assignment. This caused + problems when particularly split-fleet regattas assigned their different fleets to different + course areas at the same time. See also + bug 3465. Now it is + possible to select more than one course area from the enclosing event for a regatta and flexible + leaderboard. As an immediate effect, the SAP Sailing Race Manager app which uses the course area + as a filter for the regattas to display for an event can now see the same regatta on different devices + configured for different course areas. For example, if a regatta splits its Gold and Silver fleets + to race on course areas Delta and Echo, respectively, then now users of the SAP Sailing Race Manager app + will see this regatta when they are logged on to course area Delta or to course area Echo. +
  • +
  • When setting a start time for a race through the "Race Log," such as from the SAP Sailing Race + Manager app or, e.g., the AdminConsole.html start time setting feature, the course area on which the + start will happen can be captured in the log entry. If not provided, and if the regatta / leaderboard + to whose log the entry is added has exactly one course area configured then that will be the default + course area for the race. With this new feature is it possible to specify exactly on which course area + a race has started, even if the enclosing regatta uses multiple course areas, either over time or even + at the same time. The SAP Sailing Race Manager app now uses the course area to which the user logs on to + fill this new field in the start time log entries. +
  • +
  • An incompatible change has been applied to the JSON output format of the + sailingserver/api/v1/regattas/{regatta-name} end point. Instead of delivering + the ID of the single "default" course area in the field courseAreaId now a list + of such IDs is provided in the courseAreaIds field as an array that is always present + but may be empty. +

April 2020

    diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/Util.java b/java/com.sap.sse.common/src/com/sap/sse/common/Util.java index 05a7de6b6da..b551a4da42b 100755 --- a/java/com.sap.sse.common/src/com/sap/sse/common/Util.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/Util.java @@ -362,7 +362,13 @@ public class Util { } }); } - + + public static ArrayList mapToArrayList(final Iterable iterable, final Mapper mapper) { + final ArrayList result = new ArrayList<>(); + addAll(map(iterable, mapper), result); + return result; + } + public static Iterable filter(final Iterable iterable, final Predicate predicate) { return StreamSupport.stream(iterable.spliterator(), /* parallel */ false).filter(predicate)::iterator; } @@ -386,6 +392,22 @@ public class Util { return false; } } + + /** + * @return {@code true} if {@code ts} {@link #contains(Iterable, Object) contains} at least one of the elements in + * {@code isAnyOfTheseContained}. This means in particular that if {@code isAnyOfTheseContained} is + * {@code null} or is empty, {@code false} will result. + */ + public static boolean containsAny(Iterable ts, Iterable isAnyOfTheseContained) { + if (isAnyOfTheseContained != null) { + for (final T t : isAnyOfTheseContained) { + if (contains(ts, t)) { + return true; + } + } + } + return false; + } /** * Checks whether for all elements from {@code what} the method {@link #contains(Iterable, Object)} diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractGenericSerializableSettings.java b/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractGenericSerializableSettings.java index 42c2d7adc56..ceac4321bda 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractGenericSerializableSettings.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractGenericSerializableSettings.java @@ -140,8 +140,7 @@ public abstract class AbstractGenericSerializableSettings extends AbstractSettin * TODO make abstract when all Settings are ported to the new system * */ - protected void addChildSettings() { - } + protected abstract void addChildSettings(); // TODO make protected public Value getValue(String settingName) { diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractSetting.java b/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractSetting.java index b83b7849b36..dc82148aba0 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractSetting.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/AbstractSetting.java @@ -1,7 +1,5 @@ package com.sap.sse.common.settings.generic; - - public abstract class AbstractSetting implements Setting { protected transient final AbstractGenericSerializableSettings settings; @@ -10,7 +8,7 @@ public abstract class AbstractSetting implements Setting { public AbstractSetting(String settingName, AbstractGenericSerializableSettings settings) { this.settingName = settingName; this.settings = settings; - if(settings != null) { + if (settings != null) { settings.addSetting(settingName, this); } } diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/SettingsList.java b/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/SettingsList.java index 7fa5f4ab54a..601c43e2b8a 100644 --- a/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/SettingsList.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/settings/generic/SettingsList.java @@ -40,7 +40,7 @@ public class SettingsList extends private ValueCollectionValue> ensureValue() { ValueListValue result = getValue(); - if(result == null) { + if (result == null) { result = new ValueListValue(); settings.setValue(settingName, result); } @@ -79,8 +79,8 @@ public class SettingsList extends this.values.clear(); ValueCollectionValue> valueObject = ensureValue(); valueObject.clear(); - if(values != null) { - for(T value : values) { + if (values != null) { + for (T value : values) { this.values.add(value); valueObject.addValue(value.getInnerValueObject()); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java index fbc08f813ff..1e4049a7aa0 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java @@ -20,7 +20,6 @@ public class DefaultErrorReporter implements ErrorRepo public DefaultErrorReporter(S stringMessages) { this.stringMessages = stringMessages; - /* TODO: Make this more generic (e.g. make it support all kinds of messages) */ errorDialogBox = createErrorDialog(); persistentAlertLabel = new Label(""); diff --git a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerWithSingleServiceReplicationTest.java b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerWithSingleServiceReplicationTest.java index 8d07094f647..e68f7f496d9 100755 --- a/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerWithSingleServiceReplicationTest.java +++ b/java/com.sap.sse.replication.testsupport/src/com/sap/sse/replication/testsupport/AbstractServerWithSingleServiceReplicationTest.java @@ -17,7 +17,6 @@ public abstract class AbstractServerWithSingleServiceReplicationTest replicaReplicator; protected ReplicationMasterDescriptor masterDescriptor; - public AbstractServerWithSingleServiceReplicationTest(AbstractServerReplicationTestSetUp testSetUp) { this.testSetUp = testSetUp; } diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/PredefinedRoles.java b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/PredefinedRoles.java index ccc0cc16e66..1c81baced1c 100644 --- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/PredefinedRoles.java +++ b/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/PredefinedRoles.java @@ -13,7 +13,7 @@ import java.util.UUID; * */ public enum PredefinedRoles { - spectator(UUID.fromString("244cb84c-2b8a-4557-b175-db963072cfbc"), "spectator", Collections.emptyList()), + spectator(UUID.fromString("244cb84c-2b8a-4557-b175-db963072cfbc"), "spectator", Collections.emptyList()), moderator(UUID.fromString("91c88a11-c977-4c24-b0a6-15bac0525764"), "moderator", asList("TRACKED_RACE:CAN_REPLAY_DURING_LIVE_RACES,DETAIL_TIMER")), mediaeditor(UUID.fromString("2e22583b-c9c1-4ee4-8231-e8e320a8a411"), "mediaeditor", asList("MEDIA_TRACK:*")); @@ -21,7 +21,7 @@ public enum PredefinedRoles { private final String rolename; private final Iterable permissions; - private PredefinedRoles(UUID id, String rolename, Iterable permissions) { + PredefinedRoles(UUID id, String rolename, Iterable permissions) { this.id = id; this.rolename = rolename; this.permissions = permissions; diff --git a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java index a64472623fc..ba73aab950c 100644 --- a/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java +++ b/java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/UserStoreImpl.java @@ -1039,7 +1039,6 @@ public class UserStoreImpl implements UserStore { @Override public void registerPreferenceConverter(String preferenceKey, PreferenceConverter converter) { PreferenceConverter alreadyAssociatedConverter = preferenceConverters.putIfAbsent(preferenceKey, converter); - if (alreadyAssociatedConverter == null) { final Set usersToProcess = new HashSet<>(preferences.keySet()); for (String user : usersToProcess) { diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/DataStore.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/DataStore.java index c7763694a1b..05dc2ecdd49 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/DataStore.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/DataStore.java @@ -78,8 +78,12 @@ public interface DataStore { void setEventUUID(Serializable uuid); - UUID getCourseUUID(); - - void setCourseUUID(UUID uuid); + /** + * The ID of the course area to which the user has logged on. Used as filter when + * loading regattas / race groups, and used in start time events when scheduling + * a race, assuming that this is the course area where the start will happen. + */ + UUID getCourseAreaId(); + void setCourseAreaId(UUID uuid); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/InMemoryDataStore.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/InMemoryDataStore.java index f3c9fb300b8..db591a805cb 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/InMemoryDataStore.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/InMemoryDataStore.java @@ -41,7 +41,7 @@ public enum InMemoryDataStore implements DataStore { private SharedDomainFactory domainFactory; private Serializable eventUUID; - private UUID courseUUID; + private UUID courseAreaId; private RaceStateService mService; private boolean mBound; @@ -68,7 +68,7 @@ public enum InMemoryDataStore implements DataStore { domainFactory = new SharedDomainFactoryImpl(new AndroidRaceLogResolver()); eventUUID = null; - courseUUID = null; + courseAreaId = null; if (mContext != null && mBound) { mContext.unbindService(mConnection); @@ -328,13 +328,13 @@ public enum InMemoryDataStore implements DataStore { } @Override - public UUID getCourseUUID() { - return courseUUID; + public UUID getCourseAreaId() { + return courseAreaId; } @Override - public void setCourseUUID(UUID uuid) { - courseUUID = uuid; + public void setCourseAreaId(UUID uuid) { + courseAreaId = uuid; } public void registerRaces(Collection races) { diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OfflineDataManager.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OfflineDataManager.java index ea362fc14ca..45ba8125a9e 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OfflineDataManager.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/data/OfflineDataManager.java @@ -89,15 +89,17 @@ public class OfflineDataManager extends DataManager { UUID.randomUUID(), Collections. emptySet(), Collections.emptySet())); EventBase newEvent = new StrippedEventImpl("Extreme Sailing Series 2013 (Muscat)", startDate, endDate, "Muscat", true, UUID.randomUUID(), Collections. emptySet(), Collections.emptySet()); - newEvent.getVenue().addCourseArea(new CourseAreaImpl("Offshore", UUID.randomUUID())); - newEvent.getVenue().addCourseArea(new CourseAreaImpl("Stadium", UUID.randomUUID())); + final CourseAreaImpl offshore = new CourseAreaImpl("Offshore", UUID.randomUUID()); + newEvent.getVenue().addCourseArea(offshore); + final CourseAreaImpl stadium = new CourseAreaImpl("Stadium", UUID.randomUUID()); + newEvent.getVenue().addCourseArea(stadium); dataStore.addEvent(newEvent); SeriesWithRows qualifying = new SeriesWithRowsImpl("Qualifying", false, /* isFleetsCanRunInParallel */ true, null); SeriesWithRows medal = new SeriesWithRowsImpl("Medal", true, /* isFleetsCanRunInParallel */ true, null); RaceGroup raceGroup = new RaceGroupImpl("ESS", /* displayName */ null, new BoatClassImpl("X40", false), false, - null, Arrays.asList(qualifying, medal), new EmptyRegattaConfiguration()); + Arrays.asList(qualifying, medal), new EmptyRegattaConfiguration()); List competitors = new ArrayList(); competitors.add(new CompetitorImpl(UUID.randomUUID(), "SAP Extreme Sailing Team", "SAP", Color.BLUE, null, null, @@ -116,31 +118,25 @@ public class OfflineDataManager extends DataManager { final AbstractLogEventAuthor author = AppPreferences.on(context).getAuthor(); ConfigurationLoader configuration = PreferencesRegattaConfigurationLoader .loadFromPreferences(preferences); - log.add(new RaceLogStartTimeEventImpl(new MillisecondsTimePoint(new Date().getTime() - 2000), author, 1, - new MillisecondsTimePoint(new Date().getTime() - 1000))); - + new MillisecondsTimePoint(new Date().getTime() - 1000), /* course area ID */ stadium.getId())); log.add(new RaceLogRaceStatusEventImpl(new MillisecondsTimePoint(new Date().getTime()), AppPreferences.on(context).getAuthor(), 1, RaceLogRaceStatus.FINISHING)); - ManagedRace q1 = new ManagedRaceImpl( new ManagedRaceIdentifierImpl("A.B", new FleetImpl("A"), qualifying, raceGroup), RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration), /* zeroBasedIndexInFleet */ 0); - log = new RaceLogImpl(UUID.randomUUID()); /* * log.add(factory.createStartTimeEvent( new MillisecondsTimePoint(new Date()), 1, RaceLogRaceStatus.SCHEDULED, * new MillisecondsTimePoint(new Date().getTime() + 100000))); */ - ManagedRace q2 = new ManagedRaceImpl( new ManagedRaceIdentifierImpl("B", new FleetImpl("A.A"), qualifying, raceGroup), RaceStateImpl.create(new AndroidRaceLogResolver(), log, AppPreferences.on(context).getAuthor(), configuration), /* zeroBasedIndexInFleet */ 1); - log = new RaceLogImpl(UUID.randomUUID()); /* * log.add(factory.createRaceStatusEvent( new MillisecondsTimePoint(new Date()), 5, @@ -159,11 +155,9 @@ public class OfflineDataManager extends DataManager { dataStore.addRace(q2); dataStore.addRace(q3); // dataStore.addRace(m1); - Mark m1 = new MarkImpl("Red"); Mark m2 = new MarkImpl("Green"); Mark m3 = new MarkImpl("White"); - dataStore.addMark(raceGroup, m1); dataStore.addMark(raceGroup, m2); dataStore.addMark(raceGroup, m3); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java index 8778ec1c764..9f9608c5de7 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java @@ -239,7 +239,7 @@ public class RaceStateService extends Service { DataStore dataStore = dataManager.getDataStore(); dataStore.getRaces().clear(); dataStore.setEventUUID(null); - dataStore.setCourseUUID(null); + dataStore.setCourseAreaId(null); ExLog.i(this, TAG, "handleClearRaces: Cleared all races."); stopSelf(); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java index ecec4e0db61..c79e9603426 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java @@ -159,7 +159,7 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener private void login() { DataStore dataStore = dataManager.getDataStore(); dataStore.setEventUUID(mSelectedEventId); - dataStore.setCourseUUID(mSelectedCourseAreaUUID); + dataStore.setCourseAreaId(mSelectedCourseAreaUUID); switchToRacingActivity(); } @@ -308,7 +308,7 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener DataStore dataStore = dataManager.getDataStore(); // Check if the user has been logged in before and if so bring him directly to the racing activity - mSelectedCourseAreaUUID = dataStore.getCourseUUID(); + mSelectedCourseAreaUUID = dataStore.getCourseAreaId(); mSelectedEventId = dataStore.getEventUUID(); if (mSelectedEventId != null && mSelectedCourseAreaUUID != null) { if (preferences.getAccessToken() != null) { diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/PasswordActivity.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/PasswordActivity.java index 8b682d3069e..e4c45dfd581 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/PasswordActivity.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/PasswordActivity.java @@ -56,9 +56,9 @@ public class PasswordActivity extends BaseActivity { public void onReceive(Context context, Intent intent) { DataStore dataStore = DataManager.create(PasswordActivity.this).getDataStore(); Intent start; - if (dataStore.getCourseUUID() != null) { + if (dataStore.getCourseAreaId() != null) { start = new Intent(PasswordActivity.this, RacingActivity.class); - start.putExtra(AppConstants.COURSE_AREA_UUID_KEY, dataStore.getCourseUUID()); + start.putExtra(AppConstants.COURSE_AREA_UUID_KEY, dataStore.getCourseAreaId()); start.putExtra(AppConstants.EventIdTag, dataStore.getEventUUID()); } else { start = new Intent(PasswordActivity.this, LoginActivity.class); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/GeneralPreferenceFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/GeneralPreferenceFragment.java index 5b3b404dd8d..848e82aaf7a 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/GeneralPreferenceFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/GeneralPreferenceFragment.java @@ -64,7 +64,7 @@ public class GeneralPreferenceFragment extends BasePreferenceFragment { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { AppPreferences.on(getActivity()).setNeedConfigRefresh(true); - if (DataManager.create(getActivity()).getDataStore().getCourseUUID() != null) { + if (DataManager.create(getActivity()).getDataStore().getCourseAreaId() != null) { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle(getString(R.string.non_public_changed_title)); builder.setMessage(getString(R.string.app_refresh_message)); @@ -124,7 +124,7 @@ public class GeneralPreferenceFragment extends BasePreferenceFragment { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { AppPreferences.on(getActivity()).setNeedConfigRefresh(true); - if (DataManager.create(getActivity()).getDataStore().getCourseUUID() != null) { + if (DataManager.create(getActivity()).getDataStore().getCourseAreaId() != null) { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); builder.setTitle(getString(R.string.url_refresh_title)); builder.setMessage(getString(R.string.app_refresh_message)); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java index f2271865690..0c7c794879f 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/AbortFlagsFragment.java @@ -24,6 +24,8 @@ import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.racecommittee.app.AppConstants; import com.sap.sailing.racecommittee.app.R; +import com.sap.sailing.racecommittee.app.data.OnlineDataManager; +import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; import com.sap.sailing.racecommittee.app.domain.ManagedRace; import com.sap.sailing.racecommittee.app.logging.LogEvent; import com.sap.sailing.racecommittee.app.ui.activities.RacingActivity; @@ -193,7 +195,9 @@ public class AbortFlagsFragment extends RaceFragment implements AbortFlagItemCli @Override public void run() { super.run(); - getRaceToAbort().getState().forceNewDependentStartTime(getNow(), startTimeDiff, dependentOn); + final ReadonlyDataManager dataManager = OnlineDataManager.create(getActivity()); + getRaceToAbort().getState().forceNewDependentStartTime(getNow(), startTimeDiff, dependentOn, + dataManager.getDataStore().getCourseAreaId()); } } @@ -209,7 +213,8 @@ public class AbortFlagsFragment extends RaceFragment implements AbortFlagItemCli @Override public void run() { - onRace.getState().forceNewStartTime(getNow(), absoluteStartTime); + final ReadonlyDataManager dataManager = OnlineDataManager.create(getActivity()); + onRace.getState().forceNewStartTime(getNow(), absoluteStartTime, dataManager.getDataStore().getCourseAreaId()); } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/MainScheduleFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/MainScheduleFragment.java index cba9e5d2a8e..cf032f318d3 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/MainScheduleFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/MainScheduleFragment.java @@ -24,6 +24,8 @@ import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.racecommittee.app.R; import com.sap.sailing.racecommittee.app.data.DataManager; +import com.sap.sailing.racecommittee.app.data.OnlineDataManager; +import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; import com.sap.sailing.racecommittee.app.domain.ManagedRace; import com.sap.sailing.racecommittee.app.domain.impl.SelectionItem; import com.sap.sailing.racecommittee.app.ui.activities.RacingActivity; @@ -38,6 +40,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; import java.util.ArrayList; import java.util.Calendar; +import java.util.UUID; public class MainScheduleFragment extends BaseFragment implements View.OnClickListener, SelectionAdapter.ItemClick { @@ -259,7 +262,6 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi private void startRace() { ConfigurableStartModeFlagRacingProcedure lineStartRacingProcedure = null; - Flags flag = null; if (getRaceState().getRacingProcedure() instanceof ConfigurableStartModeFlagRacingProcedure) { lineStartRacingProcedure = getRaceState().getTypedRacingProcedure(); @@ -272,10 +274,12 @@ public class MainScheduleFragment extends BaseFragment implements View.OnClickLi /* enable */ mItemRaceGroup.isChecked(), AdditionalScoringInformationType.MAX_POINTS_DECREASE_MAX_SCORE); } + final ReadonlyDataManager dataManager = OnlineDataManager.create(getActivity()); + final UUID courseAreaId = dataManager.getDataStore().getCourseAreaId(); if (mStartTimeDiff == null && mRaceId == null) { - getRaceState().forceNewStartTime(now, mStartTime); + getRaceState().forceNewStartTime(now, mStartTime, courseAreaId); } else { - getRaceState().forceNewDependentStartTime(now, mStartTimeDiff, mRaceId); + getRaceState().forceNewDependentStartTime(now, mStartTimeDiff, mRaceId, courseAreaId); } if (lineStartRacingProcedure != null) { lineStartRacingProcedure.setStartModeFlag(MillisecondsTimePoint.now(), flag); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java index 93c95e1cd3e..fb35c022435 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/RaceTimeChangeFragment.java @@ -8,6 +8,8 @@ import com.sap.sailing.android.shared.util.BroadcastManager; import com.sap.sailing.android.shared.util.ViewHelper; import com.sap.sailing.racecommittee.app.AppConstants; import com.sap.sailing.racecommittee.app.R; +import com.sap.sailing.racecommittee.app.data.OnlineDataManager; +import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; import com.sap.sailing.racecommittee.app.domain.impl.Result; import com.sap.sailing.racecommittee.app.ui.layouts.HeaderLayout; import com.sap.sailing.racecommittee.app.utils.ThemeHelper; @@ -146,7 +148,6 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick @Override public void onClick(View v) { TimePoint time = getPickerTime(); - Result result; switch (getArguments().getInt(TIME_MODE)) { case FINISHING_TIME_MODE: @@ -155,19 +156,17 @@ public class RaceTimeChangeFragment extends BaseFragment implements View.OnClick Toast.makeText(getActivity(), result.getMessage(getActivity()), Toast.LENGTH_LONG).show(); } break; - case FINISHED_TIME_MODE: result = getRace().setFinishedTime(time); if (result.hasError()) { Toast.makeText(getActivity(), result.getMessage(getActivity()), Toast.LENGTH_LONG).show(); } break; - default: // START_TIME_MODE - getRaceState().forceNewStartTime(MillisecondsTimePoint.now(), time); + final ReadonlyDataManager dataManager = OnlineDataManager.create(getActivity()); + getRaceState().forceNewStartTime(MillisecondsTimePoint.now(), time, dataManager.getDataStore().getCourseAreaId()); break; } - closeFragment(); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartProcedureFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartProcedureFragment.java index 6528a11c435..b09da6f169b 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartProcedureFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartProcedureFragment.java @@ -8,6 +8,8 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.RacingProce import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.racecommittee.app.AppConstants; import com.sap.sailing.racecommittee.app.R; +import com.sap.sailing.racecommittee.app.data.OnlineDataManager; +import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; import com.sap.sailing.racecommittee.app.ui.adapters.checked.CheckedItemAdapter; import com.sap.sailing.racecommittee.app.ui.adapters.checked.StartProcedureItem; import com.sap.sailing.racecommittee.app.ui.layouts.HeaderLayout; @@ -128,7 +130,9 @@ public class StartProcedureFragment extends BaseFragment { if (sameProcedure) { sendIntent(AppConstants.INTENT_ACTION_SHOW_MAIN_CONTENT); } else { - getRaceState().forceNewStartTime(MillisecondsTimePoint.now(), getRaceState().getStartTime()); + final ReadonlyDataManager dataManager = OnlineDataManager.create(getActivity()); + getRaceState().forceNewStartTime(MillisecondsTimePoint.now(), getRaceState().getStartTime(), + dataManager.getDataStore().getCourseAreaId()); } } } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java index b5ef23a50bb..8c974817fbd 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java @@ -793,10 +793,10 @@ public class StartTimeFragment extends BaseFragment if (getArguments().getInt(START_MODE, START_MODE_PRESETUP) != START_MODE_PRESETUP) { if (startTimeDiff == null && identifier == null) { // absolute start time - getRaceState().forceNewStartTime(now, startTime); + getRaceState().forceNewStartTime(now, startTime, mDataStore.getCourseAreaId()); } else { // relative start time - getRaceState().forceNewDependentStartTime(now, startTimeDiff, identifier); + getRaceState().forceNewDependentStartTime(now, startTimeDiff, identifier, mDataStore.getCourseAreaId()); } fragment = RaceFlagViewerFragment.newInstance(); viewId = R.id.race_content;