mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-13 22:15:26 +00:00
Merge branch 'bug4757_2'
This commit is contained in:
commit
7d417feb22
16 files changed
+600
-210
No files matched your search
+9
-37
@@ -1,65 +1,37 @@
|
||||
package com.sap.sailing.server.gateway.serialization.impl;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.base.Boat;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class ManeuversJsonSerializer extends AbstractTrackedRaceDataJsonSerializer {
|
||||
public class ManeuversJsonSerializer implements JsonSerializer<Iterable<Pair<Competitor, Iterable<Maneuver>>>> {
|
||||
public final static String MANEUVERS = "maneuvers";
|
||||
|
||||
private final CompetitorAndBoatJsonSerializer competitorAndBoatSerializer;
|
||||
private final ManeuverJsonSerializer maneuverSerializer;
|
||||
|
||||
public ManeuversJsonSerializer(CompetitorAndBoatJsonSerializer competitorSerializer, ManeuverJsonSerializer maneuverSerializer) {
|
||||
super();
|
||||
this.competitorAndBoatSerializer = competitorSerializer;
|
||||
public ManeuversJsonSerializer(ManeuverJsonSerializer maneuverSerializer) {
|
||||
this.maneuverSerializer = maneuverSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject serialize(TrackedRace trackedRace) {
|
||||
public JSONObject serialize(Iterable<Pair<Competitor, Iterable<Maneuver>>> data) {
|
||||
final JSONObject result = new JSONObject();
|
||||
JSONArray byCompetitorJson = new JSONArray();
|
||||
result.put(BYCOMPETITOR, byCompetitorJson);
|
||||
for (Entry<Competitor, Boat> competitorAndBoatEntry : trackedRace.getRace().getCompetitorsAndTheirBoats().entrySet()) {
|
||||
Competitor competitor = competitorAndBoatEntry.getKey();
|
||||
Boat boat = competitorAndBoatEntry.getValue();
|
||||
result.put(AbstractTrackedRaceDataJsonSerializer.BYCOMPETITOR, byCompetitorJson);
|
||||
for (Pair<Competitor, Iterable<Maneuver>> singleCompetitorData : data) {
|
||||
final JSONObject forCompetitorJson = new JSONObject();
|
||||
byCompetitorJson.add(forCompetitorJson);
|
||||
forCompetitorJson.put(COMPETITOR, competitorAndBoatSerializer.serialize(new Pair<>(competitor, boat)));
|
||||
final Competitor competitor = singleCompetitorData.getA();
|
||||
forCompetitorJson.put(AbstractTrackedRaceDataJsonSerializer.COMPETITOR, String.valueOf(competitor.getId()));
|
||||
final JSONArray maneuvers = new JSONArray();
|
||||
forCompetitorJson.put(MANEUVERS, maneuvers);
|
||||
for (final Maneuver maneuver : getManeuversDuringRace(trackedRace, competitor)) {
|
||||
for (final Maneuver maneuver : singleCompetitorData.getB()) {
|
||||
maneuvers.add(maneuverSerializer.serialize(maneuver));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Iterable<Maneuver> getManeuversDuringRace(TrackedRace trackedRace, Competitor competitor) {
|
||||
final TimePoint startTime = trackedRace.getStartOfRace();
|
||||
final TimePoint endOfRace = trackedRace.getEndOfRace();
|
||||
final TimePoint endTime;
|
||||
if (endOfRace != null) {
|
||||
endTime = endOfRace;
|
||||
} else {
|
||||
final TimePoint endOfTracking = trackedRace.getEndOfTracking();
|
||||
if (endOfTracking == null || endOfTracking.after(MillisecondsTimePoint.now())) {
|
||||
endTime = MillisecondsTimePoint.now();
|
||||
} else {
|
||||
endTime = endOfTracking;
|
||||
}
|
||||
}
|
||||
return trackedRace.getManeuvers(competitor, startTime, endTime, /* waitForLatest */ false);
|
||||
}
|
||||
|
||||
}
|
||||
+38
-13
@@ -4,6 +4,7 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -16,12 +17,17 @@ import org.json.simple.JSONValue;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.SeriesImpl;
|
||||
import com.sap.sailing.domain.common.RegattaName;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.server.gateway.jaxrs.api.RegattasResource;
|
||||
@@ -30,7 +36,8 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class RegattasResourceTest extends AbstractJaxRsApiTest {
|
||||
private String boatClassName = "49er";
|
||||
private String regattaName = "TestRegatta";
|
||||
private String regattaNamePart = "TestRegatta";
|
||||
private String regattaName = RegattaImpl.getDefaultName(regattaNamePart, boatClassName);
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@@ -45,34 +52,52 @@ public class RegattasResourceTest extends AbstractJaxRsApiTest {
|
||||
final TimePoint startDate = new MillisecondsTimePoint(cal.getTime());
|
||||
cal.set(2014, 5, 8, 16, 00);
|
||||
final TimePoint endDate = new MillisecondsTimePoint(cal.getTime());
|
||||
Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, /* isFleetsCanRunInParallel */ true, fleets, raceColumnNames, /* trackedRegattaRegistry */null);
|
||||
Series testSeries = new SeriesImpl("TestSeries", /* isMedal */false, /* isFleetsCanRunInParallel */ true,
|
||||
fleets, raceColumnNames, /* trackedRegattaRegistry */null);
|
||||
series.add(testSeries);
|
||||
racingEventService.createRegatta(RegattaImpl.getDefaultName(regattaName, boatClassName), boatClassName,
|
||||
/* canBoatsOfCompetitorsChangePerRace */ true, startDate, endDate, UUID.randomUUID(), series, /*persistent*/ true,
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
|
||||
racingEventService.createRegatta(regattaName, boatClassName, /* canBoatsOfCompetitorsChangePerRace */ true,
|
||||
startDate, endDate, UUID.randomUUID(), series, /* persistent */ true,
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null,
|
||||
/* buoyZoneRadiusInHullLengths */2.0, /* useStartTimeInference */ true,
|
||||
/* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
|
||||
testSeries.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
|
||||
testSeries.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
|
||||
|
||||
Course course = new CourseImpl("emptyCourse", Collections.emptySet());
|
||||
// get the same instance! of the boat class object, as else addRace will fail
|
||||
BoatClass boatClass = racingEventService.getBaseDomainFactory().getOrCreateBoatClass(boatClassName);
|
||||
racingEventService.addRace(new RegattaName(regattaName), new RaceDefinitionImpl("Race 1", course, boatClass));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRegattas() throws Exception {
|
||||
public void testGetRegattas() throws Exception {
|
||||
RegattasResource resource = new RegattasResource();
|
||||
RegattasResource spyResource = spyResource(resource);
|
||||
|
||||
|
||||
Response regattasResponse = spyResource.getRegattas();
|
||||
|
||||
|
||||
String jsonString = (String) regattasResponse.getEntity();
|
||||
Object obj= JSONValue.parse(jsonString);
|
||||
JSONArray array= (JSONArray) obj;
|
||||
Object obj = JSONValue.parse(jsonString);
|
||||
JSONArray array = (JSONArray) obj;
|
||||
|
||||
assertTrue(array.size() == 1);
|
||||
|
||||
JSONObject firstElement = (JSONObject) array.get(0);
|
||||
JSONObject firstElement = (JSONObject) array.get(0);
|
||||
String jsonName = (String) firstElement.get("name");
|
||||
String jsonBoatClass = (String) firstElement.get("boatclass");
|
||||
|
||||
assertTrue(RegattaImpl.getDefaultName(regattaName, boatClassName).equals(jsonName));
|
||||
|
||||
assertTrue(RegattaImpl.getDefaultName(regattaNamePart, boatClassName).equals(jsonName));
|
||||
assertTrue(boatClassName.equals(jsonBoatClass));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullCheckForTrackedRaceInGetManeuvers() throws Exception {
|
||||
RegattasResource resource = new RegattasResource();
|
||||
RegattasResource spyResource = spyResource(resource);
|
||||
|
||||
Response response = spyResource.getManeuvers(regattaName, "Race 1", null, null);
|
||||
// the current race is not tracked, expect an error
|
||||
assertTrue(response.getStatus() != 200);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,7 +82,7 @@ bearerToken = com.sap.sailing.server.security.BearerTokenOrBasicOrFormAuthentica
|
||||
# Secure the access (in particular the POST/write access) to posting race log events
|
||||
/rc/racelog = bearerToken
|
||||
|
||||
# Secure access to additional apis used for SailRacer
|
||||
# Secure access to additional apis used for SailRacer and SailInsight 2.0
|
||||
/api/v1/leaderboards/*/enableRaceLogForCompetitorRegistration/** = bearerToken
|
||||
/api/v1/leaderboards/*/denoteForTracking/** = bearerToken
|
||||
/api/v1/leaderboards/*/removeTrackedRace/** = bearerToken
|
||||
@@ -91,3 +91,6 @@ bearerToken = com.sap.sailing.server.security.BearerTokenOrBasicOrFormAuthentica
|
||||
/api/v1/mark/addMarkFix/** = bearerToken
|
||||
/api/v1/mark/addCourseDefinitionToRaceLog/** = bearerToken
|
||||
/api/v1/wind/putWind/** = bearerToken
|
||||
|
||||
#Secure access for preferences api, used for SailInsight 2.0
|
||||
/api/v1/preferences/** = bearerToken
|
||||
+10
-11
@@ -196,15 +196,14 @@ public abstract class AbstractLeaderboardsResource extends AbstractSailingServer
|
||||
|
||||
protected JSONObject getLeaderboardJson(ResultStates resultState, Integer maxCompetitorsCount,
|
||||
TimePoint requestTimePoint, Leaderboard leaderboard, TimePoint timePoint, List<String> raceColumnNames,
|
||||
List<String> raceDetailNames)
|
||||
throws NoWindException, InterruptedException, ExecutionException {
|
||||
final JSONObject jsonLeaderboard;
|
||||
if (timePoint != null || resultState == ResultStates.Live) {
|
||||
jsonLeaderboard = getLeaderboardJson(leaderboard, timePoint, resultState, maxCompetitorsCount, raceColumnNames, raceDetailNames);
|
||||
} else {
|
||||
jsonLeaderboard = createEmptyLeaderboardJson(leaderboard, resultState, maxCompetitorsCount);
|
||||
}
|
||||
return jsonLeaderboard;
|
||||
}
|
||||
|
||||
List<String> raceDetailNames) throws NoWindException, InterruptedException, ExecutionException {
|
||||
final JSONObject jsonLeaderboard;
|
||||
if (timePoint != null || resultState == ResultStates.Live) {
|
||||
jsonLeaderboard = getLeaderboardJson(leaderboard, timePoint, resultState, maxCompetitorsCount,
|
||||
raceColumnNames, raceDetailNames);
|
||||
} else {
|
||||
jsonLeaderboard = createEmptyLeaderboardJson(leaderboard, resultState, maxCompetitorsCount);
|
||||
}
|
||||
return jsonLeaderboard;
|
||||
}
|
||||
}
|
||||
+23
-5
@@ -10,6 +10,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
@@ -105,7 +106,8 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource {
|
||||
List<DetailType> raceDetailsToShow = calculateRaceDetailTypesToShow(raceDetailNames);
|
||||
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(
|
||||
resultTimePoint, raceColumnsToShow, /* addOverallDetails */
|
||||
false, getService(), getService().getBaseDomainFactory(),
|
||||
hasOverallDetail(raceDetailNames),
|
||||
getService(), getService().getBaseDomainFactory(),
|
||||
/* fillTotalPointsUncorrected */false);
|
||||
JSONObject jsonLeaderboard = new JSONObject();
|
||||
writeCommonLeaderboardData(jsonLeaderboard, leaderboard, resultState, leaderboardDTO.getTimePoint(), maxCompetitorsCount);
|
||||
@@ -204,9 +206,9 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource {
|
||||
JSONObject jsonRaceDetails = new JSONObject();
|
||||
jsonEntry.put("data", jsonRaceDetails);
|
||||
for (DetailType type : raceDetailsToShow) {
|
||||
Pair<String, Object> valueForRaceDetailType = getValueForRaceDetailType(type, leaderboardEntry, currentLegEntry);
|
||||
Pair<String, Object> valueForRaceDetailType = getValueForRaceDetailType(type, leaderboardRowDTO, leaderboardEntry, currentLegEntry);
|
||||
if (valueForRaceDetailType != null && valueForRaceDetailType.getA() != null && valueForRaceDetailType.getB() != null) {
|
||||
jsonRaceDetails.put(valueForRaceDetailType.getA(), valueForRaceDetailType.getB());
|
||||
jsonRaceDetails.put(valueForRaceDetailType.getA(), valueForRaceDetailType.getB());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,6 +218,13 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource {
|
||||
return jsonLeaderboard;
|
||||
}
|
||||
|
||||
private boolean hasOverallDetail(List<String> raceDetailNames) {
|
||||
final HashSet<String> availableOverallDetailTypeNames = new HashSet<>(
|
||||
Arrays.asList(getAvailableOverallDetailColumnTypes()).stream().map(dt->dt.name()).collect(Collectors.toSet()));
|
||||
// returns true if the set changed, meaning there was an overall detail type in the raceDetailNames
|
||||
return availableOverallDetailTypeNames.removeAll(raceDetailNames);
|
||||
}
|
||||
|
||||
private List<DetailType> calculateRaceDetailTypesToShow(List<String> raceDetailTypesNames) {
|
||||
List<DetailType> result = new ArrayList<>();
|
||||
if (raceDetailTypesNames.size() == 0) {
|
||||
@@ -252,10 +261,15 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource {
|
||||
DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS,
|
||||
DetailType.NUMBER_OF_MANEUVERS,
|
||||
DetailType.RACE_CURRENT_LEG };
|
||||
DetailType.RACE_CURRENT_LEG,
|
||||
DetailType.OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS };
|
||||
}
|
||||
|
||||
private Pair<String, Object> getValueForRaceDetailType(DetailType type, LeaderboardEntryDTO entry, LegEntryDTO currentLegEntry) {
|
||||
private DetailType[] getAvailableOverallDetailColumnTypes() {
|
||||
return new DetailType[] { DetailType.OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS };
|
||||
}
|
||||
|
||||
private Pair<String, Object> getValueForRaceDetailType(DetailType type, LeaderboardRowDTO leaderboardRowDTO, LeaderboardEntryDTO entry, LegEntryDTO currentLegEntry) {
|
||||
String name;
|
||||
Object value = null;
|
||||
Pair<String, Object> result = null;
|
||||
@@ -322,6 +336,10 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource {
|
||||
value = currentLegNumber;
|
||||
}
|
||||
break;
|
||||
case OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS:
|
||||
name = "maxSpeedOverGroundInKnots";
|
||||
value = leaderboardRowDTO.maximumSpeedOverGroundInKnots==null?null:roundDouble(leaderboardRowDTO.maximumSpeedOverGroundInKnots, 2);
|
||||
break;
|
||||
default:
|
||||
name = null;
|
||||
break;
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package com.sap.sailing.server.gateway.jaxrs.api;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sse.security.UserStore;
|
||||
|
||||
@Path("/v1/preferences")
|
||||
public class PreferencesResource extends AbstractSailingServerResource {
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{settingsKey}")
|
||||
public Response getPreference(@PathParam("settingsKey") String settingsKey)
|
||||
throws ParseException, JsonDeserializationException {
|
||||
Response response = null;
|
||||
if (SecurityUtils.getSubject().isAuthenticated()) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
UserStore userStore = getService(UserStore.class);
|
||||
String settings = userStore.getPreference(username, settingsKey);
|
||||
if (settings == null) {
|
||||
response = Response.noContent().build();
|
||||
} else {
|
||||
response = Response.ok(settings).build();
|
||||
}
|
||||
} else {
|
||||
response = Response.status(401).build();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Path("{settingsKey}")
|
||||
public Response putPreference(@PathParam("settingsKey") String settingsKey, String json)
|
||||
throws ParseException, JsonDeserializationException {
|
||||
Response response = null;
|
||||
if (SecurityUtils.getSubject().isAuthenticated()) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
UserStore userStore = getService(UserStore.class);
|
||||
userStore.setPreference(username, settingsKey, json);
|
||||
response = Response.ok().build();
|
||||
} else {
|
||||
response = Response.status(401).build();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{settingsKey}")
|
||||
public Response deletePreference(@PathParam("settingsKey") String settingsKey)
|
||||
throws ParseException, JsonDeserializationException {
|
||||
Response response = null;
|
||||
if (SecurityUtils.getSubject().isAuthenticated()) {
|
||||
String username = SecurityUtils.getSubject().getPrincipal().toString();
|
||||
UserStore userStore = getService(UserStore.class);
|
||||
userStore.unsetPreference(username, settingsKey);
|
||||
response = Response.ok().build();
|
||||
} else {
|
||||
response = Response.status(401).build();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+117
-37
@@ -11,6 +11,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -35,7 +36,6 @@ import org.json.simple.JSONValue;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.datamining.SailingPredefinedQueries;
|
||||
import com.sap.sailing.domain.base.Boat;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CompetitorWithBoat;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
@@ -65,6 +65,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -118,6 +119,7 @@ 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.Util.Pair;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.RGBColor;
|
||||
@@ -318,24 +320,16 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
return response;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/competitors/createandadd")
|
||||
public Response createAndAddCompetitor(@PathParam("regattaname") String regattaName,
|
||||
@QueryParam("boatclass") String boatClassName, @QueryParam("sailid") String sailId,
|
||||
@QueryParam("nationalityIOC") String nationalityThreeLetterIOCCode,
|
||||
@QueryParam("timeontimefactor") Double timeOnTimeFactor,
|
||||
@QueryParam("timeondistanceallowancepernauticalmileasmillis") Long timeOnDistanceAllowancePerNauticalMileAsMillis,
|
||||
@QueryParam("searchtag") String searchTag, @QueryParam("competitorName") String competitorName,
|
||||
@QueryParam("competitorEmail") String competitorEmail) {
|
||||
private Response createAndAddCompetitor(String regattaName,
|
||||
String nationalityThreeLetterIOCCode, Double timeOnTimeFactor,
|
||||
Long timeOnDistanceAllowancePerNauticalMileAsMillis, String searchTag, String competitorName,
|
||||
String competitorEmail, Function<String, DynamicBoat> boatObtainer) {
|
||||
final Subject subject = SecurityUtils.getSubject();
|
||||
subject.checkPermission(Permission.REGATTA.getStringPermissionForObjects(Mode.UPDATE, regattaName));
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = getBadRegattaErrorResponse(regattaName);
|
||||
} else if (boatClassName == null) {
|
||||
response = getBadBoatClassResponse(boatClassName);
|
||||
} else {
|
||||
final User user = getService(SecurityService.class).getCurrentUser();
|
||||
final String shortName;
|
||||
@@ -353,27 +347,65 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
} else {
|
||||
email = user.getEmail();
|
||||
}
|
||||
|
||||
final Boat boat = new BoatImpl(UUID.randomUUID(), shortName, getService().getBaseDomainFactory()
|
||||
.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */ true), sailId);
|
||||
|
||||
DynamicBoat boat = boatObtainer.apply(shortName);
|
||||
final TeamImpl team = new TeamImpl(shortName,
|
||||
Collections.singleton(new PersonImpl(name,
|
||||
getService().getBaseDomainFactory()
|
||||
.getOrCreateNationality(nationalityThreeLetterIOCCode),
|
||||
getService().getBaseDomainFactory().getOrCreateNationality(nationalityThreeLetterIOCCode),
|
||||
/* dateOfBirth */ null, /* description */ null)),
|
||||
/* coach */ null);
|
||||
final CompetitorWithBoat competitor = getService().getCompetitorAndBoatStore()
|
||||
.getOrCreateCompetitorWithBoat(UUID.randomUUID(),
|
||||
name, /* shortName */ null, /* displayColor */ null, email, /* flagImageURI */ null,
|
||||
team,
|
||||
timeOnTimeFactor,
|
||||
.getOrCreateCompetitorWithBoat(UUID.randomUUID(), name, /* shortName */ null,
|
||||
/* displayColor */ null, email, /* flagImageURI */ null, team, timeOnTimeFactor,
|
||||
timeOnDistanceAllowancePerNauticalMileAsMillis == null ? null
|
||||
: new MillisecondsDurationImpl(timeOnDistanceAllowancePerNauticalMileAsMillis),
|
||||
searchTag, (DynamicBoat) boat);
|
||||
searchTag, boat);
|
||||
regatta.registerCompetitor(competitor);
|
||||
response = Response.ok(CompetitorJsonSerializer.create().serialize(competitor).toJSONString()).
|
||||
header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
|
||||
response = Response.ok(CompetitorJsonSerializer.create().serialize(competitor).toJSONString())
|
||||
.header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/competitors/createandadd")
|
||||
public Response createAndAddCompetitor(@PathParam("regattaname") String regattaName,
|
||||
@QueryParam("boatclass") String boatClassName, @QueryParam("sailid") String sailId,
|
||||
@QueryParam("nationalityIOC") String nationalityThreeLetterIOCCode,
|
||||
@QueryParam("timeontimefactor") Double timeOnTimeFactor,
|
||||
@QueryParam("timeondistanceallowancepernauticalmileasmillis") Long timeOnDistanceAllowancePerNauticalMileAsMillis,
|
||||
@QueryParam("searchtag") String searchTag, @QueryParam("competitorName") String competitorName,
|
||||
@QueryParam("competitorEmail") String competitorEmail) {
|
||||
Response response;
|
||||
if (boatClassName == null) {
|
||||
response = getBadBoatClassResponse(boatClassName);
|
||||
} else {
|
||||
response = createAndAddCompetitor(regattaName, nationalityThreeLetterIOCCode, timeOnTimeFactor,
|
||||
timeOnDistanceAllowancePerNauticalMileAsMillis, searchTag, competitorName, competitorEmail,
|
||||
shortName -> new BoatImpl(UUID.randomUUID(), shortName, getService().getBaseDomainFactory()
|
||||
.getOrCreateBoatClass(boatClassName, /* typicallyStartsUpwind */ true), sailId));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/competitors/createandaddwithboat")
|
||||
public Response createAndAddCompetitorWithBoat(@PathParam("regattaname") String regattaName,
|
||||
@QueryParam("boatId") String boatId, @QueryParam("sailid") String sailId,
|
||||
@QueryParam("nationalityIOC") String nationalityThreeLetterIOCCode,
|
||||
@QueryParam("timeontimefactor") Double timeOnTimeFactor,
|
||||
@QueryParam("timeondistanceallowancepernauticalmileasmillis") Long timeOnDistanceAllowancePerNauticalMileAsMillis,
|
||||
@QueryParam("searchtag") String searchTag, @QueryParam("competitorName") String competitorName,
|
||||
@QueryParam("competitorEmail") String competitorEmail) {
|
||||
Response response;
|
||||
DynamicBoat boat = getService().getCompetitorAndBoatStore().getExistingBoatByIdAsString(boatId);
|
||||
if (boat == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity("Boat is not valid").type(MediaType.TEXT_PLAIN).build();
|
||||
} else {
|
||||
response = createAndAddCompetitor(regattaName, nationalityThreeLetterIOCCode, timeOnTimeFactor,
|
||||
timeOnDistanceAllowancePerNauticalMileAsMillis, searchTag, competitorName, competitorEmail,
|
||||
t -> boat);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -397,7 +429,8 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
competitorId = competitorIdAsString;
|
||||
}
|
||||
|
||||
final Competitor competitor = getService().getCompetitorAndBoatStore().getExistingCompetitorById(competitorId);
|
||||
final Competitor competitor = getService().getCompetitorAndBoatStore()
|
||||
.getExistingCompetitorById(competitorId);
|
||||
if (competitor == null) {
|
||||
response = getBadCompetitorIdResponse(competitorId);
|
||||
} else {
|
||||
@@ -1029,29 +1062,76 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
return response;
|
||||
}
|
||||
|
||||
private TimePoint determineEndTimeForManeuverDetection(TrackedRace trackedRace) {
|
||||
final TimePoint endOfRace = trackedRace.getEndOfRace();
|
||||
final TimePoint endTime;
|
||||
if (endOfRace != null) {
|
||||
endTime = endOfRace;
|
||||
} else {
|
||||
final TimePoint endOfTracking = trackedRace.getEndOfTracking();
|
||||
if (endOfTracking == null || endOfTracking.after(MillisecondsTimePoint.now())) {
|
||||
endTime = MillisecondsTimePoint.now();
|
||||
} else {
|
||||
endTime = endOfTracking;
|
||||
}
|
||||
}
|
||||
return endTime;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/races/{racename}/maneuvers")
|
||||
public Response getManeuvers(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName) {
|
||||
public Response getManeuvers(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
|
||||
@QueryParam("competitorId") String competitorId, @QueryParam("fromTime") String fromTime) {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a regatta with name '" + StringEscapeUtils.escapeHtml(regattaName) + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
.entity("Could not find a regatta with name '" + StringEscapeUtils.escapeHtml(regattaName) + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = Response.status(Status.NOT_FOUND)
|
||||
.entity("Could not find a race with name '" + StringEscapeUtils.escapeHtml(raceName) + "'.").type(MediaType.TEXT_PLAIN)
|
||||
.build();
|
||||
.entity("Could not find a race with name '" + StringEscapeUtils.escapeHtml(raceName) + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
ManeuversJsonSerializer serializer = new ManeuversJsonSerializer(CompetitorAndBoatJsonSerializer.create(),
|
||||
new ManeuverJsonSerializer(new GPSFixJsonSerializer(), new DistanceJsonSerializer()));
|
||||
JSONObject jsonMarkPassings = serializer.serialize(trackedRace);
|
||||
String json = jsonMarkPassings.toJSONString();
|
||||
return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
|
||||
if (trackedRace == null) {
|
||||
response = Response.status(Status.NOT_FOUND).entity(
|
||||
"Could not find a trackedrace with name '" + StringEscapeUtils.escapeHtml(raceName) + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
} else {
|
||||
List<Pair<Competitor, Iterable<Maneuver>>> data = new ArrayList<>();
|
||||
Iterable<Competitor> competitors = trackedRace.getRace().getCompetitors();
|
||||
UUID competitorFilter = null;
|
||||
if (competitorId != null) {
|
||||
competitorFilter = UUID.fromString(competitorId);
|
||||
}
|
||||
final TimePoint endTime = determineEndTimeForManeuverDetection(trackedRace);
|
||||
final TimePoint startTime;
|
||||
if (fromTime != null) {
|
||||
startTime = new MillisecondsTimePoint(Long.parseLong(fromTime));
|
||||
} else {
|
||||
startTime = trackedRace.getStartOfRace();
|
||||
}
|
||||
|
||||
for (Competitor competitor : competitors) {
|
||||
if (competitorFilter == null || competitor.getId().equals(competitorFilter)) {
|
||||
|
||||
Iterable<Maneuver> maneuversForCompetitor = trackedRace.getManeuvers(competitor, startTime,
|
||||
endTime, false);
|
||||
data.add(new Pair<Competitor, Iterable<Maneuver>>(competitor, maneuversForCompetitor));
|
||||
}
|
||||
}
|
||||
|
||||
ManeuversJsonSerializer serializer = new ManeuversJsonSerializer(
|
||||
new ManeuverJsonSerializer(new GPSFixJsonSerializer(), new DistanceJsonSerializer()));
|
||||
JSONObject jsonMarkPassings = serializer.serialize(data);
|
||||
String json = jsonMarkPassings.toJSONString();
|
||||
return Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return response;
|
||||
|
||||
+1
-1
@@ -8,7 +8,6 @@ import javax.ws.rs.core.Application;
|
||||
import com.sap.sailing.server.gateway.jaxrs.exceptions.ExceptionManager;
|
||||
import com.sap.sse.security.jaxrs.ShiroAuthorizationExceptionTo401ResponseMapper;
|
||||
|
||||
|
||||
public class RestApiApplication extends Application {
|
||||
public Set<Class<?>> getClasses() {
|
||||
HashSet<Class<?>> classes = new HashSet<>();
|
||||
@@ -30,6 +29,7 @@ public class RestApiApplication extends Application {
|
||||
classes.add(StatisticsResource.class);
|
||||
classes.add(WindResource.class);
|
||||
classes.add(MarkRessource.class);
|
||||
classes.add(PreferencesResource.class);
|
||||
classes.add(TagsResource.class);
|
||||
|
||||
// Exception Mappers
|
||||
|
||||
@@ -137,10 +137,14 @@ name to make sure you get to the event you're interested in.
|
||||
<td><a href="regattaUpdateOrCreateSeries.html">/api/v1/regattas/updateOrCreateSeries</a></td>
|
||||
<td>Creates a Series in an regatta, and configures the Series it contains</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="regattasCompetitorsCreateAndAdd.html">/api/v1/regattas/{regattaname}/competitors/createandadd</a></td>
|
||||
<td>Creates a competitor and adds it to the regatta</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="regattasCompetitorsCreateAndAddWithBoat.html">/api/v1/regattas/{regattaname}/competitors/createandaddwithboat</a></td>
|
||||
<td>Creates a competitor using an already exising boat and adds it to the regatta</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="regattasCompetitorsCreateAndAdd.html">/api/v1/regattas/{regattaname}/competitors/createandadd</a></td>
|
||||
<td>Creates a competitor and adds it to the regatta</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/api/v1/regattas/{regattaname}/competitors/{competitorId}/add</td>
|
||||
<td>Registers a competitor with a regatta (POST)</td>
|
||||
@@ -288,6 +292,20 @@ name to make sure you get to the event you're interested in.
|
||||
<td><a href="tagsCRUDDoc.html">/api/v1/{leaderboard}/{racecolumn}/{fleet}/tags</a></td>
|
||||
<td>Gets, creates, updates and deletes tags.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="preferencesPut.html">/api/v1/preferences/{SETTINGS_KEY}</a></td>
|
||||
<td>Stores a document into the preferences of a user</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="preferencesGet.html">/api/v1/preferences/{SETTINGS_KEY}</a></td>
|
||||
<td>Retrives a document that was stored into the preferences of a user</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="preferencesDelete.html">/api/v1/preferences/{SETTINGS_KEY}</a></td>
|
||||
<td>Removes a document from the preferences of a user</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<b>Description:</b>
|
||||
<p>Gets all maneuvers performed by all competitors of a race.<br/>
|
||||
All slashes contained within the {regattaname} and {racename} parameters must be replaced by "__" (double underscore).</p>
|
||||
Additionally it is possible to reduce the results to only contain one competitor, by
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
@@ -30,8 +31,9 @@ All slashes contained within the {regattaname} and {racename} parameters must be
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<td> competitorId (UUID) , fromTime UnixTimestamp
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
@@ -40,6 +42,10 @@ All slashes contained within the {regattaname} and {racename} parameters must be
|
||||
<td>Example:</td>
|
||||
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/regattas/TW%202013%20(Finn)/races/Finn%20Race%204/maneuvers">http://www.sapsailing.com/sailingserver/api/v1/regattas/TW%202013%20(Finn)/races/Finn%20Race%204/maneuvers</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example with optional Parameters</td>
|
||||
<td><a href="https://www.sapsailing.com/sailingserver/api/v1/regattas/ESS 2016 Cardiff/races/Race 2/maneuvers?competitorId=b9a8a600-c829-0133-a4ec-448a5b8481d8&fromTime=1466689117500">https://www.sapsailing.com/sailingserver/api/v1/regattas/ESS 2016 Cardiff/races/Race 2/maneuvers?competitorId=b9a8a600-c829-0133-a4ec-448a5b8481d8&fromTime=1466689117500 </a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/preferences/{SETTINGS_KEY}/</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Deletes whatever is stored under SETTINGS_KEY in currently logged in users preferences</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>none</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>none</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Examples:</td>
|
||||
<td>
|
||||
Delete @ http://sapsailing.com/sailingserver/api/v1/preferences/testSetting/
|
||||
<br>
|
||||
Answer: Ok
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/preferences/{SETTINGS_KEY}/</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Gets the stored Json data for the currently logged in user with the key SETTINGS_KEY</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>none</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Examples:</td>
|
||||
<td>
|
||||
Get @ http://sapsailing.com/sailingserver/api/v1/preferences/testSetting/
|
||||
<br>
|
||||
Answer: No Content Status code, or Ok and the stored document
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/preferences/{SETTINGS_KEY}/</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Stores the body of the request in the currently logged in users preferences under the given SETTINGS_KEY, the body is expected to be UTF-8 Json</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>none</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>none</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Examples:</td>
|
||||
<td>
|
||||
Put @ http://sapsailing.com/sailingserver/api/v1/preferences/testSetting/
|
||||
<br>
|
||||
Answer: Ok
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +1,11 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
@@ -14,101 +14,112 @@
|
||||
<b>Description:</b>
|
||||
<p>Gets the maneuvers of the race specified, keyed by competitor. The mark passings are computed
|
||||
from the start of the race up to the end of the race, or the end of tracking or the current time point,
|
||||
whichever is earliest.<p>
|
||||
The maneuvers for each competitor are ordered by the time point.<br/>
|
||||
All slashes contained within the {regattaname} and {racename} parameters must be replaced by "__" (double underscore).</p>
|
||||
<br/>
|
||||
whichever is earliest.
|
||||
<p>
|
||||
The maneuvers for each competitor are ordered by the time point.<br />
|
||||
All slashes contained within the {regattaname} and {racename}
|
||||
parameters must be replaced by "__" (double underscore).
|
||||
</p>
|
||||
There are two optional parameters: competitorId and fromTime.
|
||||
<br>
|
||||
With competitorId, it is possible to filter the result to include only the given competitor.
|
||||
<br>
|
||||
With fromTime it is possible to filter based on the maneuvers time. If not given, all maneuvers of a race will be returned.
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/regattas/TW%202013%20(Finn)/races/Finn%20Race%204/maneuvers">http://www.sapsailing.com/sailingserver/api/v1/regattas/TW%202013%20(Finn)/races/Finn%20Race%204/maneuvers</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example Output:</td>
|
||||
<td><pre>
|
||||
{
|
||||
"bycompetitor": [
|
||||
{
|
||||
"competitor": {
|
||||
"idtype": "java.util.UUID",
|
||||
"id": "acc1c7d1-cb00-44f6-bb8a-33b6386bc444",
|
||||
"name": "Agerup/Agerup",
|
||||
"displayColor": null,
|
||||
"email": null,
|
||||
"searchTag": null,
|
||||
"sailID": "NOR 888",
|
||||
"nationality": "NOR",
|
||||
"nationalityISO2": "NO",
|
||||
"nationalityISO3": "NOR",
|
||||
"timeOnTimeFactor": 1,
|
||||
"timeOnDistanceAllowanceInSecondsPerNauticalMile": 0
|
||||
},
|
||||
"maneuvers": [
|
||||
{
|
||||
"maneuverType": "TACK",
|
||||
"newTack": "PORT",
|
||||
"speedBeforeInKnots": 4.890263402216131,
|
||||
"cogBeforeInTrueDegrees": 11.910947801778443,
|
||||
"speedAfterInKnots": 7.882531591630032,
|
||||
"cogAfterInTrueDegrees": 124.81663607363905,
|
||||
"directionChangeInDegrees": 112.9056882718606,
|
||||
"maneuverLoss": {
|
||||
"geographicalMiles": 0.00022784339412441508,
|
||||
"seaMiles": 0.0002278338270171018,
|
||||
"nauticalMiles": 0.00022787073314458839,
|
||||
"meters": 0.4220165977837777,
|
||||
"kilometers": 0.0004220165977837777,
|
||||
"centralAngleDeg": 0.0000037973899020735846,
|
||||
"centralAngleRad": 6.62769567731691e-8
|
||||
},
|
||||
"positionAndTime": {
|
||||
"type": "GPSFix",
|
||||
"lat_deg": -34.462334,
|
||||
"lon_deg": -58.460133000000006,
|
||||
"unixtime": 1447696753000
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>fromTime unixTimeStamp, competitorId UUID</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/regattas/TW%202013%20(Finn)/races/Finn%20Race%204/maneuvers">http://www.sapsailing.com/sailingserver/api/v1/regattas/TW%202013%20(Finn)/races/Finn%20Race%204/maneuvers</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example Output:</td>
|
||||
<td><pre>
|
||||
{
|
||||
"bycompetitor":[
|
||||
{
|
||||
"competitor":{
|
||||
"idtype":"java.util.UUID",
|
||||
"id":"acc1c7d1-cb00-44f6-bb8a-33b6386bc444",
|
||||
"name":"Agerup/Agerup",
|
||||
"displayColor":null,
|
||||
"email":null,
|
||||
"searchTag":null,
|
||||
"sailID":"NOR 888",
|
||||
"nationality":"NOR",
|
||||
"nationalityISO2":"NO",
|
||||
"nationalityISO3":"NOR",
|
||||
"timeOnTimeFactor":1,
|
||||
"timeOnDistanceAllowanceInSecondsPerNauticalMile":0
|
||||
},
|
||||
"maneuvers":[
|
||||
{
|
||||
"maneuverType":"TACK",
|
||||
"newTack":"PORT",
|
||||
"speedBeforeInKnots":4.890263402216131,
|
||||
"cogBeforeInTrueDegrees":11.910947801778443,
|
||||
"speedAfterInKnots":7.882531591630032,
|
||||
"cogAfterInTrueDegrees":124.81663607363905,
|
||||
"directionChangeInDegrees":112.9056882718606,
|
||||
"maneuverLoss":{
|
||||
"geographicalMiles":0.00022784339412441508,
|
||||
"seaMiles":0.0002278338270171018,
|
||||
"nauticalMiles":0.00022787073314458839,
|
||||
"meters":0.4220165977837777,
|
||||
"kilometers":0.0004220165977837777,
|
||||
"centralAngleDeg":0.0000037973899020735846,
|
||||
"centralAngleRad":6.62769567731691e-8
|
||||
},
|
||||
"positionAndTime":{
|
||||
"type":"GPSFix",
|
||||
"lat_deg":-34.462334,
|
||||
"lon_deg":-58.460133000000006,
|
||||
"unixtime":1447696753000
|
||||
}
|
||||
},
|
||||
{
|
||||
"maneuverType":"HEAD_UP",
|
||||
"newTack":"PORT",
|
||||
"speedBeforeInKnots":7.882531591630032,
|
||||
"cogBeforeInTrueDegrees":124.81663607363905,
|
||||
"speedAfterInKnots":7.748306094677595,
|
||||
"cogAfterInTrueDegrees":101.79781832895219,
|
||||
"directionChangeInDegrees":-23.018817744686856,
|
||||
"maneuverLoss":null,
|
||||
"positionAndTime":{
|
||||
"type":"GPSFix",
|
||||
"lat_deg":-34.46242499999999,
|
||||
"lon_deg":-58.45991099999999,
|
||||
"unixtime":1447696762000
|
||||
}
|
||||
},
|
||||
...
|
||||
]
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"maneuverType": "HEAD_UP",
|
||||
"newTack": "PORT",
|
||||
"speedBeforeInKnots": 7.882531591630032,
|
||||
"cogBeforeInTrueDegrees": 124.81663607363905,
|
||||
"speedAfterInKnots": 7.748306094677595,
|
||||
"cogAfterInTrueDegrees": 101.79781832895219,
|
||||
"directionChangeInDegrees": -23.018817744686856,
|
||||
"maneuverLoss": null,
|
||||
"positionAndTime": {
|
||||
"type": "GPSFix",
|
||||
"lat_deg": -34.46242499999999,
|
||||
"lon_deg": -58.45991099999999,
|
||||
"unixtime": 1447696762000
|
||||
}
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
</tr>
|
||||
</pre>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/regattas/{regattaname}/competitors/createandaddwithboat</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Creates a new competitor and registers it with the regatta. Competitor creation can be controlled by a number of
|
||||
parameter; defaults will be obtained from the authenticated user invoking the service.<br/>
|
||||
All slashes contained within the {regattaname} parameter must be replaced by "__" (double underscore).</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>The competitor as a JSON document</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>boatId the string representation of the boat to use</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>
|
||||
<div>nationalityIOC three-letter IOC country code</div>
|
||||
<div>timeontimefactor a double value telling the ToT / TMF / time-on-time factor for handicap racing</div>
|
||||
<div>timeondistanceallowancepernauticalmileasmillis distance-based allowance in milliseconds for handicap racing (ToD)</div>
|
||||
<div>searchtag a search tag for the competitor</div>
|
||||
<div>competitorName the name to use for the newly created/added competitor</div>
|
||||
<div>competitorEmail the email to use for the newly created/added competitor</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>curl -X POST -H "Authorization: Bearer KMTuXevrA0CYyM6O1W2E0QB9gVeP8tIpgjafJOvj/6A=" "http://127.0.0.1:8888/sailingserver/api/v1/regattas/Smartphone%20Tracking%20Test%2022.06/competitors/createandadd?boatclass=49er&sailid=GER+123&nationalityIOC=GER"</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -23,7 +23,8 @@ The supported race detail constants right now are:<br/>
|
||||
- RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS<br/>
|
||||
- RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS<br/>
|
||||
- NUMBER_OF_MANEUVERS<br/>
|
||||
- CURRENT_LEG<br/>
|
||||
- CURRENT_LEG
|
||||
- OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS<br/>
|
||||
There is also the 'ALL' shortcut constant which can be used to get all available race details without the need to enumerate them.<br/>
|
||||
<br/>
|
||||
<table>
|
||||
|
||||
Reference in new issue
Block a user