bug5451: working version that has exported a bunch of races and stats

This commit is contained in:
Axel Uhl
2021-07-02 18:38:22 +02:00
parent 3d7adf4b04
commit 826d5b678d
11 changed files with 209 additions and 31 deletions
@@ -838,7 +838,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalysis) throws NoWindException {
boolean waitForLatestAnalysis) {
return null;
}
@@ -429,7 +429,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException {
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis) {
return null;
}
@@ -954,13 +954,8 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
final Distance windwardDistanceToCompetitorFarthestAhead = trackedRace == null ? null : trackedRace
.getWindwardDistanceToCompetitorFarthestAhead(competitor, timePoint, WindPositionMode.LEG_MIDDLE, rankingInfo, cache);
Distance averageAbsoluteCrossTrackError;
try {
averageAbsoluteCrossTrackError = trackedRace == null ? null : trackedRace.getAverageAbsoluteCrossTrackError(
competitor, timePoint, waitForLatestAnalyses, cache);
} catch (NoWindException nwe) {
// without wind information, use null meaning "unknown"
averageAbsoluteCrossTrackError = null;
}
averageAbsoluteCrossTrackError = trackedRace == null ? null : trackedRace.getAverageAbsoluteCrossTrackError(
competitor, timePoint, waitForLatestAnalyses, cache);
Distance averageSignedCrossTrackError;
averageSignedCrossTrackError = trackedRace == null ? null : trackedRace.getAverageSignedCrossTrackError(
competitor, timePoint, waitForLatestAnalyses, cache);
@@ -393,7 +393,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalysis) throws NoWindException {
boolean waitForLatestAnalysis) {
return null;
}
@@ -742,15 +742,14 @@ public interface TrackedRace
* if <code>true</code> and any cache update is currently going on, wait for the update to complete and
* then fetch the updated value; otherwise, serve this requests from whatever is currently in the cache
*/
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
throws NoWindException;
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis);
/**
* Same as {@link #getAverageAbsoluteCrossTrackError(Competitor, TimePoint, boolean)}, only that a cache for leg type,
* wind on leg and leg bearing is provided.
*/
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalyses,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) throws NoWindException;
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly, boolean waitForLatestAnalyses);
@@ -976,7 +975,7 @@ public interface TrackedRace
/**
* The average wind speed with confidence for this race.
*/
SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(TimePoint formTimePoint, TimePoint toTimePoint, int numberOfSamples);
SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(TimePoint fromTimePoint, TimePoint toTimePoint, int numberOfSamples);
/**
* Computes the center point of the course's marks at the given time point.
@@ -1645,15 +1645,13 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
throws NoWindException {
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis) {
return getAverageAbsoluteCrossTrackError(competitor, timePoint, waitForLatestAnalysis, new LeaderboardDTOCalculationReuseCache(timePoint));
}
@Override
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache)
throws NoWindException {
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
NavigableSet<MarkPassing> markPassings = getMarkPassings(competitor);
TimePoint from = null;
lockForRead(markPassings);
@@ -27,6 +27,7 @@ Require-Bundle: com.sap.db.jdbc;bundle-version="2.8.14",
com.sap.sse,
com.sap.sailing.server.interface,
com.sap.sse.datamining,
org.apache.commons.io;bundle-version="2.2.0"
org.apache.commons.io;bundle-version="2.2.0",
com.sap.sailing.domain.shared.android
Web-ContextPath: /hanaexport
Bundle-Activator: com.sap.sailing.hanaexport.impl.Activator
@@ -5,5 +5,6 @@ DELETE FROM "Event" CASCADE;
DELETE FROM "IRM" CASCADE;
DELETE FROM "Race" CASCADE;
DELETE FROM "RaceResult" CASCADE;
DELETE FROM "RaceStats" CASCADE;
DELETE FROM "Regatta" CASCADE;
DELETE FROM "ScoringScheme" CASCADE;
@@ -1,8 +1,8 @@
CREATE COLUMN TABLE "Event" (
"id" NVARCHAR(36) PRIMARY KEY,
"name" NVARCHAR(255) NOT NULL,
"startDate" DATE NOT NULL,
"endDate" DATE NOT NULL,
"startDate" TIMESTAMP NOT NULL,
"endDate" TIMESTAMP NOT NULL,
"venue" NVARCHAR(255),
"isListed" BOOLEAN,
"description" NVARCHAR(5000)
@@ -37,15 +37,13 @@ CREATE COLUMN TABLE "Race" (
"regatta" NVARCHAR(255) NOT NULL,
"raceColumn" NVARCHAR(255) NOT NULL,
"fleet" NVARCHAR(255) NOT NULL,
"boatClass" NVARCHAR(20) NOT NULL,
"startOfTracking" DATE NOT NULL,
"startOfRace" DATE NOT NULL,
"endOfTracking" DATE NOT NULL,
"endOfRace" DATE NOT NULL,
"startOfTracking" TIMESTAMP NOT NULL,
"startOfRace" TIMESTAMP NOT NULL,
"endOfTracking" TIMESTAMP NOT NULL,
"endOfRace" TIMESTAMP NOT NULL,
"avgWindSpeedInKnots" DECIMAL(5, 3),
PRIMARY KEY ("name", "regatta"),
FOREIGN KEY ("regatta") REFERENCES "Regatta" ("name"),
FOREIGN KEY ("boatClass") REFERENCES "BoatClass" ("id")
FOREIGN KEY ("regatta") REFERENCES "Regatta" ("name")
);
CREATE TABLE "Competitor" (
"id" NVARCHAR(36) PRIMARY KEY,
@@ -63,12 +61,20 @@ CREATE TABLE "CompetitorRace" (
FOREIGN KEY ("race", "regatta") REFERENCES "Race" ("name", "regatta")
);
CREATE TABLE "RaceResult" (
"regatta" NVARCHAR(255) NOT NULL,
"raceColumn" NVARCHAR(255) NOT NULL,
"competitorId" NVARCHAR(36) NOT NULL,
"points" DECIMAL(10, 2),
"discarded" BOOLEAN,
"irm" NVARCHAR(4),
PRIMARY KEY ("regatta", "raceColumn", "competitorId"),
FOREIGN KEY ("competitorId") REFERENCES "Competitor" ("id")
);
CREATE TABLE "RaceStats" (
"race" NVARCHAR(255) NOT NULL,
"regatta" NVARCHAR(255) NOT NULL,
"competitorId" NVARCHAR(20) NOT NULL,
"competitorId" NVARCHAR(36) NOT NULL,
"rankOneBased" INTEGER,
"points" DECIMAL(10, 2),
"irm" NVARCHAR(4),
"distanceSailedInMeters" DECIMAL(10, 2),
"elapsedTimeInSeconds" DECIMAL(10, 2),
"avgCrossTrackErrorInMeters" DECIMAL(10, 2),
@@ -5,5 +5,6 @@ DROP TABLE "Event" CASCADE;
DROP TABLE "IRM" CASCADE;
DROP TABLE "Race" CASCADE;
DROP TABLE "RaceResult" CASCADE;
DROP TABLE "RaceStats" CASCADE;
DROP TABLE "Regatta" CASCADE;
DROP TABLE "ScoringScheme" CASCADE;
@@ -6,6 +6,7 @@ import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.NavigableSet;
import java.util.logging.Logger;
import javax.ws.rs.POST;
@@ -18,14 +19,30 @@ import org.apache.commons.io.IOUtils;
import org.apache.shiro.SecurityUtils;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CompetitorWithBoat;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.SpeedWithConfidence;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.hanaexport.HanaConnectionFactory;
import com.sap.sailing.server.interfaces.RacingEventService;
import com.sap.sailing.shared.server.gateway.jaxrs.SharedAbstractSailingServerResource;
import com.sap.sse.ServerInfo;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
import com.sap.sse.security.shared.impl.SecuredSecurityTypes;
@@ -60,11 +77,26 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou
exportBoatClasses(racingEventService, connection);
exportIrms(racingEventService, connection);
exportScoringSchemes(racingEventService, connection);
exportCompetitors(racingEventService, connection);
exportEvents(racingEventService, connection);
exportRaces(racingEventService, connection);
logger.info("Done exporting HANA Cloud SAILING DB content on behalf of user "+SecurityUtils.getSubject().getPrincipal());
return Response.ok().build();
}
private void exportCompetitors(RacingEventService racingEventService, Connection connection) throws SQLException {
final PreparedStatement insertCompetitors = connection.prepareStatement(
"INSERT INTO \"Competitor\" (\"id\", \"name\", \"shortName\", \"nationality\", \"sailNumber\") VALUES (?, ?, ?, ?, ?);");
for (final Competitor competitor : racingEventService.getCompetitorAndBoatStore().getAllCompetitors()) {
insertCompetitors.setString(1, competitor.getId().toString());
insertCompetitors.setString(2, competitor.getName());
insertCompetitors.setString(3, competitor.getShortName());
insertCompetitors.setString(4, competitor.getNationality() == null ? " " : competitor.getNationality().getThreeLetterIOCAcronym());
insertCompetitors.setString(5, competitor.hasBoat() ? ((CompetitorWithBoat) competitor).getBoat().getSailID() : null);
insertCompetitors.execute();
}
}
private void exportIrms(RacingEventService racingEventService, Connection connection) throws SQLException {
final PreparedStatement insertBoatClasses = connection.prepareStatement(
"INSERT INTO \"IRM\" (\"name\", \"discardable\", \"advanceCompetitorsTrackedWorse\", \"appliesAtStartOfRace\") VALUES (?, ?, ?, ?);");
@@ -118,6 +150,151 @@ public class HanaCloudSacExportResource extends SharedAbstractSailingServerResou
}
}
private void exportRaces(RacingEventService racingEventService, Connection connection) throws SQLException {
final TimePoint now = TimePoint.now();
final PreparedStatement insertRegattas = connection.prepareStatement(
"INSERT INTO \"Regatta\" (\"name\", \"boatClass\", \"scoringScheme\", \"rankingMetric\") "+
"VALUES (?, ?, ?, ?);");
final PreparedStatement insertRaces = connection.prepareStatement(
"INSERT INTO \"Race\" (\"name\", \"regatta\", \"raceColumn\", \"fleet\", \"startOfTracking\", "+
"\"startOfRace\", \"endOfTracking\", \"endOfRace\", \"avgWindSpeedInKnots\") "+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);");
final PreparedStatement insertRaceResults = connection.prepareStatement(
"INSERT INTO \"RaceResult\" (\"regatta\", \"raceColumn\", \"competitorId\", \"points\", "+
"\"discarded\", \"irm\") "+
"VALUES (?, ?, ?, ?, ?, ?);");
final PreparedStatement insertRaceStats = connection.prepareStatement(
"INSERT INTO \"RaceStats\" (\"race\", \"regatta\", \"competitorId\", \"rankOneBased\", \"distanceSailedInMeters\", \"elapsedTimeInSeconds\", "+
"\"avgCrossTrackErrorInMeters\", \"absoluteAvGCrossTrackErrorInMeters\", \"numberOfTacks\", "+
"\"numberOfGybes\", \"numberOfPenaltyCircles\", \"startDelayInSeconds\", \"distanceFromStartLineInMetersAtStart\") "+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
for (final Regatta regatta : racingEventService.getAllRegattas()) {
insertRegattas.setString(1, regatta.getName());
insertRegattas.setString(2, regatta.getBoatClass().getName());
insertRegattas.setString(3, regatta.getScoringScheme().getType().name());
insertRegattas.setString(4, regatta.getRankingMetricType().name());
insertRegattas.execute();
final Leaderboard leaderboard = racingEventService.getLeaderboardByName(regatta.getName());
if (leaderboard != null) {
for (final RaceColumn raceColumn : leaderboard.getRaceColumns()) {
for (final Competitor competitor : raceColumn.getAllCompetitors()) {
parameterizeInsertRaceResult(insertRaceResults, now, competitor, leaderboard, raceColumn, regatta);
insertRaceResults.execute();
}
for (final Fleet fleet : raceColumn.getFleets()) {
final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
if (trackedRace != null) {
parameterizeInsertRacesStatement(insertRaces, regatta, raceColumn, fleet, trackedRace);
insertRaces.execute();
}
final Waypoint startWaypoint = trackedRace == null ? null : trackedRace.getRace().getCourse().getFirstWaypoint();
if (trackedRace != null) {
for (final Competitor competitor : trackedRace.getRace().getCompetitors()) {
parameterizeInsertRaceStats(insertRaceStats, now, startWaypoint, competitor, trackedRace);
insertRaceStats.execute();
}
}
}
}
}
}
}
private void parameterizeInsertRaceStats(PreparedStatement insertRaceStats, TimePoint now, Waypoint startWaypoint,
Competitor competitor, TrackedRace trackedRace) throws SQLException {
insertRaceStats.setString(1, trackedRace.getRace().getName());
insertRaceStats.setString(2, trackedRace.getTrackedRegatta().getRegatta().getName());
insertRaceStats.setString(3, competitor.getId().toString());
insertRaceStats.setInt(4, trackedRace.getRank(competitor, now));
insertRaceStats.setDouble(5, metersOr0ForNull(trackedRace.getDistanceTraveled(competitor, now)));
insertRaceStats.setDouble(6, secondsOr0ForNull(trackedRace.getTimeSailedSinceRaceStart(competitor, now)));
insertRaceStats.setDouble(7, metersOr0ForNull(trackedRace.getAverageSignedCrossTrackError(competitor, now, /* waitForLatest */ false)));
insertRaceStats.setDouble(8, metersOr0ForNull(trackedRace.getAverageAbsoluteCrossTrackError(competitor, now, /* waitForLatest */ false)));
final Iterable<Maneuver> maneuvers = trackedRace.getManeuvers(competitor, /* waitForLatest */ false);
insertRaceStats.setInt(9, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.TACK)));
insertRaceStats.setInt(10, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.JIBE)));
insertRaceStats.setInt(11, Util.size(Util.filter(maneuvers, m->m.getType() == ManeuverType.PENALTY_CIRCLE)));
final TimePoint startOfRace = trackedRace.getStartOfRace();
final double startDelay;
if (startWaypoint != null && startOfRace != null) {
NavigableSet<MarkPassing> competitorMarkPassings = trackedRace.getMarkPassings(competitor);
trackedRace.lockForRead(competitorMarkPassings);
try {
if (!Util.isEmpty(competitorMarkPassings)) {
final MarkPassing competitorStartMarkPassing = competitorMarkPassings.iterator().next();
startDelay = secondsOr0ForNull(startOfRace.until(competitorStartMarkPassing.getTimePoint()));
} else {
startDelay = 0;
}
} finally {
trackedRace.unlockAfterRead(competitorMarkPassings);
}
} else {
startDelay = 0;
}
insertRaceStats.setDouble(12, startDelay);
if (startOfRace != null) {
insertRaceStats.setDouble(13, metersOr0ForNull(trackedRace.getDistanceToStartLine(competitor, startOfRace)));
} else {
insertRaceStats.setDouble(13, 0);
}
}
private double metersOr0ForNull(final Distance distance) {
return distance == null ? 0 : distance.getMeters();
}
private double secondsOr0ForNull(final Duration duration) {
return duration == null ? 0 : duration.asSeconds();
}
private void parameterizeInsertRaceResult(PreparedStatement insertRaceResults, TimePoint now,
Competitor competitor, Leaderboard leaderboard, RaceColumn raceColumn, Regatta regatta) throws SQLException {
insertRaceResults.setString(1, regatta.getName());
insertRaceResults.setString(2, raceColumn.getName());
insertRaceResults.setString(3, competitor.getId().toString());
final Double totalPoints = leaderboard.getTotalPoints(competitor, raceColumn, now);
insertRaceResults.setDouble(4, totalPoints == null ? 0 : totalPoints);
insertRaceResults.setBoolean(5, leaderboard.isDiscarded(competitor, raceColumn, now));
final MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumn, now);
insertRaceResults.setString(6, (maxPointsReason == null ? MaxPointsReason.NONE : maxPointsReason).name());
}
private void parameterizeInsertRacesStatement(final PreparedStatement insertRaces, final Regatta regatta,
final RaceColumn raceColumn, final Fleet fleet, final TrackedRace trackedRace) throws SQLException {
insertRaces.setString(1, trackedRace.getRace().getName());
assert trackedRace.getTrackedRegatta().getRegatta() == regatta;
insertRaces.setString(2, trackedRace.getTrackedRegatta().getRegatta().getName());
insertRaces.setString(3, raceColumn.getName());
insertRaces.setString(4, fleet.getName());
if (trackedRace.getStartOfTracking() != null) {
insertRaces.setDate(5, new Date(trackedRace.getStartOfTracking().asMillis()));
} else {
insertRaces.setDate(5, null);
}
if (trackedRace.getStartOfRace() != null) {
insertRaces.setDate(6, new Date(trackedRace.getStartOfRace().asMillis()));
} else {
insertRaces.setDate(6, null);
}
if (trackedRace.getEndOfTracking() != null) {
insertRaces.setDate(7, new Date(trackedRace.getEndOfTracking().asMillis()));
} else {
insertRaces.setDate(7, null);
}
if (trackedRace.getEndOfRace() != null) {
insertRaces.setDate(8, new Date(trackedRace.getEndOfRace().asMillis()));
} else {
insertRaces.setDate(8, null);
}
final SpeedWithConfidence<TimePoint> averageWind = trackedRace.getAverageWindSpeedWithConfidenceWithNumberOfSamples(/* number of samples */ 5);
if (averageWind != null) {
insertRaces.setDouble(9, averageWind.getObject().getKnots());
} else {
insertRaces.setDouble(9, 0.0);
}
}
@POST
@Produces("application/json;charset=UTF-8")
@Path("createtables")