mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-24 14:38:45 +00:00
Merge branch 'master' into bug5060
This commit is contained in:
+119
@@ -0,0 +1,119 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Given a string that is assumed to contain a "sail number" or "sail ID" this class offers methods to canonicalize and
|
||||
* parse that string in order to make matches across different systems that handle such sail numbers / IDs. For example,
|
||||
* the canonicalization process ignores case and whitespace, and offers to use a default nationality when comparing in
|
||||
* case a sail number doesn't have a nationality.
|
||||
* <p>
|
||||
*
|
||||
* Since the logic is intended to work for both, browser GWT client as well as OSGi Java back-end, and because GWT does
|
||||
* not have a regular expression library that is compatible with the Java default regexp library, the regular expression
|
||||
* constant needs to be turned into a regular expression matcher in a "platform-specific" way, making this class
|
||||
* abstract.
|
||||
*
|
||||
* @param <CompetitorType>
|
||||
* the type of competitor; different for GWT client where it will be the {@code CompetitorDTO}, and back-end
|
||||
* where it will be the {@link Competitor}, leading to specific method implementations for extracting the
|
||||
* sail number
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractSailNumberCanonicalizerAndMatcher<CompetitorType> {
|
||||
protected static final String sailIdRegexpPattern = "^([A-Z][A-Z][A-Z])\\s*[^0-9]*([0-9]*)$";
|
||||
|
||||
public static class SailNumberMatch {
|
||||
private final String iocCode;
|
||||
private final String number;
|
||||
public SailNumberMatch(String iocCode, String number) {
|
||||
super();
|
||||
this.iocCode = iocCode;
|
||||
this.number = number;
|
||||
}
|
||||
public String getIocCode() {
|
||||
return iocCode;
|
||||
}
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a sail number into a nationality code expected at the beginning of the string,
|
||||
* and a numeric part assumed to be at the end of the string. Before matching, the {@code sailId}
|
||||
* string will be {@link String#trim() trimmed}, removing leading and trailing whitespace. If
|
||||
* the parameter matches the {@link #sailIdRegexpPattern} then a valid {@link SailNumberMatch}
|
||||
* object is returned which may, however, deliver empty or {@code null} parts for the
|
||||
* {@link SailNumberMatch#getIocCode() IOC code} or the {@link SailNumberMatch#getNumber() number}.
|
||||
* If the string is not matched, {@code null} is returned.
|
||||
*/
|
||||
abstract protected SailNumberMatch match(String sailId);
|
||||
|
||||
/**
|
||||
* Try to match three-letter country code and number, optionally separated by whitespaces. If there is no match,
|
||||
* use the first 20 characters of the sailID.
|
||||
*/
|
||||
public String canonicalizeSailID(String sailID, String defaultNationality) {
|
||||
String result = null;
|
||||
SailNumberMatch m = match(sailID);
|
||||
if (m != null) {
|
||||
String iocCode = m.getIocCode();
|
||||
if (iocCode != null) {
|
||||
iocCode = iocCode.toUpperCase();
|
||||
}
|
||||
if (defaultNationality != null && (iocCode == null || iocCode.trim().length() == 0)) {
|
||||
iocCode = defaultNationality.toUpperCase();
|
||||
}
|
||||
if (iocCode != null && iocCode.trim().length() > 0) {
|
||||
String number = m.getNumber();
|
||||
result = iocCode + number;
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = sailID.substring(0, Math.min(20, sailID.length()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the sail IDs contained in {@code sailNumbers} to the {@code competitors} passed. The match making ignores
|
||||
* all whitespaces in the sail IDs on both sides (see {@link #canonicalizeSailID(String, String)}). If a
|
||||
* competitor's sail number does not start with a letter it is assumed the country code is missing. In this case,
|
||||
* {@link #getThreeLetterIocCountryCode(Object) it} is prepended before comparing to the sail ID from
|
||||
* {@code sailNumbers}. The sail ID number is extracted by trimming and using all trailing digits.
|
||||
* @return
|
||||
*
|
||||
* @return a map mapping the sailIDs as found in {@code sailNumbers} to the {@code competitors}; values may be
|
||||
* {@code null} if no matching competitor was found for the sail ID in the {@code competitors} collection
|
||||
*/
|
||||
public Map<String, CompetitorType> mapCompetitorsAndInitializeAllOfficialRaceIDs(final Iterable<CompetitorType> competitors, Iterable<String> sailNumbers) {
|
||||
final Map<String, CompetitorType> result = new HashMap<>();
|
||||
final Map<String, CompetitorType> canonicalizedSailIDToCompetitors = canonicalizeLeaderboardSailIDs(competitors);
|
||||
for (final String sailNumber : sailNumbers) {
|
||||
final String canonicalizedSailNumber = canonicalizeSailID(sailNumber, /* defaultNationality */ null);
|
||||
final CompetitorType competitor = canonicalizedSailIDToCompetitors.get(canonicalizedSailNumber);
|
||||
result.put(sailNumber, competitor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, CompetitorType> canonicalizeLeaderboardSailIDs(final Iterable<CompetitorType> competitors) {
|
||||
Map<String, CompetitorType> result = new HashMap<>();
|
||||
for (final CompetitorType competitor : competitors) {
|
||||
final String competitorIdentifyingText = getCompetitorIdentifyingText(competitor);
|
||||
String canonicalizedSailID = canonicalizeSailID(competitorIdentifyingText.trim(), getThreeLetterIocCountryCode(competitor).trim());
|
||||
if (canonicalizedSailID != null) {
|
||||
result.put(canonicalizedSailID, competitor);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
abstract protected String getThreeLetterIocCountryCode(CompetitorType competitor);
|
||||
|
||||
abstract protected String getCompetitorIdentifyingText(CompetitorType competitor);
|
||||
}
|
||||
+5
-2
@@ -1,14 +1,17 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
public class BranchIOConstants {
|
||||
public static final String SAILINSIGHT_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_APP_BRANCHIO = "https://sailinsight-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_3_APP_BRANCHIO = "https://sailinsight30-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
public static final String BUOYPINGER_APP_BRANCHIO = "https://buoypinger-app.sapsailing.com/invite";
|
||||
public static final String BUOYPINGER_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
public static final String OPEN_REGATTA_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/publicInvite";
|
||||
public static final String OPEN_REGATTA_2_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
public static final String OPEN_REGATTA_3_APP_BRANCHIO = "https://sailinsight30-app.sapsailing.com/publicInvite";
|
||||
public static final String OPEN_REGATTA_3_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
|
||||
private BranchIOConstants() {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+22
-1
@@ -100,6 +100,9 @@ public enum DetailType implements Serializable {
|
||||
OVERALL_TOTAL_DURATION_FOILED_IN_SECONDS(0, DESCENDING, "TOTAL_DURATION_FOILED_IN_SECONDS"),
|
||||
OVERALL_TOTAL_DISTANCE_FOILED_IN_METERS(0, DESCENDING, "TOTAL_DISTANCE_FOILED_IN_METERS"),
|
||||
RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING),
|
||||
RACE_CURRENT_COURSE_OVER_GROUND_IN_TRUE_DEGREES(1, ASCENDING),
|
||||
RACE_CURRENT_POSITION_LAT_DEG(10, ASCENDING),
|
||||
RACE_CURRENT_POSITION_LNG_DEG(10, ASCENDING),
|
||||
BRAVO_RACE_CURRENT_RIDE_HEIGHT_IN_METERS(2, DESCENDING),
|
||||
RACE_CURRENT_DISTANCE_FOILED_IN_METERS(0, DESCENDING),
|
||||
RACE_CURRENT_DURATION_FOILED_IN_SECONDS(0, DESCENDING),
|
||||
@@ -110,6 +113,7 @@ public enum DetailType implements Serializable {
|
||||
RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING),
|
||||
RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING),
|
||||
RACE_CALCULATED_TIME_TRAVELED(0, ASCENDING),
|
||||
RACE_IMPLIED_WIND(5, DESCENDING),
|
||||
RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD(0, ASCENDING),
|
||||
RACE_TIME_TRAVELED(0, ASCENDING),
|
||||
RACE_TIME_TRAVELED_UPWIND(0, ASCENDING),
|
||||
@@ -259,6 +263,7 @@ public enum DetailType implements Serializable {
|
||||
availableDetailsTypes.add(DetailType.LEG_GAP_TO_LEADER_IN_SECONDS);
|
||||
availableDetailsTypes.add(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
|
||||
availableDetailsTypes.add(DetailType.RACE_RANK);
|
||||
availableDetailsTypes.add(DetailType.RACE_IMPLIED_WIND);
|
||||
availableDetailsTypes.add(DetailType.REGATTA_RANK);
|
||||
availableDetailsTypes.add(DetailType.CHART_DISTANCE_TO_START_LINE);
|
||||
availableDetailsTypes.add(DetailType.CHART_BEAT_ANGLE);
|
||||
@@ -365,6 +370,7 @@ public enum DetailType implements Serializable {
|
||||
allowed.add(RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START);
|
||||
allowed.add(RACE_TIME_TRAVELED);
|
||||
allowed.add(RACE_CALCULATED_TIME_TRAVELED);
|
||||
allowed.add(RACE_IMPLIED_WIND);
|
||||
allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD);
|
||||
allowed.add(RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
|
||||
allowed.add(RACE_CURRENT_DISTANCE_FOILED_IN_METERS);
|
||||
@@ -406,6 +412,7 @@ public enum DetailType implements Serializable {
|
||||
allowed.add(OVERALL_TOTAL_AVERAGE_SPEED_OVER_GROUND);
|
||||
allowed.add(OVERALL_TOTAL_TIME_SAILED_IN_SECONDS);
|
||||
allowed.add(OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS);
|
||||
// TODO bug5209 make ToT / ToD details depend on ranking metric
|
||||
allowed.add(OVERALL_TIME_ON_TIME_FACTOR);
|
||||
allowed.add(OVERALL_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE);
|
||||
allowed.add(OVERALL_TOTAL_SCORED_RACE_COUNT);
|
||||
@@ -422,6 +429,21 @@ public enum DetailType implements Serializable {
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public static Collection<? extends DetailType> getAllToTToDHandicapDetailTypes() {
|
||||
final Collection<DetailType> allowed = new LinkedHashSet<>();
|
||||
allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD);
|
||||
allowed.add(RACE_CALCULATED_TIME_TRAVELED);
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public static Collection<? extends DetailType> getAllOrcPerformanceCurveDetailTypes() {
|
||||
final Collection<DetailType> allowed = new LinkedHashSet<>();
|
||||
allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD);
|
||||
allowed.add(RACE_CALCULATED_TIME_TRAVELED);
|
||||
allowed.add(RACE_IMPLIED_WIND);
|
||||
return allowed;
|
||||
}
|
||||
|
||||
public static Collection<DetailType> getLegDetailColumnTypes() {
|
||||
final Collection<DetailType> allowed = new LinkedHashSet<>();
|
||||
allowed.add(LEG_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS);
|
||||
@@ -571,5 +593,4 @@ public enum DetailType implements Serializable {
|
||||
}
|
||||
throw new IllegalArgumentException("Could not restore " + value + " to an DetailType enum");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-5
@@ -41,16 +41,15 @@ import java.io.Serializable;
|
||||
*/
|
||||
public interface DeviceIdentifier extends Serializable {
|
||||
/**
|
||||
* The returned {@link String} is used to look up corresponding services for serialization
|
||||
* and persistence.
|
||||
* The returned {@link String} is used to look up corresponding services for serialization and persistence.
|
||||
*
|
||||
* The reason for this design choice is that in future, third parties could easily write their own adapter and only
|
||||
* have to register new OSGi service, and not touch the SAP Sailing Analytics code.
|
||||
* The reason for this design choice is that in the future third parties could easily write their own adapter and
|
||||
* only have to register a new OSGi service, and not touch the SAP Sailing Analytics code.
|
||||
*/
|
||||
String getIdentifierType();
|
||||
|
||||
/**
|
||||
* Create a string representation, that can identify this device.
|
||||
* Create a string representation that can identify this device.
|
||||
* The returned values should be unique for this identifier within its {@link #getIdentifierType() type},
|
||||
* but need not include the {@link #getIdentifierType() type} itself in the representation.
|
||||
*/
|
||||
|
||||
+44
-3
@@ -1,9 +1,50 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
public enum MailInvitationType {
|
||||
LEGACY,
|
||||
SailInsight1,
|
||||
SailInsight2;
|
||||
// Legacy invitations do not need any Branch.io information because
|
||||
// links are constructed by different implementation.
|
||||
LEGACY(/* supportsOpenRegattas */ false,
|
||||
/* branchIOinviteURL */ null,
|
||||
/* branchIOcheckinPath */ null,
|
||||
/* branchIOopenRegattaURL */ null),
|
||||
SailInsight1(/* supportsOpenRegattas */ false,
|
||||
/* branchIOinviteURL */ BranchIOConstants.SAILINSIGHT_APP_BRANCHIO,
|
||||
/* branchIOcheckinPath */ BranchIOConstants.SAILINSIGHT_APP_BRANCHIO_PATH,
|
||||
/* branchIOopenRegattaURL */ null),
|
||||
SailInsight2(/* supportsOpenRegattas */ true,
|
||||
/* branchIOinviteURL */ BranchIOConstants.SAILINSIGHT_2_APP_BRANCHIO,
|
||||
/* branchIOcheckinPath */ BranchIOConstants.OPEN_REGATTA_2_APP_BRANCHIO_PATH,
|
||||
/* branchIOopenRegattaURL */ BranchIOConstants.OPEN_REGATTA_2_APP_BRANCHIO),
|
||||
SailInsight3(/* supportsOpenRegattas */ true,
|
||||
/* branchIOinviteURL */ BranchIOConstants.SAILINSIGHT_3_APP_BRANCHIO,
|
||||
/* branchIOcheckinPath */ BranchIOConstants.OPEN_REGATTA_3_APP_BRANCHIO_PATH,
|
||||
/* branchIOopenRegattaURL */ BranchIOConstants.OPEN_REGATTA_3_APP_BRANCHIO);
|
||||
|
||||
public static final String SYSTEM_PROPERTY_NAME = "com.sap.sailing.domain.tracking.MailInvitationType";
|
||||
|
||||
private MailInvitationType(boolean supportsOpenRegattas, String branchIOinviteURL, String branchIOcheckinPath,
|
||||
String branchIOopenRegattaURL) {
|
||||
this.supportsOpenRegattas = supportsOpenRegattas;
|
||||
this.branchIOinviteURL = branchIOinviteURL;
|
||||
this.branchIOcheckinPath = branchIOcheckinPath;
|
||||
this.branchIOopenRegattaURL = branchIOopenRegattaURL;
|
||||
}
|
||||
|
||||
public boolean isSupportsOpenRegattas() {
|
||||
return supportsOpenRegattas;
|
||||
}
|
||||
public String getBranchIOinviteURL() {
|
||||
return branchIOinviteURL;
|
||||
}
|
||||
public String getBranchIOcheckinPath() {
|
||||
return branchIOcheckinPath;
|
||||
}
|
||||
public String getBranchIOopenRegattaURL() {
|
||||
return branchIOopenRegattaURL;
|
||||
}
|
||||
|
||||
private final boolean supportsOpenRegattas;
|
||||
private final String branchIOinviteURL;
|
||||
private final String branchIOcheckinPath;
|
||||
private final String branchIOopenRegattaURL;
|
||||
}
|
||||
+2
-2
@@ -63,7 +63,7 @@ public class RegattaNameAndRaceName extends RegattaName implements RegattaAndRac
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,7 +76,7 @@ public class RegattaNameAndRaceName extends RegattaName implements RegattaAndRac
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.TRACKED_RACE;
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -15,6 +15,10 @@ public interface RegattaScoreCorrections {
|
||||
public interface ScoreCorrectionsForRace {
|
||||
String getRaceNameOrNumber();
|
||||
|
||||
/**
|
||||
* Matched against the sail ID/number if the competitor is assigned a boat throughout the leaderboard,
|
||||
* or against the competitor's short name otherwise.
|
||||
*/
|
||||
Set<String> getSailIDs();
|
||||
|
||||
ScoreCorrectionForCompetitorInRace getScoreCorrectionForCompetitor(String sailID);
|
||||
|
||||
+8
-2
@@ -1,5 +1,6 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -22,7 +23,7 @@ public interface ScoreCorrectionProvider extends Named {
|
||||
* score corrections taken at different times. Later score corrections are expected to be cumulative, meaning they
|
||||
* also contain all previous corrections.
|
||||
*/
|
||||
public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() throws Exception;
|
||||
Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() throws Exception;
|
||||
|
||||
/**
|
||||
* @param eventName
|
||||
@@ -35,5 +36,10 @@ public interface ScoreCorrectionProvider extends Named {
|
||||
* a time point as returned in the {@link Pair#getB()} component of the values in the map returned by
|
||||
* {@link #getHasResultsForBoatClassFromDateByEventName()}.
|
||||
*/
|
||||
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName, TimePoint timePoint) throws Exception;
|
||||
RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName, TimePoint timePoint) throws Exception;
|
||||
|
||||
/**
|
||||
* Produces a single {@link RegattaScoreCorrections} object from a single {@link InputStream}.
|
||||
*/
|
||||
RegattaScoreCorrections getScoreCorrections(InputStream inputStream) throws Exception;
|
||||
}
|
||||
|
||||
+2
-2
@@ -122,13 +122,13 @@ public class BoatDTO extends NamedSecuredObjectDTO implements WithID, Serializab
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.BOAT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
|
||||
+6
@@ -73,4 +73,10 @@ public interface CompetitorDTO extends Named, WithID, SecuredDTO, Serializable,
|
||||
* from the previous version of the enclosing leaderboard.
|
||||
*/
|
||||
CompetitorDTO getCompetitorFromPrevious(LeaderboardDTO previousVersion);
|
||||
|
||||
/**
|
||||
* Erases the fields a user is not supposed to read with only the READ_PUBLIC and not the
|
||||
* READ permission.
|
||||
*/
|
||||
void clearNonPublicFields();
|
||||
}
|
||||
|
||||
+7
-2
@@ -247,17 +247,22 @@ public class CompetitorDTOImpl extends NamedSecuredObjectDTO implements Competit
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.COMPETITOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
return new TypeRelativeObjectIdentifier(idAsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearNonPublicFields() {
|
||||
email = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Speed;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
|
||||
/**
|
||||
@@ -121,6 +122,19 @@ public class LeaderboardEntryDTO implements Serializable {
|
||||
*/
|
||||
public Duration calculatedTime;
|
||||
|
||||
/**
|
||||
* If this is a leaderboard entry for an ORC Performance Curve Scoring (PCS) race,
|
||||
* the field holds the so-called "implied wind" as a speed. It is the wind speed with which,
|
||||
* according to its polar, the competitor would have sailed the part of the course sailed
|
||||
* so far in the time elapsed so far. For ORC PCS before 2015 this was the primary ranking
|
||||
* criterion. Since 2015, however, implied wind has lost in significance because it is
|
||||
* used only to compute a default wind speed by maximizing all implied wind values of all
|
||||
* competitors in the race, and then using this wind speed to determine the time allowance
|
||||
* for each competitor so that a different between time elapsed and the allowance can then
|
||||
* be used for ranking.
|
||||
*/
|
||||
public Speed impliedWind;
|
||||
|
||||
/**
|
||||
* The corrections applied to the time and distance sailed when the competitor would have reached the
|
||||
* competitor farthest ahead, based on average VMG on the current leg and equal performance to the boat
|
||||
|
||||
+8
-2
@@ -150,6 +150,12 @@ public class PreviousCompetitorDTOImpl extends NamedSecuredObjectDTO implements
|
||||
" need to be replaced by an object of "+CompetitorWithBoatDTOImpl.class.getName()+" after deserialization");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearNonPublicFields() {
|
||||
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
|
||||
" need to be replaced by an object of "+CompetitorWithBoatDTOImpl.class.getName()+" after deserialization");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getTimeOnTimeFactor() {
|
||||
throw new RuntimeException("Internal error. Objects of type "+PreviousCompetitorDTOImpl.class.getName()+
|
||||
@@ -174,13 +180,13 @@ public class PreviousCompetitorDTOImpl extends NamedSecuredObjectDTO implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.COMPETITOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
|
||||
+2
-2
@@ -129,13 +129,13 @@ public class RaceDTO extends BasicRaceDTO implements SecuredDTO {
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.TRACKED_RACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
public TypeRelativeObjectIdentifier getTypeRelativeObjectIdentifier() {
|
||||
|
||||
+2
-2
@@ -171,11 +171,11 @@ public class MediaTrack implements Serializable, WithQualifiedObjectIdentifier {
|
||||
|
||||
@Override
|
||||
public QualifiedObjectIdentifier getIdentifier() {
|
||||
return getType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
return getPermissionType().getQualifiedObjectIdentifier(getTypeRelativeObjectIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasPermissions getType() {
|
||||
public HasPermissions getPermissionType() {
|
||||
return SecuredDomainType.MEDIA_TRACK;
|
||||
}
|
||||
|
||||
|
||||
+55
-6
@@ -3,33 +3,67 @@ package com.sap.sailing.domain.common.orc;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sse.common.Bearing;
|
||||
import com.sap.sse.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Speed;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.WithID;
|
||||
import com.sap.sse.common.impl.DegreeBearingImpl;
|
||||
|
||||
/**
|
||||
* Represents semantically a real ORC certificate for a {@link Competitor}, which is used to rate different type of
|
||||
* boats for different insohre and offshore race conditions.
|
||||
* boats for different inshore and offshore race conditions.
|
||||
* <p>
|
||||
* An ORC certificate is issued by the "Member National Authorities" of World Sailing and are available for insight at
|
||||
* https://www.orc.org/index.asp . Other information about the whole scoring system and different variants are available
|
||||
* too.
|
||||
* <p>
|
||||
* One implementing class provides all necessary functionalities to score the Competitors with a choosen
|
||||
* {@link RankingMetric}.<p>
|
||||
* {@link RankingMetric}.
|
||||
* <p>
|
||||
*
|
||||
* The {@link WithID} interface is to be implemented such that a {@link String} is produced as the ID that
|
||||
* contains the concatenation (without intermediate white space) of the {@code NatAuth}, the {@code CertNo}
|
||||
* and the {@code BIN} fields, as provided as single fields in the JSON representation, and as provided
|
||||
* in the concatenated form in the {@code NATCERTN.FILE_ID} column in the RMS format.
|
||||
* The {@link WithID} interface is to be implemented such that a {@link String} is produced as the ID that contains the
|
||||
* concatenation (without intermediate white space) of the {@code NatAuth}, the {@code CertNo} and the {@code BIN}
|
||||
* fields, as provided as single fields in the JSON representation, and as provided in the concatenated form in the
|
||||
* {@code NATCERTN.FILE_ID} column in the RMS format.
|
||||
* <p>
|
||||
*
|
||||
* The certificate contains time allowances (or, conversely, speed predictions) for a combination of true wind speeds
|
||||
* (TWS) and true wind angles (TWA). There are defaults for the values used for the true wind speeds and the true wind
|
||||
* angles, forming a regular, fully populated matrix of allowances / speed predictions. These defaults can be found in
|
||||
* {@link #ALLOWANCES_TRUE_WIND_SPEEDS} and {@link #ALLOWANCES_TRUE_WIND_SPEEDS}.
|
||||
* <p>
|
||||
*
|
||||
* Certificates may also specify non-default values for the slots or "bins" of the matrix that describes the allowances
|
||||
* / speed predictions. As for the default values, the allowances matrix has to be fully populated. The values in the
|
||||
* TWS and TWA arrays are expected to be increasing monotonously.
|
||||
*
|
||||
* @author Daniel Lisunkin (i505543)
|
||||
*
|
||||
*/
|
||||
public interface ORCCertificate extends WithID, Serializable {
|
||||
/**
|
||||
* Equals the column heading of the allowances table of an ORC certificate. The speeds are set by the offshore
|
||||
* racing congress. The speeds occur in the array in ascending order.
|
||||
*
|
||||
* There are references in the persistance module. If the values change, there will be an adjustment needed
|
||||
* in {@link MongoObjectFactoryImpl.speedToKnotsString}.
|
||||
*/
|
||||
Speed[] ALLOWANCES_TRUE_WIND_SPEEDS = { new KnotSpeedImpl(6), new KnotSpeedImpl(8),
|
||||
new KnotSpeedImpl(10), new KnotSpeedImpl(12), new KnotSpeedImpl(14), new KnotSpeedImpl(16),
|
||||
new KnotSpeedImpl(20) };
|
||||
/**
|
||||
* Equals the line heading of the allowances table of an ORC certificate. The true wind angles are set by the
|
||||
* offshore racing congress. The angles occur in the array in ascending order.
|
||||
*
|
||||
* There are references in the persistance module. If the values change, there will be an adjustment needed
|
||||
* in {@link MongoObjectFactoryImpl.bearingToDegreeString}.
|
||||
*/
|
||||
Bearing[] ALLOWANCES_TRUE_WIND_ANGLES = { new DegreeBearingImpl(52), new DegreeBearingImpl(60),
|
||||
new DegreeBearingImpl(75), new DegreeBearingImpl(90), new DegreeBearingImpl(110),
|
||||
new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) };
|
||||
/**
|
||||
* As the ID of an ORC certificate we use the concatenation (without intermediate white space) of the
|
||||
* {@code NatAuth}, the {@code CertNo} and the {@code BIN} fields, as provided as single fields in the JSON
|
||||
@@ -128,4 +162,19 @@ public interface ORCCertificate extends WithID, Serializable {
|
||||
Map<Speed, Map<Bearing, Speed>> getVelocityPredictionPerTrueWindSpeedAndAngle();
|
||||
|
||||
String getBoatName();
|
||||
|
||||
/**
|
||||
* @return the true wind speeds used for the matrix returned by
|
||||
* {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. The {@link Speed} values returned by this
|
||||
* method form the key set of the map returned by {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}.
|
||||
*/
|
||||
Speed[] getTrueWindSpeeds();
|
||||
|
||||
/**
|
||||
* @return the true wind angles used for the matrix returned by
|
||||
* {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}. The {@link Bearing} values returned by this
|
||||
* method form the key set of all the maps returned as values in the map returned by
|
||||
* {@link #getVelocityPredictionPerTrueWindSpeedAndAngle()}.
|
||||
*/
|
||||
Bearing[] getTrueWindAngles();
|
||||
}
|
||||
+69
-34
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.common.orc.impl;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
|
||||
import com.sap.sailing.domain.common.orc.ORCCertificate;
|
||||
import com.sap.sse.common.Bearing;
|
||||
@@ -12,7 +11,6 @@ import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Speed;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.DegreeBearingImpl;
|
||||
|
||||
/**
|
||||
* Holds an ORC boat certificate including various of the metrics found in it. Note that multiple different certificates
|
||||
@@ -28,28 +26,6 @@ public class ORCCertificateImpl implements ORCCertificate {
|
||||
|
||||
private final String idConsistingOfNatAuthCertNoAndBIN;
|
||||
|
||||
/**
|
||||
* Equals the column heading of the allowances table of an ORC certificate. The speeds are set by the offshore
|
||||
* racing congress. The speeds occur in the array in ascending order.
|
||||
*
|
||||
* There are references in the persistance module. If the values change, there will be an adjustment needed
|
||||
* in {@link MongoObjectFactoryImpl.speedToKnotsString}.
|
||||
*/
|
||||
public static final Speed[] ALLOWANCES_TRUE_WIND_SPEEDS = { new KnotSpeedImpl(6), new KnotSpeedImpl(8),
|
||||
new KnotSpeedImpl(10), new KnotSpeedImpl(12), new KnotSpeedImpl(14), new KnotSpeedImpl(16),
|
||||
new KnotSpeedImpl(20) };
|
||||
|
||||
/**
|
||||
* Equals the line heading of the allowances table of an ORC certificate. The true wind angles are set by the
|
||||
* offshore racing congress. The angles occur in the array in ascending order.
|
||||
*
|
||||
* There are references in the persistance module. If the values change, there will be an adjustment needed
|
||||
* in {@link MongoObjectFactoryImpl.bearingToDegreeString}.
|
||||
*/
|
||||
public static final Bearing[] ALLOWANCES_TRUE_WIND_ANGLES = { new DegreeBearingImpl(52), new DegreeBearingImpl(60),
|
||||
new DegreeBearingImpl(75), new DegreeBearingImpl(90), new DegreeBearingImpl(110),
|
||||
new DegreeBearingImpl(120), new DegreeBearingImpl(135), new DegreeBearingImpl(150) };
|
||||
|
||||
public final static Distance NAUTICAL_MILE = new NauticalMileDistance(1);
|
||||
|
||||
private final String sailNumber;
|
||||
@@ -60,7 +36,16 @@ public class ORCCertificateImpl implements ORCCertificate {
|
||||
private final Double cdl;
|
||||
private final TimePoint issueDate;
|
||||
|
||||
// TODO add meaningful Javadoc
|
||||
/**
|
||||
* The "core" of the certificate for performance curve scoring; the values in the map tell how fast the boat
|
||||
* described by this certificate is expected to sail for the true wind speed (TWS) provided by the key, and the true
|
||||
* wind angle (TWA) provided by the value map's key. This is the reciprocal of the duration it takes the boat to
|
||||
* sail a certain distance.
|
||||
* <p>
|
||||
*
|
||||
* The keys are the speeds as provided by {@link #allowancesTrueWindSpeeds}; the keys of the value maps are the
|
||||
* angles as provided by {@link #allowancesTrueWindAngles}.
|
||||
*/
|
||||
private final Map<Speed, Map<Bearing, Speed>> velocityPredictionPerTrueWindSpeedAndAngle;
|
||||
|
||||
/**
|
||||
@@ -110,20 +95,34 @@ public class ORCCertificateImpl implements ORCCertificate {
|
||||
private final Map<Speed, Speed> circularRandomSpeedPredictionPerTrueWindSpeed;
|
||||
|
||||
private final Map<Speed, Speed> nonSpinnakerSpeedPredictionPerTrueWindSpeed;
|
||||
|
||||
public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN,
|
||||
String sailnumber, String boatName, String boatClassName,
|
||||
Distance length, Duration gph,
|
||||
Double cdl, TimePoint issueDate,
|
||||
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle,
|
||||
Map<Speed, Bearing> beatAngles, Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed,
|
||||
Map<Speed, Duration> beatAllowancePerTrueWindSpeed, Map<Speed, Bearing> runAngles,
|
||||
Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
|
||||
private final Speed[] allowancesTrueWindSpeeds;
|
||||
private final Bearing[] allowancesTrueWindAngles;
|
||||
|
||||
/**
|
||||
* Can be used to specify non-default TWS/TWA values for the time allowances / speed predictions.
|
||||
*
|
||||
* @param allowancesTrueWindSpeeds
|
||||
* a monotonously-increasing sequence of true wind speed (TWS) values for the matrix of time allowances /
|
||||
* velocity predictions for this certificate. See also
|
||||
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_SPEEDS}.
|
||||
* @param allowancesTrueWindAngles
|
||||
* a monotonously-increasing sequence of true wind angle (TWA) values for the matrix of time allowances /
|
||||
* velocity predictions for this certificate. See also
|
||||
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}.
|
||||
*/
|
||||
public ORCCertificateImpl(Speed[] allowancesTrueWindSpeeds, Bearing[] allowancesTrueWindAngles,
|
||||
String idConsistingOfNatAuthCertNoAndBIN, String sailnumber, String boatName, String boatClassName,
|
||||
Distance length, Duration gph, Double cdl, TimePoint issueDate,
|
||||
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle, Map<Speed, Bearing> beatAngles,
|
||||
Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed, Map<Speed, Duration> beatAllowancePerTrueWindSpeed,
|
||||
Map<Speed, Bearing> runAngles, Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
|
||||
Map<Speed, Duration> runAllowancePerTrueWindSpeed,
|
||||
Map<Speed, Speed> windwardLeewardSpeedPredictionsPerTrueWindSpeed,
|
||||
Map<Speed, Speed> longDistanceSpeedPredictionsPerTrueWindSpeed,
|
||||
Map<Speed, Speed> circularRandomSpeedPredictionsPerTrueWindSpeed,
|
||||
Map<Speed, Speed> nonSpinnakerSpeedPredictionsPerTrueWindSpeed) {
|
||||
this.allowancesTrueWindAngles = allowancesTrueWindAngles;
|
||||
this.allowancesTrueWindSpeeds = allowancesTrueWindSpeeds;
|
||||
this.idConsistingOfNatAuthCertNoAndBIN = idConsistingOfNatAuthCertNoAndBIN;
|
||||
this.sailNumber = sailnumber;
|
||||
this.boatName = boatName;
|
||||
@@ -145,6 +144,32 @@ public class ORCCertificateImpl implements ORCCertificate {
|
||||
this.circularRandomSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(circularRandomSpeedPredictionsPerTrueWindSpeed);
|
||||
this.nonSpinnakerSpeedPredictionPerTrueWindSpeed = Collections.unmodifiableMap(nonSpinnakerSpeedPredictionsPerTrueWindSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the default TWS/TWA values from {@link ORCCertificate#ALLOWANCES_TRUE_WIND_SPEEDS} and
|
||||
* {@link ORCCertificate#ALLOWANCES_TRUE_WIND_ANGLES}, respectively, for the matrix of time allowances
|
||||
* or, conversely, the speed predictions.
|
||||
*/
|
||||
public ORCCertificateImpl(String idConsistingOfNatAuthCertNoAndBIN,
|
||||
String sailnumber, String boatName, String boatClassName,
|
||||
Distance length, Duration gph,
|
||||
Double cdl, TimePoint issueDate,
|
||||
Map<Speed, Map<Bearing, Speed>> velocityPredictionsPerTrueWindSpeedAndAngle,
|
||||
Map<Speed, Bearing> beatAngles, Map<Speed, Speed> beatVMGPredictionPerTrueWindSpeed,
|
||||
Map<Speed, Duration> beatAllowancePerTrueWindSpeed, Map<Speed, Bearing> runAngles,
|
||||
Map<Speed, Speed> runVMGPredictionPerTrueWindSpeed,
|
||||
Map<Speed, Duration> runAllowancePerTrueWindSpeed,
|
||||
Map<Speed, Speed> windwardLeewardSpeedPredictionsPerTrueWindSpeed,
|
||||
Map<Speed, Speed> longDistanceSpeedPredictionsPerTrueWindSpeed,
|
||||
Map<Speed, Speed> circularRandomSpeedPredictionsPerTrueWindSpeed,
|
||||
Map<Speed, Speed> nonSpinnakerSpeedPredictionsPerTrueWindSpeed) {
|
||||
this(ALLOWANCES_TRUE_WIND_SPEEDS, ALLOWANCES_TRUE_WIND_ANGLES, idConsistingOfNatAuthCertNoAndBIN, sailnumber,
|
||||
boatName, boatClassName, length, gph, cdl, issueDate, velocityPredictionsPerTrueWindSpeedAndAngle,
|
||||
beatAngles, beatVMGPredictionPerTrueWindSpeed, beatAllowancePerTrueWindSpeed, runAngles,
|
||||
runVMGPredictionPerTrueWindSpeed, runAllowancePerTrueWindSpeed,
|
||||
windwardLeewardSpeedPredictionsPerTrueWindSpeed, longDistanceSpeedPredictionsPerTrueWindSpeed,
|
||||
circularRandomSpeedPredictionsPerTrueWindSpeed, nonSpinnakerSpeedPredictionsPerTrueWindSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
@@ -255,4 +280,14 @@ public class ORCCertificateImpl implements ORCCertificate {
|
||||
return "Certificate with ID \""+getId()+"\" for "+getSailNumber()+" / "+getBoatName() + " - Issued on: " + (getIssueDate()==null?"n/a":getIssueDate().asDate())+" with GPH "+
|
||||
Util.padPositiveValue(getGPHInSecondsToTheMile(), 1, 1, /* round */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed[] getTrueWindSpeeds() {
|
||||
return allowancesTrueWindSpeeds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bearing[] getTrueWindAngles() {
|
||||
return allowancesTrueWindAngles;
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -86,4 +86,8 @@ public class SecuredDomainType extends HasPermissionsImpl {
|
||||
"SWISS_TIMING_ARCHIVE_ACCOUNT");
|
||||
public static final HasPermissions TRACTRAC_ACCOUNT = new SecuredDomainType("TRACTRAC_ACCOUNT");
|
||||
public static final HasPermissions WIND_ESTIMATION_MODELS = new SecuredDomainType("WIND_ESTIMATION_MODELS");
|
||||
public static final HasPermissions MARK_PROPERTIES = new SecuredDomainType("MARK_PROPERTIES");
|
||||
public static final HasPermissions MARK_TEMPLATE = new SecuredDomainType("MARK_TEMPLATE");
|
||||
public static final HasPermissions COURSE_TEMPLATE = new SecuredDomainType("COURSE_TEMPLATE");
|
||||
public static final HasPermissions MARK_ROLE = new SecuredDomainType("MARK_ROLE");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user