mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-16 10:48:47 +00:00
bug6133: merge main
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ FROM eclipse-temurin:8-jdk
|
||||
# the configuration/github-download-release-assets.sh script to
|
||||
# obtain the tar.gz file for a specific or the latest "main" release.
|
||||
# To build with an SAP JVM 8 base image built using Dockerfile_sapjvm, use something like:
|
||||
#FROM sapjvm8:8.1.108
|
||||
#FROM sapjvm8:8.1.109
|
||||
ARG RELEASE
|
||||
LABEL maintainer=axel.uhl@sap.com
|
||||
LABEL org.opencontainers.image.description="Sailing Analytics with Java 8"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
FROM buildpack-deps:bullseye
|
||||
ARG SAPJVM_VERSION=8.1.108
|
||||
ARG SAPJVM_VERSION=8.1.109
|
||||
LABEL maintainer=axel.uhl@sap.com
|
||||
# Download and extract the SAP JVM 8
|
||||
ENV PATH=${PATH}:/opt/sapjvm_8/bin
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM sapmachine:25.0.1
|
||||
FROM sapmachine:25.0.3
|
||||
# This Dockerfile assumes that the release to use is provided as
|
||||
# ${RELEASE}.tar.gz in the current Docker workspace. Use, e.g.,
|
||||
# the configuration/github-download-release-assets.sh script to
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM sapmachine:25.0.1
|
||||
FROM sapmachine:25.0.3
|
||||
LABEL maintainer=axel.uhl@sap.com
|
||||
# Download and extract the release
|
||||
WORKDIR /home/sailing
|
||||
|
||||
+4
-2
@@ -15,6 +15,7 @@ import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
|
||||
import com.sap.sailing.domain.common.orc.ORCCertificate;
|
||||
import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase.CertificateFamily;
|
||||
import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase.CertificateHandle;
|
||||
import com.sap.sailing.domain.orc.ORCPublicCertificateDatabase.CountryOverview;
|
||||
import com.sap.sailing.domain.orc.impl.ORCPublicCertificateDatabaseImpl;
|
||||
@@ -52,8 +53,9 @@ public class FailIfNoValidOrcCertificateRule implements BeforeTestExecutionCallb
|
||||
countryWithMostValidCertificates = StreamSupport
|
||||
.stream(db.getCountriesWithValidCertificates().spliterator(), /* parallel */ false)
|
||||
.max((c1, c2) -> c1.getCertCount() - c2.getCertCount()).get();
|
||||
Iterable<CertificateHandle> certificateHandles = db.search(countryWithMostValidCertificates.getIssuingCountry(),
|
||||
countryWithMostValidCertificates.getVPPYear(), null, null, null, null, /* includeInvalid */ false);
|
||||
Iterable<CertificateHandle> certificateHandles = Util.filter(db.search(countryWithMostValidCertificates.getIssuingCountry(),
|
||||
countryWithMostValidCertificates.getVPPYear(), null, null, null, null, /* includeInvalid */ false),
|
||||
certHandle->certHandle.getFamily() != CertificateFamily.ORC_LIGHT); // exclude LITE certificates
|
||||
final List<CertificateHandle> randomSubset = new ArrayList<>();
|
||||
Util.addAll(certificateHandles, randomSubset);
|
||||
Collections.shuffle(randomSubset);
|
||||
|
||||
+18
-4
@@ -2,9 +2,11 @@ package com.sap.sailing.domain.orc;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -13,6 +15,8 @@ import com.sap.sailing.domain.common.orc.ORCCertificate;
|
||||
|
||||
|
||||
public class TestORCCertificateImporterJSON extends AbstractORCCertificateImporterTest {
|
||||
private static final Logger logger = Logger.getLogger(TestORCCertificateImporterJSON.class.getName());
|
||||
|
||||
@Test
|
||||
public void testSimpleLocalJSONFileRead() throws IOException, ParseException {
|
||||
testSimpleLocalFileRead("GER2019.json", "GER20041179");
|
||||
@@ -27,9 +31,19 @@ public class TestORCCertificateImporterJSON extends AbstractORCCertificateImport
|
||||
@Test
|
||||
public void testSimpleOnlineFileRead() throws IOException, ParseException, InterruptedException {
|
||||
Collection<ORCCertificate> certificates = FailIfNoValidOrcCertificateRule.getAvailableCerts();
|
||||
final ORCCertificate referenceCert = certificates.stream().findFirst().get();
|
||||
assertNotNull(referenceCert);
|
||||
assertTrue(referenceCert.getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10);
|
||||
assertTrue(referenceCert.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10);
|
||||
for (final ORCCertificate referenceCert : certificates) {
|
||||
assertNotNull(referenceCert);
|
||||
// some certificates are not fully filled with allowances for all types of PCS pre-sets; we need to check whether
|
||||
// the certificate at hand has those we need for this test; else, we keep going and use another one.
|
||||
// We're already excluding LITE certificates to reduce chances for this case
|
||||
if (referenceCert.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]) != null) {
|
||||
assertTrue(referenceCert.getWindwardLeewardSpeedPrediction().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10);
|
||||
assertTrue(referenceCert.getLongDistanceSpeedPredictions().get(ORCCertificate.ALLOWANCES_TRUE_WIND_SPEEDS[0]).getDuration(ORCCertificate.NAUTICAL_MILE).asSeconds() > 10);
|
||||
return;
|
||||
} else {
|
||||
logger.info("No valid GPH found in certificate "+referenceCert.getBoatName()+" with sail number "+referenceCert.getSailNumber());
|
||||
}
|
||||
}
|
||||
fail("Found only certificates with no valid long distance speed predictions; this seems unlikely and is probably an error");
|
||||
}
|
||||
}
|
||||
|
||||
+53
-44
@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
@@ -153,51 +154,59 @@ public class TestORCPublicCertificateDatabase {
|
||||
@Test
|
||||
public void testGetCertificate() throws Exception {
|
||||
Collection<ORCCertificate> certificates = FailIfNoValidOrcCertificateRule.getAvailableCerts();
|
||||
final ORCCertificate cert = certificates.stream().findFirst().get();
|
||||
Iterable<CertificateHandle> certHandles = db.search(/* country */ null, LocalDate.now().getYear(), /* referenceNumber */ null, cert.getBoatName(),
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
if (Util.isEmpty(certHandles)) {
|
||||
// there were certs; get one from the previous year
|
||||
certHandles = db.search(null, LocalDate.now().getYear()-1, null, cert.getBoatName(),
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
// some certificates seem to be lacking a GPH value; can't use those for this test; we're explicitly excluding LITE certificates already...
|
||||
for (final ORCCertificate cert : certificates) {
|
||||
if (cert.getGPH() != null) {
|
||||
Iterable<CertificateHandle> certHandles = db.search(/* country */ null, LocalDate.now().getYear(), /* referenceNumber */ null, cert.getBoatName(),
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
if (Util.isEmpty(certHandles)) {
|
||||
// there were certs; get one from the previous year
|
||||
certHandles = db.search(null, LocalDate.now().getYear()-1, null, cert.getBoatName(),
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
}
|
||||
if (Util.isEmpty(certHandles)) {
|
||||
// there were certs; try searching by reference number
|
||||
certHandles = db.search(null, LocalDate.now().getYear(), cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null,
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
}
|
||||
if (Util.isEmpty(certHandles)) {
|
||||
// still nothing? Then try by reference number in previous year:
|
||||
certHandles = db.search(null, LocalDate.now().getYear()-1, cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null,
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
}
|
||||
Optional<CertificateHandle> certificateHandle = Optional.ofNullable(certHandles.iterator().hasNext() ? certHandles.iterator().next() : null);
|
||||
assertTrue(certificateHandle.isPresent(), "No certificate found for handle "+certificateHandle+
|
||||
" extracted from certificates "+certificates);
|
||||
final String referenceNumber = certificateHandle.get().getReferenceNumber();
|
||||
final CertificateHandle handle = db.getCertificateHandle(referenceNumber);
|
||||
final ORCCertificate result = db.getCertificate(referenceNumber, handle.getFamily());
|
||||
assertNotNull(result, "Unable to load certificate for reference number "+referenceNumber+" from handle "+certificateHandle);
|
||||
assertEquals(handle.getGPH(), result.getGPH().asSeconds(), 0.00001);
|
||||
// Use some tolerance as we found differences as much as 5s between the dxtDate in the handle coming from the XML search result
|
||||
// and the IssueDate field in the JSON. Both suggest to report millisecond accuracy, but dxtDate always seems to have the
|
||||
// milliseconds as "000" explaining many sub-second differences. But in some cases differences were significantly bigger.
|
||||
assertEquals(handle.getIssueDate().asMillis(), result.getIssueDate().asMillis(), 10000.0, "Issue dates of certificate with reference number "+referenceNumber+
|
||||
" varies between current year result handle ("+handle.getIssueDate()+") and certificate ("+
|
||||
result.getIssueDate()+").");
|
||||
assertEquals(handle.getSailNumber(), result.getSailNumber());
|
||||
return;
|
||||
} else {
|
||||
logger.info("No valid GPH found in certificate "+cert.getBoatName()+" with sail number "+cert.getSailNumber());
|
||||
}
|
||||
}
|
||||
if (Util.isEmpty(certHandles)) {
|
||||
// there were certs; try searching by reference number
|
||||
certHandles = db.search(null, LocalDate.now().getYear(), cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null,
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
}
|
||||
if (Util.isEmpty(certHandles)) {
|
||||
// still nothing? Then try by reference number in previous year:
|
||||
certHandles = db.search(null, LocalDate.now().getYear()-1, cert.getReferenceNumber(), /* boat name may have deviated due to special characters */ null,
|
||||
cert.getSailNumber(), /*
|
||||
* boat class name; could be set to cert.getBoatClassName() but there are
|
||||
* deviations in ORC DBs and query API, so leaving null:
|
||||
*/ null, /* includeInvalid */ false);
|
||||
}
|
||||
Optional<CertificateHandle> certificateHandle = Optional.ofNullable(certHandles.iterator().hasNext() ? certHandles.iterator().next() : null);
|
||||
assertTrue(certificateHandle.isPresent(), "No certificate found for handle "+certificateHandle+
|
||||
" extracted from certificates "+certificates);
|
||||
final String referenceNumber = certificateHandle.get().getReferenceNumber();
|
||||
final CertificateHandle handle = db.getCertificateHandle(referenceNumber);
|
||||
final ORCCertificate result = db.getCertificate(referenceNumber, handle.getFamily());
|
||||
assertNotNull(result, "Unable to load certificate for reference number "+referenceNumber+" from handle "+certificateHandle);
|
||||
assertEquals(handle.getGPH(), result.getGPH().asSeconds(), 0.00001);
|
||||
// Use some tolerance as we found differences as much as 5s between the dxtDate in the handle coming from the XML search result
|
||||
// and the IssueDate field in the JSON. Both suggest to report millisecond accuracy, but dxtDate always seems to have the
|
||||
// milliseconds as "000" explaining many sub-second differences. But in some cases differences were significantly bigger.
|
||||
assertEquals(handle.getIssueDate().asMillis(), result.getIssueDate().asMillis(), 10000.0, "Issue dates of certificate with reference number "+referenceNumber+
|
||||
" varies between current year result handle ("+handle.getIssueDate()+") and certificate ("+
|
||||
result.getIssueDate()+").");
|
||||
assertEquals(handle.getSailNumber(), result.getSailNumber());
|
||||
fail("No certificate found with a valid GPH; that seems very suspicious and lets this test case fail.");
|
||||
}
|
||||
|
||||
@FailIfNoValidOrcCertificates
|
||||
|
||||
+2
-2
@@ -72,8 +72,8 @@ public class SmartFutureCacheTest {
|
||||
sfc.triggerUpdate("humba", /* update interval */ null);
|
||||
try {
|
||||
// during the first call, expecting exception
|
||||
sfc.get("humba", /* waitForLatest */ true);
|
||||
fail("Expected RuntimeException because computeCacheUpdate threw one");
|
||||
final String result = sfc.get("humba", /* waitForLatest */ true);
|
||||
fail("Expected RuntimeException because computeCacheUpdate threw one; instead, it returned "+result);
|
||||
} catch (RuntimeException expected) {
|
||||
assertSame(ExecutionException.class, expected.getCause().getClass());
|
||||
}
|
||||
|
||||
+84
-3
@@ -105,7 +105,7 @@ public interface ORCPublicCertificateDatabase {
|
||||
ORCPublicCertificateDatabase INSTANCE = new ORCPublicCertificateDatabaseImpl();
|
||||
|
||||
public enum CertificateFamily {
|
||||
UNKNOWN(0, ""), ORC(1, "ORC"), SUPER_YACHT(2, "SY"), DOUBLE_HANDED(3, "DH"), MULTI_HULL(4, "Mu");
|
||||
UNKNOWN(0, ""), ORC(1, "ORC"), SUPER_YACHT(2, "SY"), DOUBLE_HANDED(3, "DH"), MULTI_HULL(4, "Mu"), NON_SPINNAKER(5, "NS"), ORC_LIGHT(6, "LITE");
|
||||
|
||||
private final int familyId;
|
||||
private final String familyQueryParamValue;
|
||||
@@ -137,8 +137,8 @@ public interface ORCPublicCertificateDatabase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Data about valid certificates in a country, as obtained, e.g., from http://data.orc.org/public/WPub.dll/RMS. Such a
|
||||
* record, in its original XML representation, looks like this:
|
||||
* Data about valid certificates in a country, as obtained, e.g., from http://data.orc.org/public/WPub.dll/RMS. Such
|
||||
* a record, in its original XML representation, looks like this:
|
||||
*
|
||||
* <pre>
|
||||
<CountryId>AUS</CountryId>
|
||||
@@ -152,6 +152,87 @@ public interface ORCPublicCertificateDatabase {
|
||||
<RMSCode>CLUB</RMSCode>
|
||||
* </pre>
|
||||
*
|
||||
* Mappings of family and type work as follows:
|
||||
*
|
||||
* Used for parsing the ORC public API (WPub.dll) RMS/JSON outputs. Note: Family 6 (ORC Light) certificates
|
||||
* generally lack GPH and Performance Curve data required for PCS calculations.
|
||||
*
|
||||
* <table border="1">
|
||||
* <caption>ORC Family and Certificate Type Codes</caption>
|
||||
* <tr>
|
||||
* <th>Family ID</th>
|
||||
* <th>Family Name</th>
|
||||
* <th>certType</th>
|
||||
* <th>Certificate Name</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>1</td>
|
||||
* <td>ORC Standard</td>
|
||||
* <td>2</td>
|
||||
* <td>International (ORCi)</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>1</td>
|
||||
* <td>ORC Standard</td>
|
||||
* <td>3</td>
|
||||
* <td>Club</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>2</td>
|
||||
* <td>Super Yacht</td>
|
||||
* <td>7</td>
|
||||
* <td>ORCsy</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>3</td>
|
||||
* <td>Double Handed</td>
|
||||
* <td>8</td>
|
||||
* <td>DH International</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>3</td>
|
||||
* <td>Double Handed</td>
|
||||
* <td>9</td>
|
||||
* <td>DH Club</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>4</td>
|
||||
* <td>Multihull</td>
|
||||
* <td>15</td>
|
||||
* <td>Multihull International</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>4</td>
|
||||
* <td>Multihull</td>
|
||||
* <td>16</td>
|
||||
* <td>Multihull Club</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>5</td>
|
||||
* <td>Non Spinnaker</td>
|
||||
* <td>10</td>
|
||||
* <td>NS International</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>5</td>
|
||||
* <td>Non Spinnaker</td>
|
||||
* <td>11</td>
|
||||
* <td>NS Club</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>6</td>
|
||||
* <td>ORC Light</td>
|
||||
* <td>13</td>
|
||||
* <td>Light (Standard)</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>6</td>
|
||||
* <td>ORC Light</td>
|
||||
* <td>14</td>
|
||||
* <td>Light (Double Handed)</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*/
|
||||
public interface CountryOverview {
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@ public class FavoritesResult implements Result {
|
||||
public FavoritesResult(FavoriteBoatClassesDTO favoriteBoatClasses, FavoriteCompetitorsDTO favoriteCompetitors, boolean isSubscribedToFeatureAndCommunityUpdates) {
|
||||
this.favoriteBoatClasses = favoriteBoatClasses;
|
||||
this.favoriteCompetitors = favoriteCompetitors;
|
||||
this.isSubscribedToFeatureAndCommunityUpdates = isSubscribedToFeatureAndCommunityUpdates;
|
||||
}
|
||||
|
||||
public FavoriteBoatClassesDTO getFavoriteBoatClasses() {
|
||||
@@ -26,5 +27,4 @@ public class FavoritesResult implements Result {
|
||||
public boolean getIsSubscribedToFeatureAndCommunityUpdates() {
|
||||
return isSubscribedToFeatureAndCommunityUpdates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ public class GetFavoritesAction implements SailingAction<FavoritesResult> {
|
||||
@GwtIncompatible
|
||||
public FavoritesResult execute(SailingDispatchContext ctx) throws DispatchException {
|
||||
return new FavoritesResult(getFavoriteBoatClasses(ctx), getFavoriteCompetitors(ctx),
|
||||
getDidOptOutOfFeatureAndCommunityEmails(ctx));
|
||||
!getDidOptOutOfFeatureAndCommunityEmails(ctx));
|
||||
}
|
||||
|
||||
@GwtIncompatible
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
Data Mining as a dimension for filtering and grouping.</li>
|
||||
<ii>When updates to user accounts are carried out using the API, not passing a property (such as
|
||||
company affiliation or preferred locale) will no longer reset that value to <tt>null</tt>
|
||||
but instead leave it unchanged.</ii>
|
||||
but instead leave it unchanged.</li>
|
||||
<li>A new "Eraser" action has been added to the leaderboards table at the top of the
|
||||
Connectors / Smartphone Tracking panel. If the leadeboard is a "Regatta" leaderboard
|
||||
Connectors / Smartphone Tracking panel. If the leaderboard is a "Regatta" leaderboard
|
||||
and the corresponding Regatta is configured to control tracking times from race
|
||||
start/finish times, using the eraser action will remove any explicit start/end of tracking
|
||||
time specifications of the race logs of all races in the leaderboard that have valid
|
||||
|
||||
@@ -29,7 +29,7 @@ Displays designs displaying the login pages for the management-console.
|
||||
The View is already implemented. Overall Authorization and related event propagation is missing.
|
||||
|
||||

|
||||
See https://static.sapsailing.com/management-console/login for a full list of designs.
|
||||
See [https://static.sapsailing.com/management-console/login](https://static.sapsailing.com/management-console/login) for a full list of designs.
|
||||
|
||||
### Event
|
||||
|
||||
|
||||
Reference in New Issue
Block a user