mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-25 05:23:51 +00:00
Merge remote-tracking branch 'origin/master' into design2015
Conflicts: java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch java/com.sap.sailing.gwt.ui/SailingGWT.launch java/com.sap.sse.security.ui/Security UI sdm.launch
This commit is contained in:
commit
cea18823c7
47 files changed
+1108
-222
No files matched your search
-2
@@ -31,8 +31,6 @@ public class WindBotDataRetriever implements TimeListener, RaceSelectionChangeLi
|
||||
private final int ONE_HOUR_IN_MILLISECONDS = 1000*60*60;
|
||||
private final String LODA_WIND_CHART_DATA_CATEGORY = "loadWindChartData";
|
||||
private boolean didInitialLoading;
|
||||
|
||||
|
||||
|
||||
public WindBotDataRetriever(SailingServiceAsync sailingService) {
|
||||
this.sailingService = sailingService;
|
||||
|
||||
+3
-3
@@ -35,11 +35,11 @@ public class DeclinationServiceTest extends AbstractDeclinationTest {
|
||||
|
||||
@Test
|
||||
public void testDeclinationQueryNotMatchedInStore() throws IOException, ClassNotFoundException, ParseException {
|
||||
Declination result = service.getDeclination(new MillisecondsTimePoint(simpleDateFormat.parse("2016-02-03").getTime()),
|
||||
Declination result = service.getDeclination(new MillisecondsTimePoint(simpleDateFormat.parse("2019-02-03").getTime()),
|
||||
new DegreePosition(51, -5), /* timeoutForOnlineFetchInMilliseconds */ 30000);
|
||||
assertNotNull(result);
|
||||
assertEquals(-2.41498, result.getBearing().getDegrees(), 0.0001);
|
||||
assertEquals(0.15318, result.getAnnualChange().getDegrees(), 0.0001);
|
||||
assertEquals(-1.9565, result.getBearing().getDegrees(), 0.0001);
|
||||
assertEquals(0.15244, result.getAnnualChange().getDegrees(), 0.0001);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+8
-7
@@ -20,11 +20,13 @@ public enum BoatClassMasterdata {
|
||||
A_CAT ("A-Catamaran", true, 5.49, 2.30, BoatHullType.CATAMARAN, false, "A-Cat", "ACat", "A-Class Catamaran"),
|
||||
ALBIN_EXPRESS ("Albin Express", true, 7.77, 2.50, BoatHullType.MONOHULL, false),
|
||||
ALBIN_BALLAD ("Albin Ballad", true, 9.12, 2.95, BoatHullType.MONOHULL, false),
|
||||
B_ONE ("B/ONE", true, 7.80, 2.49, BoatHullType.MONOHULL, true, "B-ONE"),
|
||||
CANOE_IC ("International Canoe", true, 5.20, 1.01, BoatHullType.MONOHULL, true, "Canoe IC", "Canoe-IC", "IC", "Kanu IC", "International Canoe", "International 10 Sq.m. Sailing Canoe"),
|
||||
CANOE_TAIFUN ("Canoe Taifun", true, 5.20, 1.32, BoatHullType.MONOHULL, false, "Taifun", "Taifun Kanu", "Kanu Taifun"),
|
||||
B_ONE ("B/ONE", true, 7.80, 2.49, BoatHullType.MONOHULL, true, "B-ONE"),
|
||||
CONTENDER ("Contender", true, 4.88, 1.42, BoatHullType.MONOHULL, false),
|
||||
D_ONE ("D-One", true, 4.23, 2.31, BoatHullType.MONOHULL, true, "Devoti D-One", "DOne", "D_One"),
|
||||
DRAGON_INT ("Dragon Int.", true, 8.89, 1.96, BoatHullType.MONOHULL, true, "Drachen", "Dragon"),
|
||||
DYAS("Dyas", true, 7.15, 1.95, BoatHullType.MONOHULL, true),
|
||||
EXTREME_40 ("Extreme 40", false, 12.2, 6.60, BoatHullType.CATAMARAN, true, "Extreme-40", "Extreme40", "ESS40"),
|
||||
D_35 ("D35", false, 10.81, 6.89, BoatHullType.CATAMARAN, false),
|
||||
ELLIOTT_6M ("Elliott 6m", true, 6.0, 2.35, BoatHullType.MONOHULL, true, "Elliott6m"),
|
||||
@@ -32,6 +34,7 @@ public enum BoatClassMasterdata {
|
||||
F_18 ("Formula 18", true, 6.85, 2.25, BoatHullType.CATAMARAN, true, "F18", "F-18"),
|
||||
FARR_30 ("Farr 30", true, 9.42, 3.08, BoatHullType.MONOHULL, true, "F30", "F-30", "Farr-30"),
|
||||
FINN ("Finn", true, 4.50, 1.51, BoatHullType.MONOHULL, false),
|
||||
FLYING_DUTCHMAN ("Flying Dutchman", true, 6.10, 1.80, BoatHullType.MONOHULL, true),
|
||||
FLYING_PHANTOM ("Flying Phantom", true, 5.52, 3.00, BoatHullType.CATAMARAN, true),
|
||||
FOLKBOAT ("Folkboat", true, 7.68, 2.20, BoatHullType.MONOHULL, false, "Folke", "Folkeboot"),
|
||||
FUN ("FUN", true, 7.20, 2.45, BoatHullType.MONOHULL, true, "FUN O.D.", "FUN OD", "Open FUN"),
|
||||
@@ -40,6 +43,7 @@ public enum BoatClassMasterdata {
|
||||
H_BOAT ("H-Boat", true, 8.28, 2.18, BoatHullType.MONOHULL, true, "HB"),
|
||||
HOBIE_TIGER ("Hobie Tiger", true, 5.51, 2.60, BoatHullType.CATAMARAN, true),
|
||||
HOBIE_WILD_CAT ("Hobie Wild Cat", true, 5.49, 2.59, BoatHullType.CATAMARAN, true, "Hobie Wild Cat F18"),
|
||||
INTERNATIONAL_14("International 14", true, 4.27, 1.83, BoatHullType.MONOHULL, true, "I14", "Int.14", "Int14"),
|
||||
J22 ("J/22", true, 6.86, 2.44, BoatHullType.MONOHULL, true, "J22", "J-22"),
|
||||
J24 ("J/24", true, 7.32, 2.67, BoatHullType.MONOHULL, true, "J24", "J-24"),
|
||||
J70 ("J/70", true, 6.93, 2.25, BoatHullType.MONOHULL, true, "J70", "J-70"),
|
||||
@@ -58,9 +62,12 @@ public enum BoatClassMasterdata {
|
||||
NACRA_17 ("Nacra 17", true, 5.25, 2.59, BoatHullType.CATAMARAN, true, "N17", "Nacra-17"),
|
||||
O_JOLLE ("O-Jolle", true, 5.00, 1.66, BoatHullType.MONOHULL, false, "O Jolle", "OJolle", "Olympiajolle"),
|
||||
OK ("OK Dinghy", true, 5.25, 2.59, BoatHullType.MONOHULL, false, "OK-Dinghy", "OK-Jolle", "OK"),
|
||||
OPEN_BIC("O'pen BIC", true, 2.75, 1.14, BoatHullType.MONOHULL, false, "OpenBIC"),
|
||||
OPTIMIST ("Optimist", true, 2.34, 1.07, BoatHullType.MONOHULL, false, "Opti", "Optimist Dinghy"),
|
||||
PIRATE ("Pirate", true, 5.00, 1.61, BoatHullType.MONOHULL, false, "Pirat", "Piraten"),
|
||||
PLATU_25 ("Platu 25", true, 7.53, 2.62, BoatHullType.MONOHULL, true, "Platu", "Platu-25", "PLA", "B25"),
|
||||
PWA ("PWA", true, 5.0, 0.6, BoatHullType.MONOHULL, true, "Professional Windsurfers Association", "PWA World Tour"),
|
||||
RC44 ("RC44", true, 13.35, 2.75, BoatHullType.MONOHULL, true),
|
||||
RS_X ("RS:X", true, 2.86, 0.93, BoatHullType.SURFERBOARD, false, "RS-X", "RSX", "RS:X"),
|
||||
RS_FEVA ("RS Feva", true, 3.64, 1.42, BoatHullType.MONOHULL, true, "RSFeva"),
|
||||
SONAR ("Sonar", true, 7.01, 2.39, BoatHullType.MONOHULL, true),
|
||||
@@ -71,12 +78,6 @@ public enum BoatClassMasterdata {
|
||||
TORNADO ("Tornado Catamaran", true, 6.10, 3.02, BoatHullType.CATAMARAN, true, "Tornado", "Tornado Cat"),
|
||||
TRIAS ("Trias", true, 9.20, 2.12, BoatHullType.MONOHULL, true),
|
||||
X_99 ("X-99", true, 9.96, 2.95, BoatHullType.MONOHULL, true, "X99"),
|
||||
CONTENDER ("Contender", true, 4.88, 1.42, BoatHullType.MONOHULL, false),
|
||||
RC44 ("RC44", true, 13.35, 2.75, BoatHullType.MONOHULL, true),
|
||||
FLYING_DUTCHMAN ("Flying Dutchman", true, 6.10, 1.80, BoatHullType.MONOHULL, true),
|
||||
DYAS("Dyas", true, 7.15, 1.95, BoatHullType.MONOHULL, true),
|
||||
INTERNATIONAL_14("International 14", true, 4.27, 1.83, BoatHullType.MONOHULL, true, "I14", "Int.14", "Int14"),
|
||||
OPEN_BIC("O'pen BIC", true, 2.75, 1.14, BoatHullType.MONOHULL, false, "OpenBIC"),
|
||||
|
||||
// multi-class "boat classes"; to be replaced at some later point in time by something like a RegattaClass
|
||||
ORC ("ORC", true, 13.83, 3.91, BoatHullType.MONOHULL, true),
|
||||
|
||||
+1
-1
@@ -3,5 +3,5 @@ package com.sap.sailing.domain.common;
|
||||
public enum ScoringSchemeType {
|
||||
LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN,
|
||||
HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE, HIGH_POINT_WINNER_GETS_EIGHT,
|
||||
HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT
|
||||
HIGH_POINT_WINNER_GETS_EIGHT_AND_INTERPOLATION, HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT, LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07
|
||||
}
|
||||
+2
-1
@@ -8,6 +8,7 @@ import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
|
||||
import com.sap.sailing.domain.tracking.impl.TimedComparator;
|
||||
import com.sap.sse.common.Timed;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Comparator sorting by pass, then by {@link AbstractLogEventAuthor}, then by {@link RaceLogEvent#getCreatedAt()}
|
||||
@@ -43,7 +44,7 @@ public class LogEventComparator implements Comparator<Timed>, Serializable {
|
||||
if (result != 0) return result;
|
||||
|
||||
//compare logical timepoints
|
||||
result = e1.getLogicalTimePoint().compareTo(e2.getLogicalTimePoint());
|
||||
result = Util.compareToWithNull(e1.getLogicalTimePoint(), e2.getLogicalTimePoint(), /* nullIsLess */ false);
|
||||
if (result != 0) return result;
|
||||
|
||||
//compare ids
|
||||
|
||||
+3
-2
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Analysis returns the most recent {@link RaceLogFlagEvent}s.
|
||||
@@ -42,7 +43,7 @@ public class LastFlagsFinder extends RaceLogAnalyzer<List<RaceLogFlagEvent>> {
|
||||
List<RaceLogFlagEvent> result = new ArrayList<RaceLogFlagEvent>();
|
||||
TimePoint logicalTime = flagEvent.getLogicalTimePoint();
|
||||
|
||||
while (flagEvent.getLogicalTimePoint().equals(logicalTime)) {
|
||||
while (Util.compareToWithNull(flagEvent.getLogicalTimePoint(), logicalTime, /* nullIsLess */ false) == 0) {
|
||||
result.add(flagEvent);
|
||||
flagEvent = getNextFlagEvent(iterator);
|
||||
if (flagEvent == null) {
|
||||
@@ -84,7 +85,7 @@ public class LastFlagsFinder extends RaceLogAnalyzer<List<RaceLogFlagEvent>> {
|
||||
Collections.sort(sortedEvents, new Comparator<RaceLogFlagEvent>() {
|
||||
@Override
|
||||
public int compare(RaceLogFlagEvent left, RaceLogFlagEvent right) {
|
||||
int result = right.getLogicalTimePoint().compareTo(left.getLogicalTimePoint());
|
||||
int result = Util.compareToWithNull(right.getLogicalTimePoint(), left.getLogicalTimePoint(), /* nullIsLess */ false);
|
||||
if (result == 0) {
|
||||
result = Boolean.valueOf(right.isDisplayed()).compareTo(left.isDisplayed());
|
||||
if (result == 0) {
|
||||
|
||||
+438
-11
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -69,6 +70,7 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
|
||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndZeroRanks;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
@@ -293,10 +295,14 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
}
|
||||
// expect all results to be valid because a fleet will have its score counted even if not all fleets have raced;
|
||||
// see discussion for bug 961, but more importantly later on bug 1023.
|
||||
assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
|
||||
for (final Competitor competitor : competitors) {
|
||||
assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, competitor, later));
|
||||
}
|
||||
// now add a score correction for Q2/Blue to make it count:
|
||||
leaderboard.getScoreCorrection().correctScore(competitors.get(9), q2Column, 42.);
|
||||
assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, later));
|
||||
for (final Competitor competitor : competitors) {
|
||||
assertTrue(leaderboard.getScoringScheme().isValidInTotalScore(leaderboard, q2Column, competitor, later));
|
||||
}
|
||||
// the new order in Q2 expected to be { (9, 3), ... } (we don't know about any competitor in q2Blue but #9
|
||||
// therefore the new total points for #9 are
|
||||
// { 9: 5+42=47 }
|
||||
@@ -644,7 +650,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
/* medal */ false, "testTieBreakWithTwoVersusOneWins",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3);
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
|
||||
assertEquals(Arrays.asList(new Competitor[] { c[0], c[1], c[2] }), rankedCompetitors);
|
||||
@@ -663,12 +669,382 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
/* medal */ false, "testTieBreakWithTwoVersusOneSeconds",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
|
||||
assertTrue(rankedCompetitors.indexOf(c[0]) == rankedCompetitors.indexOf(c[1])-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicElminationScoringScheme() throws NoWindException {
|
||||
Regatta regatta = createRegattaWithEliminations(1, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
Competitor[] c = createCompetitors(64).toArray(new Competitor[64]);
|
||||
// first round with 64 competitors, eight per heat:
|
||||
Competitor[][] competitorsForHeatsInRound1 = new Competitor[8][];
|
||||
TimePoint later = null;
|
||||
for (int heat=0; heat<8; heat++) {
|
||||
competitorsForHeatsInRound1[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInRound1[heat][i] = c[8*heat+i];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(0), "Heat "+(heat+1), /* withScores */ true, competitorsForHeatsInRound1[heat]);
|
||||
}
|
||||
// quarter-finals has promoted top four competitors of first round in heats with eight competitors each:
|
||||
Competitor[][] competitorsForHeatsInQuarterFinals = new Competitor[4][];
|
||||
for (int heat=0; heat<4; heat++) {
|
||||
competitorsForHeatsInQuarterFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInQuarterFinals[heat][i] = c[8*(2*heat+(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(1), "Heat "+(heat+9), /* withScores */ true, competitorsForHeatsInQuarterFinals[heat]);
|
||||
}
|
||||
// semi-finals has promoted top four competitors of quarter finals which are the top four of each other first-round heat:
|
||||
Competitor[][] competitorsForHeatsInSemiFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInSemiFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInSemiFinals[heat][i] = c[8*(4*heat+2*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(2), "Heat "+(heat+13), /* withScores */ true, competitorsForHeatsInSemiFinals[heat]);
|
||||
}
|
||||
// finals has promoted top four competitors of semi finals which are the top four of first and fifth first-round heats
|
||||
// for the final, and the top four of the first round's third and seventh heat
|
||||
Competitor[][] competitorsForHeatsInFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInFinals[heat][i] = c[8*(2*heat+4*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(3), "Heat "+(heat+15), /* withScores */ true, competitorsForHeatsInFinals[heat]);
|
||||
}
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertSame(c[0], rankedCompetitors.get(0)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
|
||||
assertEquals(0.7, leaderboard.getTotalPoints(c[0], later), 0.000000001);
|
||||
assertSame(c[1], rankedCompetitors.get(1)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
|
||||
assertEquals(2, leaderboard.getTotalPoints(c[1], later), 0.000000001);
|
||||
// first four of second heat get promoted to quarter final but lose their heat
|
||||
assertNull(leaderboard.getTotalPoints(c[ 8], series.get(0).getRaceColumns().iterator().next(), later));
|
||||
assertNull(leaderboard.getTotalPoints(c[ 9], series.get(0).getRaceColumns().iterator().next(), later));
|
||||
assertNull(leaderboard.getTotalPoints(c[10], series.get(0).getRaceColumns().iterator().next(), later));
|
||||
assertNull(leaderboard.getTotalPoints(c[11], series.get(0).getRaceColumns().iterator().next(), later));
|
||||
assertEquals(18.5, leaderboard.getTotalPoints(c[ 8], later), 0.000000001);
|
||||
assertEquals(22.5, leaderboard.getTotalPoints(c[ 9], later), 0.000000001);
|
||||
assertEquals(26.5, leaderboard.getTotalPoints(c[10], later), 0.000000001);
|
||||
assertEquals(30.5, leaderboard.getTotalPoints(c[11], later), 0.000000001);
|
||||
// last four competitors in last heat of first round don't get promoted and have null scores in all other rounds but the first
|
||||
assertEquals(36.5, leaderboard.getTotalPoints(c[60], later), 0.000000001);
|
||||
assertEquals(44.5, leaderboard.getTotalPoints(c[61], later), 0.000000001);
|
||||
assertEquals(52.5, leaderboard.getTotalPoints(c[62], later), 0.000000001);
|
||||
assertEquals(60.5, leaderboard.getTotalPoints(c[63], later), 0.000000001);
|
||||
for (int i=1; i<=3; i++) {
|
||||
for (int j=60; j<=63; j++) {
|
||||
assertNull(leaderboard.getTotalPoints(c[j], series.get(i).getRaceColumns().iterator().next(), later));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiElminationScoringScheme() throws NoWindException {
|
||||
final int NUMBER_OF_ELIMINATIONS = 3;
|
||||
Regatta regatta = createRegattaWithEliminations(NUMBER_OF_ELIMINATIONS, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
|
||||
// also test that no discards occur for three eliminations when discards start to apply only with four races completed
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[] { NUMBER_OF_ELIMINATIONS + 1 });
|
||||
Competitor[] c = createCompetitors(64).toArray(new Competitor[64]);
|
||||
TimePoint later = null;
|
||||
for (int elimination=0; elimination<NUMBER_OF_ELIMINATIONS; elimination++) {
|
||||
// first round with 64 competitors, eight per heat:
|
||||
Competitor[][] competitorsForHeatsInRound1 = new Competitor[8][];
|
||||
for (int heat=0; heat<8; heat++) {
|
||||
competitorsForHeatsInRound1[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInRound1[heat][i] = c[8*heat+i];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(4*elimination+0), "Heat "+(heat+1), /* withScores */ true, competitorsForHeatsInRound1[heat]);
|
||||
}
|
||||
// quarter-finals has promoted top four competitors of first round in heats with eight competitors each:
|
||||
Competitor[][] competitorsForHeatsInQuarterFinals = new Competitor[4][];
|
||||
for (int heat=0; heat<4; heat++) {
|
||||
competitorsForHeatsInQuarterFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInQuarterFinals[heat][i] = c[8*(2*heat+(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(4*elimination+1), "Heat "+(heat+9), /* withScores */ true, competitorsForHeatsInQuarterFinals[heat]);
|
||||
}
|
||||
// semi-finals has promoted top four competitors of quarter finals which are the top four of each other first-round heat:
|
||||
Competitor[][] competitorsForHeatsInSemiFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInSemiFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInSemiFinals[heat][i] = c[8*(4*heat+2*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(4*elimination+2), "Heat "+(heat+13), /* withScores */ true, competitorsForHeatsInSemiFinals[heat]);
|
||||
}
|
||||
// finals has promoted top four competitors of semi finals which are the top four of first and fifth first-round heats
|
||||
// for the final, and the top four of the first round's third and seventh heat
|
||||
Competitor[][] competitorsForHeatsInFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInFinals[heat][i] = c[8*(2*heat+4*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(4*elimination+3), "Heat "+(heat+15), /* withScores */ true, competitorsForHeatsInFinals[heat]);
|
||||
}
|
||||
}
|
||||
for (int numberOfEliminationsExpectedToScore=NUMBER_OF_ELIMINATIONS; numberOfEliminationsExpectedToScore>=NUMBER_OF_ELIMINATIONS-1; numberOfEliminationsExpectedToScore--) {
|
||||
assertEquals(numberOfEliminationsExpectedToScore*18.5, leaderboard.getTotalPoints(c[ 8], later), 0.000000001);
|
||||
assertEquals(numberOfEliminationsExpectedToScore*22.5, leaderboard.getTotalPoints(c[ 9], later), 0.000000001);
|
||||
assertEquals(numberOfEliminationsExpectedToScore*26.5, leaderboard.getTotalPoints(c[10], later), 0.000000001);
|
||||
assertEquals(numberOfEliminationsExpectedToScore*30.5, leaderboard.getTotalPoints(c[11], later), 0.000000001);
|
||||
// last four competitors in last heat of first round don't get promoted and have null scores in all other rounds but the first
|
||||
assertEquals(numberOfEliminationsExpectedToScore*36.5, leaderboard.getTotalPoints(c[60], later), 0.000000001);
|
||||
assertEquals(numberOfEliminationsExpectedToScore*44.5, leaderboard.getTotalPoints(c[61], later), 0.000000001);
|
||||
assertEquals(numberOfEliminationsExpectedToScore*52.5, leaderboard.getTotalPoints(c[62], later), 0.000000001);
|
||||
assertEquals(numberOfEliminationsExpectedToScore*60.5, leaderboard.getTotalPoints(c[63], later), 0.000000001);
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertSame(c[0], rankedCompetitors.get(0)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
|
||||
assertEquals(numberOfEliminationsExpectedToScore*0.7, leaderboard.getTotalPoints(c[0], later), 0.000000001);
|
||||
assertSame(c[1], rankedCompetitors.get(1)); // should be the winner of the final round's Final heat and take the "crown" for the elimination
|
||||
assertEquals(numberOfEliminationsExpectedToScore*2, leaderboard.getTotalPoints(c[1], later), 0.000000001);
|
||||
for (int elimination=0; elimination<NUMBER_OF_ELIMINATIONS; elimination++) {
|
||||
// first four of second heat get promoted to quarter final but lose their heat
|
||||
assertNull(leaderboard.getTotalPoints(c[ 8], series.get(4*elimination+0).getRaceColumns().iterator().next(), later));
|
||||
assertNull(leaderboard.getTotalPoints(c[ 9], series.get(4*elimination+0).getRaceColumns().iterator().next(), later));
|
||||
assertNull(leaderboard.getTotalPoints(c[10], series.get(4*elimination+0).getRaceColumns().iterator().next(), later));
|
||||
assertNull(leaderboard.getTotalPoints(c[11], series.get(4*elimination+0).getRaceColumns().iterator().next(), later));
|
||||
for (int i=1; i<=3; i++) {
|
||||
for (int j=60; j<=63; j++) {
|
||||
assertNull(leaderboard.getTotalPoints(c[j], series.get(4*elimination+i).getRaceColumns().iterator().next(), later));
|
||||
}
|
||||
}
|
||||
}
|
||||
// for next loop iteration discard the appropriate number of races
|
||||
leaderboard.setCrossLeaderboardResultDiscardingRule(new ThresholdBasedResultDiscardingRuleImpl(new int[] { numberOfEliminationsExpectedToScore }));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElminationScoringSchemeWithDifferentlySizedHeatsInFirstRound() throws NoWindException {
|
||||
Regatta regatta = createRegattaWithEliminations(1, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
Competitor[] c = createCompetitors(65).toArray(new Competitor[64]);
|
||||
// first round with 64 competitors, eight per heat:
|
||||
Competitor[][] competitorsForHeatsInRound1 = new Competitor[8][];
|
||||
TimePoint later = null;
|
||||
for (int heat=0; heat<8; heat++) {
|
||||
final int numberOfCompetitorsInHeat = heat==7?9:8;
|
||||
competitorsForHeatsInRound1[heat] = new Competitor[numberOfCompetitorsInHeat];
|
||||
for (int i=0; i<numberOfCompetitorsInHeat; i++) {
|
||||
competitorsForHeatsInRound1[heat][i] = c[8*heat+i];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(0), "Heat "+(heat+1), /* withScores */ true, competitorsForHeatsInRound1[heat]);
|
||||
}
|
||||
// quarter-finals has promoted top four competitors of first round in heats with eight competitors each:
|
||||
Competitor[][] competitorsForHeatsInQuarterFinals = new Competitor[4][];
|
||||
for (int heat=0; heat<4; heat++) {
|
||||
competitorsForHeatsInQuarterFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInQuarterFinals[heat][i] = c[8*(2*heat+(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(1), "Heat "+(heat+9), /* withScores */ true, competitorsForHeatsInQuarterFinals[heat]);
|
||||
}
|
||||
// semi-finals has promoted top four competitors of quarter finals which are the top four of each other first-round heat:
|
||||
Competitor[][] competitorsForHeatsInSemiFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInSemiFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInSemiFinals[heat][i] = c[8*(4*heat+2*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(2), "Heat "+(heat+13), /* withScores */ true, competitorsForHeatsInSemiFinals[heat]);
|
||||
}
|
||||
// finals has promoted top four competitors of semi finals which are the top four of first and fifth first-round heats
|
||||
// for the final, and the top four of the first round's third and seventh heat
|
||||
Competitor[][] competitorsForHeatsInFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInFinals[heat][i] = c[8*(2*heat+4*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(3), "Heat "+(heat+15), /* withScores */ true, competitorsForHeatsInFinals[heat]);
|
||||
}
|
||||
// validate first-round points for drop-outs; competitor #64 (the 9th in heat #8) shall have 65 points
|
||||
assertEquals(65, leaderboard.getTotalPoints(c[64], later), 0.0000001);
|
||||
for (int heat = 0; heat < 8; heat++) {
|
||||
for (int i = 4; i < 8; i++) {
|
||||
assertEquals((8.0*i+1.0 // best overall rank for drop-outs ranking i+1 in own fleet
|
||||
+8.0*i+8.0) // worst overall rank for drop-outs ranking i+1 in own fleet
|
||||
/2.0, leaderboard.getTotalPoints(c[8*heat+i], later), 0.0000001);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElminationScoringSchemeWithFinalNotSailed() throws NoWindException {
|
||||
Regatta regatta = createRegattaWithEliminations(1, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
Competitor[] c = createCompetitors(64).toArray(new Competitor[64]);
|
||||
// first round with 64 competitors, eight per heat:
|
||||
Competitor[][] competitorsForHeatsInRound1 = new Competitor[8][];
|
||||
TimePoint later = null;
|
||||
for (int heat=0; heat<8; heat++) {
|
||||
competitorsForHeatsInRound1[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInRound1[heat][i] = c[8*heat+i];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(0), "Heat "+(heat+1), /* withScores */ true, competitorsForHeatsInRound1[heat]);
|
||||
}
|
||||
// quarter-finals has promoted top four competitors of first round in heats with eight competitors each:
|
||||
Competitor[][] competitorsForHeatsInQuarterFinals = new Competitor[4][];
|
||||
for (int heat=0; heat<4; heat++) {
|
||||
competitorsForHeatsInQuarterFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInQuarterFinals[heat][i] = c[8*(2*heat+(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(1), "Heat "+(heat+9), /* withScores */ true, competitorsForHeatsInQuarterFinals[heat]);
|
||||
}
|
||||
// semi-finals has promoted top four competitors of quarter finals which are the top four of each other first-round heat:
|
||||
Competitor[][] competitorsForHeatsInSemiFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInSemiFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInSemiFinals[heat][i] = c[8*(4*heat+2*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(2), "Heat "+(heat+13), /* withScores */ true, competitorsForHeatsInSemiFinals[heat]);
|
||||
}
|
||||
// finals has promoted top four competitors of semi finals which are the top four of first and fifth first-round heats
|
||||
// for the final, and the top four of the first round's third and seventh heat; we assume here that the finals have not
|
||||
// been sailed and therefore expect the scores to be obtained by interpolating, using 1.0 instead of 0.7 for averaging.
|
||||
Competitor[][] competitorsForHeatsInFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInFinals[heat][i] = c[8*(2*heat+4*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(3), "Heat "+(heat+15), /* withScores */ false, competitorsForHeatsInFinals[heat]);
|
||||
}
|
||||
// 1 instead of 0.7 for a final that was not sailed; clarified with Juergen Bonne in an e-mail as of 18-09-2015T09:03:00Z
|
||||
final double expectedPointsForFirstEightBoats = (1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0 + 8.0) / 8.0;
|
||||
for (int i=0; i<4; i++) {
|
||||
assertEquals(expectedPointsForFirstEightBoats, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
for (int i=32; i<36; i++) {
|
||||
assertEquals(expectedPointsForFirstEightBoats, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
final double expectedPointsForLastEightBoats = (9.0 + 10.0 + 11.0 + 12.0 + 13.0 + 14.0 + 15.0 + 16.0) / 8.0;
|
||||
for (int i=16; i<20; i++) {
|
||||
assertEquals(expectedPointsForLastEightBoats, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
for (int i=48; i<52; i++) {
|
||||
assertEquals(expectedPointsForLastEightBoats, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElminationScoringSchemeWithFinalSailedButNotTracked() throws NoWindException {
|
||||
Regatta regatta = createRegattaWithEliminations(1, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
Competitor[] c = createCompetitors(64).toArray(new Competitor[64]);
|
||||
// first round with 64 competitors, eight per heat:
|
||||
Competitor[][] competitorsForHeatsInRound1 = new Competitor[8][];
|
||||
TimePoint later = null;
|
||||
for (int heat=0; heat<8; heat++) {
|
||||
competitorsForHeatsInRound1[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInRound1[heat][i] = c[8*heat+i];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(0), "Heat "+(heat+1), /* withScores */ true, competitorsForHeatsInRound1[heat]);
|
||||
}
|
||||
// quarter-finals has promoted top four competitors of first round in heats with eight competitors each:
|
||||
Competitor[][] competitorsForHeatsInQuarterFinals = new Competitor[4][];
|
||||
for (int heat=0; heat<4; heat++) {
|
||||
competitorsForHeatsInQuarterFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInQuarterFinals[heat][i] = c[8*(2*heat+(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(1), "Heat "+(heat+9), /* withScores */ true, competitorsForHeatsInQuarterFinals[heat]);
|
||||
}
|
||||
// semi-finals has promoted top four competitors of quarter finals which are the top four of each other first-round heat:
|
||||
Competitor[][] competitorsForHeatsInSemiFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInSemiFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInSemiFinals[heat][i] = c[8*(4*heat+2*(i/4))+(i%4)];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(2), "Heat "+(heat+13), /* withScores */ true, competitorsForHeatsInSemiFinals[heat]);
|
||||
}
|
||||
// finals has promoted top four competitors of semi finals which are the top four of first and fifth first-round heats
|
||||
// for the final, and the top four of the first round's third and seventh heat; we assume here that the finals have not
|
||||
// been sailed and therefore expect the scores to be obtained by interpolating, using 1.0 instead of 0.7 for averaging.
|
||||
Competitor[][] competitorsForHeatsInFinals = new Competitor[2][];
|
||||
for (int heat=0; heat<2; heat++) {
|
||||
competitorsForHeatsInFinals[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
final Competitor comp = c[8*(2*heat+4*(i/4))+(i%4)];
|
||||
competitorsForHeatsInFinals[heat][i] = comp;
|
||||
leaderboard.getScoreCorrection().correctScore(comp, series.get(3).getRaceColumns().iterator().next(), heat==0&&i==0 ? 0.7 : 8*heat+i+1);
|
||||
}
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
assertEquals(i==0?0.7:i+1, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
for (int i=32; i<36; i++) {
|
||||
assertEquals(i-27, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
for (int i=16; i<20; i++) {
|
||||
assertEquals(i-7, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
for (int i=48; i<52; i++) {
|
||||
assertEquals(i-35, leaderboard.getTotalPoints(c[i], later), 0.000000001);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElminationScoringSchemeWithOnlyFirstRoundSailed() throws NoWindException {
|
||||
Regatta regatta = createRegattaWithEliminations(1, new int[] { 8, 4, 2, 2 }, "testBasicElminationScoringScheme",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
Competitor[] c = createCompetitors(64).toArray(new Competitor[64]);
|
||||
// first round with 64 competitors, eight per heat:
|
||||
Competitor[][] competitorsForHeatsInRound1 = new Competitor[8][];
|
||||
TimePoint later = null;
|
||||
for (int heat=0; heat<8; heat++) {
|
||||
competitorsForHeatsInRound1[heat] = new Competitor[8];
|
||||
for (int i=0; i<8; i++) {
|
||||
competitorsForHeatsInRound1[heat][i] = c[8*heat+i];
|
||||
}
|
||||
later = createAndAttachTrackedRaces(series.get(0), "Heat "+(heat+1), /* withScores */ true, competitorsForHeatsInRound1[heat]);
|
||||
}
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
// Clarified with Juergen Bonne in an e-mail as of 18-09-2015T09:03:00Z that a final race's winner
|
||||
// is scored with 0.7 only if the final race has actually been sailed. If the competitors are qualified
|
||||
// for the final race but it's not sailed, average scores are to be assigned to all competitors qualified
|
||||
// for the final race, but this average assumes 1.0 points for the first rank instead of the 0.7 assigned
|
||||
// to the winner if the race is actually sailed. Similarly, if not even the semi-finals have been sailed,
|
||||
// score averaging uses 1.0 for the first rank.
|
||||
final double expectedPointsForFirstEightBoats = (1.0 + 2.0 + 3.0 + 4.0 + 5.0 + 6.0 + 7.0 + 8.0) / 8.0;
|
||||
for (int i=0; i<8; i++) {
|
||||
assertEquals(expectedPointsForFirstEightBoats, leaderboard.getTotalPoints(c[8*i], later), 0.0000001);
|
||||
assertEquals("Competitor "+rankedCompetitors.get(i)+" not in list of best eight ",
|
||||
0, Arrays.asList(c).indexOf(rankedCompetitors.get(i))%8); // each first competitor in a heat ranks in the top 8
|
||||
}
|
||||
for (int i=1; i<=3; i++) {
|
||||
for (Competitor comp : c) {
|
||||
assertNull(leaderboard.getTotalPoints(comp, series.get(i).getRaceColumns().iterator().next(), later));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScoringConsideringNotAllRaces() throws NoWindException {
|
||||
// one discard at four races
|
||||
@@ -710,7 +1086,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[] { 4 });
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
|
||||
Map<RaceColumn, List<Competitor>> rankedCompetitorsFromBestToWorstAfterEachRaceColumn =
|
||||
leaderboard.getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(later);
|
||||
assertEquals(Arrays.asList(c[2], c[0], c[1], c[3]),
|
||||
@@ -763,8 +1139,8 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
/* medal */ true, "testTieBreakWithTwoVersusOneSeconds",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2);
|
||||
createAndAttachTrackedRaces(series.get(2), "Medal", m1);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2);
|
||||
createAndAttachTrackedRaces(series.get(2), "Medal", /* withScores */ true, m1);
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
// assert that both have equal score
|
||||
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
|
||||
@@ -786,7 +1162,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
|
||||
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
|
||||
@@ -806,7 +1182,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true),
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.HIGH_POINT));
|
||||
Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", f1, f2, f3, f4, f5, f6);
|
||||
TimePoint later = createAndAttachTrackedRaces(series.get(1), "Default", /* withScores */ true, f1, f2, f3, f4, f5, f6);
|
||||
List<Competitor> rankedCompetitors = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||
assertEquals(leaderboard.getTotalPoints(c[0], later), leaderboard.getTotalPoints(c[1], later), 0.000000001);
|
||||
assertEquals(rankedCompetitors.indexOf(c[0]), rankedCompetitors.indexOf(c[1])-1);
|
||||
@@ -1268,13 +1644,18 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
assertEquals(0., leaderboard1.getTotalPoints(c[3], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
|
||||
}
|
||||
|
||||
private TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, Competitor[]... competitorLists) {
|
||||
private TimePoint createAndAttachTrackedRaces(Series theSeries, String fleetName, boolean withScores, Competitor[]... competitorLists) {
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
|
||||
for (Competitor[] competitorList : competitorLists) {
|
||||
RaceColumn raceColumn = columnIter.next();
|
||||
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitorList));
|
||||
final TrackedRace trackedRace;
|
||||
if (withScores) {
|
||||
trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(now, Arrays.asList(competitorList));
|
||||
} else {
|
||||
trackedRace = new MockedTrackedRaceWithStartTimeAndZeroRanks(now, Arrays.asList(competitorList));
|
||||
}
|
||||
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
|
||||
}
|
||||
return later;
|
||||
@@ -1363,6 +1744,52 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
return regatta;
|
||||
}
|
||||
|
||||
private Regatta createRegattaWithEliminations(final int numberOfEliminations, final int[] numbersOfHeatsPerRound,
|
||||
final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||
series = new ArrayList<Series>();
|
||||
// example for numbersOfHeatsPerRound: [8, 4, 2, 2]
|
||||
for (int elimination=1; elimination<=numberOfEliminations; elimination++) {
|
||||
int heatNumber = 1;
|
||||
// create one elimination consisting of a number of rounds, each consisting of a number of heats
|
||||
int roundNumber = 1;
|
||||
for (int numberOfHeatsPerRound : numbersOfHeatsPerRound) {
|
||||
final boolean isFinalRound = roundNumber == numbersOfHeatsPerRound.length;
|
||||
// create one round as a series that has one fleet per heat
|
||||
List<Fleet> fleetsInRound = new ArrayList<Fleet>();
|
||||
for (int heatInRound=1; heatInRound<=numberOfHeatsPerRound; heatInRound++) {
|
||||
final int ordering = numbersOfHeatsPerRound.length-roundNumber+1+
|
||||
// in final round distinguish Final and Losers Final
|
||||
(isFinalRound ? heatInRound-1 : 1);
|
||||
fleetsInRound.add(new FleetImpl("Heat "+(heatNumber++), ordering));
|
||||
}
|
||||
List<String> raceColumnNameForRound = new ArrayList<String>();
|
||||
raceColumnNameForRound.add("E"+elimination+"R"+roundNumber);
|
||||
final String roundName;
|
||||
if (numbersOfHeatsPerRound.length-roundNumber == 2) {
|
||||
roundName = "Quarter-Final";
|
||||
} else if (numbersOfHeatsPerRound.length-roundNumber == 1) {
|
||||
roundName = "Semi-Final";
|
||||
} else if (numbersOfHeatsPerRound.length-roundNumber == 0) {
|
||||
roundName = "Final";
|
||||
} else {
|
||||
roundName = "Round "+roundNumber;
|
||||
}
|
||||
Series seriesForRound = new SeriesImpl("E"+elimination+" "+roundName, /* isMedal */ false, fleetsInRound,
|
||||
raceColumnNameForRound, /* trackedRegattaRegistry */null);
|
||||
if (isFinalRound) {
|
||||
// last "Final" round; here, the fleets are contiguously scored
|
||||
seriesForRound.setSplitFleetContiguousScoring(true);
|
||||
}
|
||||
series.add(seriesForRound);
|
||||
roundNumber++;
|
||||
}
|
||||
}
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
|
||||
/*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme,
|
||||
/* ID */ "123", /* course area */ null, OneDesignRankingMetric::new);
|
||||
return regatta;
|
||||
}
|
||||
|
||||
/**
|
||||
* See bug 1260. There must be a possibility to have ordered fleets that are scored such that the winner of the race of the best fleet
|
||||
* gets the best score in that column; and the winner of the second-best fleet gets the n-th best score in the column with n being the
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.sap.sailing.domain.test.mock;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
/**
|
||||
* Returns 0 for {@link #getRank(Competitor)} and {@link #getRank(Competitor, TimePoint)} in all cases. Still, the competitor
|
||||
* set participating in this race is maintained. This can be used, e.g., to mock a race that hasn't started yet and therefore
|
||||
* the competitors have no rank yet.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class MockedTrackedRaceWithStartTimeAndZeroRanks extends MockedTrackedRaceWithStartTimeAndRanks {
|
||||
private static final long serialVersionUID = 7726627832998127727L;
|
||||
|
||||
protected MockedTrackedRaceWithStartTimeAndZeroRanks(TimePoint startTime,
|
||||
List<Competitor> competitors, Regatta regatta) {
|
||||
super(startTime, competitors, regatta);
|
||||
}
|
||||
|
||||
public MockedTrackedRaceWithStartTimeAndZeroRanks(TimePoint startTime, List<Competitor> competitors) {
|
||||
super(startTime, competitors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRank(Competitor competitor) throws NoWindException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRank(Competitor competitor, TimePoint timePoint) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -50,6 +50,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsFive;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsSix;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPointWinnerGetsZero;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPointWithEliminationsAndRoundsWinnerGets07;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -113,6 +114,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain
|
||||
return new HighPointWinnerGetsEightAndInterpolation();
|
||||
case HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT:
|
||||
return new HighPointFirstGets10Or8AndLastBreaksTie();
|
||||
case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07:
|
||||
return new LowPointWithEliminationsAndRoundsWinnerGets07();
|
||||
}
|
||||
throw new RuntimeException("Unknown scoring scheme type "+scoringSchemeType.name());
|
||||
}
|
||||
|
||||
@@ -272,8 +272,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
|
||||
/**
|
||||
* {@link RaceColumnListeners} may not be de-serialized (yet) when the regatta
|
||||
* is de-serialized. Do avoid re-registering empty objects most probably leading
|
||||
* to null pointer exception one need to initialize all listeners after
|
||||
* is de-serialized. To avoid re-registering empty objects most probably leading
|
||||
* to null pointer exception one needs to initialize all listeners after
|
||||
* all objects have been read.
|
||||
*/
|
||||
public void initializeSeriesAfterDeserialize() {
|
||||
|
||||
@@ -354,7 +354,7 @@ public interface Leaderboard extends LeaderboardBase {
|
||||
* that column shall be considered for discarding and counts for determining the number of races so far. Also, if a
|
||||
* tracked race is connected to the column and has started already, the column is to be considered for discarding
|
||||
* unless the column has several unordered fleets and not all fleets have started their race yet (see
|
||||
* {@link ScoringScheme#isValidInTotalScore(Leaderboard, RaceColumn, TimePoint)}).
|
||||
* {@link ScoringScheme#isValidInTotalScore(Leaderboard, RaceColumn, Competitor, TimePoint)}).
|
||||
*/
|
||||
boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint);
|
||||
|
||||
|
||||
+8
-5
@@ -50,10 +50,11 @@ public interface ScoringScheme extends Serializable {
|
||||
* If the <code>competitor</code> has no {@link RaceColumn#getTrackedRace(Competitor) tracked race} in the column in which
|
||||
* the competitor participated, <code>null</code> is returned, meaning the competitor has no score assigned for that
|
||||
* race.
|
||||
* @param timePoint TODO
|
||||
*/
|
||||
Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank,
|
||||
Callable<Integer> numberOfCompetitorsInRaceFetcher,
|
||||
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher);
|
||||
Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor,
|
||||
int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher,
|
||||
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* If a competitor is disqualified, a penalty score is attributed by this scoring scheme. Some schemes require to
|
||||
@@ -94,9 +95,11 @@ public interface ScoringScheme extends Serializable {
|
||||
* Under certain circumstances, a scoring scheme may decide that the scores of a column are not (yet) to be used
|
||||
* for the leaderboard's total scores. This may, e.g., be the case if a column is split into more than one fleet and
|
||||
* those fleets are unordered. In that case, scores need to be available for all fleets before the column counts
|
||||
* for the total scores.
|
||||
* for the total scores. Another example is a scoring scheme that defines elimination rounds and awards no points
|
||||
* to a competitor in a round from which the competitor got promoted to the next round. Such promotion columns
|
||||
* then have no scores and don't count in the number of races starting from where discards are applied.<p>
|
||||
*/
|
||||
boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at);
|
||||
boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, TimePoint at);
|
||||
|
||||
/**
|
||||
* Some scoring schemes are applied to {@link LeaderboardGroupMetaLeaderboard} instances. These instances of a
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
* more races.
|
||||
*/
|
||||
@Override
|
||||
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at) {
|
||||
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, TimePoint at) {
|
||||
boolean result;
|
||||
Iterable<? extends Fleet> fleets = raceColumn.getFleets();
|
||||
if (Util.size(fleets) <= 1 || allFleetsOrdered(fleets)) {
|
||||
|
||||
+2
-2
@@ -532,7 +532,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
if (raceColumn.isStartsWithZeroScore()) {
|
||||
needToResetScoreUponNextNonEmptyEntry = true;
|
||||
}
|
||||
if (getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint)) {
|
||||
if (getScoringScheme().isValidInTotalScore(this, raceColumn, competitor, timePoint)) {
|
||||
final Double totalPoints = getTotalPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
if (totalPoints != null) {
|
||||
if (needToResetScoreUponNextNonEmptyEntry) {
|
||||
@@ -651,7 +651,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
@Override
|
||||
public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
|
||||
TrackedRace trackedRaceForCompetitorInColumn;
|
||||
return getScoringScheme().isValidInTotalScore(this, raceColumn, timePoint) &&
|
||||
return getScoringScheme().isValidInTotalScore(this, raceColumn, competitor, timePoint) &&
|
||||
(getScoreCorrection().isScoreCorrected(competitor, raceColumn, timePoint) ||
|
||||
((trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null &&
|
||||
trackedRaceForCompetitorInColumn.hasStarted(timePoint)));
|
||||
|
||||
+4
-4
@@ -27,9 +27,9 @@ public class HighPoint extends AbstractScoringSchemeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank,
|
||||
Callable<Integer> numberOfCompetitorsInRaceFetcher,
|
||||
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
|
||||
public Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor,
|
||||
int rank,
|
||||
Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
Double result;
|
||||
if (rank == 0) {
|
||||
result = null;
|
||||
@@ -74,7 +74,7 @@ public class HighPoint extends AbstractScoringSchemeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at) {
|
||||
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, TimePoint at) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -36,8 +36,8 @@ public class HighPointExtremeSailingSeriesOverall extends HighPoint {
|
||||
private static final int MIN_RACES_REQUIRED_TO_BE_SCORED = 5;
|
||||
|
||||
@Override
|
||||
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank,
|
||||
Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
|
||||
public Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor,
|
||||
int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
Double result;
|
||||
if (rank == 0) {
|
||||
result = null;
|
||||
|
||||
+4
-2
@@ -11,7 +11,9 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
@@ -87,8 +89,8 @@ public class HighPointFirstGets10Or8AndLastBreaksTie extends HighPointFirstGetsF
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
|
||||
Double effectiveScore = super.getScoreForRank(raceColumn, competitor, rank, numberOfCompetitorsInRaceFetcher, numberOfCompetitorsInLeaderboardFetcher);
|
||||
public Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
Double effectiveScore = super.getScoreForRank(leaderboard, raceColumn, competitor, rank, numberOfCompetitorsInRaceFetcher, numberOfCompetitorsInLeaderboardFetcher, timePoint);
|
||||
return checkForOverwrittenScore(raceColumn, rank, effectiveScore);
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -5,7 +5,9 @@ import java.util.concurrent.Callable;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
|
||||
/**
|
||||
@@ -41,7 +43,7 @@ public abstract class HighPointFirstGetsFixedScore extends HighPoint {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
|
||||
public Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
final int effectiveRank = getEffectiveRank(raceColumn, competitor, rank);
|
||||
final Double result;
|
||||
if (effectiveRank == 0) {
|
||||
|
||||
+3
-1
@@ -5,7 +5,9 @@ import java.util.concurrent.Callable;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
/**
|
||||
* High point system with a fixed 'highest score' (eight points) and
|
||||
@@ -31,7 +33,7 @@ public class HighPointWinnerGetsEightAndInterpolation extends HighPointFirstGets
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
|
||||
public Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
final int effectiveRank = getEffectiveRank(raceColumn, competitor, rank);
|
||||
final Double result;
|
||||
if (effectiveRank == 0) {
|
||||
|
||||
+2
-1
@@ -110,7 +110,8 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
for (RaceColumn raceColumn : getLeaderboard().getRaceColumns()) {
|
||||
needToResetO1ScoreUponNextValidResult = raceColumn.isStartsWithZeroScore();
|
||||
needToResetO2ScoreUponNextValidResult = raceColumn.isStartsWithZeroScore();
|
||||
if (getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, timePoint)) {
|
||||
if (getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, o1, timePoint)
|
||||
&& getLeaderboard().getScoringScheme().isValidInTotalScore(getLeaderboard(), raceColumn, o2, timePoint)) {
|
||||
int preemptiveColumnResult = 0;
|
||||
final Double o1Score = totalPointsCache.get(new Util.Pair<Competitor, RaceColumn>(o1, raceColumn));
|
||||
if (o1Score != null) {
|
||||
|
||||
+18
-6
@@ -27,10 +27,23 @@ public class LowPoint extends AbstractScoringSchemeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRaceFetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
|
||||
final int effectiveRank;
|
||||
public Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, int rank,
|
||||
Callable<Integer> numberOfCompetitorsInRaceFetcher,
|
||||
NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
final Double result;
|
||||
int competitorFleetOrdering;
|
||||
final int effectiveRank = getEffectiveRank(raceColumn, competitor, rank);
|
||||
result = effectiveRank == 0 ? null : (double) effectiveRank;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Considers contiguous scoring for split-fleet columns, finding out in which fleet the <code>competitor</code> races
|
||||
* in the <code>raceColumn</code> and figuring out how many competitors race in better fleets. For non-contiguous
|
||||
* scoring, the effective rank equals the <code>rank</code> in the competitor's fleet.
|
||||
*/
|
||||
protected int getEffectiveRank(RaceColumn raceColumn, Competitor competitor, int rank) {
|
||||
final int competitorFleetOrdering;
|
||||
final int effectiveRank;
|
||||
if (rank == 0) {
|
||||
effectiveRank = 0;
|
||||
} else if (raceColumn.hasSplitFleetContiguousScoring() && (competitorFleetOrdering=raceColumn.getFleetOfCompetitor(competitor).getOrdering()) != 0) {
|
||||
@@ -39,8 +52,7 @@ public class LowPoint extends AbstractScoringSchemeImpl {
|
||||
} else {
|
||||
effectiveRank = rank;
|
||||
}
|
||||
result = effectiveRank == 0 ? null : (double) effectiveRank;
|
||||
return result;
|
||||
return effectiveRank;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +73,7 @@ public class LowPoint extends AbstractScoringSchemeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint at) {
|
||||
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, TimePoint at) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -5,7 +5,9 @@ import java.util.concurrent.Callable;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
/**
|
||||
* As compared to the regular low-point scheme, the 2013 ISAF World Cup will score the winner of a race with
|
||||
@@ -31,7 +33,7 @@ public class LowPointWinnerGetsZero extends LowPoint {
|
||||
private static final long serialVersionUID = 3405249102284162690L;
|
||||
|
||||
@Override
|
||||
public Double getScoreForRank(RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRacefetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher) {
|
||||
public Double getScoreForRank(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, int rank, Callable<Integer> numberOfCompetitorsInRacefetcher, NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
return rank == 0 ? null : rank == 1 ? 0.0 : (double) rank;
|
||||
}
|
||||
|
||||
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnInSeries;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Implements an elimination scheme as used by surfing championships, based on a general low point scoring scheme. A
|
||||
* regatta is divided into "eliminations" where each elimination consists of a sequence of "rounds." Each round is in
|
||||
* turn divided into a number of "heats" (races). The final round in an elimination consists of a "final" race and a
|
||||
* "losers final" race. They constitute, in our terminology, one series with contiguous scoring. The winner of the
|
||||
* "losers final" gets one point more than the competitor ranking last in the "final" race. The winner of the final race
|
||||
* obtains 0.7 points. All other ranks are assigned points equal to the rank or the average of the ranks attained by
|
||||
* competitors ranking equal.
|
||||
* <p>
|
||||
*
|
||||
* The semi-final has two heats, and the better half of the competitors in those heats get promoted to the "final," the
|
||||
* competitors ranking worse will participate in the "losers final." Points are usually not awarded in the semi-final
|
||||
* (exceptions for races not sailed see below).
|
||||
* <p>
|
||||
*
|
||||
* In the quarter-final there are four heats. Usually, the competitors ranking in the top half of their heat are
|
||||
* promoted to the semi-final and are not awarded points for the quarter-final round (exceptions for races not sailed
|
||||
* see below). However, the race committee may decide how many competitors are promoted, also based on the number of
|
||||
* participant and resulting number and set-up of heats. Therefore, successful promotion can only be detected by looking
|
||||
* at the competitor assignments in the next round.
|
||||
* <p>
|
||||
*
|
||||
* Those competitors ranking in the bottom part of their heat are not promoted to the next round and are instead awarded
|
||||
* points for the elimination based on their rank in their heat. Across all quarter-final heats, competitors not
|
||||
* promoted and having the same rank obtain equal points regardless their heat. The number of points is calculated as
|
||||
* the average of their rank in the elimination. Quarter-final participants rank better than those eliminated in earlier
|
||||
* rounds. Eliminated competitors rank better in the elimination if they obtained a better rank in their heat than other
|
||||
* competitors eliminated in the same round. For example, if each of the four quarter-final heat has eight competitors,
|
||||
* the four competitors ranking last (8th) in their heat get the average points for ranks 29-32 (30.5 points), whereas
|
||||
* the four competitors ranking 7th in their heat get the average points for ranks 25-28 (26.5 points), and so on.
|
||||
* <p>
|
||||
*
|
||||
* Other rounds preceding the quarter-final work by the same principle as the quarter-final.
|
||||
* <p>
|
||||
*
|
||||
* Should a planned heat not be sailed then all competitors assigned to that heat will obtain equal points based on the
|
||||
* average of the ranks for which they would have sailed. For example, if the final heat cannot be sailed, all
|
||||
* competitors who qualified for the final race obtain the average of 0.7, 2, 3, 4, 5, 6, 7 and 8 points (4.46 points).
|
||||
* <p>
|
||||
*
|
||||
* During live tracking promotions cannot generally be decided due to the race committee's decision authority.
|
||||
* Therefore, also those competitors who ultimately will be promoted have to be given scores in their heat based on the
|
||||
* average points obtained by all competitors taking the same rank in their respective heat in the same round. This will
|
||||
* usually lead to a change in points awarded to the competitors promoted as soon as the promotion becomes "official"
|
||||
* which technically needs to be expressed by assigning the competitors to their heat in the next round. As soon as this
|
||||
* happens, the scores in the heat from which a competitor got promoted are set to <code>null</code>, and a score
|
||||
* corresponding to the average points given to the competitors of the heat to which the competitor got promoted is
|
||||
* inferred for that heat. Only when the race in that next heat produces non-zero ranks, the scoring rules for that
|
||||
* heat are applied again normally.<p>
|
||||
*
|
||||
* The regatta is expected to be modeled such that fleet ordering numbers are used in a special way. Ordering
|
||||
* <code>1</code> is to be used to identify the Final heat, <code>2</code> for the Losers Final heat. For each earlier
|
||||
* round the ordering is increased by one and applied equally to all heats of that round. Therefore, the two semi-final
|
||||
* heats use ordering <code>3</code>, the four quarter-final heats use <code>4</code>, and so on.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class LowPointWithEliminationsAndRoundsWinnerGets07 extends LowPoint {
|
||||
private static final long serialVersionUID = -2318652113347853873L;
|
||||
|
||||
private static int LOSERS_FINAL_FLEET_ORDERING = 2;
|
||||
|
||||
@Override
|
||||
public ScoringSchemeType getType() {
|
||||
return ScoringSchemeType.LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07;
|
||||
}
|
||||
|
||||
/**
|
||||
* The competitor gets a score in <code>raceColumn</code> if she does not participate in a race in the round that
|
||||
* follows the round represented by <code>raceColumn</code>. If <code>raceColumn</code> represents the last round in
|
||||
* the elimination, <code>competitor</code> can not participate in a next round and therefore obtains a score. This
|
||||
* can either happen when <code>raceColumn</code> is a final round or if the elimination has been aborted and no
|
||||
* further rounds exist in that elimination. The competitor is considered to participate in the next round if an
|
||||
* explicit score correction exists for the <code>competitor</code> for the next round, or if a tracked race is
|
||||
* attached to the race column following <code>raceColumn</code> that lists <code>competitor</code> as part of its
|
||||
* competitors.
|
||||
* <p>
|
||||
*
|
||||
* If the <code>competitor</code> is to receive points for <code>raceColumn</code>, the following rules apply. For a
|
||||
* <code>0</code> rank this method assumes that the race has not started yet (and it could happen that it is never
|
||||
* started at all); in this case the number of points is calculated as the arithmetic mean of all points for which
|
||||
* the participants of the heat would race, with the first rank counting as 1.0 for this average. If the
|
||||
* <code>rank</code> is not <code>0</code> then the competitor receives the average of the points given to all
|
||||
* competitors from all heats in this round that rank equal when sorting the competitors of that round in the
|
||||
* leaderboard by their rank. For example, if there are two heats in the semi-final with eight competitors each, the
|
||||
* two competitors ranking second in their heat will get the average of 3.0 and 4.0 points, so 3.5 points.<p>
|
||||
*
|
||||
* In the exceptional case of the final round, 0.7 points are awarded to the first rank. This special rules only
|
||||
* applies if a <code>rank</code> 1 is actually observed. It does not apply to the averaging process that occurs
|
||||
* should the final not have been sailed (yet) such that the final participants receive the points average of
|
||||
* 1.0, 2.0, ..., <<i>number-of-final-participants</i>><p>
|
||||
*/
|
||||
@Override
|
||||
public Double getScoreForRank(final Leaderboard leaderboard, final RaceColumn raceColumn, final Competitor competitor, final int rank,
|
||||
final Callable<Integer> numberOfCompetitorsInRaceFetcher,
|
||||
final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher, TimePoint timePoint) {
|
||||
assert raceColumn instanceof RaceColumnInSeries;
|
||||
// Clarified with Juergen Bonne in an e-mail as of 18-09-2015T09:03:00Z that a final race's winner
|
||||
// is scored with 0.7 only if the final race has actually been sailed. If the competitors are qualified
|
||||
// for the final race but it's not sailed, average scores are to be assigned to all competitors qualified
|
||||
// for the final race, but this average assumes 1.0 points for the first rank instead of the 0.7 assigned
|
||||
// to the winner if the race is actually sailed.
|
||||
final RaceColumnInSeries raceColumnInSeries = (RaceColumnInSeries) raceColumn;
|
||||
final Double result;
|
||||
final Series series = ((RaceColumnInSeries) raceColumn).getSeries();
|
||||
final Regatta regatta = series.getRegatta();
|
||||
Iterator<? extends Series> seriesInRegattaIter=regatta.getSeries().iterator();
|
||||
Series seriesInRegatta = null;
|
||||
while (seriesInRegattaIter.hasNext() && (seriesInRegatta=seriesInRegattaIter.next()) != series)
|
||||
;
|
||||
if (seriesInRegatta == null) {
|
||||
throw new IllegalArgumentException("Race column "+raceColumn+" not found in its owning regatta");
|
||||
}
|
||||
final Fleet fleetOfCompetitor = raceColumn.getFleetOfCompetitor(competitor);
|
||||
if (fleetOfCompetitor == null || participatesInNextRound(leaderboard, raceColumnInSeries, competitor, timePoint, seriesInRegattaIter)) {
|
||||
// the competitor does not participate in this round according to the set of tracked races
|
||||
// attached to the raceColumn or obtains a score in the next round which means that the
|
||||
// competitor will not get a score yet for the raceColumn passed
|
||||
result = null;
|
||||
} else {
|
||||
assert fleetOfCompetitor != null;
|
||||
if (rank == 0) {
|
||||
// calculate the average rank of all competitors of the heat; for contiguous scoring across heats (final round)
|
||||
// this is the average of the points (giving 1.0 for first rank in the final) in the single heat; for rounds
|
||||
// with unordered heats (fleets all having equal ordering, non-contiguous series scoring) this is the average
|
||||
// of all the points assigned to all the ranks occupied in the elimination by the participants of this round.
|
||||
if (series.hasSplitFleetContiguousScoring()) {
|
||||
// final round; check how many competitors are in better fleet; then average between (that number plus 1)
|
||||
// and (that number plus the number of competitors in competitor's fleet)
|
||||
final int competitorFleetOrdering = fleetOfCompetitor.getOrdering();
|
||||
final int numberOfCompetitorsInBetterFleet = getNumberOfCompetitorsInBetterFleets(raceColumnInSeries, competitorFleetOrdering);
|
||||
final TrackedRace finalTrackedRaceOfCompetitor = raceColumn.getTrackedRace(competitor); // assumed to be != null because fleetOfCompetitor != null
|
||||
final int numberOfCompetitorsInCompetitorsFleet = Util.size(finalTrackedRaceOfCompetitor.getRace().getCompetitors());
|
||||
result = ((double)numberOfCompetitorsInBetterFleet + 1.0 + // best score in competitor's fleet
|
||||
(double)numberOfCompetitorsInBetterFleet + (double)numberOfCompetitorsInCompetitorsFleet) // worst score in competitor's fleet
|
||||
/2.0;
|
||||
} else {
|
||||
// count competitors in this round and average the points, assuming that the participants that got promoted
|
||||
// into this non-final round will race for scores 1.0 .. <number-of-competitors-in-the-round>.
|
||||
int numberOfCompetitorsInRound = 0;
|
||||
for (final Fleet fleet : raceColumn.getFleets()) {
|
||||
final TrackedRace trackedRaceInRound = raceColumn.getTrackedRace(fleet);
|
||||
if (trackedRaceInRound != null) {
|
||||
numberOfCompetitorsInRound += Util.size(trackedRaceInRound.getRace().getCompetitors());
|
||||
}
|
||||
}
|
||||
result = ((double) numberOfCompetitorsInRound + 1.0) / 2.0;
|
||||
}
|
||||
} else {
|
||||
// rank is non-zero; there is a tracked race that provides a valid score for the competitor
|
||||
final int effectiveRank = getEffectiveRank(raceColumn, competitor, rank);
|
||||
if (series.hasSplitFleetContiguousScoring()) {
|
||||
result = effectiveRank == 1 ? 0.7 : effectiveRank;
|
||||
} else {
|
||||
final int numberOfCompetitorsInBetterFleets = getNumberOfCompetitorsInBetterFleets(raceColumnInSeries, fleetOfCompetitor.getOrdering());
|
||||
final int numberOfFleetsWithEqualOrder = getNumberOfFleetsWithOrdering(series, fleetOfCompetitor.getOrdering());
|
||||
final int numberOfCompetitorsWithSameRankInRound = getNumberOfCompetitorsWithSameRankInRound(raceColumnInSeries, competitor, effectiveRank);
|
||||
final int bestOverallRankForAnyEqualRankingCompetitor =
|
||||
numberOfCompetitorsInBetterFleets + 1 + // best possible score for any competitor in competitor's fleet
|
||||
numberOfFleetsWithEqualOrder*(effectiveRank-1); // number of places assigned to better competitors in own and other equal fleets
|
||||
final int worstOverallRankForAnyEqualRankingCompetitor =
|
||||
bestOverallRankForAnyEqualRankingCompetitor + numberOfCompetitorsWithSameRankInRound - 1;
|
||||
result = ((double) bestOverallRankForAnyEqualRankingCompetitor + (double) worstOverallRankForAnyEqualRankingCompetitor) / 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidInTotalScore(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, TimePoint at) {
|
||||
final RaceColumnInSeries raceColumnInSeries = (RaceColumnInSeries) raceColumn;
|
||||
final Iterator<? extends Series> seriesInRegattaIter = raceColumnInSeries.getSeries().getRegatta().getSeries().iterator();
|
||||
while (seriesInRegattaIter.hasNext() && seriesInRegattaIter.next() != raceColumnInSeries.getSeries())
|
||||
;
|
||||
return !participatesInNextRound(leaderboard, (RaceColumnInSeries) raceColumn, competitor, at, seriesInRegattaIter);
|
||||
}
|
||||
|
||||
private int getNumberOfFleetsWithOrdering(Series series, int ordering) {
|
||||
int result = 0;
|
||||
for (final Fleet fleet : series.getFleets()) {
|
||||
if (fleet.getOrdering() == ordering) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A competitor can participate in the next round of an elimination after having been promoted from
|
||||
* <code>raceColumnInSeries</code>. If the round represented by <code>raceColumnInSeries</code> is the last round in
|
||||
* the elimination, the competitor obviously cannot participate in any next round, therefore <code>false</code> is
|
||||
* returned in this case. Otherwise, next round participation is decided by first looking for a score correction.
|
||||
* The score correction for the next column has to be valid at <code>timePoint</code>, then the competitor is considered
|
||||
* having participated in that next round at <code>timePoint</code>. If no score correction is found for the next column,
|
||||
* the next column is checked for a tracked race in which <code>competitor</code> competes. If such a race is found,
|
||||
* the question remains whether this participation is considered "active" at <code>timePoint</code>. This is assumed
|
||||
* if in the current round represented by <code>raceColumnInSeries</code> no tracked race is found for <code>competitor</code>
|
||||
* (unlikely, but it means we cannot tell whether <code>competitor</code> has already finished her current race, and by
|
||||
* default we then have to assume that it's over) or the end of that race was before <code>timePoint</code>.<p>
|
||||
*
|
||||
* This will keep <code>competitor</code>'s score in <code>raceColumnInSeries</code> for time points where the race
|
||||
* in this column is still running. Only after the race has finished will the promotion become active and competitor
|
||||
* lose her points in the current column.
|
||||
*/
|
||||
private boolean participatesInNextRound(Leaderboard leaderboard, RaceColumnInSeries raceColumnInSeries,
|
||||
Competitor competitor, TimePoint timePoint, Iterator<? extends Series> seriesInRegattaIter) {
|
||||
final boolean result;
|
||||
if (isLastRoundInElimination(raceColumnInSeries, seriesInRegattaIter)) {
|
||||
result = false;
|
||||
} else {
|
||||
final Series nextSeries = seriesInRegattaIter.next();
|
||||
final TrackedRace trackedRace;
|
||||
result = leaderboard.getScoreCorrection().isScoreCorrected(competitor, nextSeries.getRaceColumns().iterator().next(), timePoint) ||
|
||||
(nextSeries.getRaceColumns().iterator().next().getFleetOfCompetitor(competitor) != null &&
|
||||
((trackedRace=raceColumnInSeries.getTrackedRace(competitor)) == null || trackedRace.getEndOfRace() == null ||
|
||||
trackedRace.getEndOfRace().before(timePoint)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumption: no fleet has a number of competitors exceeding that of any other fleet in the same round by two or
|
||||
* more.
|
||||
* <p>
|
||||
*
|
||||
* Background: an additional competitor will always be added to the fleet with the fewest competitors, implying
|
||||
* above rule.
|
||||
* <p>
|
||||
*
|
||||
* Reason for assumption: with this, we can infer that there will be as many competitors ranking one rank better
|
||||
* than "rank" as there are fleets; were the assumption not fulfilled then there could be a fleet in the round that
|
||||
* has no competitor ranking one rank better. With this assumption we easily infer the best possible rank for all
|
||||
* competitors with "rank" which is the number of fleets in the round times "rank" plus 1.
|
||||
* <p>
|
||||
*
|
||||
* Example: If rank is 7 and we have eight fleets, the best rank for all competitors with this rank will be 7*8+1 =
|
||||
* 57. The worst rank for all competitors with "rank" therefore can be calculated by the adding to the best rank the
|
||||
* number of competitors with this rank. We have to assume that this number may not be equal to the number of heats
|
||||
* in this round because with different heat sizes in a round the worst rank may be different for different heats
|
||||
* (e.g., 7 in some, and 8 in others, therefore fewer 8th ranks than 7th ranks will exist). This "uneven" case can
|
||||
* only apply if the competitor ranks last in the heat which is easy to exclude by looking at the tracked race, from
|
||||
* there the RaceDefinition and for that the number of competitors in that race.
|
||||
*/
|
||||
private int getNumberOfCompetitorsWithSameRankInRound(final RaceColumnInSeries raceColumn, final Competitor competitor, final int rank) {
|
||||
final int numberOfCompetitorsWithSameRankInRound;
|
||||
if (raceColumn.getSeries().hasSplitFleetContiguousScoring()) {
|
||||
numberOfCompetitorsWithSameRankInRound = 1;
|
||||
} else {
|
||||
final Fleet fleet = raceColumn.getFleetOfCompetitor(competitor);
|
||||
if (fleet == null) {
|
||||
// Can't determine number of competitors in heat; assuming equal fleet distribution
|
||||
numberOfCompetitorsWithSameRankInRound = Util.size(raceColumn.getSeries().getFleets());
|
||||
} else {
|
||||
final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet);
|
||||
final boolean isWorstRank = rank == Util.size(trackedRace.getRace().getCompetitors());
|
||||
if (!isWorstRank) {
|
||||
numberOfCompetitorsWithSameRankInRound = getNumberOfFleetsWithSameOrderingAsCompetitor(raceColumn, competitor, fleet);
|
||||
} else {
|
||||
// for all other fleets in the round that have tracked races attached, we can count how many of them
|
||||
// have fewer than "rank" competitors; for fleets with no tracked race attached we then again assume
|
||||
// maximum competitor number
|
||||
int count = 0;
|
||||
for (final Fleet f : raceColumn.getSeries().getFleets()) {
|
||||
if (f.getOrdering() == fleet.getOrdering()) { // only consider fleets with equal order to that of competitor's
|
||||
final TrackedRace trackedRaceForFleet = raceColumn.getTrackedRace(f);
|
||||
if (trackedRaceForFleet == null) {
|
||||
count++; // by default assume that other race has at least as many competitors as the competitor's heat
|
||||
} else {
|
||||
if (Util.size(trackedRaceForFleet.getRace().getCompetitors()) >= rank) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
numberOfCompetitorsWithSameRankInRound = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return numberOfCompetitorsWithSameRankInRound;
|
||||
}
|
||||
|
||||
private int getNumberOfFleetsWithSameOrderingAsCompetitor(final RaceColumnInSeries raceColumn, Competitor competitor, Fleet fleetOfCompetitor) {
|
||||
int result = 0;
|
||||
final int orderingOfCompetitorsFleet = fleetOfCompetitor.getOrdering();
|
||||
for (final Fleet fleet : raceColumn.getFleets()) {
|
||||
if (fleet.getOrdering() == orderingOfCompetitorsFleet) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isLastRoundInElimination(RaceColumnInSeries raceColumn, Iterator<? extends Series> seriesInRegattaIter) {
|
||||
final boolean result;
|
||||
if (raceColumn.getFleets().iterator().next().getOrdering() <= LOSERS_FINAL_FLEET_ORDERING) {
|
||||
// the fleet ordering indicates that raceColumn represents a final round in an elimination
|
||||
result = true;
|
||||
} else {
|
||||
// check if there is a next column
|
||||
result = !seriesInRegattaIter.hasNext();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -401,13 +401,13 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
|
||||
public Double getUncorrectedScore() {
|
||||
Double resultUncorrected = 0.0;
|
||||
try {
|
||||
resultUncorrected = scoringScheme.getScoreForRank(raceColumn, competitor,
|
||||
trackedRankProvider.call(), new Callable<Integer>() {
|
||||
resultUncorrected = scoringScheme.getScoreForRank(leaderboard, raceColumn,
|
||||
competitor, trackedRankProvider.call(), new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() {
|
||||
return getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher);
|
||||
}
|
||||
}, numberOfCompetitorsInLeaderboardFetcher);
|
||||
}, numberOfCompetitorsInLeaderboardFetcher, timePoint);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -448,13 +448,13 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
|
||||
if (correctedNonMaxedScore == null || isCertainlyBeforeRaceFinish(timePoint, raceColumn, competitor)) {
|
||||
try {
|
||||
int trackedRank = trackedRankProvider.call();
|
||||
result = scoringScheme.getScoreForRank(raceColumn, competitor, trackedRank,
|
||||
new Callable<Integer>() {
|
||||
result = scoringScheme.getScoreForRank(leaderboard, raceColumn, competitor,
|
||||
trackedRank, new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() {
|
||||
return getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher);
|
||||
}
|
||||
}, numberOfCompetitorsInLeaderboardFetcher);
|
||||
}, numberOfCompetitorsInLeaderboardFetcher, timePoint);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Exception while computing corrected non maxed score", e);
|
||||
throw new RuntimeException(e);
|
||||
|
||||
+56
-36
@@ -82,7 +82,19 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
TreeSet<Candidate> fixedPasses = new TreeSet<Candidate>(new Comparator<Candidate>() {
|
||||
@Override
|
||||
public int compare(Candidate o1, Candidate o2) {
|
||||
return o1.getOneBasedIndexOfWaypoint() - o2.getOneBasedIndexOfWaypoint();
|
||||
final int result;
|
||||
if (o1 == null) {
|
||||
if (o2 == null) {
|
||||
result = 0;
|
||||
} else {
|
||||
result = -1;
|
||||
}
|
||||
} else if (o2 == null) {
|
||||
result = 1;
|
||||
} else {
|
||||
result = o1.getOneBasedIndexOfWaypoint() - o2.getOneBasedIndexOfWaypoint();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
fixedPassings.put(c, fixedPasses);
|
||||
@@ -133,14 +145,16 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
public void setFixedPassing(Competitor c, Integer zeroBasedIndexOfWaypoint, TimePoint t) {
|
||||
Candidate fixedCan = new CandidateImpl(zeroBasedIndexOfWaypoint + 1, t, 1, Util.get(race.getRace().getCourse().getWaypoints(), zeroBasedIndexOfWaypoint));
|
||||
NavigableSet<Candidate> fixed = fixedPassings.get(c);
|
||||
if (!fixed.add(fixedCan)) {
|
||||
Candidate old = fixed.ceiling(fixedCan);
|
||||
fixed.remove(old);
|
||||
removeCandidates(c, Arrays.asList(old));
|
||||
fixed.add(fixedCan);
|
||||
if (fixed != null) { // can only set the mark passing if the competitor is still part of this race
|
||||
if (!fixed.add(fixedCan)) {
|
||||
Candidate old = fixed.ceiling(fixedCan);
|
||||
fixed.remove(old);
|
||||
removeCandidates(c, Arrays.asList(old));
|
||||
fixed.add(fixedCan);
|
||||
}
|
||||
addCandidates(c, Arrays.asList(fixedCan));
|
||||
findShortestPath(c);
|
||||
}
|
||||
addCandidates(c, Arrays.asList(fixedCan));
|
||||
findShortestPath(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,9 +166,11 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fixedPassings.get(c).remove(toRemove);
|
||||
removeCandidates(c, Arrays.asList(toRemove));
|
||||
findShortestPath(c);
|
||||
if (toRemove != null) {
|
||||
fixedPassings.get(c).remove(toRemove);
|
||||
removeCandidates(c, Arrays.asList(toRemove));
|
||||
findShortestPath(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -294,30 +310,34 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
0, race.getRace().getCourse().getNumberOfWaypoints()), 0.0));
|
||||
while (!endFound) {
|
||||
Util.Pair<Edge, Double> cheapestEdgeWithCost = currentEdgesCheapestFirst.pollFirst();
|
||||
Edge currentCheapestEdge = cheapestEdgeWithCost.getA();
|
||||
Double currentCheapestCost = cheapestEdgeWithCost.getB();
|
||||
// If the shortest path to this candidate is already known the new edge is not added.
|
||||
if (!candidateWithParentAndSmallestTotalCost.containsKey(currentCheapestEdge.getEnd())) {
|
||||
// The cheapest edge taking us to currentCheapestEdge.getEnd() is found. Remember it.
|
||||
candidateWithParentAndSmallestTotalCost.put(currentCheapestEdge.getEnd(), new Util.Pair<Candidate, Double>(
|
||||
currentCheapestEdge.getStart(), currentCheapestCost));
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("Added "+ currentCheapestEdge + "as cheapest edge for " + c);
|
||||
}
|
||||
endFound = currentCheapestEdge.getEnd() == endOfFixedInterval;
|
||||
if (!endFound) {
|
||||
// the end of the segment was not yet found; add edges leading away from
|
||||
// currentCheapestEdge.getEnd(), summing up their cost with the cost required
|
||||
// to reach currentCheapestEdge.getEnd()
|
||||
Set<Edge> edgesForNewCandidate = allCompetitorEdges.get(currentCheapestEdge.getEnd());
|
||||
if (edgesForNewCandidate != null) {
|
||||
for (Edge e : edgesForNewCandidate) {
|
||||
int oneBasedIndexOfEndOfEdge = e.getEnd().getOneBasedIndexOfWaypoint();
|
||||
// only add edge if it stays within the current segment, not exceeding
|
||||
// the next fixed mark passing
|
||||
if (oneBasedIndexOfEndOfEdge <= indexOfEndOfFixedInterval
|
||||
&& (oneBasedIndexOfEndOfEdge < oneBasedIndexOfSuppressedWaypoint || e.getEnd() == end)) {
|
||||
currentEdgesCheapestFirst.add(new Util.Pair<Edge, Double>(e, currentCheapestCost + e.getCost()));
|
||||
if (cheapestEdgeWithCost == null) {
|
||||
endFound = true;
|
||||
} else {
|
||||
Edge currentCheapestEdge = cheapestEdgeWithCost.getA();
|
||||
Double currentCheapestCost = cheapestEdgeWithCost.getB();
|
||||
// If the shortest path to this candidate is already known the new edge is not added.
|
||||
if (!candidateWithParentAndSmallestTotalCost.containsKey(currentCheapestEdge.getEnd())) {
|
||||
// The cheapest edge taking us to currentCheapestEdge.getEnd() is found. Remember it.
|
||||
candidateWithParentAndSmallestTotalCost.put(currentCheapestEdge.getEnd(), new Util.Pair<Candidate, Double>(
|
||||
currentCheapestEdge.getStart(), currentCheapestCost));
|
||||
if (logger.isLoggable(Level.FINEST)) {
|
||||
logger.finest("Added "+ currentCheapestEdge + "as cheapest edge for " + c);
|
||||
}
|
||||
endFound = currentCheapestEdge.getEnd() == endOfFixedInterval;
|
||||
if (!endFound) {
|
||||
// the end of the segment was not yet found; add edges leading away from
|
||||
// currentCheapestEdge.getEnd(), summing up their cost with the cost required
|
||||
// to reach currentCheapestEdge.getEnd()
|
||||
Set<Edge> edgesForNewCandidate = allCompetitorEdges.get(currentCheapestEdge.getEnd());
|
||||
if (edgesForNewCandidate != null) {
|
||||
for (Edge e : edgesForNewCandidate) {
|
||||
int oneBasedIndexOfEndOfEdge = e.getEnd().getOneBasedIndexOfWaypoint();
|
||||
// only add edge if it stays within the current segment, not exceeding
|
||||
// the next fixed mark passing
|
||||
if (oneBasedIndexOfEndOfEdge <= indexOfEndOfFixedInterval
|
||||
&& (oneBasedIndexOfEndOfEdge < oneBasedIndexOfSuppressedWaypoint || e.getEnd() == end)) {
|
||||
currentEdgesCheapestFirst.add(new Util.Pair<Edge, Double>(e, currentCheapestCost + e.getCost()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,7 +468,7 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
|
||||
private void removeCandidates(Competitor c, Iterable<Candidate> wrongCandidates) {
|
||||
for (Candidate can : wrongCandidates) {
|
||||
logger.finest("Removing all edges containing " + can.toString() + "of "+ c);
|
||||
logger.finest("Removing all edges containing " + can + "of "+ c);
|
||||
candidates.get(c).remove(can);
|
||||
Map<Candidate, Set<Edge>> edges = allEdges.get(c);
|
||||
edges.remove(can);
|
||||
|
||||
+5
-4
@@ -40,6 +40,7 @@ public class BoatClassImageResolver {
|
||||
boatClassIconsMap.put(BoatClassMasterdata.CANOE_TAIFUN.getDisplayName(), imageResources.CanoeTaifunIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.CONTENDER.getDisplayName(), imageResources.ContenderIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.DRAGON_INT.getDisplayName(), imageResources.DragonIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.DYAS.getDisplayName(), imageResources.DyasIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.EUROPE_INT.getDisplayName(), imageResources.EuropeIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.EXTREME_40.getDisplayName(), imageResources.Extreme40Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.D_ONE.getDisplayName(), imageResources.DOneIcon());
|
||||
@@ -55,6 +56,7 @@ public class BoatClassImageResolver {
|
||||
boatClassIconsMap.put(BoatClassMasterdata.HOBIE_16.getDisplayName(), imageResources.HobieIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.HOBIE_TIGER.getDisplayName(), imageResources.HobieIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.HOBIE_WILD_CAT.getDisplayName(), imageResources.HobieIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.INTERNATIONAL_14.getDisplayName(), imageResources.International14Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.J22.getDisplayName(), imageResources.J22Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.J24.getDisplayName(), imageResources.J24Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.J70.getDisplayName(), imageResources.J70Icon());
|
||||
@@ -72,9 +74,12 @@ public class BoatClassImageResolver {
|
||||
boatClassIconsMap.put(BoatClassMasterdata.NACRA_17.getDisplayName(), imageResources.Nacra17Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.O_JOLLE.getDisplayName(), imageResources.OJolleIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.OK.getDisplayName(), imageResources.OKIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.OPEN_BIC.getDisplayName(), imageResources.OpenBicIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.OPTIMIST.getDisplayName(), imageResources.OptimistIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.PIRATE.getDisplayName(), imageResources.PiratIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.PLATU_25.getDisplayName(), imageResources.Platu25Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.PWA.getDisplayName(), imageResources.PWAIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.RS800.getDisplayName(), imageResources.RS800Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.RS_X.getDisplayName(), imageResources.RSXIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.RS_FEVA.getDisplayName(), imageResources.RSFevaIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.SONAR.getDisplayName(), imageResources.SonarIcon());
|
||||
@@ -85,10 +90,6 @@ public class BoatClassImageResolver {
|
||||
boatClassIconsMap.put(BoatClassMasterdata.TORNADO.getDisplayName(), imageResources.TornadoIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.TRIAS.getDisplayName(), imageResources.TriasIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.X_99.getDisplayName(), imageResources.X99Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.INTERNATIONAL_14.getDisplayName(), imageResources.International14Icon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.DYAS.getDisplayName(), imageResources.DyasIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.OPEN_BIC.getDisplayName(), imageResources.OpenBicIcon());
|
||||
boatClassIconsMap.put(BoatClassMasterdata.RS800.getDisplayName(), imageResources.RS800Icon());
|
||||
}
|
||||
|
||||
public static ImageResource getBoatClassIconResource(String displayName) {
|
||||
|
||||
+3
@@ -196,4 +196,7 @@ public interface BoatClassImageResources extends ClientBundle {
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/D_ONE.png")
|
||||
ImageResource DOneIcon();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/boatclass/PWA.png")
|
||||
ImageResource PWAIcon();
|
||||
|
||||
}
|
||||
+5
-1
@@ -212,7 +212,7 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement
|
||||
|
||||
raceSelectionProvider = new RaceSelectionModel();
|
||||
trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher,
|
||||
raceSelectionProvider, stringMessages, /* multiselection */false, /* actionButtonsEnabled */ false);
|
||||
raceSelectionProvider, stringMessages, /* multiselection */false, isActionButtonsEnabled());
|
||||
trackedRacesListComposite.ensureDebugId("TrackedRacesListComposite");
|
||||
trackedRacesPanel.add(trackedRacesListComposite);
|
||||
trackedRacesListComposite.addTrackedRaceChangeListener(this);
|
||||
@@ -253,6 +253,10 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement
|
||||
|
||||
addSelectedLeaderboardRacesControls(selectedLeaderboardRaceButtonPanel);
|
||||
}
|
||||
|
||||
protected boolean isActionButtonsEnabled() {
|
||||
return /* actionButtonsEnabled */ false;
|
||||
}
|
||||
|
||||
protected abstract void addLeaderboardControls(Panel controlsPanel);
|
||||
protected abstract void addSelectedLeaderboardRacesControls(Panel racesPanel);
|
||||
|
||||
+7
@@ -70,6 +70,13 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf
|
||||
importContent.add(deviceIdentifierTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* When doing race log tracking, the Remove and Stop Tracking buttons are required.
|
||||
*/
|
||||
protected boolean isActionButtonsEnabled() {
|
||||
return /* actionButtonsEnabled */ true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnsToLeaderboardTableAndSetSelectionModel(CellTable<StrippedLeaderboardDTO> leaderboardTable, AdminConsoleTableResources tableResources) {
|
||||
ListHandler<StrippedLeaderboardDTO> leaderboardColumnListHandler = new ListHandler<StrippedLeaderboardDTO>(
|
||||
|
||||
+27
-17
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.cell.client.AbstractCell;
|
||||
@@ -317,23 +318,32 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer
|
||||
}
|
||||
}));
|
||||
|
||||
for (SeriesDTO series : editedRegatta.series) {
|
||||
sailingService.updateSeries(regattaName, series.getName(), series.getName(), series.isMedal(),
|
||||
series.getDiscardThresholds(), series.isStartsWithZeroScore(),
|
||||
series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring(),
|
||||
series.getFleets(), new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error trying to update regatta " + editedRegatta.getName()
|
||||
+ ": " + caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
regattaRefresher.fillRegattas();
|
||||
}
|
||||
}));
|
||||
}
|
||||
final Iterator<SeriesDTO> seriesIter = editedRegatta.series.iterator();
|
||||
final Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (seriesIter.hasNext()) {
|
||||
final SeriesDTO series = seriesIter.next();
|
||||
sailingService.updateSeries(regattaName, series.getName(), series.getName(), series.isMedal(),
|
||||
series.getDiscardThresholds(), series.isStartsWithZeroScore(),
|
||||
series.isFirstColumnIsNonDiscardableCarryForward(), series.hasSplitFleetContiguousScoring(),
|
||||
series.getFleets(), new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error trying to update regatta " + editedRegatta.getName()
|
||||
+ ": " + caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
regattaRefresher.fillRegattas();
|
||||
run(); // update next series if iterator has next element
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
r.run();
|
||||
}
|
||||
|
||||
protected List<RegattaDTO> getSelectedRegattas() {
|
||||
|
||||
+2
@@ -410,6 +410,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
|
||||
String scoringSchemeWinnerGetsSix();
|
||||
String scoringSchemeWinnerGetsEight();
|
||||
String scoringSchemeWinnerGetsEightAndInterpolation();
|
||||
String scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07();
|
||||
String scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description();
|
||||
String scoringSystem();
|
||||
String createFlexibleLeaderboard();
|
||||
String createRegattaLeaderboard();
|
||||
|
||||
+3
-1
@@ -422,6 +422,8 @@ scoringSchemeWinnerGetsFive=High Point System, Winner Gets 5 Points
|
||||
scoringSchemeWinnerGetsSix=High Point System, Winner Gets 6 Points
|
||||
scoringSchemeWinnerGetsEight=High Point System, Winner Gets 8 Points
|
||||
scoringSchemeWinnerGetsEightAndInterpolation=High Point System, Winner 8 Points, Interpolated
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Low Point with Eliminations and Rounds, Winner Gets 0.7 Points
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=A variant of the low-point scoring scheme, using eliminations consisting of rounds.\nEach round shall be modeled as a series in the regatta, and the fleet ordering counts backwards\nstarting with the highest number for the first round.\nIf a final and losers final take place, use 1 and 2 as their ordering. Start with 3 for the semi-final round.
|
||||
scoringSystem=Scoring system
|
||||
createFlexibleLeaderboard=Create flexible leaderboard
|
||||
createRegattaLeaderboard=Create regatta leaderboard
|
||||
@@ -1353,4 +1355,4 @@ photosCount={0,number} photos
|
||||
photosCount[one]={0,number} photo
|
||||
eventsHaveTakenPlace={0} events have taken place
|
||||
eventsHaveTakenPlace[one]= One event has taken place
|
||||
raceOffice=Race Office
|
||||
raceOffice=Race Office
|
||||
+3
-1
@@ -423,6 +423,8 @@ scoringSchemeWinnerGetsFive=High Point System, Gewinner erhält 5 Punkte
|
||||
scoringSchemeWinnerGetsSix=High Point System, Gewinner erhält 6 Punkte
|
||||
scoringSchemeWinnerGetsEight=High Point System, Gewinner erhält 8 Punkte
|
||||
scoringSchemeWinnerGetsEightAndInterpolation=High Point System, Gewinner erhält 8 Punkte, interpoliert
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Low Point System mit K.O.-Runden, Gewinner erhält 0,7 Punkte
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=Eine Variante des Low Point Systems mit mehreren K.O.-Wertungen.\nJede Runde einer Wertung wird als "Serie" in der Regatta modelliert, und die Gruppen-Ordnung wird rückwärts zählend verwendet, mit der größten Zahl für die erste Runde.\nWenn die Final-Runde in großes und kleines Finale unterteilt ist, soll das große Finale mit Ordnungszahl 1 und das kleine mit Ordnungszahl 2 modelliert werden. Das Halbfinale verwendet dann 3, usw.
|
||||
scoringSystem=Bepunktungssystem
|
||||
createFlexibleLeaderboard=Flexible Rangliste anlegen
|
||||
createRegattaLeaderboard=Rangliste für Regatta anlegen
|
||||
@@ -1342,4 +1344,4 @@ photosCount={0,number} Bilder
|
||||
photosCount[one]={0,number} Bild
|
||||
eventsHaveTakenPlace={0} Veranstaltungen haben stattgefunden
|
||||
eventsHaveTakenPlace[one]= Eine Veranstaltung hat stattgefunden
|
||||
raceOffice=Wettfahrtleitung
|
||||
raceOffice=Wettfahrtleitung
|
||||
+3
-1
@@ -408,6 +408,8 @@ scoringSchemeWinnerGetsFive=Высокобальная система подсч
|
||||
scoringSchemeWinnerGetsSix=Высокобальная система подсчета, победитель получает 6 очков
|
||||
scoringSchemeWinnerGetsEight=Высокобальная система подсчета, победитель получает 8 очков
|
||||
scoringSchemeWinnerGetsEightAndInterpolation=Высокобальная система подсчета, победитель получает 8 очков, Interpolated
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Low Point with Eliminations and Rounds, Winner Gets 0.7 Points
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=A variant of the low-point scoring scheme, using eliminations consisting of rounds.\nEach round shall be modeled as a series in the regatta, and the fleet ordering counts backwards\nstarting with the highest number for the first round.\nIf a final and losers final take place, use 1 and 2 as their ordering. Start with 3 for the semi-final round.
|
||||
scoringSystem=Система подсчета очков
|
||||
createFlexibleLeaderboard=Создание адаптивной таблицы лидеров
|
||||
createRegattaLeaderboard=Создание таблицы лидеров регаты
|
||||
@@ -1292,4 +1294,4 @@ photosCount={0,number} photos
|
||||
photosCount[one]={0,number} photo
|
||||
eventsHaveTakenPlace={0} events have taken place
|
||||
eventsHaveTakenPlace[one]= One event has taken place
|
||||
raceOffice=Race Office
|
||||
raceOffice=Race Office
|
||||
+3
-1
@@ -523,6 +523,8 @@ imageURL=Image URL
|
||||
mimeType=MIME Type
|
||||
hideMap=隐藏地图
|
||||
scoringSchemeLowPointWinnerGetsZero=Low Point System, Winner Gets Zero
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Low Point with Eliminations and Rounds, Winner Gets 0.7 Points
|
||||
scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=A variant of the low-point scoring scheme, using eliminations consisting of rounds.\nEach round shall be modeled as a series in the regatta, and the fleet ordering counts backwards\nstarting with the highest number for the first round.\nIf a final and losers final take place, use 1 and 2 as their ordering. Start with 3 for the semi-final round.
|
||||
averageAbsoluteCrossTrackErrorInMeters=u2205 XTE
|
||||
showOnlyRacesOfSameDay=Show only races of same day
|
||||
waypoints=航路点
|
||||
@@ -1328,4 +1330,4 @@ photosCount={0,number} photos
|
||||
photosCount[one]={0,number} photo
|
||||
eventsHaveTakenPlace={0} events have taken place
|
||||
eventsHaveTakenPlace[one]= One event has taken place
|
||||
raceOffice=Race Office
|
||||
raceOffice=Race Office
|
||||
+41
-82
@@ -43,15 +43,12 @@ import com.sap.sse.common.filter.Filter;
|
||||
import com.sap.sse.common.filter.FilterSet;
|
||||
import com.sap.sse.common.settings.AbstractSettings;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.async.AsyncAction;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
import com.sap.sse.gwt.client.shared.components.Component;
|
||||
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
|
||||
|
||||
public class EditMarkPassingsPanel extends AbsolutePanel implements Component<AbstractSettings>, RaceSelectionChangeListener,
|
||||
CompetitorSelectionChangeListener {
|
||||
|
||||
private static class AnchorCell extends AbstractCell<SafeHtml> {
|
||||
@Override
|
||||
public void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml safeHtml, SafeHtmlBuilder sb) {
|
||||
@@ -61,7 +58,6 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
|
||||
private final SailingServiceAsync sailingService;
|
||||
private RegattaAndRaceIdentifier raceIdentifier;
|
||||
private final AsyncActionsExecutor asyncExecutor;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final StringMessages stringMessages;
|
||||
private final CompetitorSelectionProvider competitorSelectionModel;
|
||||
@@ -91,13 +87,11 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
private final Button removeSuppressedPassingButton;
|
||||
private Label selectCompetitorLabel = new Label();
|
||||
|
||||
public EditMarkPassingsPanel(final SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
|
||||
final RegattaAndRaceIdentifier raceIdentifier, final StringMessages stringMessages,
|
||||
final CompetitorSelectionProvider competitorSelectionModel, final ErrorReporter errorReporter,
|
||||
final Timer timer) {
|
||||
public EditMarkPassingsPanel(final SailingServiceAsync sailingService, final RegattaAndRaceIdentifier raceIdentifier,
|
||||
final StringMessages stringMessages, final CompetitorSelectionProvider competitorSelectionModel,
|
||||
final ErrorReporter errorReporter, final Timer timer) {
|
||||
this.sailingService = sailingService;
|
||||
this.raceIdentifier = raceIdentifier;
|
||||
this.asyncExecutor = asyncActionsExecutor;
|
||||
this.errorReporter = errorReporter;
|
||||
this.competitorSelectionModel = competitorSelectionModel;
|
||||
this.stringMessages = stringMessages;
|
||||
@@ -172,13 +166,8 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
removeFixedMarkPassingsButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
asyncExecutor.execute(new AsyncAction<Void>() {
|
||||
@Override
|
||||
public void execute(AsyncCallback<Void> callback) {
|
||||
sailingService.updateFixedMarkPassing(leaderboardName, column, column.getFleet(raceIdentifier),
|
||||
waypointSelectionModel.getSelectedObject().getA(), null, competitor, callback);
|
||||
}
|
||||
}, new AsyncCallback<Void>() {
|
||||
sailingService.updateFixedMarkPassing(leaderboardName, column, column.getFleet(raceIdentifier),
|
||||
waypointSelectionModel.getSelectedObject().getA(), null, competitor, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorRemovingFixedPassing(caught.getMessage()));
|
||||
@@ -195,14 +184,9 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
setTimeAsMarkPassingsButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
asyncExecutor.execute(new AsyncAction<Void>() {
|
||||
@Override
|
||||
public void execute(AsyncCallback<Void> callback) {
|
||||
sailingService.updateFixedMarkPassing(leaderboardName, column, column.getFleet(raceIdentifier),
|
||||
sailingService.updateFixedMarkPassing(leaderboardName, column, column.getFleet(raceIdentifier),
|
||||
waypointSelectionModel.getSelectedObject().getA(), timer.getTime(), competitor,
|
||||
callback);
|
||||
}
|
||||
}, new AsyncCallback<Void>() {
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorSettingFixedPassing(caught.getMessage()));
|
||||
@@ -221,14 +205,9 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
suppressPassingsButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
asyncExecutor.execute(new AsyncAction<Void>() {
|
||||
@Override
|
||||
public void execute(AsyncCallback<Void> callback) {
|
||||
sailingService.updateSuppressedMarkPassings(leaderboardName, column,
|
||||
sailingService.updateSuppressedMarkPassings(leaderboardName, column,
|
||||
column.getFleet(raceIdentifier), waypointSelectionModel.getSelectedObject().getA(),
|
||||
competitor, callback);
|
||||
}
|
||||
}, new AsyncCallback<Void>() {
|
||||
competitor, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorSuppressingPassing(caught.getMessage()));
|
||||
@@ -246,13 +225,9 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
removeSuppressedPassingButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
asyncExecutor.execute(new AsyncAction<Void>() {
|
||||
@Override
|
||||
public void execute(AsyncCallback<Void> callback) {
|
||||
sailingService.updateSuppressedMarkPassings(leaderboardName, column,
|
||||
column.getFleet(raceIdentifier), null, competitor, callback);
|
||||
}
|
||||
}, new AsyncCallback<Void>() {
|
||||
sailingService.updateSuppressedMarkPassings(leaderboardName, column,
|
||||
column.getFleet(raceIdentifier), null, competitor,
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorRemovingSupressedPassing(caught.getMessage()));
|
||||
@@ -301,14 +276,14 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
}
|
||||
|
||||
private void processCompetitorSelectionChange(boolean visible) {
|
||||
waypointSelectionModel.clear();
|
||||
if (visible && Util.size(competitorSelectionModel.getSelectedCompetitors()) == 1) {
|
||||
selectCompetitorLabel.setText("");
|
||||
refillList();
|
||||
} else {
|
||||
disableEditing();
|
||||
selectCompetitorLabel.setText(stringMessages.selectCompetitor());
|
||||
}
|
||||
waypointSelectionModel.clear();
|
||||
if (visible && Util.size(competitorSelectionModel.getSelectedCompetitors()) == 1) {
|
||||
selectCompetitorLabel.setText("");
|
||||
refillList();
|
||||
} else {
|
||||
disableEditing();
|
||||
selectCompetitorLabel.setText(stringMessages.selectCompetitor());
|
||||
}
|
||||
}
|
||||
|
||||
private void disableEditing() {
|
||||
@@ -316,17 +291,11 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
clearInfo();
|
||||
}
|
||||
|
||||
|
||||
private void refillList() {
|
||||
clearInfo();
|
||||
competitor = competitorSelectionModel.getSelectedCompetitors().iterator().next();
|
||||
// Get current mark passings
|
||||
asyncExecutor.execute(new AsyncAction<Map<Integer, Date>>() {
|
||||
@Override
|
||||
public void execute(AsyncCallback<Map<Integer, Date>> callback) {
|
||||
sailingService.getCompetitorMarkPassings(raceIdentifier, competitor, callback);
|
||||
}
|
||||
}, new AsyncCallback<Map<Integer, Date>>() {
|
||||
sailingService.getCompetitorMarkPassings(raceIdentifier, competitor, new AsyncCallback<Map<Integer, Date>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorTryingToObtainMarkPassing(caught.getMessage()));
|
||||
@@ -341,32 +310,27 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
}
|
||||
waypointList.getList().clear();
|
||||
waypointList.getList().addAll(newMarkPassings);
|
||||
}
|
||||
});
|
||||
// Get current edits
|
||||
asyncExecutor.execute(new AsyncAction<Map<Integer, Date>>() {
|
||||
@Override
|
||||
public void execute(AsyncCallback<Map<Integer, Date>> callback) {
|
||||
// Get current edits
|
||||
sailingService.getCompetitorRaceLogMarkPassingData(leaderboardName, column,
|
||||
column.getFleet(raceIdentifier), competitor, callback);
|
||||
}
|
||||
}, new AsyncCallback<Map<Integer, Date>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorTryingToObtainRaceLogMarkPassingData(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Map<Integer, Date> result) {
|
||||
for (Entry<Integer, Date> data : result.entrySet()) {
|
||||
if (data.getValue() == null) {
|
||||
zeroBasedIndexOfFirstSuppressedWaypoint = data.getKey();
|
||||
} else {
|
||||
currentCompetitorEdits.put(data.getKey(), data.getValue());
|
||||
column.getFleet(raceIdentifier), competitor, new AsyncCallback<Map<Integer, Date>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorTryingToObtainRaceLogMarkPassingData(caught.getMessage()));
|
||||
}
|
||||
}
|
||||
enableButtons();
|
||||
wayPointSelectionTable.redraw();
|
||||
|
||||
@Override
|
||||
public void onSuccess(Map<Integer, Date> result) {
|
||||
for (Entry<Integer, Date> data : result.entrySet()) {
|
||||
if (data.getValue() == null) {
|
||||
zeroBasedIndexOfFirstSuppressedWaypoint = data.getKey();
|
||||
} else {
|
||||
currentCompetitorEdits.put(data.getKey(), data.getValue());
|
||||
}
|
||||
}
|
||||
enableButtons();
|
||||
wayPointSelectionTable.redraw();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -382,12 +346,7 @@ public class EditMarkPassingsPanel extends AbsolutePanel implements Component<Ab
|
||||
}
|
||||
|
||||
private void refreshWaypoints() {
|
||||
asyncExecutor.execute(new AsyncAction<RaceCourseDTO>() {
|
||||
@Override
|
||||
public void execute(AsyncCallback<RaceCourseDTO> callback) {
|
||||
sailingService.getRaceCourse(raceIdentifier, new Date(), callback);
|
||||
}
|
||||
}, new AsyncCallback<RaceCourseDTO>() {
|
||||
sailingService.getRaceCourse(raceIdentifier, new Date(), new AsyncCallback<RaceCourseDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(stringMessages.errorTryingToObtainRaceCourse(caught.getMessage()));
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ public class BoatClassVectorGraphicsResolver {
|
||||
BoatClassMasterdata.LASER_RADIAL, BoatClassMasterdata.LASER_4_7, BoatClassMasterdata.LASER_2,
|
||||
BoatClassMasterdata.CONTENDER,
|
||||
BoatClassMasterdata.FINN, BoatClassMasterdata.MUSTO_SKIFF, BoatClassMasterdata.OPEN_BIC,
|
||||
BoatClassMasterdata.OPTIMIST, BoatClassMasterdata.RS_X, BoatClassMasterdata.STAR);
|
||||
BoatClassMasterdata.OPTIMIST, BoatClassMasterdata.PWA, BoatClassMasterdata.RS_X, BoatClassMasterdata.STAR);
|
||||
BoatClassVectorGraphics _49er = new _49erVectorGraphics(BoatClassMasterdata._49ER, BoatClassMasterdata._49ERFX,
|
||||
BoatClassMasterdata._29ER, BoatClassMasterdata._18Footer);
|
||||
BoatClassVectorGraphics extreme40 = new Extreme40VectorGraphics(BoatClassMasterdata.EXTREME_40,
|
||||
|
||||
+4
@@ -30,6 +30,8 @@ public class ScoringSchemeTypeFormatter {
|
||||
return stringMessages.scoringSchemeWinnerGetsEightAndInterpolation();
|
||||
case HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT:
|
||||
return stringMessages.scoringSchemeHighPointFirstGetsTenOrEight();
|
||||
case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07:
|
||||
return stringMessages.scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -40,6 +42,8 @@ public class ScoringSchemeTypeFormatter {
|
||||
return stringMessages.scoringSchemeHighPointFirstGetsTenOrEightDescription();
|
||||
case HIGH_POINT_ESS_OVERALL:
|
||||
return stringMessages.scoringSchemeHighPointEssOverallDescription();
|
||||
case LOW_POINT_WITH_ELIMINATIONS_AND_ROUNDS_WINNER_GETS_07:
|
||||
return stringMessages.scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description();
|
||||
default:
|
||||
return format(scoringSchemeType, stringMessages);
|
||||
}
|
||||
|
||||
+2
-2
@@ -214,8 +214,8 @@ public class RaceBoardPanel extends SimplePanel implements RaceSelectionChangeLi
|
||||
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||
windChart.getEntryWidget().setTitle(stringMessages.windChart());
|
||||
components.add(windChart);
|
||||
editMarkPassingPanel = new EditMarkPassingsPanel(sailingService, asyncActionsExecutor, selectedRaceIdentifier,
|
||||
stringMessages, competitorSelectionModel, errorReporter, timer);
|
||||
editMarkPassingPanel = new EditMarkPassingsPanel(sailingService, selectedRaceIdentifier, stringMessages,
|
||||
competitorSelectionModel, errorReporter, timer);
|
||||
editMarkPassingPanel.setLeaderboardNameAndColumn(leaderboardPanel.getLeaderboard());
|
||||
editMarkPassingPanel.getEntryWidget().setTitle(stringMessages.editMarkPassings());
|
||||
components.add(editMarkPassingPanel);
|
||||
|
||||
+1
-2
@@ -28,8 +28,7 @@
|
||||
<!-- Specify the paths for translatable code -->
|
||||
<extend-property name="locale" values="de"/>
|
||||
<extend-property name="locale" values="ru"/>
|
||||
|
||||
|
||||
|
||||
<inherits name='com.sap.sailing.gwt.ui.SourceCommon' />
|
||||
<inherits name='com.sap.sailing.gwt.ui.SourceClient' />
|
||||
<inherits name='com.sap.sailing.gwt.ui.SourceShared' />
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
+1
-1
@@ -61,7 +61,7 @@ public abstract class BaseRaceLogEventDeserializer implements JsonDeserializer<R
|
||||
Helpers.tryUuidConversion(id),
|
||||
new MillisecondsTimePoint(createdAt.longValue()),
|
||||
author,
|
||||
new MillisecondsTimePoint(timeStamp.longValue()),
|
||||
timeStamp == null ? null : new MillisecondsTimePoint(timeStamp.longValue()),
|
||||
passId.intValue(), competitors);
|
||||
}
|
||||
|
||||
|
||||
+37
@@ -13,6 +13,13 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogEventRestoreFactory;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.CompetitorJsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogEndOfTrackingEventDeserializer;
|
||||
import com.sap.sailing.server.gateway.deserialization.racelog.impl.RaceLogStartOfTrackingEventDeserializer;
|
||||
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.BoatClassJsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.BoatJsonSerializer;
|
||||
@@ -34,12 +41,42 @@ public class TrackingTimesEventSerializerTest {
|
||||
return new RaceLogEndOfTrackingEventSerializer(createCompetitorSerializer());
|
||||
}
|
||||
|
||||
private JsonDeserializer<RaceLogEvent> createStartOfTrackingEventDeserializer() {
|
||||
return new RaceLogStartOfTrackingEventDeserializer(createCompetitorDeserializer());
|
||||
}
|
||||
|
||||
private JsonDeserializer<Competitor> createCompetitorDeserializer() {
|
||||
return new CompetitorJsonDeserializer(DomainFactory.INSTANCE.getCompetitorStore());
|
||||
}
|
||||
|
||||
private JsonDeserializer<RaceLogEvent> createEndOfTrackingEventDeserializer() {
|
||||
return new RaceLogEndOfTrackingEventDeserializer(createCompetitorDeserializer());
|
||||
}
|
||||
|
||||
private CompetitorJsonSerializer createCompetitorSerializer() {
|
||||
return new CompetitorJsonSerializer(new TeamJsonSerializer(
|
||||
new PersonJsonSerializer(new NationalityJsonSerializer())), new BoatJsonSerializer(
|
||||
new BoatClassJsonSerializer()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullStartOfTrackingTimeAttributeDeserialization() throws JsonDeserializationException {
|
||||
final RaceLogStartOfTrackingEvent event = RaceLogEventRestoreFactory.INSTANCE.createStartOfTrackingEvent(/* startOfTracking */null,
|
||||
new LogEventAuthorImpl("Axel", 0), UUID.randomUUID(), /* competitors */null, /* passId */1);
|
||||
JSONObject json = createStartOfTrackingEventSerializer().serialize(event);
|
||||
RaceLogEvent deserialized = createStartOfTrackingEventDeserializer().deserialize(json);
|
||||
assertNull(deserialized.getLogicalTimePoint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullEndOfTrackingTimeAttributeDeserialization() throws JsonDeserializationException {
|
||||
final RaceLogEndOfTrackingEvent event = RaceLogEventRestoreFactory.INSTANCE.createEndOfTrackingEvent(/* endOfTracking */null,
|
||||
new LogEventAuthorImpl("Axel", 0), UUID.randomUUID(), /* competitors */null, /* passId */1);
|
||||
JSONObject json = createStartOfTrackingEventSerializer().serialize(event);
|
||||
RaceLogEvent deserialized = createEndOfTrackingEventDeserializer().deserialize(json);
|
||||
assertNull(deserialized.getLogicalTimePoint());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullStartOfTrackingTimeAttribute() {
|
||||
final RaceLogStartOfTrackingEvent event = RaceLogEventRestoreFactory.INSTANCE.createStartOfTrackingEvent(/* startOfTracking */null,
|
||||
|
||||
+3
-3
@@ -176,13 +176,13 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
|
||||
final int numberOfCompetitorsInRace,
|
||||
Competitor competitor, int rankByRaceCommittee) throws NoWindException {
|
||||
boolean scoreHasBeenCorrected = false;
|
||||
Double scoreByRaceCommittee = leaderboard.getScoringScheme().getScoreForRank(raceColumn, competitor, rankByRaceCommittee,
|
||||
new Callable<Integer>() {
|
||||
Double scoreByRaceCommittee = leaderboard.getScoringScheme().getScoreForRank(leaderboard, raceColumn, competitor,
|
||||
rankByRaceCommittee, new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() {
|
||||
return numberOfCompetitorsInRace;
|
||||
}
|
||||
}, leaderboard.getNumberOfCompetitorsInLeaderboardFetcher());
|
||||
}, leaderboard.getNumberOfCompetitorsInLeaderboardFetcher(), timePoint);
|
||||
|
||||
// Do ALWAYS apply score corrections from race committee
|
||||
applyScoreCorrectionOperation(leaderboard, raceColumn, competitor, scoreByRaceCommittee, timePoint);
|
||||
|
||||
@@ -25,6 +25,20 @@
|
||||
<div id="mainContent">
|
||||
<h2 class="releaseHeadline">Release Notes - Administration Console</h2>
|
||||
<div class="innerContent">
|
||||
<h2 class="articleSubheadline">September 2015</h2>
|
||||
<ul class="bulletList">
|
||||
<li>A new scoring scheme for elimination rounds has been added. It will first be used
|
||||
at a major wind surfing event.
|
||||
</li>
|
||||
<li>The "Device Configuration" tab has been moved into a new top-level tab
|
||||
"Race Committee App".
|
||||
</li>
|
||||
<li>Included magnetic declination data for 2016 already.
|
||||
</li>
|
||||
<li>Bug fix: in rare cases, adding several series to a regatta in one go swapped the order
|
||||
of the series to be created. This has now been fixed.
|
||||
</li>
|
||||
</ul>
|
||||
<h2 class="articleSubheadline">August 2015</h2>
|
||||
<ul class="bulletList">
|
||||
<li>Race log tracking now allows administrators to set the start and end of tracking for a race.
|
||||
@@ -37,6 +51,8 @@
|
||||
created by the server if not known yet. Note that for such boat classes no boat class icon
|
||||
will be present when showing the regatta on the web page.
|
||||
</li>
|
||||
<li>Added boat classes Soling, Lago 26, Kielzugvogel, PWA (Professional Windsurfers Association)
|
||||
</li>
|
||||
</ul>
|
||||
<h2 class="articleSubheadline">June 2015</h2>
|
||||
<ul class="bulletList">
|
||||
|
||||
@@ -44,7 +44,7 @@ public class AsyncActionsExecutor {
|
||||
@Override
|
||||
public void onSuccess(T result) {
|
||||
try {
|
||||
GWT.log("Execution success for action of type: " + getType());
|
||||
GWT.log("Execution success for action of type " + getType() + ", category "+getCategory());
|
||||
this.callback.onSuccess(result);
|
||||
} finally {
|
||||
AsyncActionsExecutor.this.callCompleted(this);
|
||||
@@ -54,7 +54,7 @@ public class AsyncActionsExecutor {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
try {
|
||||
GWT.log("Execution failure for action of type: " + getType());
|
||||
GWT.log("Execution failure for action of type " + getType() + ", category "+getCategory());
|
||||
this.callback.onFailure(caught);
|
||||
} finally {
|
||||
AsyncActionsExecutor.this.callCompleted(this);
|
||||
@@ -62,7 +62,7 @@ public class AsyncActionsExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
private static final int DEFAULT_MAX_PENDING_CALLS = 6;
|
||||
private static final int DEFAULT_MAX_PENDING_CALLS = 10;
|
||||
private static final int DEFAULT_MAX_PENDING_CALLS_PER_TYPE = 4;
|
||||
|
||||
/**
|
||||
@@ -145,7 +145,7 @@ public class AsyncActionsExecutor {
|
||||
}
|
||||
numActionsOfType = 0;
|
||||
} else {
|
||||
GWT.log("Dropping action of type: " + job.getType());
|
||||
GWT.log("Dropping action of type " + job.getType() + ", category "+job.getCategory());
|
||||
/* don't put the call into the execution queue, but save it as the last one of each type
|
||||
* after each successful execution of a job checkForEmptyCallQueue will check if there
|
||||
* are other jobs of that type that need execution and execute the last one thus
|
||||
|
||||
-3
@@ -9,9 +9,6 @@ import android.os.AsyncTask;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
public class BroadcastManager {
|
||||
|
||||
private final static String TAG = BroadcastManager.class.getName();
|
||||
|
||||
private static BroadcastManager mInstance;
|
||||
private final Context mContext;
|
||||
private Queue<Intent> mQueue;
|
||||
|
||||
Reference in new issue
Block a user