mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-23 20:43:53 +00:00
Merge branch 'master' into bug3785
This commit is contained in:
commit
98b9b5d4b8
10 files changed
+475
-281
No files matched your search
@@ -155,7 +155,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "http://$HOSTNAME/dashboards/RibDashboard.html?leaderboardName=$LEADERBOARD_NAME&%{QUERY_STRING}" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "http://$HOSTNAME/dashboards/RibDashboard.html?leaderboardName=$LEADERBOARD_NAME&%1" [L,NE]
|
||||||
|
|
||||||
Use Rewrite $IP $PORT
|
Use Rewrite $IP $PORT
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -216,7 +218,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%{QUERY_STRING}#/event/:eventId=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%1#/event/:eventId=$EVENTUUID" [L,NE]
|
||||||
|
|
||||||
Use Rewrite $IP $PORT
|
Use Rewrite $IP $PORT
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -236,7 +240,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%{QUERY_STRING}#/event/:eventId=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%1#/event/:eventId=$EVENTUUID" [L,NE]
|
||||||
|
|
||||||
Use Rewrite $IP $PORT
|
Use Rewrite $IP $PORT
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -251,7 +257,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%{QUERY_STRING}#/series/:seriesId=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%1#/series/:seriesId=$EVENTUUID" [L,NE]
|
||||||
|
|
||||||
Use Rewrite $IP $PORT
|
Use Rewrite $IP $PORT
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -271,7 +279,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%{QUERY_STRING}#/series/:seriesId=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%1#/series/:seriesId=$EVENTUUID" [L,NE]
|
||||||
|
|
||||||
Use Rewrite $IP $PORT
|
Use Rewrite $IP $PORT
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -286,7 +296,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html#/regatta/overview/:eventId=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%1#/regatta/overview/:eventId=$EVENTUUID" [L,NE]
|
||||||
|
|
||||||
Use Rewrite $IP $PORT
|
Use Rewrite $IP $PORT
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -316,7 +328,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html#/event/:eventId=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/Home.html?%1#/event/:eventId=$EVENTUUID" [L,NE]
|
||||||
|
|
||||||
Use Rewrite 172.31.30.89 8888
|
Use Rewrite 172.31.30.89 8888
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -336,7 +350,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html#/event/:eventId=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "https://$HOSTNAME/gwt/Home.html?%1#/event/:eventId=$EVENTUUID" [L,NE]
|
||||||
|
|
||||||
Use Rewrite 172.31.30.89 8888
|
Use Rewrite 172.31.30.89 8888
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -352,7 +368,9 @@
|
|||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
|
||||||
RewriteCond %{REQUEST_URI} "^(/)?$"
|
RewriteCond %{REQUEST_URI} "^(/)?$"
|
||||||
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/RegattaOverview.html?event=$EVENTUUID" [L,NE]
|
# The following matches the query string and captures it in group %1 for the RewriteRule to use before the fragment
|
||||||
|
RewriteCond %{QUERY_STRING} "^(.*)$"
|
||||||
|
RewriteRule ^(/)?$ "http://$HOSTNAME/gwt/RegattaOverview.html?event=$EVENTUUID&%1" [L,NE]
|
||||||
|
|
||||||
Use Rewrite $IP $PORT
|
Use Rewrite $IP $PORT
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
@@ -556,4 +574,4 @@
|
|||||||
|
|
||||||
Use Rewrite 172.31.18.218 8888
|
Use Rewrite 172.31.18.218 8888
|
||||||
</VirtualHost>
|
</VirtualHost>
|
||||||
</Macro>
|
</Macro>
|
||||||
+11
-11
@@ -287,7 +287,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
|||||||
for (Competitor competitor : competitors) {
|
for (Competitor competitor : competitors) {
|
||||||
final String toAddress = competitor.getEmail();
|
final String toAddress = competitor.getEmail();
|
||||||
if (toAddress != null) {
|
if (toAddress != null) {
|
||||||
String leaderboardName = leaderboard.getName();
|
String leaderboardName = leaderboard.getDisplayName() == null ? leaderboard.getName() : leaderboard.getDisplayName();
|
||||||
String competitorName = competitor.getName();
|
String competitorName = competitor.getName();
|
||||||
String url = DeviceMappingConstants.getDeviceMappingForRegattaLogUrl(serverUrlWithoutTrailingSlash,
|
String url = DeviceMappingConstants.getDeviceMappingForRegattaLogUrl(serverUrlWithoutTrailingSlash,
|
||||||
event.getId().toString(), leaderboardName, DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING,
|
event.getId().toString(), leaderboardName, DeviceMappingConstants.URL_COMPETITOR_ID_AS_STRING,
|
||||||
@@ -299,8 +299,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
||||||
sendInvitationEmail(locale, toAddress, leaderboardName, competitorName, url, B.get(locale, "sailInSightAppName"),
|
sendInvitationEmail(locale, toAddress, leaderboardName, event.getName(), competitorName, url,
|
||||||
iOSAppUrl, androidAppUrl, logoUrl);
|
B.get(locale, "sailInSightAppName"), iOSAppUrl, androidAppUrl, logoUrl);
|
||||||
} catch (MailException e) {
|
} catch (MailException e) {
|
||||||
occuredExceptions.append(e.getMessage() + "\r\n");
|
occuredExceptions.append(e.getMessage() + "\r\n");
|
||||||
}
|
}
|
||||||
@@ -311,8 +311,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendInvitationEmail(Locale locale, final String toAddress, String leaderboardName, String invitee,
|
private void sendInvitationEmail(Locale locale, final String toAddress, String leaderboardName, String eventName,
|
||||||
String url, String appName, String iOSAppUrl, String androidAppUrl, String logoUrl) throws MailException {
|
String invitee, String url, String appName, String iOSAppUrl, String androidAppUrl, String logoUrl) throws MailException {
|
||||||
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
||||||
String subject = String.format("%s %s", B.get(locale, "trackingInvitationFor"), invitee);
|
String subject = String.format("%s %s", B.get(locale, "trackingInvitationFor"), invitee);
|
||||||
|
|
||||||
@@ -325,8 +325,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
|||||||
StringBuilder htmlText = new StringBuilder();
|
StringBuilder htmlText = new StringBuilder();
|
||||||
htmlText.append("<!doctype html>\n");
|
htmlText.append("<!doctype html>\n");
|
||||||
htmlText.append("<html>");
|
htmlText.append("<html>");
|
||||||
htmlText.append("<head><title>");
|
final String welcomeText = B.get(locale, "welcomeTo")+" "+eventName+", "+leaderboardName;
|
||||||
htmlText.append(B.get(locale, "welcomeTo")).append(" ").append(leaderboardName).append("</title>");
|
htmlText.append("<head><title>").append(welcomeText).append("</title>");
|
||||||
htmlText.append("<style type=\"text/css\">\n");
|
htmlText.append("<style type=\"text/css\">\n");
|
||||||
htmlText.append(".b,.b:active,.b:visited {padding:15px;margin:10px;width:200px;display:inline-block;background-color:#337ab7;border-radius:4px;color:#ffffff;border:1px solid #2e6da4;text-decoration:none;}\n");
|
htmlText.append(".b,.b:active,.b:visited {padding:15px;margin:10px;width:200px;display:inline-block;background-color:#337ab7;border-radius:4px;color:#ffffff;border:1px solid #2e6da4;text-decoration:none;}\n");
|
||||||
htmlText.append(".b:hover {background-color:#2b618e;border:1px solid #204d74;}\n");
|
htmlText.append(".b:hover {background-color:#2b618e;border:1px solid #204d74;}\n");
|
||||||
@@ -338,7 +338,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
|||||||
if (hasLogoUrl) {
|
if (hasLogoUrl) {
|
||||||
htmlText.append("<p><img src=\"").append(logoUrl).append("\" /></p> ");
|
htmlText.append("<p><img src=\"").append(logoUrl).append("\" /></p> ");
|
||||||
}
|
}
|
||||||
htmlText.append("<h1>").append(B.get(locale, "welcomeTo")).append(" ").append(leaderboardName).append("</h1> ");
|
htmlText.append("<h1>").append(welcomeText).append("</h1> ");
|
||||||
htmlText.append("<p>").append(B.get(locale, "scanQRCodeOrVisitUrlToRegisterAs", appName)).append(" <b>").append(invitee)
|
htmlText.append("<p>").append(B.get(locale, "scanQRCodeOrVisitUrlToRegisterAs", appName)).append(" <b>").append(invitee)
|
||||||
.append("</b></p> ");
|
.append("</b></p> ");
|
||||||
htmlText.append("<p class=\"qr\"><img src=\"cid:image\" title=\"").append(url).append("\" /></p> ");
|
htmlText.append("<p class=\"qr\"><img src=\"cid:image\" title=\"").append(url).append("\" /></p> ");
|
||||||
@@ -392,7 +392,7 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
|||||||
String emails, String iOSAppUrl, String androidAppUrl, Locale locale) throws MailException {
|
String emails, String iOSAppUrl, String androidAppUrl, Locale locale) throws MailException {
|
||||||
StringBuilder occuredExceptions = new StringBuilder();
|
StringBuilder occuredExceptions = new StringBuilder();
|
||||||
String[] emailArray = emails.split(",");
|
String[] emailArray = emails.split(",");
|
||||||
String leaderboardName = leaderboard.getName();
|
String leaderboardName = leaderboard.getDisplayName() == null ? leaderboard.getName() : leaderboard.getDisplayName();
|
||||||
String eventId = event.getId().toString();
|
String eventId = event.getId().toString();
|
||||||
String logoUrl = null;
|
String logoUrl = null;
|
||||||
List<ImageDescriptor> imagesWithTag = event.findImagesWithTag(MediaTagConstants.LOGO);
|
List<ImageDescriptor> imagesWithTag = event.findImagesWithTag(MediaTagConstants.LOGO);
|
||||||
@@ -406,8 +406,8 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
|||||||
try {
|
try {
|
||||||
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
|
||||||
sendInvitationEmail(locale, toAddress, leaderboardName,
|
sendInvitationEmail(locale, toAddress, leaderboardName,
|
||||||
RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "buoyTender"), url, B.get(locale, "buoyPingerAppName"), iOSAppUrl,
|
event.getName(), RaceLogTrackingI18n.STRING_MESSAGES.get(locale, "buoyTender"), url, B.get(locale, "buoyPingerAppName"),
|
||||||
androidAppUrl, logoUrl);
|
iOSAppUrl, androidAppUrl, logoUrl);
|
||||||
} catch (MailException e) {
|
} catch (MailException e) {
|
||||||
occuredExceptions.append(e.getMessage() + "\r\n");
|
occuredExceptions.append(e.getMessage() + "\r\n");
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-190
@@ -55,7 +55,6 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
|||||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
|
||||||
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
||||||
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
||||||
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
|
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
|
||||||
@@ -70,7 +69,6 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
|||||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||||
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
|
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.MarkPassing;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||||
@@ -82,10 +80,8 @@ import com.sap.sse.common.Util;
|
|||||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||||
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||||
|
|
||||||
public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRankingTestBase {
|
||||||
private ArrayList<Series> series;
|
protected Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||||
|
|
||||||
private Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
|
||||||
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
|
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
|
||||||
return new RegattaLeaderboardImpl(regatta, discardingRules);
|
return new RegattaLeaderboardImpl(regatta, discardingRules);
|
||||||
}
|
}
|
||||||
@@ -1250,19 +1246,6 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
|||||||
checkScoresAfterSomeRaces(leaderboard, raceColumnsToConsider, scoresAfter6Races, later, c);
|
checkScoresAfterSomeRaces(leaderboard, raceColumnsToConsider, scoresAfter6Races, later, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkScoresAfterSomeRaces(Leaderboard leaderboard, List<RaceColumn> raceColumnsToConsider,
|
|
||||||
double[][] scoresAfterNRaces, TimePoint timePoint, Competitor[] competitors) throws NoWindException {
|
|
||||||
for (int competitorIndex=0; competitorIndex<scoresAfterNRaces.length; competitorIndex++) {
|
|
||||||
final Set<RaceColumn> discardedRaceColumns = leaderboard.getResultDiscardingRule()
|
|
||||||
.getDiscardedRaceColumns(competitors[competitorIndex], leaderboard, raceColumnsToConsider, timePoint);
|
|
||||||
for (int raceColumnIndex=0; raceColumnIndex<raceColumnsToConsider.size(); raceColumnIndex++) {
|
|
||||||
assertEquals(scoresAfterNRaces[competitorIndex][raceColumnIndex],
|
|
||||||
leaderboard.getNetPoints(competitors[competitorIndex], raceColumnsToConsider.get(raceColumnIndex),
|
|
||||||
timePoint, discardedRaceColumns), 0.00000001);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTieBreakByMedalRaceScoreOnlyIfEqualNetScore() throws NoWindException {
|
public void testTieBreakByMedalRaceScoreOnlyIfEqualNetScore() throws NoWindException {
|
||||||
Competitor[] c = createCompetitors(2).toArray(new Competitor[0]);
|
Competitor[] c = createCompetitors(2).toArray(new Competitor[0]);
|
||||||
@@ -1863,177 +1846,6 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
|||||||
assertEquals(0., leaderboard1.getNetPoints(c[3], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
|
assertEquals(0., leaderboard1.getNetPoints(c[3], beforeStartOfR1), 0.00000001); // not even the R1 scores apply before the start time of R1
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createAndAttachTrackedRacesWithStartTimeAndLastMarkPassingTimes(
|
|
||||||
Series theSeries, String fleetName, Competitor[][] competitorLists, TimePoint[] startTimes,
|
|
||||||
Map<Competitor, TimePoint>[] lastMarkPassingTimesForCompetitors) {
|
|
||||||
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
|
|
||||||
int i=0;
|
|
||||||
for (Competitor[] competitorList : competitorLists) {
|
|
||||||
RaceColumn raceColumn = columnIter.next();
|
|
||||||
final Map<Competitor, TimePoint> lastMarkPassingTimes = lastMarkPassingTimesForCompetitors[i];
|
|
||||||
final Waypoint start = new WaypointImpl(new ControlPointWithTwoMarksImpl(new MarkImpl("Left StartBuoy"), new MarkImpl("Right StartBuoy"), "Start"));
|
|
||||||
final Waypoint finish = new WaypointImpl(new MarkImpl("FinishBuoy"));
|
|
||||||
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(startTimes[i], Arrays.asList(competitorList)) {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
@Override
|
|
||||||
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
|
|
||||||
ArrayListNavigableSet<MarkPassing> result = new ArrayListNavigableSet<>(new TimedComparator());
|
|
||||||
result.add(new MarkPassingImpl(lastMarkPassingTimes.get(competitor), finish, competitor));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
trackedRace.getRace().getCourse().addWaypoint(0, start);
|
|
||||||
trackedRace.getRace().getCourse().addWaypoint(1, finish);
|
|
||||||
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Competitor> createCompetitors(int numberOfCompetitorsToCreate) {
|
|
||||||
List<Competitor> result = new ArrayList<Competitor>();
|
|
||||||
for (int i=1; i<=numberOfCompetitorsToCreate; i++) {
|
|
||||||
result.add(createCompetitor("C"+i));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Regatta createSimpleRegatta(final int numberOfRaces, final String regattaName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
|
||||||
series = new ArrayList<Series>();
|
|
||||||
List<Fleet> fleets = new ArrayList<Fleet>();
|
|
||||||
fleets.add(new FleetImpl("Default"));
|
|
||||||
|
|
||||||
List<String> raceColumnNames = new ArrayList<String>();
|
|
||||||
for (int i = 1; i <= numberOfRaces; i++) {
|
|
||||||
raceColumnNames.add("R" + i);
|
|
||||||
}
|
|
||||||
Series defaultSeries = new SeriesImpl("Default", /* isMedal */false, /* isFleetsCanRunInParallel */true,
|
|
||||||
fleets, raceColumnNames, /* trackedRegattaRegistry */null);
|
|
||||||
series.add(defaultSeries);
|
|
||||||
|
|
||||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass,
|
|
||||||
/* startDate */null, /* endDate */null, series, /* persistent */false, scoringScheme, "123", null,
|
|
||||||
OneDesignRankingMetric::new);
|
|
||||||
return regatta;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Regatta createRegatta(final int numberOfQualifyingRaces, String[] qualifyingFleetNames, final int numberOfFinalRaces,
|
|
||||||
String[] finalFleetNames, boolean medalRaceAndSeries, final int numberOfMedalRaces, final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
|
||||||
series = new ArrayList<Series>();
|
|
||||||
|
|
||||||
// -------- qualifying series ------------
|
|
||||||
if (qualifyingFleetNames != null && qualifyingFleetNames.length > 0) {
|
|
||||||
List<Fleet> qualifyingFleets = new ArrayList<Fleet>();
|
|
||||||
for (String qualifyingFleetName : qualifyingFleetNames) {
|
|
||||||
qualifyingFleets.add(new FleetImpl(qualifyingFleetName));
|
|
||||||
}
|
|
||||||
List<String> qualifyingRaceColumnNames = new ArrayList<String>();
|
|
||||||
for (int i = 1; i <= numberOfQualifyingRaces; i++) {
|
|
||||||
qualifyingRaceColumnNames.add("Q" + i);
|
|
||||||
}
|
|
||||||
Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualifyingFleets,
|
|
||||||
qualifyingRaceColumnNames, /* trackedRegattaRegistry */null);
|
|
||||||
series.add(qualifyingSeries);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- final series ------------
|
|
||||||
if (finalFleetNames != null && finalFleetNames.length > 0) {
|
|
||||||
List<Fleet> finalFleets = new ArrayList<Fleet>();
|
|
||||||
int fleetOrdering = 1;
|
|
||||||
for (String finalFleetName : finalFleetNames) {
|
|
||||||
finalFleets.add(new FleetImpl(finalFleetName, fleetOrdering++));
|
|
||||||
}
|
|
||||||
List<String> finalRaceColumnNames = new ArrayList<String>();
|
|
||||||
for (int i = 1; i <= numberOfFinalRaces; i++) {
|
|
||||||
finalRaceColumnNames.add("F" + i);
|
|
||||||
}
|
|
||||||
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, /* isFleetsCanRunInParallel */ true, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
|
||||||
series.add(finalSeries);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (medalRaceAndSeries) {
|
|
||||||
// ------------ medal --------------
|
|
||||||
List<Fleet> medalFleets = new ArrayList<Fleet>();
|
|
||||||
medalFleets.add(new FleetImpl("Medal"));
|
|
||||||
List<String> medalRaceColumnNames = new ArrayList<String>();
|
|
||||||
if(numberOfMedalRaces == 1) {
|
|
||||||
medalRaceColumnNames.add("M");
|
|
||||||
} else if(numberOfMedalRaces > 1) {
|
|
||||||
for (int i = 1; i <= numberOfMedalRaces; i++) {
|
|
||||||
medalRaceColumnNames.add("M" + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Series medalSeries = new SeriesImpl("Medal", /* isMedal */true, /* isFleetsCanRunInParallel */ true, medalFleets, medalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
|
||||||
series.add(medalSeries);
|
|
||||||
}
|
|
||||||
|
|
||||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
|
|
||||||
/*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme, "123", null, OneDesignRankingMetric::new);
|
|
||||||
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, /* isFleetsCanRunInParallel */ true, 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
|
* 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
|
* 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
|
||||||
|
|||||||
+233
@@ -0,0 +1,233 @@
|
|||||||
|
package com.sap.sailing.domain.test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NavigableSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.base.BoatClass;
|
||||||
|
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.Regatta;
|
||||||
|
import com.sap.sailing.domain.base.Series;
|
||||||
|
import com.sap.sailing.domain.base.Waypoint;
|
||||||
|
import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.SeriesImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||||
|
import com.sap.sailing.domain.common.NoWindException;
|
||||||
|
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||||
|
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||||
|
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||||
|
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
|
||||||
|
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||||
|
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.impl.MarkPassingImpl;
|
||||||
|
import com.sap.sailing.domain.tracking.impl.TimedComparator;
|
||||||
|
import com.sap.sse.common.TimePoint;
|
||||||
|
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||||
|
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||||
|
|
||||||
|
public class LeaderboardScoringAndRankingTestBase extends AbstractLeaderboardTest {
|
||||||
|
protected ArrayList<Series> series;
|
||||||
|
|
||||||
|
protected Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||||
|
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
|
||||||
|
return new RegattaLeaderboardImpl(regatta, discardingRules);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkScoresAfterSomeRaces(Leaderboard leaderboard, List<RaceColumn> raceColumnsToConsider,
|
||||||
|
double[][] scoresAfterNRaces, TimePoint timePoint, Competitor[] competitors) throws NoWindException {
|
||||||
|
for (int competitorIndex=0; competitorIndex<scoresAfterNRaces.length; competitorIndex++) {
|
||||||
|
final Set<RaceColumn> discardedRaceColumns = leaderboard.getResultDiscardingRule()
|
||||||
|
.getDiscardedRaceColumns(competitors[competitorIndex], leaderboard, raceColumnsToConsider, timePoint);
|
||||||
|
for (int raceColumnIndex=0; raceColumnIndex<raceColumnsToConsider.size(); raceColumnIndex++) {
|
||||||
|
assertEquals(scoresAfterNRaces[competitorIndex][raceColumnIndex],
|
||||||
|
leaderboard.getNetPoints(competitors[competitorIndex], raceColumnsToConsider.get(raceColumnIndex),
|
||||||
|
timePoint, discardedRaceColumns), 0.00000001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void createAndAttachTrackedRacesWithStartTimeAndLastMarkPassingTimes(
|
||||||
|
Series theSeries, String fleetName, Competitor[][] competitorLists, TimePoint[] startTimes,
|
||||||
|
Map<Competitor, TimePoint>[] lastMarkPassingTimesForCompetitors) {
|
||||||
|
Iterator<? extends RaceColumn> columnIter = theSeries.getRaceColumns().iterator();
|
||||||
|
int i=0;
|
||||||
|
for (Competitor[] competitorList : competitorLists) {
|
||||||
|
RaceColumn raceColumn = columnIter.next();
|
||||||
|
final Map<Competitor, TimePoint> lastMarkPassingTimes = lastMarkPassingTimesForCompetitors[i];
|
||||||
|
final Waypoint start = new WaypointImpl(new ControlPointWithTwoMarksImpl(new MarkImpl("Left StartBuoy"), new MarkImpl("Right StartBuoy"), "Start"));
|
||||||
|
final Waypoint finish = new WaypointImpl(new MarkImpl("FinishBuoy"));
|
||||||
|
TrackedRace trackedRace = new MockedTrackedRaceWithStartTimeAndRanks(startTimes[i], Arrays.asList(competitorList)) {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
@Override
|
||||||
|
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
|
||||||
|
ArrayListNavigableSet<MarkPassing> result = new ArrayListNavigableSet<>(new TimedComparator());
|
||||||
|
result.add(new MarkPassingImpl(lastMarkPassingTimes.get(competitor), finish, competitor));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trackedRace.getRace().getCourse().addWaypoint(0, start);
|
||||||
|
trackedRace.getRace().getCourse().addWaypoint(1, finish);
|
||||||
|
raceColumn.setTrackedRace(raceColumn.getFleetByName(fleetName), trackedRace);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Competitor> createCompetitors(int numberOfCompetitorsToCreate) {
|
||||||
|
List<Competitor> result = new ArrayList<Competitor>();
|
||||||
|
for (int i=1; i<=numberOfCompetitorsToCreate; i++) {
|
||||||
|
result.add(createCompetitor("C"+i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Regatta createSimpleRegatta(final int numberOfRaces, final String regattaName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||||
|
series = new ArrayList<Series>();
|
||||||
|
List<Fleet> fleets = new ArrayList<Fleet>();
|
||||||
|
fleets.add(new FleetImpl("Default"));
|
||||||
|
|
||||||
|
List<String> raceColumnNames = new ArrayList<String>();
|
||||||
|
for (int i = 1; i <= numberOfRaces; i++) {
|
||||||
|
raceColumnNames.add("R" + i);
|
||||||
|
}
|
||||||
|
Series defaultSeries = new SeriesImpl("Default", /* isMedal */false, /* isFleetsCanRunInParallel */true,
|
||||||
|
fleets, raceColumnNames, /* trackedRegattaRegistry */null);
|
||||||
|
series.add(defaultSeries);
|
||||||
|
|
||||||
|
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass,
|
||||||
|
/* startDate */null, /* endDate */null, series, /* persistent */false, scoringScheme, "123", null,
|
||||||
|
OneDesignRankingMetric::new);
|
||||||
|
return regatta;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Regatta createRegatta(final int numberOfQualifyingRaces, String[] qualifyingFleetNames, final int numberOfFinalRaces,
|
||||||
|
String[] finalFleetNames, boolean medalRaceAndSeries, final int numberOfMedalRaces, final String regattaBaseName, BoatClass boatClass, ScoringScheme scoringScheme) {
|
||||||
|
series = new ArrayList<Series>();
|
||||||
|
|
||||||
|
// -------- qualifying series ------------
|
||||||
|
if (qualifyingFleetNames != null && qualifyingFleetNames.length > 0) {
|
||||||
|
List<Fleet> qualifyingFleets = new ArrayList<Fleet>();
|
||||||
|
for (String qualifyingFleetName : qualifyingFleetNames) {
|
||||||
|
qualifyingFleets.add(new FleetImpl(qualifyingFleetName));
|
||||||
|
}
|
||||||
|
List<String> qualifyingRaceColumnNames = new ArrayList<String>();
|
||||||
|
for (int i = 1; i <= numberOfQualifyingRaces; i++) {
|
||||||
|
qualifyingRaceColumnNames.add("Q" + i);
|
||||||
|
}
|
||||||
|
Series qualifyingSeries = new SeriesImpl("Qualifying", /* isMedal */false, /* isFleetsCanRunInParallel */ true, qualifyingFleets,
|
||||||
|
qualifyingRaceColumnNames, /* trackedRegattaRegistry */null);
|
||||||
|
series.add(qualifyingSeries);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- final series ------------
|
||||||
|
if (finalFleetNames != null && finalFleetNames.length > 0) {
|
||||||
|
List<Fleet> finalFleets = new ArrayList<Fleet>();
|
||||||
|
int fleetOrdering = 1;
|
||||||
|
for (String finalFleetName : finalFleetNames) {
|
||||||
|
finalFleets.add(new FleetImpl(finalFleetName, fleetOrdering++));
|
||||||
|
}
|
||||||
|
List<String> finalRaceColumnNames = new ArrayList<String>();
|
||||||
|
for (int i = 1; i <= numberOfFinalRaces; i++) {
|
||||||
|
finalRaceColumnNames.add("F" + i);
|
||||||
|
}
|
||||||
|
Series finalSeries = new SeriesImpl("Final", /* isMedal */false, /* isFleetsCanRunInParallel */ true, finalFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||||
|
series.add(finalSeries);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (medalRaceAndSeries) {
|
||||||
|
// ------------ medal --------------
|
||||||
|
List<Fleet> medalFleets = new ArrayList<Fleet>();
|
||||||
|
medalFleets.add(new FleetImpl("Medal"));
|
||||||
|
List<String> medalRaceColumnNames = new ArrayList<String>();
|
||||||
|
if(numberOfMedalRaces == 1) {
|
||||||
|
medalRaceColumnNames.add("M");
|
||||||
|
} else if(numberOfMedalRaces > 1) {
|
||||||
|
for (int i = 1; i <= numberOfMedalRaces; i++) {
|
||||||
|
medalRaceColumnNames.add("M" + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Series medalSeries = new SeriesImpl("Medal", /* isMedal */true, /* isFleetsCanRunInParallel */ true, medalFleets, medalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||||
|
series.add(medalSeries);
|
||||||
|
}
|
||||||
|
|
||||||
|
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass,
|
||||||
|
/*startDate*/ null, /*endDate*/ null, series, /* persistent */ false, scoringScheme, "123", null, OneDesignRankingMetric::new);
|
||||||
|
return regatta;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected 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, /* isFleetsCanRunInParallel */ true, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable → Regular
+74
-31
@@ -26,7 +26,29 @@ import com.sap.sailing.gwt.ui.client.shared.controls.AbstractSortableColumnWithM
|
|||||||
*/
|
*/
|
||||||
public abstract class ExpandableSortableColumn<C> extends LeaderboardSortableColumnWithMinMax<LeaderboardRowDTO, C> {
|
public abstract class ExpandableSortableColumn<C> extends LeaderboardSortableColumnWithMinMax<LeaderboardRowDTO, C> {
|
||||||
private boolean enableExpansion;
|
private boolean enableExpansion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set when the {@link #toggleExpansion()} method begins with its work. Three things can happen:
|
||||||
|
* <ol>
|
||||||
|
* <li>the column is to be collapsed. This can happen synchronously, and the flag is cleared again before the method
|
||||||
|
* returns</li>
|
||||||
|
* <li>the column is to be expanded and all information required for this is already available; again, this type
|
||||||
|
* of request can be handled synchronously, and the flag is cleared before the method returns</li>
|
||||||
|
* <li>the column is to be expanded but not all information required is available yet; it has to be requested using
|
||||||
|
* an asynchronous call; the method returns with the flag still set, and the callback will clear it when done</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
private boolean togglingInProcess;
|
private boolean togglingInProcess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If {@link #togglingInProcess} is {@code true} and a call to {@link #toggleExpansion()} is received,
|
||||||
|
* instead of running the method, the request is "queued" only by incrementing this counter. When the
|
||||||
|
* asynchronous task completes the running toggle action, the counter is decremented by one and
|
||||||
|
* {@link #toggleExpansion()} is called. This way, overlapping asynchronous requests triggered by calls
|
||||||
|
* to {@link #toggleExpansion()} are avoiided.
|
||||||
|
*/
|
||||||
|
private int numberOfQueuedToggleRequests;
|
||||||
|
|
||||||
private boolean suppressSortingOnce;
|
private boolean suppressSortingOnce;
|
||||||
private final LeaderboardPanel leaderboardPanel;
|
private final LeaderboardPanel leaderboardPanel;
|
||||||
private final Map<DetailType, AbstractSortableColumnWithMinMax<LeaderboardRowDTO, ?>> detailColumnsMap;
|
private final Map<DetailType, AbstractSortableColumnWithMinMax<LeaderboardRowDTO, ?>> detailColumnsMap;
|
||||||
@@ -145,42 +167,63 @@ public abstract class ExpandableSortableColumn<C> extends LeaderboardSortableCol
|
|||||||
* <p>
|
* <p>
|
||||||
*
|
*
|
||||||
* Precondition: this column must currently be contained in the {@link CellTable} showing the
|
* Precondition: this column must currently be contained in the {@link CellTable} showing the
|
||||||
* {@link #leaderboardPanel leaderboard}.
|
* {@link #leaderboardPanel leaderboard}.<p>
|
||||||
|
*
|
||||||
|
* When the method starts to toggle the expansion state, it sets the {@link #togglingInProcess} flag. When the
|
||||||
|
* method returns the {@link #isExpanded()} method yields the new expansion state. However, when the column contents
|
||||||
|
* need to be loaded asynchronously first, the expansion will not have affected the table structure yet and is
|
||||||
|
* still "pending." This state is indicated by the {@link #isTogglingInProcess()} method returning {@code true}
|
||||||
|
* after this method has returned. Only after the asynchronous data loading has happened and the table has
|
||||||
|
* actually been updated to reflect the expanded state, the {@link #isTogglingInProcess()} will then again
|
||||||
|
* return {@code false}.
|
||||||
*/
|
*/
|
||||||
public void toggleExpansion() {
|
public void toggleExpansion() {
|
||||||
if (isExpansionEnabled()) {
|
if (isExpansionEnabled()) {
|
||||||
final boolean oldBusyState = getLeaderboardPanel().isBusy();
|
if (isTogglingInProcess()) {
|
||||||
getLeaderboardPanel().setBusyState(true);
|
numberOfQueuedToggleRequests++;
|
||||||
setTogglingInProcess(true);
|
|
||||||
final CellTable<LeaderboardRowDTO> table = getLeaderboardPanel().getLeaderboardTable();
|
|
||||||
if (isExpanded()) {
|
|
||||||
for (AbstractSortableColumnWithMinMax<LeaderboardRowDTO, ?> column : getAllVisibleChildren()) {
|
|
||||||
getLeaderboardPanel().removeColumn(column); // removes only the children currently displayed
|
|
||||||
}
|
|
||||||
getLeaderboardPanel().setBusyState(false);
|
|
||||||
setTogglingInProcess(false);
|
|
||||||
// important: toggle expanded state after asking for all visible children
|
|
||||||
setExpanded(!isExpanded());
|
|
||||||
} else {
|
} else {
|
||||||
// important: toggle expanded state before asking for all visible children
|
final boolean oldBusyState = getLeaderboardPanel().isBusy();
|
||||||
setExpanded(!isExpanded());
|
getLeaderboardPanel().setBusyState(true);
|
||||||
ensureExpansionDataIsLoaded(new Runnable() {
|
setTogglingInProcess(true);
|
||||||
public void run() {
|
final CellTable<LeaderboardRowDTO> table = getLeaderboardPanel().getLeaderboardTable();
|
||||||
int insertIndex = table.getColumnIndex(ExpandableSortableColumn.this);
|
if (isExpanded()) {
|
||||||
// The check "insertIndex != -1" is necessary, because the child-columns might be deleted asynchronously
|
for (AbstractSortableColumnWithMinMax<LeaderboardRowDTO, ?> column : getAllVisibleChildren()) {
|
||||||
// while toggling the columns.
|
getLeaderboardPanel().removeColumn(column); // removes only the children currently displayed
|
||||||
if (insertIndex != -1) {
|
|
||||||
insertIndex++;
|
|
||||||
for (AbstractSortableColumnWithMinMax<LeaderboardRowDTO, ?> column : getAllVisibleChildren()) {
|
|
||||||
column.updateMinMax();
|
|
||||||
getLeaderboardPanel().insertColumn(insertIndex++, column);
|
|
||||||
}
|
|
||||||
getLeaderboardPanel().getLeaderboardTable().redraw();
|
|
||||||
}
|
|
||||||
getLeaderboardPanel().setBusyState(oldBusyState);
|
|
||||||
setTogglingInProcess(false);
|
|
||||||
}
|
}
|
||||||
});
|
getLeaderboardPanel().setBusyState(false);
|
||||||
|
setTogglingInProcess(false);
|
||||||
|
// important: toggle expanded state after asking for all visible children
|
||||||
|
setExpanded(!isExpanded());
|
||||||
|
} else {
|
||||||
|
// important: toggle expanded state before asking for all visible children
|
||||||
|
setExpanded(!isExpanded());
|
||||||
|
ensureExpansionDataIsLoaded(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
// column may have been collapsed meanwhile, so that the columns must not be added to the table right now
|
||||||
|
if (isExpanded()) {
|
||||||
|
int insertIndex = table.getColumnIndex(ExpandableSortableColumn.this);
|
||||||
|
// The check "insertIndex != -1" is necessary, because the child-columns might be deleted
|
||||||
|
// asynchronously while toggling the columns.
|
||||||
|
if (insertIndex != -1) {
|
||||||
|
insertIndex++;
|
||||||
|
for (AbstractSortableColumnWithMinMax<LeaderboardRowDTO, ?> column : getAllVisibleChildren()) {
|
||||||
|
column.updateMinMax();
|
||||||
|
if (table.getColumnIndex(column) < 0) {
|
||||||
|
getLeaderboardPanel().insertColumn(insertIndex++, column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getLeaderboardPanel().getLeaderboardTable().redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getLeaderboardPanel().setBusyState(oldBusyState);
|
||||||
|
setTogglingInProcess(false);
|
||||||
|
if (numberOfQueuedToggleRequests > 0) {
|
||||||
|
numberOfQueuedToggleRequests--;
|
||||||
|
toggleExpansion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2484,7 +2484,7 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Due to a course change, a race may change its number of legs. All expanded race columns that show leg columns and
|
* Due to a course change, a race may change its number of legs. All expanded race columns that show leg columns and
|
||||||
* whose leg count changed need to be collapsed before the leaderboard is replaced, and expanded afterwards again.
|
* whose leg count changed need to be collapsed before the leaderboard is replaced, and expanded afterwards again.
|
||||||
* Race columns whose toggling is {@link ExpandableSortableColumn#isTogglingInProcess() currently in progress} are
|
* Race columns whose toggling is {@link ExpandableSortableColumn#isTogglingInProcess() currently in progress} are
|
||||||
|
|||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
package com.sap.sailing.server.test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||||
|
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
|
||||||
|
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||||
|
import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultImpl;
|
||||||
|
import com.sap.sailing.domain.abstractlog.race.impl.CompetitorResultsImpl;
|
||||||
|
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
|
||||||
|
import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl;
|
||||||
|
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.impl.RacingProcedureFactoryImpl;
|
||||||
|
import com.sap.sailing.domain.base.Competitor;
|
||||||
|
import com.sap.sailing.domain.base.DomainFactory;
|
||||||
|
import com.sap.sailing.domain.base.RaceColumn;
|
||||||
|
import com.sap.sailing.domain.base.Regatta;
|
||||||
|
import com.sap.sailing.domain.base.configuration.impl.EmptyRegattaConfiguration;
|
||||||
|
import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.BoatImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.NationalityImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.PersonImpl;
|
||||||
|
import com.sap.sailing.domain.base.impl.TeamImpl;
|
||||||
|
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||||
|
import com.sap.sailing.domain.common.NoWindException;
|
||||||
|
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||||
|
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||||
|
import com.sap.sailing.domain.test.LeaderboardScoringAndRankingTestBase;
|
||||||
|
import com.sap.sailing.domain.test.mock.MockedTrackedRaceWithStartTimeAndRanks;
|
||||||
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
|
import com.sap.sailing.server.RacingEventService;
|
||||||
|
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||||
|
import com.sap.sse.common.Color;
|
||||||
|
import com.sap.sse.common.TimePoint;
|
||||||
|
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||||
|
|
||||||
|
public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTestBase {
|
||||||
|
/**
|
||||||
|
* See also bug 3794: make sure that different variants of scores and max points reasons / penalties are applied to the
|
||||||
|
* leaderboard.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testApplicationOfScoresFromRaceLog() throws NoWindException {
|
||||||
|
final RacingEventService service = new RacingEventServiceImpl();
|
||||||
|
final List<Competitor> competitors = new ArrayList<>();
|
||||||
|
for (int i=0; i<20; i++) {
|
||||||
|
final String competitorName = "C"+i;
|
||||||
|
competitors.add(service.getBaseDomainFactory().getCompetitorStore().getOrCreateCompetitor(UUID.randomUUID(),
|
||||||
|
competitorName, /* displayColor */ Color.RED, /* email */ null, /* flagImageURI */ null,
|
||||||
|
new TeamImpl("STG", Collections.singleton(
|
||||||
|
new PersonImpl(competitorName, new NationalityImpl("GER"),
|
||||||
|
/* dateOfBirth */ null, "This is famous "+competitorName)),
|
||||||
|
new PersonImpl("Rigo van Maas", new NationalityImpl("NED"),
|
||||||
|
/* dateOfBirth */null, "This is Rigo, the coach")), new BoatImpl(competitorName + "'s boat",
|
||||||
|
new BoatClassImpl("505", /* typicallyStartsUpwind */ true), /* sailID */ null),
|
||||||
|
/* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null));
|
||||||
|
}
|
||||||
|
final Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */1,
|
||||||
|
new String[] { "Default" },
|
||||||
|
/* medal */false, /* medal */ 0, "testOneStartedRaceWithDifferentScores",
|
||||||
|
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||||
|
TimePoint now = MillisecondsTimePoint.now();
|
||||||
|
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
|
||||||
|
TrackedRace f1 = new MockedTrackedRaceWithStartTimeAndRanks(now, competitors);
|
||||||
|
RaceColumn f1Column = series.get(1).getRaceColumnByName("F1");
|
||||||
|
f1Column.setTrackedRace(f1Column.getFleets().iterator().next(), f1);
|
||||||
|
final Leaderboard leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||||
|
|
||||||
|
service.addLeaderboard(leaderboard); // should add a RaceLogScoringReplicator as listener to the leaderboard
|
||||||
|
service.addRegattaWithoutReplication(regatta);
|
||||||
|
final Map<Competitor, Double> scores = new HashMap<>();
|
||||||
|
final Map<Competitor, MaxPointsReason> mprs = new HashMap<>();
|
||||||
|
int oneBasedRank = 1;
|
||||||
|
final CompetitorResults results = new CompetitorResultsImpl();
|
||||||
|
for (final Competitor c : competitors) {
|
||||||
|
final MaxPointsReason mpr = new MaxPointsReason[] { null, MaxPointsReason.NONE, MaxPointsReason.DNF, MaxPointsReason.OCS }[oneBasedRank%4];
|
||||||
|
final Double score = oneBasedRank%5 == 0 ? null : 20*Math.random();
|
||||||
|
scores.put(c, score);
|
||||||
|
mprs.put(c, mpr);
|
||||||
|
results.add(new CompetitorResultImpl(c.getId(), c.getName(),
|
||||||
|
oneBasedRank++, mpr, score, /* finishingTime */ null, /* comment */ null));
|
||||||
|
}
|
||||||
|
final RaceLog f1RaceLog = f1Column.getRaceLog(f1Column.getFleets().iterator().next());
|
||||||
|
final LogEventAuthorImpl author = new LogEventAuthorImpl("Axel", 0);
|
||||||
|
final RaceState f1RaceState = new RaceStateImpl(service, f1RaceLog, author,
|
||||||
|
new RacingProcedureFactoryImpl(author, new EmptyRegattaConfiguration()));
|
||||||
|
f1RaceState.setFinishPositioningListChanged(now, results);
|
||||||
|
final List<Competitor> rankedCompetitorsBeforeApplying = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||||
|
assertEquals(competitors, rankedCompetitorsBeforeApplying); // no effects of preliminary results list yet
|
||||||
|
f1RaceState.setFinishPositioningConfirmed(now);
|
||||||
|
for (final Competitor c : competitors) {
|
||||||
|
assertEquals(scores.get(c)==null?competitors.indexOf(c)+1:scores.get(c), leaderboard.getTotalPoints(c, f1Column, now), 0.00000001);
|
||||||
|
}
|
||||||
|
final List<Competitor> expectedNewOrder = new ArrayList<>(competitors);
|
||||||
|
expectedNewOrder.sort((c1, c2)->
|
||||||
|
new Double(scores.get(c1)==null?competitors.indexOf(c1)+1:scores.get(c1)).compareTo(
|
||||||
|
new Double(scores.get(c2)==null?competitors.indexOf(c2)+1:scores.get(c2))));
|
||||||
|
final List<Competitor> rankedCompetitorsAfterApplying = leaderboard.getCompetitorsFromBestToWorst(later);
|
||||||
|
assertEquals(expectedNewOrder, rankedCompetitorsAfterApplying);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ boatClassUpcomingRaceSubject=A race for boat class {0} will start soon
|
|||||||
# 0: boatClassName, 1: raceDescription, 2: time
|
# 0: boatClassName, 1: raceDescription, 2: time
|
||||||
boatClassUpcomingRaceBody=For boat class {0} {1} will start approximately at {2}.
|
boatClassUpcomingRaceBody=For boat class {0} {1} will start approximately at {2}.
|
||||||
|
|
||||||
subscriptionInformation=You are receiving notifications from SAP Sailing Analytics. To turn off notifications, please visit user your profile at the link below.
|
subscriptionInformation=You are receiving notifications from SAP Sailing Analytics. To turn off notifications, please visit your user profile at the link below.
|
||||||
userProfile=User Profile
|
userProfile=User Profile
|
||||||
imprint=Imprint
|
imprint=Imprint
|
||||||
privacy=Privacy
|
privacy=Privacy
|
||||||
|
|||||||
+10
-35
@@ -1,7 +1,5 @@
|
|||||||
package com.sap.sailing.server.impl;
|
package com.sap.sailing.server.impl;
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
|
|
||||||
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
|
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
|
||||||
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
|
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
|
||||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||||
@@ -15,7 +13,6 @@ import com.sap.sailing.domain.base.Fleet;
|
|||||||
import com.sap.sailing.domain.base.RaceColumn;
|
import com.sap.sailing.domain.base.RaceColumn;
|
||||||
import com.sap.sailing.domain.base.RaceColumnListener;
|
import com.sap.sailing.domain.base.RaceColumnListener;
|
||||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||||
import com.sap.sailing.domain.common.NoWindException;
|
|
||||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||||
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
|
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
|
||||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||||
@@ -148,21 +145,13 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
|
|||||||
if (positioningList != null) {
|
if (positioningList != null) {
|
||||||
for (CompetitorResult positionedCompetitor : positioningList) {
|
for (CompetitorResult positionedCompetitor : positioningList) {
|
||||||
Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(positionedCompetitor.getCompetitorId());
|
Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(positionedCompetitor.getCompetitorId());
|
||||||
if (positionedCompetitor.getMaxPointsReason().equals(MaxPointsReason.NONE)) {
|
int rankByRaceCommittee = getRankInPositioningListByRaceCommittee(positionedCompetitor);
|
||||||
try {
|
correctScoreInLeaderboard(leaderboard, raceColumn, timePoint, numberOfCompetitorsInRace,
|
||||||
resetMaxPointsReasonIfNecessary(leaderboard, raceColumn, timePoint, competitor);
|
competitor, rankByRaceCommittee, positionedCompetitor.getScore());
|
||||||
int rankByRaceCommittee = getRankInPositioningListByRaceCommittee(positionedCompetitor);
|
setMaxPointsReasonInLeaderboardIfNecessary(leaderboard, raceColumn, timePoint, positionedCompetitor, competitor);
|
||||||
correctScoreInLeaderboard(leaderboard, raceColumn, timePoint, numberOfCompetitorsInRace,
|
|
||||||
competitor, rankByRaceCommittee, positionedCompetitor.getScore());
|
|
||||||
} catch (NoWindException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setMaxPointsReasonInLeaderboardIfNecessary(leaderboard, raceColumn, timePoint, positionedCompetitor, competitor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//Since the metadata update is used by the Sailing suite to determine the final state of a race, it has to be triggered, even though
|
// Since the metadata update is used by the Sailing suite to determine the final state of a race, it has to
|
||||||
//no score correction was performed
|
// be triggered, even though no score correction may have been performed
|
||||||
applyMetadataUpdate(leaderboard, timePoint, COMMENT_TEXT_ON_SCORE_CORRECTION);
|
applyMetadataUpdate(leaderboard, timePoint, COMMENT_TEXT_ON_SCORE_CORRECTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,9 +159,9 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
|
|||||||
private boolean setMaxPointsReasonInLeaderboardIfNecessary(Leaderboard leaderboard, RaceColumn raceColumn,
|
private boolean setMaxPointsReasonInLeaderboardIfNecessary(Leaderboard leaderboard, RaceColumn raceColumn,
|
||||||
TimePoint timePoint, CompetitorResult positionedCompetitor, Competitor competitor) {
|
TimePoint timePoint, CompetitorResult positionedCompetitor, Competitor competitor) {
|
||||||
boolean scoreHasBeenCorrected = false;
|
boolean scoreHasBeenCorrected = false;
|
||||||
MaxPointsReason trackedMaxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumn, timePoint);
|
MaxPointsReason oldMaxPointsReason = leaderboard.getMaxPointsReason(competitor, raceColumn, timePoint);
|
||||||
MaxPointsReason maxPointsReasonByRaceCommittee = positionedCompetitor.getMaxPointsReason();
|
MaxPointsReason maxPointsReasonByRaceCommittee = positionedCompetitor.getMaxPointsReason();
|
||||||
if (!maxPointsReasonByRaceCommittee.equals(trackedMaxPointsReason)) {
|
if (!Util.equalsWithNull(maxPointsReasonByRaceCommittee, oldMaxPointsReason)) {
|
||||||
applyMaxPointsReasonOperation(leaderboard, raceColumn, competitor, maxPointsReasonByRaceCommittee, timePoint);
|
applyMaxPointsReasonOperation(leaderboard, raceColumn, competitor, maxPointsReasonByRaceCommittee, timePoint);
|
||||||
scoreHasBeenCorrected = true;
|
scoreHasBeenCorrected = true;
|
||||||
}
|
}
|
||||||
@@ -181,16 +170,11 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
|
|||||||
|
|
||||||
private void correctScoreInLeaderboard(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint,
|
private void correctScoreInLeaderboard(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint,
|
||||||
final int numberOfCompetitorsInRace,
|
final int numberOfCompetitorsInRace,
|
||||||
Competitor competitor, int rankByRaceCommittee, Double optionalExplicitScore) throws NoWindException {
|
Competitor competitor, int rankByRaceCommittee, Double optionalExplicitScore) {
|
||||||
final Double scoreByRaceCommittee;
|
final Double scoreByRaceCommittee;
|
||||||
if (optionalExplicitScore == null) {
|
if (optionalExplicitScore == null) {
|
||||||
scoreByRaceCommittee = leaderboard.getScoringScheme().getScoreForRank(leaderboard, raceColumn, competitor,
|
scoreByRaceCommittee = leaderboard.getScoringScheme().getScoreForRank(leaderboard, raceColumn, competitor,
|
||||||
rankByRaceCommittee, new Callable<Integer>() {
|
rankByRaceCommittee, ()->numberOfCompetitorsInRace, leaderboard.getNumberOfCompetitorsInLeaderboardFetcher(), timePoint);
|
||||||
@Override
|
|
||||||
public Integer call() {
|
|
||||||
return numberOfCompetitorsInRace;
|
|
||||||
}
|
|
||||||
}, leaderboard.getNumberOfCompetitorsInLeaderboardFetcher(), timePoint);
|
|
||||||
} else {
|
} else {
|
||||||
scoreByRaceCommittee = optionalExplicitScore;
|
scoreByRaceCommittee = optionalExplicitScore;
|
||||||
}
|
}
|
||||||
@@ -198,15 +182,6 @@ public class RaceLogScoringReplicator implements RaceColumnListener {
|
|||||||
applyScoreCorrectionOperation(leaderboard, raceColumn, competitor, scoreByRaceCommittee, timePoint);
|
applyScoreCorrectionOperation(leaderboard, raceColumn, competitor, scoreByRaceCommittee, timePoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean resetMaxPointsReasonIfNecessary(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint, Competitor competitor) {
|
|
||||||
boolean scoreHasBeenCorrected = false;
|
|
||||||
if (!leaderboard.getMaxPointsReason(competitor, raceColumn, timePoint).equals(MaxPointsReason.NONE)) {
|
|
||||||
applyMaxPointsReasonOperation(leaderboard, raceColumn, competitor, MaxPointsReason.NONE, timePoint);
|
|
||||||
scoreHasBeenCorrected = true;
|
|
||||||
}
|
|
||||||
return scoreHasBeenCorrected;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyScoreCorrectionOperation(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, Double correctedScore, TimePoint timePoint) {
|
private void applyScoreCorrectionOperation(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor, Double correctedScore, TimePoint timePoint) {
|
||||||
RacingEventServiceOperation<?> operation = new UpdateLeaderboardScoreCorrection(leaderboard.getName(), raceColumn.getName(), competitor.getId().toString(), correctedScore, timePoint);
|
RacingEventServiceOperation<?> operation = new UpdateLeaderboardScoreCorrection(leaderboard.getName(), raceColumn.getName(), competitor.getId().toString(), correctedScore, timePoint);
|
||||||
service.apply(operation);
|
service.apply(operation);
|
||||||
|
|||||||
@@ -22,8 +22,9 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:ellipsize="middle"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
android:textColor="@color/toolbar_text_color"
|
android:textColor="@color/toolbar_text_color"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
@@ -32,8 +33,9 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:ellipsize="middle"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
android:textColor="@color/toolbar_text_color"
|
android:textColor="@color/toolbar_text_color"
|
||||||
android:textSize="12sp" />
|
android:textSize="12sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
Reference in new issue
Block a user