From 5adbe6463257616eed01c24e0ecd75687acae1e3 Mon Sep 17 00:00:00 2001 From: D048538 Date: Mon, 10 Jun 2013 09:20:07 +0200 Subject: [PATCH 01/20] Added RegattaDataPerRace functionality in Xcelsius adapter (extracts various data points on race/competitor level) --- .../SailingGWT Remote Profiling.launch | 108 ++--- .../RegattaDataPerRaceAction.java | 433 ++++++++++++++++++ .../xcelsiusadapter/XcelsiusServlet.java | 8 +- 3 files changed, 493 insertions(+), 56 deletions(-) create mode 100644 java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling.launch b/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling.launch index 0c012601c94..d851188e1cd 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling.launch @@ -1,54 +1,54 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java new file mode 100644 index 00000000000..4cc7f092439 --- /dev/null +++ b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java @@ -0,0 +1,433 @@ +package com.sap.sailing.xcelsiusadapter; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.jdom.Document; +import org.jdom.Element; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Fleet; +import com.sap.sailing.domain.base.Leg; +import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.impl.MeterDistance; +import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.TrackedLeg; +import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sailing.domain.tracking.WindWithConfidence; +import com.sap.sailing.server.RacingEventService; + +public class RegattaDataPerRaceAction extends HttpAction { + public RegattaDataPerRaceAction(HttpServletRequest req, HttpServletResponse res, RacingEventService service, int maxRows) { + super(req, res, service, maxRows); + } + + + public void perform() throws Exception { + + + TimePoint now = MillisecondsTimePoint.now(); + final Leaderboard leaderboard = getLeaderboard(); // Get leaderboard data from request (get value for regatta name from URL parameter regatta) + // if the regatta does not exist a tag will be returned with a text message from function + // getLeaderboard(). + if (leaderboard == null) { + return; + } + + final Document doc = new Document(); // initialize xml document + final Element regatta_node = addNamedElement(doc, "regatta"); // add root to xml + addNamedElementWithValue(regatta_node, "name", leaderboard.getName()); + addNamedElementWithValue(regatta_node, "boat_class", getBoatClassName(leaderboard)); + + + + /* + * Races + */ +// final HashMap races = getRaces(regatta); // get races for the regatta + final Element races_node = addNamedElement(regatta_node, "races"); // add node that contains all races + + for (RaceColumn r : leaderboard.getRaceColumns()) { + for (Fleet f : r.getFleets()) { + TrackedRace trackedRace = r.getTrackedRace(f); + // skip race if not tracked + if (trackedRace == null || !trackedRace.hasGPSData()) { + continue; + } + Map> rankAtWaypoint = getRankAtWaypoint(trackedRace, now); + RaceDefinition race = trackedRace.getRace(); + + final Element race_node = addNamedElement(races_node, "race"); // add race node for the current race + addNamedElementWithValue(race_node, "name", race.getName()); // add name node to current race + + + final TimePoint raceStarted = getTimePoint(trackedRace); // get TimePoint for when the race started + if (raceStarted == null) { + continue; + } + + long minNextLegStart = raceStarted.asMillis(); // variable for keeping track of when the first competitor + // started the next leg + TimePoint legStarted = new MillisecondsTimePoint(minNextLegStart); // get TimePoint for when the leg started + + addNamedElementWithValue(race_node, "start_time_ms", raceStarted.asMillis()); // add the starttime to the + if (trackedRace.getEndOfRace() != null) { + addNamedElementWithValue(race_node, "assumed_end_ms", trackedRace.getEndOfRace().asMillis()); // add the assumed enddtime + } else { + addNamedElementWithValue(race_node, "assumed_end_ms", 0); // add the assumed enddtime + } + + Calendar cal = Calendar.getInstance(); + cal.setTime(raceStarted.asDate()); + addNamedElementWithValue(race_node, "start_time_year", cal.get(Calendar.YEAR)); + int month = cal.get(Calendar.MONTH) + 1; + addNamedElementWithValue(race_node, "start_time_month", month); + addNamedElementWithValue(race_node, "start_time_day", cal.get(Calendar.DAY_OF_MONTH)); + addNamedElementWithValue(race_node, "start_time_hour", cal.get(Calendar.HOUR_OF_DAY)); + addNamedElementWithValue(race_node, "start_time_minute", cal.get(Calendar.MINUTE)); + addNamedElementWithValue(race_node, "start_time_second", cal.get(Calendar.SECOND)); + addNamedElementWithValue(race_node, "start_time_formatted", (cal.get(Calendar.DAY_OF_MONTH) < 10 ? ("0" + cal.get(Calendar.DAY_OF_MONTH)) : cal.get(Calendar.DAY_OF_MONTH)) + + "." + + (month < 10 ? ("0" + month) : month) + + "." + + cal.get(Calendar.YEAR) + + " - " + + (cal.get(Calendar.HOUR_OF_DAY) < 10 ? ("0" + cal.get(Calendar.HOUR_OF_DAY)) : cal + .get(Calendar.HOUR_OF_DAY)) + + ":" + + (cal.get(Calendar.MINUTE) < 10 ? ("0" + cal.get(Calendar.MINUTE)) : cal + .get(Calendar.MINUTE)) + + ":" + + (cal.get(Calendar.SECOND) < 10 ? ("0" + cal.get(Calendar.SECOND)) : cal + .get(Calendar.SECOND))); + + Pair averageWindSpeedofRace = calculateAverageWindSpeedofRace(trackedRace); + String wind_strength = ""; + if (averageWindSpeedofRace != null) { + if (averageWindSpeedofRace.getA() < 4) { + wind_strength = "Very light"; + } else if (averageWindSpeedofRace.getA() >= 4 && averageWindSpeedofRace.getA() < 8) { + wind_strength = "Light"; + } else if (averageWindSpeedofRace.getA() >= 8 && averageWindSpeedofRace.getA() < 14) { + wind_strength = "Medium"; + } else if (averageWindSpeedofRace.getA() >= 14 && averageWindSpeedofRace.getA() < 20) { + wind_strength = "Strong"; + } else if (averageWindSpeedofRace.getA() >= 20) { + wind_strength = "Very strong"; + } + } + + Double wind_speed = 0.0; + Double wind_confi = 0.0; + + if (averageWindSpeedofRace != null) { + wind_speed = averageWindSpeedofRace.getA(); + wind_confi = averageWindSpeedofRace.getB(); + } + + addNamedElementWithValue(race_node, "average_wind_speed", wind_speed); + addNamedElementWithValue(race_node, "average_wind_speed_confidence", wind_confi); + addNamedElementWithValue(race_node, "wind_strength", wind_strength); + + addNamedElementWithValue(race_node, "course_length_m", getCourseLengthAtStart(trackedRace).getMeters()); + + final Element competitors_node = addNamedElement(race_node, "competitors"); // add node that contains all competitors + + + for (Competitor competitor : trackedRace.getCompetitorsFromBestToWorst(trackedRace.getEndOfTracking())) { + final Element competitor_node = addNamedElement(competitors_node, "competitor"); // add competitor node + + try { + addNamedElementWithValue(competitor_node, "name", competitor.getName()); + addNamedElementWithValue(competitor_node, "nationality", competitor.getTeam() + .getNationality().getThreeLetterIOCAcronym()); + addNamedElementWithValue(competitor_node, "sail_id", competitor.getBoat().getSailID()); + String sail_id = competitor.getBoat().getSailID(); + if (sail_id.matches("^[A-Z]{3}\\s[0-9]*")) { + + Pattern regex = Pattern.compile("(^[A-Z]{3})\\s([0-9]*)"); + Matcher regexMatcher = regex.matcher(sail_id); + try { + String resultString = regexMatcher.replaceAll("$1$2"); + addNamedElementWithValue(competitor_node, "sail_id_formatted", resultString); + } catch (Exception e) { + e.printStackTrace(); + } + + + } else if (sail_id.matches("^[A-Z]{3}\\S[0-9]*")) { + addNamedElementWithValue(competitor_node, "sail_id_formatted", sail_id); + } else if (sail_id.matches("[0-9]*")){ + addNamedElementWithValue(competitor_node, "sail_id_formatted", competitor.getTeam().getNationality().getThreeLetterIOCAcronym() + sail_id); + } else { + addNamedElementWithValue(competitor_node, "sail_id_formatted", sail_id); + } + + addNamedElementWithValue(competitor_node, "race_final_rank", trackedRace.getRank(competitor, now)); + addNamedElementWithValue(competitor_node, "race_participants", trackedRace.getCompetitorsFromBestToWorst(trackedRace.getStartOfTracking()).size()); + addNamedElementWithValue(competitor_node, "race_relative_final_rank", 1.0 - ((trackedRace.getRank(competitor, now) - 1.0)/(trackedRace.getCompetitorsFromBestToWorst(trackedRace.getStartOfTracking()).size() - 1.0))); + + + + addNamedElementWithValue(competitor_node, "distance_traveled_m", trackedRace.getDistanceTraveled(competitor, trackedRace.getEndOfTracking()).getMeters()); + addNamedElementWithValue(competitor_node, "avg_xte_m", trackedRace.getAverageCrossTrackError(competitor, trackedRace.getEndOfTracking(), true).getMeters()); + + + TrackedLeg previousLeg = null; + ArrayList posGLlist = new ArrayList(); + Double race_time = 0.0; + for (final TrackedLeg trackedLeg : trackedRace.getTrackedLegs()) { + final Leg leg = trackedLeg.getLeg(); + TrackedLegOfCompetitor trackedLegOfCompetitor = trackedLeg.getTrackedLeg(competitor); // Get data + TimePoint compareLegEnd = new MillisecondsTimePoint(0); + // time elapsed / when did the competitor pass the end mark of the leg + MarkPassing mp = trackedRace.getMarkPassing(competitor, leg.getTo()); + if (mp != null) { + compareLegEnd = mp.getTimePoint(); + + } else { + continue; + } + + if (trackedLegOfCompetitor == null || compareLegEnd == null) { + continue; + } + + Double compLegTimeAlt = -1.0; + try { + compLegTimeAlt = trackedLegOfCompetitor.getTimeInMilliSeconds(compareLegEnd) / 1000.0; + race_time += compLegTimeAlt; + } catch (Exception e1) { +// e1.printStackTrace(); +// If this happens, the race is probably broken + } + + TimePoint compFinishedLeg = compareLegEnd; + + // plausibility check + // competitor has finished the leg and the leg end time is not the race start time + if (trackedLegOfCompetitor.hasFinishedLeg(compFinishedLeg) && compareLegEnd.asMillis() != 0) { + // Calculate rank loss/gain + int posGL = 0; + if (previousLeg != null) { + posGL = rankAtWaypoint.get(competitor).get(trackedLeg.getLeg().getTo()) - + rankAtWaypoint.get(competitor).get(trackedLeg.getLeg().getFrom()); + } + posGLlist.add(posGL * -1); + } + + // assign the smallest start time for the next leg + minNextLegStart = (minNextLegStart > compFinishedLeg.asMillis() ? compFinishedLeg + .asMillis() : minNextLegStart); + + legStarted = new MillisecondsTimePoint(minNextLegStart); + minNextLegStart = Long.MAX_VALUE; + previousLeg = trackedLeg; + + } + + Double avg_rank_gain = 0.0; + int temp = 0; + for (int posGLvalue : posGLlist) { + temp = temp + posGLvalue; + } + if (posGLlist.size() != 0) { + avg_rank_gain = temp * 1.0 / posGLlist.size(); + } + + addNamedElementWithValue(competitor_node, "avg_rank_gain", avg_rank_gain); // Ranks Gained/Lost + addNamedElementWithValue(competitor_node, "rank_gains", posGLlist.toString()); // Ranks Gained/Lost + + addNamedElementWithValue(competitor_node, "race_time_s", race_time); + + Double time_per_km = (race_time / getCourseLengthAtStart(trackedRace).getMeters()) * 1000; + + addNamedElementWithValue(competitor_node, "time_per_km", time_per_km); + + } catch (Exception ex) { + //competitor_data_node.removeContent(competitor_node); // if the competitor dataset is not complete, remove it from the list + // complete, remove it from the list + } + } + } + } + sendDocument(doc, leaderboard.getName() + ".xml");// output doc to client + + } // function end + + + private Distance getCourseLengthAtStart(TrackedRace race) { + return getCourseLengthAt(race, race.getStartOfRace()); + } + + private Distance getCourseLengthAt(TrackedRace race, TimePoint timePoint) { + if (timePoint == null) { + return null; + } + List legs = race.getRace().getCourse().getLegs(); + Distance raceDistance = new MeterDistance(0); + for (Leg leg : legs) { + Waypoint from = leg.getFrom(); + Waypoint to = leg.getTo(); + Position fromPos = race.getApproximatePosition(from, timePoint); + Position toPos = race.getApproximatePosition(to, timePoint); + Distance legDistance = fromPos.getDistance(toPos); + Distance inMeters = new MeterDistance(legDistance.getMeters()); + raceDistance = raceDistance.add(inMeters); + } + return raceDistance; + } + + + private Map> getRankAtWaypoint(TrackedRace trackedRace, TimePoint timePoint) { + Map> result = new HashMap<>(); + Iterable waypoints = trackedRace.getRace().getCourse().getWaypoints(); + for (Waypoint waypoint : waypoints) { + if (waypoint != trackedRace.getRace().getCourse().getFirstWaypoint()) { + TrackedLeg trackedLeg = trackedRace.getTrackedLegFinishingAt(waypoint); + LinkedHashMap ranks = trackedLeg.getRanks(timePoint); + for (Competitor competitor : trackedRace.getRace().getCompetitors()) { + Map map = result.get(competitor); + if (map == null) { + map = new HashMap<>(); + result.put(competitor, map); + } + map.put(waypoint, trackedLeg.getTrackedLeg(competitor).getRank(timePoint)); + } + } + } + return result; + } + + + private String getBoatClassName(final Leaderboard leaderboard) { + String result = null; + if (leaderboard instanceof RegattaLeaderboard) { + result = ((RegattaLeaderboard) leaderboard).getRegatta().getBoatClass().getName(); + } else { + for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { + for (Fleet fleet : raceColumn.getFleets()) { + TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); + if (trackedRace != null) { + result = trackedRace.getRace().getBoatClass().getName(); + } + } + } + } + return result; + } + + private Pair calculateAverageWindSpeedofRace(TrackedRace trackedRace) { + Pair result = null; + if (trackedRace.getEndOfRace() != null) { + TimePoint fromTimePoint = trackedRace.getStartOfRace(); + TimePoint toTimePoint = trackedRace.getEndOfRace(); + long resolutionInMilliseconds = 60 * 1000 * 5; // 5 min + + List windSourcesToDeliver = new ArrayList(); + WindSourceImpl windSource = new WindSourceImpl(WindSourceType.COMBINED); + windSourcesToDeliver.add(windSource); + + double sumWindSpeed = 0.0; + double sumWindSpeedConfidence = 0.0; + int speedCounter = 0; + + int numberOfFixes = (int) ((toTimePoint.asMillis() - fromTimePoint.asMillis()) / resolutionInMilliseconds); + WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource); + TimePoint timePoint = fromTimePoint; + for (int i = 0; i < numberOfFixes && toTimePoint != null && timePoint.compareTo(toTimePoint) < 0; i++) { + WindWithConfidence> averagedWindWithConfidence = windTrack + .getAveragedWindWithConfidence(null, timePoint); + if (averagedWindWithConfidence != null) { + double windSpeedinKnots = averagedWindWithConfidence.getObject().getKnots(); + double confidence = averagedWindWithConfidence.getConfidence(); + + sumWindSpeed += windSpeedinKnots; + sumWindSpeedConfidence += confidence; + + speedCounter++; + } + timePoint = new MillisecondsTimePoint(timePoint.asMillis() + resolutionInMilliseconds); + } + if (speedCounter > 0) { + double averageWindSpeed = sumWindSpeed / speedCounter; + double averageWindSpeedConfidence = sumWindSpeedConfidence / speedCounter; + + result = new Pair(averageWindSpeed, averageWindSpeedConfidence); + } + } else { + result = new Pair(0.0, 0.0); + } + return result; + } + + private void addNamedElementWithValue(Element parent, String newChildName, Integer i) { + if (i == null) { + addNamedElementWithValue(parent, newChildName, "0"); + } else { + addNamedElementWithValue(parent, newChildName, i.toString()); + } + + } + + private void addNamedElementWithValue(Element parent, String newChildName, Double dbl) { + if (dbl == null) { + addNamedElementWithValue(parent, newChildName, "0"); + } else { + addNamedElementWithValue(parent, newChildName, dbl.toString()); + } + + } + + private void addNamedElementWithValue(Element parent, String newChildName, Long l) { + if (l == null) { + addNamedElementWithValue(parent, newChildName, "0"); + } else { + addNamedElementWithValue(parent, newChildName, l.toString()); + } + + } + + private Element addNamedElement(Document doc, String newChildName) { + final Element newChild = new Element(newChildName); + doc.addContent(newChild); + return newChild; + } + + private Element addNamedElementWithValue(Element parent, String newChildName, String value) { + final Element newChild = new Element(newChildName); + newChild.addContent(value); + parent.addContent(newChild); + return newChild; + } + + private Element addNamedElement(Element parent, String newChildName) { + final Element newChild = new Element(newChildName); + parent.addContent(newChild); + return newChild; + } + +} diff --git a/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/XcelsiusServlet.java b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/XcelsiusServlet.java index 4036ee1414e..d835ef1da1b 100755 --- a/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/XcelsiusServlet.java +++ b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/XcelsiusServlet.java @@ -52,11 +52,15 @@ public class XcelsiusServlet extends SailingServerHttpServlet { final RegattaDataPerLegAction a = new RegattaDataPerLegAction(req, res, getService(), maxRows); a.perform(); return; - }else if ("getRegattaList".equals(action)) { + } else if ("getRegattaList".equals(action)) { final RegattaListAction a = new RegattaListAction(req, res, getService(), maxRows); a.perform(); return; - }else { + } else if ("getRegattaDataPerRace".equals(action)) { + final RegattaDataPerRaceAction a = new RegattaDataPerRaceAction(req, res, getService(), maxRows); + a.perform(); + return; + } else { } HttpAction.say("Unknown action", res); return; From adbbfd1374a3e619be9dfcf83d774638831432f9 Mon Sep 17 00:00:00 2001 From: D048538 Date: Tue, 11 Jun 2013 12:50:47 +0200 Subject: [PATCH 02/20] added more measures to RegattaDataPerRaceAction --- .../RegattaDataPerRaceAction.java | 110 ++++++++++++++++-- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java index 4cc7f092439..3788ea1ec90 100644 --- a/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java +++ b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java @@ -3,9 +3,11 @@ package com.sap.sailing.xcelsiusadapter; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.NavigableSet; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -16,18 +18,24 @@ import org.jdom.Document; import org.jdom.Element; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.CourseChange; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.SpeedWithBearing; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.MeterDistance; +import com.sap.sailing.domain.common.impl.NauticalMileDistance; import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.leaderboard.Leaderboard; @@ -36,8 +44,10 @@ import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindWithConfidence; +import com.sap.sailing.domain.tracking.impl.WindImpl; import com.sap.sailing.server.RacingEventService; public class RegattaDataPerRaceAction extends HttpAction { @@ -142,17 +152,38 @@ public class RegattaDataPerRaceAction extends HttpAction { Double wind_speed = 0.0; Double wind_confi = 0.0; + Double wind_beaufort = 0.0; if (averageWindSpeedofRace != null) { wind_speed = averageWindSpeedofRace.getA(); - wind_confi = averageWindSpeedofRace.getB(); + wind_beaufort = averageWindSpeedofRace.getB(); } addNamedElementWithValue(race_node, "average_wind_speed", wind_speed); - addNamedElementWithValue(race_node, "average_wind_speed_confidence", wind_confi); + addNamedElementWithValue(race_node, "average_wind_speed_beaufort", wind_beaufort); addNamedElementWithValue(race_node, "wind_strength", wind_strength); - addNamedElementWithValue(race_node, "course_length_m", getCourseLengthAtStart(trackedRace).getMeters()); + addNamedElementWithValue(race_node, "b1", Math.exp(Math.log(1*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b15", Math.exp(Math.log(1.5*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b159", Math.exp(Math.log(1.59*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b16", Math.exp(Math.log(1.6*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b199", Math.exp(Math.log(1.99*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b2", Math.exp(Math.log(2*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b4", Math.exp(Math.log(4*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b45", Math.exp(Math.log(4.5*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b459", Math.exp(Math.log(4.59*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b46", Math.exp(Math.log(4.6*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b499", Math.exp(Math.log(4.99*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b5", Math.exp(Math.log(5*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b7", Math.exp(Math.log(7*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b11", Math.exp(Math.log(11*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b16", Math.exp(Math.log(16*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b22", Math.exp(Math.log(22*1.852/3.6 / 0.8360)*2/3)); + addNamedElementWithValue(race_node, "b28", Math.exp(Math.log(28*1.852/3.6 / 0.8360)*2/3)); + + + addNamedElementWithValue(race_node, "course_length_m", getCourseLength(trackedRace) == null ? 0 : getCourseLength(trackedRace).getMeters()); + final Element competitors_node = addNamedElement(race_node, "competitors"); // add node that contains all competitors @@ -189,13 +220,12 @@ public class RegattaDataPerRaceAction extends HttpAction { addNamedElementWithValue(competitor_node, "race_final_rank", trackedRace.getRank(competitor, now)); addNamedElementWithValue(competitor_node, "race_participants", trackedRace.getCompetitorsFromBestToWorst(trackedRace.getStartOfTracking()).size()); addNamedElementWithValue(competitor_node, "race_relative_final_rank", 1.0 - ((trackedRace.getRank(competitor, now) - 1.0)/(trackedRace.getCompetitorsFromBestToWorst(trackedRace.getStartOfTracking()).size() - 1.0))); - - - + addNamedElementWithValue(competitor_node, "distance_traveled_m", trackedRace.getDistanceTraveled(competitor, trackedRace.getEndOfTracking()).getMeters()); addNamedElementWithValue(competitor_node, "avg_xte_m", trackedRace.getAverageCrossTrackError(competitor, trackedRace.getEndOfTracking(), true).getMeters()); + TrackedLeg previousLeg = null; ArrayList posGLlist = new ArrayList(); Double race_time = 0.0; @@ -263,9 +293,45 @@ public class RegattaDataPerRaceAction extends HttpAction { addNamedElementWithValue(competitor_node, "race_time_s", race_time); - Double time_per_km = (race_time / getCourseLengthAtStart(trackedRace).getMeters()) * 1000; + Double time_per_nm = (race_time / getCourseLength(trackedRace).getNauticalMiles()); - addNamedElementWithValue(competitor_node, "time_per_km", time_per_km); + addNamedElementWithValue(competitor_node, "time_per_nm_s", time_per_nm); + + // START ANALYSIS + // Distance to startline at race start + addNamedElementWithValue(competitor_node, "racestart_dist_to_startline_m", trackedRace.getDistanceToStartLine(competitor, trackedRace.getStartOfRace()).getMeters()); + + // Speed at race start + addNamedElementWithValue(competitor_node, "racestart_speed_kn", trackedRace.getTrack(competitor).getEstimatedSpeed(trackedRace.getStartOfRace()).getKnots()); + + // measures when competitor is passing the starting mark + NavigableSet competitorMarkPassings = trackedRace.getMarkPassings(competitor); + Speed competitorSpeedWhenPassingStart = null; + Tack startTack = null; + Distance distanceFromStarboardSideOfStartLineWhenPassingStart = null; + trackedRace.lockForRead(competitorMarkPassings); + try { + if (!competitorMarkPassings.isEmpty()) { + TimePoint competitorStartTime = competitorMarkPassings.iterator().next().getTimePoint(); + competitorSpeedWhenPassingStart = trackedRace.getTrack(competitor).getEstimatedSpeed( + competitorStartTime); + startTack = trackedRace.getTack(competitor, competitorStartTime); + distanceFromStarboardSideOfStartLineWhenPassingStart = trackedRace.getDistanceFromStarboardSideOfStartLineWhenPassingStart(competitor); + } + } finally { + trackedRace.unlockAfterRead(competitorMarkPassings); + } + + // Speed when passing start + addNamedElementWithValue(competitor_node, "racestart_speed_when_passing_start_kn", competitorSpeedWhenPassingStart.getKnots()); + + // start tack + addNamedElementWithValue(competitor_node, "racestart_start_tack", startTack.toString()); + + // distance from starboard side of startline when passing start + addNamedElementWithValue(competitor_node, "racestart_dist_from_starboard_side_of_startline_when_passing_start_m", distanceFromStarboardSideOfStartLineWhenPassingStart.getMeters()); + + // END OF START ANALYSIS } catch (Exception ex) { //competitor_data_node.removeContent(competitor_node); // if the competitor dataset is not complete, remove it from the list @@ -300,6 +366,28 @@ public class RegattaDataPerRaceAction extends HttpAction { } return raceDistance; } + + private Distance getCourseLength(TrackedRace race) { + List legs = race.getRace().getCourse().getLegs(); + Distance raceDistance = new NauticalMileDistance(0); + for (Leg leg : legs) { + Waypoint from = leg.getFrom(); + Iterable markPassings = race.getMarkPassingsInOrder(from); + Iterator markPassingsIterator = markPassings.iterator(); + if (!markPassingsIterator.hasNext()) { + return null; + } + MarkPassing firstPassing = markPassingsIterator.next(); + TimePoint timePointOfFirstPassing = firstPassing.getTimePoint(); + Waypoint to = leg.getTo(); + Position fromPos = race.getApproximatePosition(from, timePointOfFirstPassing); + Position toPos = race.getApproximatePosition(to, timePointOfFirstPassing); + Distance legDistance = fromPos.getDistance(toPos); + raceDistance = raceDistance.add(legDistance); + } + return raceDistance; + } + private Map> getRankAtWaypoint(TrackedRace trackedRace, TimePoint timePoint) { @@ -353,6 +441,7 @@ public class RegattaDataPerRaceAction extends HttpAction { double sumWindSpeed = 0.0; double sumWindSpeedConfidence = 0.0; + double sumWindSpeedBeaufort = 0.0; int speedCounter = 0; int numberOfFixes = (int) ((toTimePoint.asMillis() - fromTimePoint.asMillis()) / resolutionInMilliseconds); @@ -364,9 +453,11 @@ public class RegattaDataPerRaceAction extends HttpAction { if (averagedWindWithConfidence != null) { double windSpeedinKnots = averagedWindWithConfidence.getObject().getKnots(); double confidence = averagedWindWithConfidence.getConfidence(); + double windSpeedinBeaufort = averagedWindWithConfidence.getObject().getBeaufort(); sumWindSpeed += windSpeedinKnots; sumWindSpeedConfidence += confidence; + sumWindSpeedBeaufort += windSpeedinBeaufort; speedCounter++; } @@ -375,8 +466,9 @@ public class RegattaDataPerRaceAction extends HttpAction { if (speedCounter > 0) { double averageWindSpeed = sumWindSpeed / speedCounter; double averageWindSpeedConfidence = sumWindSpeedConfidence / speedCounter; + double averageWindSpeedBeaufort = sumWindSpeedBeaufort / speedCounter; - result = new Pair(averageWindSpeed, averageWindSpeedConfidence); + result = new Pair(averageWindSpeed, averageWindSpeedBeaufort); } } else { result = new Pair(0.0, 0.0); From 92c351b084374671c45ab642d5a13ff519f35f28 Mon Sep 17 00:00:00 2001 From: D048538 Date: Tue, 11 Jun 2013 16:38:17 +0200 Subject: [PATCH 03/20] Added more funtionality to RegattaDataPerRaceAction --- .../RegattaDataPerRaceAction.java | 86 +++++++++---------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java index 3788ea1ec90..dbecf880e88 100644 --- a/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java +++ b/java/com.sap.sailing.xcelsiusadapter/src/com/sap/sailing/xcelsiusadapter/RegattaDataPerRaceAction.java @@ -18,15 +18,12 @@ import org.jdom.Document; import org.jdom.Element; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.CourseChange; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceDefinition; -import com.sap.sailing.domain.base.SpeedWithBearing; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; -import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; @@ -44,10 +41,8 @@ import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindWithConfidence; -import com.sap.sailing.domain.tracking.impl.WindImpl; import com.sap.sailing.server.RacingEventService; public class RegattaDataPerRaceAction extends HttpAction { @@ -91,7 +86,29 @@ public class RegattaDataPerRaceAction extends HttpAction { RaceDefinition race = trackedRace.getRace(); final Element race_node = addNamedElement(races_node, "race"); // add race node for the current race - addNamedElementWithValue(race_node, "name", race.getName()); // add name node to current race + + String raceName = race.getName(); + if (raceName.matches("[A-Za-z ]*\\d")) { + Pattern regex = Pattern.compile("([A-Za-z ]*)(\\d)"); + Matcher matcher = regex.matcher(raceName); + + String raceNameFirstPart = matcher.replaceAll("$1"); + String raceNumber = matcher.replaceAll("$2"); + + + + try { + if (Integer.parseInt(raceNumber) < 10) { + raceNumber = "0" + raceNumber; + raceName = raceNameFirstPart + raceNumber; + } + } catch (Exception e) { + e.printStackTrace(); + } + + } + + addNamedElementWithValue(race_node, "name", raceName); // add name node to current race final TimePoint raceStarted = getTimePoint(trackedRace); // get TimePoint for when the race started @@ -135,55 +152,23 @@ public class RegattaDataPerRaceAction extends HttpAction { .get(Calendar.SECOND))); Pair averageWindSpeedofRace = calculateAverageWindSpeedofRace(trackedRace); - String wind_strength = ""; - if (averageWindSpeedofRace != null) { - if (averageWindSpeedofRace.getA() < 4) { - wind_strength = "Very light"; - } else if (averageWindSpeedofRace.getA() >= 4 && averageWindSpeedofRace.getA() < 8) { - wind_strength = "Light"; - } else if (averageWindSpeedofRace.getA() >= 8 && averageWindSpeedofRace.getA() < 14) { - wind_strength = "Medium"; - } else if (averageWindSpeedofRace.getA() >= 14 && averageWindSpeedofRace.getA() < 20) { - wind_strength = "Strong"; - } else if (averageWindSpeedofRace.getA() >= 20) { - wind_strength = "Very strong"; - } - } Double wind_speed = 0.0; - Double wind_confi = 0.0; - Double wind_beaufort = 0.0; +// Double wind_confi = 0.0; + long wind_beaufort = 0; if (averageWindSpeedofRace != null) { wind_speed = averageWindSpeedofRace.getA(); - wind_beaufort = averageWindSpeedofRace.getB(); + wind_beaufort = Math.round(averageWindSpeedofRace.getB()); } addNamedElementWithValue(race_node, "average_wind_speed", wind_speed); addNamedElementWithValue(race_node, "average_wind_speed_beaufort", wind_beaufort); - addNamedElementWithValue(race_node, "wind_strength", wind_strength); - - addNamedElementWithValue(race_node, "b1", Math.exp(Math.log(1*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b15", Math.exp(Math.log(1.5*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b159", Math.exp(Math.log(1.59*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b16", Math.exp(Math.log(1.6*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b199", Math.exp(Math.log(1.99*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b2", Math.exp(Math.log(2*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b4", Math.exp(Math.log(4*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b45", Math.exp(Math.log(4.5*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b459", Math.exp(Math.log(4.59*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b46", Math.exp(Math.log(4.6*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b499", Math.exp(Math.log(4.99*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b5", Math.exp(Math.log(5*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b7", Math.exp(Math.log(7*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b11", Math.exp(Math.log(11*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b16", Math.exp(Math.log(16*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b22", Math.exp(Math.log(22*1.852/3.6 / 0.8360)*2/3)); - addNamedElementWithValue(race_node, "b28", Math.exp(Math.log(28*1.852/3.6 / 0.8360)*2/3)); - - + addNamedElementWithValue(race_node, "course_length_m", getCourseLength(trackedRace) == null ? 0 : getCourseLength(trackedRace).getMeters()); + addNamedElementWithValue(race_node, "first_leg_type", trackedRace.getTrackedLeg(trackedRace.getRace().getCourse().getFirstLeg()).getLegType(raceStarted).toString()); + final Element competitors_node = addNamedElement(race_node, "competitors"); // add node that contains all competitors @@ -224,11 +209,10 @@ public class RegattaDataPerRaceAction extends HttpAction { addNamedElementWithValue(competitor_node, "distance_traveled_m", trackedRace.getDistanceTraveled(competitor, trackedRace.getEndOfTracking()).getMeters()); addNamedElementWithValue(competitor_node, "avg_xte_m", trackedRace.getAverageCrossTrackError(competitor, trackedRace.getEndOfTracking(), true).getMeters()); - - TrackedLeg previousLeg = null; ArrayList posGLlist = new ArrayList(); Double race_time = 0.0; + int rankAfterFirstLeg = -3; for (final TrackedLeg trackedLeg : trackedRace.getTrackedLegs()) { final Leg leg = trackedLeg.getLeg(); TrackedLegOfCompetitor trackedLegOfCompetitor = trackedLeg.getTrackedLeg(competitor); // Get data @@ -269,6 +253,11 @@ public class RegattaDataPerRaceAction extends HttpAction { posGLlist.add(posGL * -1); } + // get rank after first leg + if (trackedLegOfCompetitor.getLeg().equals(trackedRace.getRace().getCourse().getFirstLeg())) { + rankAfterFirstLeg = trackedLegOfCompetitor.getRank(now); + } + // assign the smallest start time for the next leg minNextLegStart = (minNextLegStart > compFinishedLeg.asMillis() ? compFinishedLeg .asMillis() : minNextLegStart); @@ -288,9 +277,12 @@ public class RegattaDataPerRaceAction extends HttpAction { avg_rank_gain = temp * 1.0 / posGLlist.size(); } - addNamedElementWithValue(competitor_node, "avg_rank_gain", avg_rank_gain); // Ranks Gained/Lost + addNamedElementWithValue(competitor_node, "avg_rank_gain", avg_rank_gain); // avg rank gain addNamedElementWithValue(competitor_node, "rank_gains", posGLlist.toString()); // Ranks Gained/Lost + addNamedElementWithValue(competitor_node, "rank_after_first_leg", rankAfterFirstLeg); // Rank after first leg + addNamedElementWithValue(competitor_node, "rank_gain_between_first_and_finish", rankAfterFirstLeg - trackedRace.getRank(competitor, now)); // Rank gain between first leg and finish + addNamedElementWithValue(competitor_node, "race_time_s", race_time); Double time_per_nm = (race_time / getCourseLength(trackedRace).getNauticalMiles()); From ef45f45c9c372c4093e938be56fd4e9c83907f16 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 18 Jun 2013 18:11:08 +0200 Subject: [PATCH 04/20] added firstColumnIsNonDiscardableCarryForward to Series with all ramifications --- .../dto/RegattaCreationParametersDTO.java | 20 ++++++++ .../dto/SeriesCreationParametersDTO.java | 51 +++++++++++++++++++ .../DelayedLeaderboardCorrectionsImpl.java | 4 ++ .../impl/DomainObjectFactoryImpl.java | 4 ++ .../domain/persistence/impl/FieldNames.java | 1 + .../impl/MongoObjectFactoryImpl.java | 1 + .../sap/sailing/domain/base/RaceColumn.java | 2 + .../domain/base/RaceColumnListener.java | 3 ++ .../com/sap/sailing/domain/base/Series.java | 4 ++ .../base/impl/RaceColumnInSeriesImpl.java | 5 ++ .../sailing/domain/base/impl/RegattaImpl.java | 5 ++ .../sailing/domain/base/impl/SeriesImpl.java | 35 +++++++++++++ .../base/impl/SimpleAbstractRaceColumn.java | 6 +++ .../leaderboard/LeaderboardCacheManager.java | 5 ++ .../impl/AbstractSimpleLeaderboardImpl.java | 5 ++ .../leaderboard/impl/RaceColumnImpl.java | 1 + ...hresholdBasedResultDiscardingRuleImpl.java | 2 +- .../meta/MetaLeaderboardColumn.java | 5 ++ .../util/impl/RaceColumnListeners.java | 6 +++ .../adminconsole/RegattaDetailsComposite.java | 1 + .../RegattaStructureManagementPanel.java | 24 +++------ .../gwt/ui/adminconsole/SeriesDescriptor.java | 8 ++- .../gwt/ui/adminconsole/SeriesEditDialog.java | 9 +++- .../sailing/gwt/ui/client/SailingService.java | 12 ++--- .../gwt/ui/client/SailingServiceAsync.java | 9 ++-- .../sailing/gwt/ui/client/StringMessages.java | 1 + .../gwt/ui/client/StringMessages.properties | 1 + .../ui/client/StringMessages_de.properties | 3 +- .../gwt/ui/server/SailingServiceImpl.java | 10 ++-- .../sap/sailing/gwt/ui/shared/SeriesDTO.java | 7 ++- .../server/impl/RaceLogReplicator.java | 4 ++ .../server/impl/RaceLogScoringReplicator.java | 4 ++ .../server/impl/RacingEventServiceImpl.java | 22 ++++---- .../AddSpecificRegatta.java | 26 +++++----- .../UpdateSeries.java | 5 +- 35 files changed, 249 insertions(+), 62 deletions(-) create mode 100755 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RegattaCreationParametersDTO.java create mode 100755 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RegattaCreationParametersDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RegattaCreationParametersDTO.java new file mode 100755 index 00000000000..f2a4bd0fe18 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/RegattaCreationParametersDTO.java @@ -0,0 +1,20 @@ +package com.sap.sailing.domain.common.dto; + +import java.io.Serializable; +import java.util.LinkedHashMap; + +public class RegattaCreationParametersDTO implements Serializable { + private static final long serialVersionUID = 6852529475564779063L; + private LinkedHashMap seriesCreationParameters; + + RegattaCreationParametersDTO() {} + + public RegattaCreationParametersDTO(LinkedHashMap seriesCreationParameters) { + super(); + this.seriesCreationParameters = seriesCreationParameters; + } + + public LinkedHashMap getSeriesCreationParameters() { + return seriesCreationParameters; + } +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java new file mode 100755 index 00000000000..668027d32c3 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/SeriesCreationParametersDTO.java @@ -0,0 +1,51 @@ +package com.sap.sailing.domain.common.dto; + +import java.io.Serializable; +import java.util.List; + +public class SeriesCreationParametersDTO implements Serializable { + private static final long serialVersionUID = -3205172230707607515L; + + private List fleets; + + private boolean medal; + + private boolean startsWithZero; + + private boolean firstColumnIsNonDiscardableCarryForward; + + private int[] discardingThresholds; + + SeriesCreationParametersDTO() {} + + public SeriesCreationParametersDTO(List fleets, boolean medal, boolean startsWithZero, boolean firstColumnIsNonDiscardableCarryForward, + int[] discardingThresholds) { + super(); + this.fleets = fleets; + this.medal = medal; + this.startsWithZero = startsWithZero; + this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; + this.discardingThresholds = discardingThresholds; + } + + public List getFleets() { + return fleets; + } + + public boolean isMedal() { + return medal; + } + + public boolean isStartsWithZero() { + return startsWithZero; + } + + public int[] getDiscardingThresholds() { + return discardingThresholds; + } + + public boolean isFirstColumnIsNonDiscardableCarryForward() { + return firstColumnIsNonDiscardableCarryForward; + } + +} diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DelayedLeaderboardCorrectionsImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DelayedLeaderboardCorrectionsImpl.java index becebb9cf2d..9fd8a217c18 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DelayedLeaderboardCorrectionsImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DelayedLeaderboardCorrectionsImpl.java @@ -379,6 +379,10 @@ public class DelayedLeaderboardCorrectionsImpl implements DelayedLeaderboardCorr public void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore) { } + @Override + public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { + } + @Override public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { return true; diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java index b4b2d06e395..59f09ea3045 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/DomainObjectFactoryImpl.java @@ -907,6 +907,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { String name = (String) dbSeries.get(FieldNames.SERIES_NAME.name()); boolean isMedal = (Boolean) dbSeries.get(FieldNames.SERIES_IS_MEDAL.name()); Boolean startsWithZeroScore = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name()); + Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name()); final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name()); Map fleetsByName = loadFleets(dbFleets); BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name()); @@ -919,6 +920,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory { if (startsWithZeroScore != null) { series.setStartsWithZeroScore(startsWithZeroScore); } + if (firstColumnIsNonDiscardableCarryForward != null) { + series.setFirstColumnIsNonDiscardableCarryForward(firstColumnIsNonDiscardableCarryForward); + } loadRaceColumnRaceLinks(dbRaceColumns, series); return series; } diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java index b50774619ca..5b9853889f6 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/FieldNames.java @@ -21,6 +21,7 @@ public enum FieldNames { SERIES_NAME, SERIES_IS_MEDAL, SERIES_STARTS_WITH_ZERO_SCORE, + SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD, SERIES_FLEETS, SERIES_RACE_COLUMNS, SERIES_DISCARDING_THRESHOLDS, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java index 3b69c9b91ec..f175862a823 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/impl/MongoObjectFactoryImpl.java @@ -477,6 +477,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory { dbSeries.put(FieldNames.SERIES_NAME.name(), s.getName()); dbSeries.put(FieldNames.SERIES_IS_MEDAL.name(), s.isMedal()); dbSeries.put(FieldNames.SERIES_STARTS_WITH_ZERO_SCORE.name(), s.isStartsWithZeroScore()); + dbSeries.put(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name(), s.isFirstColumnIsNonDiscardableCarryForward()); BasicDBList dbFleets = new BasicDBList(); for (Fleet fleet : s.getFleets()) { dbFleets.add(storeFleet(fleet)); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java index 1841eef9b2b..84cc019fcbc 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumn.java @@ -157,6 +157,8 @@ public interface RaceColumn extends Named { * the total points at this column with zero. */ boolean isStartsWithZeroScore(); + + boolean isDiscardable(); /** * @param factor if null, {@link #getFactor()} will again compute a default value; otherwise, {@link #getFactor()} will diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java index d6d8ad13adc..6dd812638a8 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/RaceColumnListener.java @@ -16,6 +16,8 @@ public interface RaceColumnListener extends Serializable { void isStartsWithZeroScoreChanged(RaceColumn raceColumn, boolean newIsStartsWithZeroScore); + void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward); + boolean canAddRaceColumnToContainer(RaceColumn raceColumn); void raceColumnAddedToContainer(RaceColumn raceColumn); @@ -43,4 +45,5 @@ public interface RaceColumnListener extends Serializable { * exclude listeners from the serialization that return true from this method. */ boolean isTransient(); + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java index 147fc203faf..5b60f81b56c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Series.java @@ -89,4 +89,8 @@ public interface Series extends SeriesBase { boolean isStartsWithZeroScore(); void setStartsWithZeroScore(boolean startsWithZeroScore); + + boolean isFirstColumnIsNonDiscardableCarryForward(); + + void setFirstColumnIsNonDiscardableCarryForward(boolean firstColumnIsNonDiscardableCarryForward); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java index f4255c45f4a..c4b8488b69c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceColumnInSeriesImpl.java @@ -101,4 +101,9 @@ public class RaceColumnInSeriesImpl extends AbstractRaceColumn implements RaceCo public boolean isStartsWithZeroScore() { return getSeries().isStartsWithZeroScore() && isFirstColumnInSeries(); } + + @Override + public boolean isDiscardable() { + return !isMedalRace() && (!isFirstColumnInSeries() || !getSeries().isFirstColumnIsNonDiscardableCarryForward()); + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java index 87f9cdfeb77..5d75c61fdfe 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RegattaImpl.java @@ -273,6 +273,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); } + @Override + public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { + raceColumnListeners.notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward); + } + @Override public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { return raceColumnListeners.canAddRaceColumnToContainer(raceColumn); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java index 380cb1c07c5..1806b00cb7f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SeriesImpl.java @@ -34,8 +34,19 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener private Regatta regatta; private final RaceColumnListeners raceColumnListeners; private ThresholdBasedResultDiscardingRule resultDiscardingRule; + + /** + * If set, the series doesn't take over the scores from any previous series but starts with zero scores for all its + * competitors + */ private boolean startsWithZeroScore; + /** + * If set, the first race column is not discardable. This is usually very helpful if the series starts with a + * carry-forward score from a previous series. + */ + private boolean firstColumnIsNonDiscardableCarryForward; + /** * @param fleets * must be non-empty @@ -238,6 +249,11 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener raceColumnListeners.notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); } + @Override + public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { + raceColumnListeners.notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward); + } + /** * A series listens on its columns; individual columns, however, don't ask whether they can be added; the series itself does. * @@ -331,4 +347,23 @@ public class SeriesImpl extends NamedImpl implements Series, RaceColumnListener } } + @Override + public boolean isFirstColumnIsNonDiscardableCarryForward() { + return firstColumnIsNonDiscardableCarryForward; + } + + @Override + public void setFirstColumnIsNonDiscardableCarryForward(boolean firstColumnIsNonDiscardableCarryForward) { + boolean oldFirstColumnIsNonDiscardableCarryForward = this.firstColumnIsNonDiscardableCarryForward; + if (oldFirstColumnIsNonDiscardableCarryForward != firstColumnIsNonDiscardableCarryForward) { + this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; + RaceColumn firstRaceColumnInSeries = getFirstRaceColumn(); + if (firstRaceColumnInSeries != null) { + raceColumnListeners.notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(firstRaceColumnInSeries, firstColumnIsNonDiscardableCarryForward); + } + } + this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; + } + + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java index 3e09291a181..480bceeb1cc 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SimpleAbstractRaceColumn.java @@ -84,4 +84,10 @@ public abstract class SimpleAbstractRaceColumn implements RaceColumn { public boolean isStartsWithZeroScore() { return false; } + + @Override + public boolean isDiscardable() { + return !isMedalRace(); + } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java index 52bca89db1e..2343220f372 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardCacheManager.java @@ -233,6 +233,11 @@ public class LeaderboardCacheManager { removeFromCache(leaderboard); } + @Override + public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { + removeFromCache(leaderboard); + } + @Override public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { return true; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 42e0a95b084..58068f80a9d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -717,6 +717,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); } + @Override + public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { + getRaceColumnListeners().notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward); + } + @Override public void raceColumnMoved(RaceColumn raceColumn, int newIndex) { getRaceColumnListeners().notifyListenersAboutRaceColumnMoved(raceColumn, newIndex); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java index 15607b4e29c..2b8eb35c6ea 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/RaceColumnImpl.java @@ -56,4 +56,5 @@ public class RaceColumnImpl extends AbstractRaceColumn implements FlexibleRaceCo public Iterable getFleets() { return fleets; } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java index 68b0c137171..a57fbeebcda 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ThresholdBasedResultDiscardingRuleImpl.java @@ -74,7 +74,7 @@ public class ThresholdBasedResultDiscardingRuleImpl implements ThresholdBasedRes } }; for (RaceColumn raceColumn : raceColumnsToConsider) { - if (!raceColumn.isMedalRace()) { + if (raceColumn.isDiscardable()) { sortedRaces.add(raceColumn); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java index 3b7660c7dfd..44a8c3063f9 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/meta/MetaLeaderboardColumn.java @@ -126,6 +126,11 @@ public class MetaLeaderboardColumn extends SimpleAbstractRaceColumn implements R getRaceColumnListeners().notifyListenersAboutIsStartsWithZeroScoreChanged(raceColumn, newIsStartsWithZeroScore); } + @Override + public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { + getRaceColumnListeners().notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward); + } + @Override public boolean canAddRaceColumnToContainer(RaceColumn raceColumn) { return getRaceColumnListeners().canAddRaceColumnToContainer(raceColumn); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java b/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java index a0592bca83a..11e4b69eca0 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/util/impl/RaceColumnListeners.java @@ -91,6 +91,12 @@ public class RaceColumnListeners implements Serializable { } } + public void notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { + for (RaceColumnListener listener : getRaceColumnListeners()) { + listener.isFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward); + } + } + public void notifyListenersAboutRaceColumnAddedToContainer(RaceColumn raceColumn) { for (RaceColumnListener listener : getRaceColumnListeners()) { listener.raceColumnAddedToContainer(raceColumn); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java index 4e02f54c639..2483babee79 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java @@ -283,6 +283,7 @@ public class RegattaDetailsComposite extends Composite { if (isMedalChanged || seriesResultDiscardingThresholdsChanged || isStartsWithZeroScoreChanged) { sailingService.updateSeries(regattaIdentifier, series.getName(), seriesDescriptor.isMedal(), seriesDescriptor.getResultDiscardingThresholds(), seriesDescriptor.isStartsWithZeroScore(), + seriesDescriptor.isFirstColumnIsNonDiscardableCarryForward(), new AsyncCallback() { @Override public void onFailure(Throwable caught) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaStructureManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaStructureManagementPanel.java index 972a4975bae..870d740f609 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaStructureManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaStructureManagementPanel.java @@ -1,6 +1,5 @@ package com.sap.sailing.gwt.ui.adminconsole; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; @@ -15,11 +14,9 @@ import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.VerticalPanel; -import com.sap.sailing.domain.common.Color; import com.sap.sailing.domain.common.RegattaIdentifier; -import com.sap.sailing.domain.common.dto.FleetDTO; -import com.sap.sailing.domain.common.impl.Util.Pair; -import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO; +import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO; import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RegattaDisplayer; @@ -118,20 +115,15 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega } private void createNewRegatta(final RegattaDTO newRegatta) { - LinkedHashMap>, Pair, int[]>> seriesStructure = - new LinkedHashMap>,Pair,int[]>>(); + LinkedHashMap seriesStructure = new LinkedHashMap(); for (SeriesDTO seriesDTO : newRegatta.series) { - List> fleets = new ArrayList>(); - for(FleetDTO fleetDTO : seriesDTO.getFleets()) { - Triple fleetTriple = new Triple(fleetDTO.getName(), fleetDTO.getOrderNo(), fleetDTO.getColor()); - fleets.add(fleetTriple); - } - Triple>, Pair, int[]> seriesPair = - new Triple>, Pair, int[]>( - fleets, new Pair(seriesDTO.isMedal(), seriesDTO.isStartsWithZeroScore()), seriesDTO.getDiscardThresholds()); + SeriesCreationParametersDTO seriesPair = + new SeriesCreationParametersDTO( + seriesDTO.getFleets(), seriesDTO.isMedal(), seriesDTO.isStartsWithZeroScore(), seriesDTO.isFirstColumnIsNonDiscardableCarryForward(), seriesDTO.getDiscardThresholds()); seriesStructure.put(seriesDTO.getName(), seriesPair); } - sailingService.createRegatta(newRegatta.getName(), newRegatta.boatClass==null?null:newRegatta.boatClass.getName(), seriesStructure, true, + sailingService.createRegatta(newRegatta.getName(), newRegatta.boatClass==null?null:newRegatta.boatClass.getName(), + new RegattaCreationParametersDTO(seriesStructure), true, newRegatta.scoringScheme, newRegatta.defaultCourseAreaIdAsString, new AsyncCallback() { @Override public void onFailure(Throwable t) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java index 4b75cfd49cd..918c5b1fb53 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesDescriptor.java @@ -17,14 +17,16 @@ public class SeriesDescriptor { private final List races; private final boolean isMedal; private final boolean startsWithZeroScore; + private final boolean firstColumnIsNonDiscardableCarryForward; private final int[] resultDiscardingThresholds; - public SeriesDescriptor(SeriesDTO series, List races, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore) { + public SeriesDescriptor(SeriesDTO series, List races, boolean isMedal, int[] resultDiscardingThresholds, boolean startsWithZeroScore, boolean firstColumnIsNonDiscardableCarryForward) { this.series = series; this.races = races; this.isMedal = isMedal; this.startsWithZeroScore = startsWithZeroScore; this.resultDiscardingThresholds = resultDiscardingThresholds; + this.firstColumnIsNonDiscardableCarryForward = firstColumnIsNonDiscardableCarryForward; } public SeriesDTO getSeries() { @@ -46,4 +48,8 @@ public class SeriesDescriptor { public boolean isStartsWithZeroScore() { return startsWithZeroScore; } + + public boolean isFirstColumnIsNonDiscardableCarryForward() { + return firstColumnIsNonDiscardableCarryForward; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java index c74bb1559b8..33d0a798802 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SeriesEditDialog.java @@ -30,6 +30,7 @@ public class SeriesEditDialog extends DataEntryDialog { private Button addRacesBtn; private CheckBox isMedalCheckbox; private CheckBox startWithZeroScoreCheckbox; + private CheckBox firstColumnIsNonDiscardableCarryForwardCheckbox; private CheckBox useSeriesResultDiscardingThresholdsCheckbox; private final List raceNameEntryFields; private final List