mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-15 06:49:56 +00:00
Merge branch 'master' into bug3992_new_concept
This commit is contained in:
commit
28644c5b74
164 files changed
+4736
-2329
No files matched your search
+4
-4
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file datatype="javapropertyresourcebundle" date="2017-04-12T15:29:20Z" original="/usr/sap/ljs/app/Converter_429408536936316109/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties" source-language="en" target-language="es">
|
||||
<file datatype="javapropertyresourcebundle" date="2017-04-19T13:56:46Z" original="/usr/sap/ljs/app/Converter_429408536936316109/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties" source-language="en" target-language="es">
|
||||
<header>
|
||||
<skl>
|
||||
<internal-file crc="9b947ad1ab0b80186f1c396a805fb4d1" form="base64">H4sIAAAAAAAAANy9a3PcSJIl+h2/At1tdyXZkqlHdffMVBusLkVKKs6IjyapUveuro2BmSCJUSaQA2SKYrXVf18/x90jAsgkqerp3XvtlpWSQLwQCER4ePjj+KJsyuvq1XlZz4ujeJ115bTabxeLerWqqr3lsjiThFxLdLkksMRPdXVbdZr3hdfZolzut82qa+e9NLjMp3aTLaVmcSo/2UqqXsi/N1+qZtUXF3ab633W39Z9f1Ev6ubaSpzHFC+ENj5Xs9fVVdtVxY91v2q7u7y9yi0jr7TcddVUXTkv3unfbC4l0d2+eC9XOS81sbouV6vS0+0uu62bWfFRfuTFmmr9peou7pYVhoo3zD+Vm/n78rKaFxc3dZ/L/6ubKketnHn57U09vcn7Nr8qO2RP28VyXq2q+V1eLZaru0nWVVdd1d8UZ/pX7hftl0pu8YdPOW/X3bRiX3K9zmblYlk1MiqHzarqvshrHnhK7kn4IPgQ/F78UrzqC311G67C/spQNPLHRwjXNkarelEV8g0qXl207XxVL5nAl8UbVatavkLey/yRam0jGfKu8+p6kr+VdBS7qjsZXUnSPA7FWno6y6+6diHjVDXj5pZl30s+UvtV2Untuqkm2XJe3p0vK+n6qVzlPS5j4o/VfFl86Kv8ty9/m8sUybvqej2XwUeJ3Ut5V62xk//2lRaYtevLebXLVJZioVIGWz5b27Dpo3ZWva/ls+AnpJxVuCr0T44UDtFBhVT+xnv2631Vfqn0w+erVrrGiuUqL7vLWj6ETGRU6P+UV/iIeV/JIpr1+dNqcj2RHv/xhXa5bapc1sR6VeVy91tLbST5GVbCDA/ML7lCZNDkkbNyVeZXGCi+5mHzb02L5cV3fvq5eZbhKxxU18VbfI2ns+r6Wda0nAB/rVYyuytpqpR/jXQbqfldtZpkq3Ylc+/T+sV3e99l07Lr7oq96XS9kBGXT5vFj1nsh8sktU+S+zT9vJpXU2mh+NuLX57/7eUvybToZVQ0cyI9PF9Pb2RUZ1V32ZbdrDhu816SZKKFtCy5FgJ1VV+vu3JVt02RVMwHOVlT3R60K/2/OK5uJ5NJVs3q1flUhrQv3shlftrWQmeQM23n60VzXMqXO2y862mvSCaZn9dN7iXypEhWzmbxiXuzGdrtqkbqxOQz3iPHFu0ZaXXTsLmL1htGMp5GAusLOQ8FMfVCH0LXSAAu2n2+CykAyulttm6QW3zgn3RA+Zh0HNnUtGymQhP3+UeWS1X21RvM6D2WP2WCzfEyx0sNCr1uy9X+XFb/uKQ8YpVPkZPN6l7mm9IrzNkf21shzXfnIBRGxk6a4kBL6YLBasDcVWoCUnlbr27ym/Y2FxJ/p8kyJJ1uDfGlPkox0Hd0fm/eScbdm6+yWfSDN2djJG54obzUgnnFkt7fixsQ+XY+64/W/ep1dbxeVF09DR1dhfx8IQVkEeeNFrmvhb1+WjUzeZuH2ii9kLeSlEeqjwhGUrp0WXUnV7oVnPtYkUyHsdIx0gk6mOlMGc9snUUXbVpS5rjNLsyztEIyFTDisoPICtSpzrm5Zfoo/atu9fvqeszaz8XJv2WLalbOUbs4wlWuGyG7yVTrMZNb2UK3rd8TSd++bLtSlsWZ/PDKN8dI1Z486XPkYN1jI+v06eQxzlhXr3NtI2Z8Q1NaOBM2QfibOVs70WttLcl4vLVe5hg2exD0s/v6JwM7L5d9Vexm1delbJHFf5d1cl28x7RaYbUfNkfC3QhhP7CErEQvrqvzdN/5tH716sUfcqZtKxCYDOmYZeuuneOd8utOWIdZPlt3mJjOa+x8aqblfKpbj69G2frqL3UvNJ0sIu+1Y+AXv1TgVy7vmIGdV+ZQNcPOgu+094/oGBr6B/es+fyurBvOuhxXISnt3ay+upI9G81Z88u2r7G56Uomr7XupMSKvA5LdNWXul0r55b15RIHAnmTvaac363qqVADTchDSkomU2qYpg/I5KDGebUCbRnSUU/Mes9NUrAE91ZSvF+dNEJ5rg+qFU4wujjBSoE4rEjwwWzOmJ3Zm/JTYo2844cKbNDJu4dLpAPrg7bx2YVD6OVL4RtfkFE+rr6uPpZ3S/AJ4AvIyxVvLva+qWD6zFBep8LqRt5zxCxjevTKVJZTnV+NNJvfWrs7+WXZO3Oe1gQh8HeSWddOa2FOF/I58uu2FTZZOtPX8nF0RmNyrqscxxKZYp2MucyoSXZdLp26xzd9J4dA6ZB+761F/B3f2Pvp8elpXCzPhOfsajnM6Iv7a1t3w4y2GU72gIMjC08OXJImU6G8kYfKEgzv7wv3p6N33DfSJjFnsBy4SJdVJ/kLrsnqP9foRavt2wO52+j7saV+fdlLQWuon3xq8PWuMQ7VfN7r9gn2tCq7eV114494267ns/wGh4Sbknya7N235L372Ek5O9b9JznrCfOPJ+OjgxYkXRx2zYZt2bX/oTuYD+q3jOdwHCeZz5EjafqdzBBfRDKa9+WlU3nbFNNDsh2eMbVuhQ48n1cV/tpCw/Cul8h8PmtvG05AjPGOTHgpIfNQWr7bye17lvO2ud4lm5w0wLWBicyvk/mjfK+6aN+1YQP7aJkJWW6lvw9W8hf9OH6H0EakvVsXYPgC9hWFafOPgZn5XxiE2INvGYfIJ7y1z7+Hrx8GR5b2r6/1+OjYqyYrwsYhSRnOx5Sv+t80PJ+a43a1nehupzVIkWkv9Fd45L7salmwWIlOQdnX+kqWuLDkUyEOWL3IrHk8XM4lX862OCnpwQxSgzcQGggrfFBdlev5aiBKoBRD0/VkKPwypWV6AMzlVqVimltezitl7i/81kU9uMfnsYoUfxmfyZObc4M8srFJJvNhSaIm6KkCW5yeIkjDNfHkig/AKUSzXH6Hw4hM+iT/jS0Bz7XueFf8vi+C5E4fO2w/VO9X7TLJa5cxa6oHZFl+kEuE6+xGZYwnV0liKnhM6mXyGeqmkgGPZYsDpuGkIt8hFk4P8X04quvBSg7Q5zIMZ9hMcM6Uk7Rw6DISnaV4Z/dQqLQ27JncwHD4tMaWbSdNnOI3gzTooJThe+9iIYxIV82YeM5LTRYeti8+nB322X/0bfOhmxf/en5ynH84e89G0BzbzNnSQWwp5lhzzEOdD12tz5VmrTCSrBgSVXabiDR0JvX5ftj6bKCym7ZfcY38aBcy9lNhTinA2b+ppp8v268yJB2YFBXJvpZtF+tM2OqDWJa8/nk6Z+yc6be5CRWyZbnuq731qhVepZ6awLY4RWpeenJuAt0MKWdD4W4sJeeyu4xyusgvUU6YP+2facbranVbVQ2etzf7AuLkRS41h63trpczck514+LC77W+CRja5hgLA8JLngvYgssFrmWarMiHyAT7uerabHrTtn31gY26JPnkKrLc2tlin8Vyfbg8WwtiNQxYcO1RVnVd2xkXjy8hdIxn7zdIz43PV6aS9BeHZNZ5W61IiVHpsCEzhk82rH9lhbSBOhYbNfbxpp5XF92dFIWsbPUxyNaH7d2inJCFOxLsFv3Lf8u2SVl622N+m1/Ny+usbpTUyvgbjW2LVSu/58I146oXrs8Z3BmJ6zn4wMjTozEIikGRVNfBKaknG9k1UVxFiJbkJ/thCT/7e6G5H468gJyWQqaKSI6koQ/L4sMyuT+QnbPAD+kGF3KyQoSC2+pgh3xl4B1Pmvmdy0gSsa6+bSuZQXCbSnNZdUs1fG2tig8qrOhX5bC3NYFXel8116ub4kIuZf7hmiupT0SiwtOR0VT5tfLad+06n9efydwJDwB5/A+Z1o8L833S3krWcX8hH0/IFi5zXGeNycxcO9QHPVG/mZeyw5rpjI9rmnpXpdSJKmWzocMGr7W9uXub6shdrIS0yaaD3+w/6ksZm3/Fb7as5GS/utuvu+lcEk/1Nrf7KEw77I/X87mK1MjPyOpfV2R7ZA62txT+p7L2oXw9VamlY6XUZ/9GZpWTmClugnzpwkQjQb6Ue8pGkaE0ZCxaCYw4+GpVL0We+1OTsJ7GmvMAakfpOrLkdd88eSKnpLqp5YhmkqODv68rUTj96/uiTOW4M+MD2XvheIuf/AiG1BzJqhWKYg8WU+UQEnNNTY/wLIED/oXLbjP2Ryb3+7acFaoc4fFfbrV9bTF8pygirBuhnpntXh+aelX0fncKGfCaG+aRTGPNfH58JDMRRwreL2S/k42s0rtP6xcvLl9k07lMnmIfv1QslV21P9I7QZ2VkpFZ1U+7emk8W7jOsIBUKE75ly2yjPrL7k6a4t+sab97LztZ1e230lLxoSFjLSNwVZt87fBkP/9ud85Cu1OUmkA6DlE4tmx5I2zw8odEkavAyCcXQc+5db8yYk8ngckXy9U2PcTk4TaEohh7/S2NRZqilSYZjj+yofwI5nO9zC6F4dq7Le8KcF55eQuFqJAc0h1eDeSpCwwkSSHI0na6BVpFUsWr7dVJz7ZXH5C3QN2mvB1mbm96Oahxz0MWZff5tOx77OdHck1FNnZ1ZDAla2U6dE72ZKp8bmTDDTQ7m7Xr63kpk38trG6na0n2ZCZKH5iq67/Pruq5TCfd397y2th+zXh9h4980kWdmnC//JZCVhJ9mpf27y+lXMGgWULq61Jb8+cskJRbe3qcCRJdPUC6CDcreeSB3pTK0+Q+XZXUq6ZLMhZLNa89ry0TupnXXXvby0BSTyMfw+6txNnAoiJpkioPbVKvvYKMX+hqToEAR4ysZCzyIVWKjjMHD00z33wFX/WXo/eFXuXySeUueZuDsr9J9E77LQS6IdE+RypSH36VVPIePk5px9Y9O6fWvdp7FId9rlfZnYzFX2UMmrY4buV4JRMRxH+P009OWdgI+FPq1DOxg/KTjSmfQTuhg/cbkpvD/t8wv4UNxd72gdyz8o/GUJPKKFeNTdWOgYWLine truncated
|
||||
@@ -3829,7 +3829,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="courseDesignerMode" resname="courseDesignerMode">
|
||||
<source>Course designer</source>
|
||||
<target state="signed-off">Diseñador de regatas</target>
|
||||
<target state="translated">Diseñador de recorrido</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="racingProcedure" resname="racingProcedure">
|
||||
<source>Racing procedure</source>
|
||||
@@ -6521,11 +6521,11 @@
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="notificationRegattaConfigurationUpdatedUsingByMarks" resname="notificationRegattaConfigurationUpdatedUsingByMarks">
|
||||
<source>Active regatta configuration was set, using the "By Marks" course designer.</source>
|
||||
<target state="signed-off">Se ha fijado la configuración de regata activa con el diseñador de regatas "Por marcas".</target>
|
||||
<target state="translated">Se ha fijado la configuración de regata activa con el diseñador de recorrido "Por marcas".</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="warningOverrideRegattaConfigurationCourseDesignerToByMarks" resname="warningOverrideRegattaConfigurationCourseDesignerToByMarks">
|
||||
<source>Active regatta configuration already exists, but with the wrong course designer setting.\nWould you like to override course designer setting to "By Marks"?</source>
|
||||
<target state="signed-off">Ya existe la configuración de regata activa pero con la opción de diseñador de regatas errónea.\¿Desea sustituir la opción de diseñador de regatas por "Por marcas"?</target>
|
||||
<target state="translated">Ya existe la configuración de regata activa pero con la opción de diseñador de recorrido errónea.\¿Desea sustituir la opción de diseñador de recorrido por "Por marcas"?</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="allRacesHaveBeenDenoted" resname="allRacesHaveBeenDenoted">
|
||||
<source>All races have been denoted for racelog tracking.</source>
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.common;
|
||||
|
||||
public enum LeaderboardType {
|
||||
RegattaLeaderboard(false, true), RegattaMetaLeaderboard(true, true),
|
||||
RegattaLeaderboardWithEliminations(false, true),
|
||||
FlexibleLeaderboard(false, false), FlexibleMetaLeaderboard(true, false);
|
||||
|
||||
boolean isRegattaLeaderboard;
|
||||
@@ -26,10 +27,10 @@ public enum LeaderboardType {
|
||||
|
||||
public String toString() {
|
||||
String result = "";
|
||||
if(isMetaLeaderboard) {
|
||||
if (isMetaLeaderboard) {
|
||||
result += "Meta, ";
|
||||
}
|
||||
result += isRegattaLeaderboard ? "Regatta" : "Flexible";
|
||||
result += isRegattaLeaderboard ? "Regatta" + (this==RegattaLeaderboardWithEliminations?" w/ Elimination":"") : "Flexible";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -28,6 +28,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardRegistry;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
|
||||
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
|
||||
@@ -52,9 +53,7 @@ public interface DomainObjectFactory {
|
||||
* @return the leaderboard loaded, if successful, or <code>null</code> if the leaderboard couldn't be loaded,
|
||||
* e.g., because the regatta for a regatta leaderboard couldn't be found
|
||||
*/
|
||||
Leaderboard loadLeaderboard(String name, RegattaRegistry regattaRegistry);
|
||||
|
||||
Iterable<Leaderboard> getAllLeaderboards(RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry);
|
||||
Leaderboard loadLeaderboard(String name, RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry);
|
||||
|
||||
RaceIdentifier loadRaceIdentifier(DBObject dbObject);
|
||||
|
||||
@@ -192,4 +191,7 @@ public interface DomainObjectFactory {
|
||||
*/
|
||||
ConnectivityParametersLoadingResult loadConnectivityParametersForRacesToRestore(
|
||||
Consumer<RaceTrackingConnectivityParameters> callback);
|
||||
|
||||
RegattaLeaderboardWithEliminations loadRegattaLeaderboardWithEliminations(DBObject dbLeaderboard,
|
||||
String leaderboardName, String wrappedRegattaLeaderboardName, LeaderboardRegistry leaderboardRegistry);
|
||||
}
|
||||
+43
-30
@@ -181,10 +181,12 @@ import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroupResolver;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardRegistry;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelayedLeaderboardCorrectionsImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
|
||||
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||
@@ -368,14 +370,14 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Leaderboard loadLeaderboard(String name, RegattaRegistry regattaRegistry) {
|
||||
public Leaderboard loadLeaderboard(String name, RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) {
|
||||
DBCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name());
|
||||
Leaderboard result = null;
|
||||
try {
|
||||
BasicDBObject query = new BasicDBObject();
|
||||
query.put(FieldNames.LEADERBOARD_NAME.name(), name);
|
||||
for (DBObject o : leaderboardCollection.find(query)) {
|
||||
result = loadLeaderboard(o, regattaRegistry, /* leaderboardRegistry */ null, /* groupForMetaLeaderboard */ null);
|
||||
result = loadLeaderboard(o, regattaRegistry, leaderboardRegistry, /* groupForMetaLeaderboard */ null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// something went wrong during DB access; report, then use empty new wind track
|
||||
@@ -386,27 +388,28 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Leaderboard> getAllLeaderboards(RegattaRegistry regattaRegistry, LeaderboardRegistry leaderboardRegistry) {
|
||||
DBCollection leaderboardCollection = database.getCollection(CollectionNames.LEADERBOARDS.name());
|
||||
Set<Leaderboard> result = new HashSet<Leaderboard>();
|
||||
try {
|
||||
for (DBObject o : leaderboardCollection.find()) {
|
||||
final Leaderboard loadedLeaderboard = loadLeaderboard(o, regattaRegistry, leaderboardRegistry, /* groupForMetaLeaderboard */ null);
|
||||
if (loadedLeaderboard != null) {
|
||||
result.add(loadedLeaderboard);
|
||||
}
|
||||
public RegattaLeaderboardWithEliminations loadRegattaLeaderboardWithEliminations(DBObject dbLeaderboard,
|
||||
String leaderboardName, String wrappedRegattaLeaderboardName, LeaderboardRegistry leaderboardRegistry) {
|
||||
final RegattaLeaderboardWithEliminations result;
|
||||
BasicDBList eliminatedCompetitorIds = (BasicDBList) dbLeaderboard.get(FieldNames.ELMINATED_COMPETITORS.name());
|
||||
result = new DelegatingRegattaLeaderboardWithCompetitorElimination(()->
|
||||
(RegattaLeaderboard) leaderboardRegistry.getLeaderboardByName(wrappedRegattaLeaderboardName), leaderboardName);
|
||||
for (Object eliminatedCompetitorId : eliminatedCompetitorIds) {
|
||||
Competitor eliminatedCompetitor = baseDomainFactory.getCompetitorStore().getExistingCompetitorById((Serializable) eliminatedCompetitorId);
|
||||
if (eliminatedCompetitor == null) {
|
||||
logger.warning("Couldn't find eliminated competitor with ID "+eliminatedCompetitorId);
|
||||
} else {
|
||||
result.setEliminated(eliminatedCompetitor, true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// something went wrong during DB access; report, then use empty new wind track
|
||||
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load leaderboards.");
|
||||
logger.log(Level.SEVERE, "getAllLeaderboards", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the DBObject has a field {@link FieldNames#REGATTA_NAME} then the object represents a
|
||||
* {@link RegattaLeaderboard}. Otherwise, a {@link FlexibleLeaderboard} will be loaded.
|
||||
* If the DBObject has a field {@link FieldNames#WRAPPED_REGATTA_LEADERBOARD_NAME} then the object represents a
|
||||
* {@link RegattaLeaderboardWithEliminations}. Otherwise, if the DBObject has a field
|
||||
* {@link FieldNames#REGATTA_NAME} then the object represents a {@link RegattaLeaderboard}. Otherwise, a
|
||||
* {@link FlexibleLeaderboard} will be loaded.
|
||||
*
|
||||
* @param leaderboardRegistry
|
||||
* if not <code>null</code>, then before creating and loading the leaderboard it is looked up in this
|
||||
@@ -430,24 +433,34 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
result = leaderboardRegistry.getLeaderboardByName(leaderboardName);
|
||||
}
|
||||
if (result == null) {
|
||||
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS);
|
||||
String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name());
|
||||
if (groupForMetaLeaderboard != null) {
|
||||
result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard,
|
||||
loadScoringScheme(dbLeaderboard), resultDiscardingRule);
|
||||
groupForMetaLeaderboard.setOverallLeaderboard(result);
|
||||
} else if (regattaName == null) {
|
||||
result = loadFlexibleLeaderboard(dbLeaderboard, resultDiscardingRule);
|
||||
String wrappedRegattaLeaderboardName = (String) dbLeaderboard.get(FieldNames.WRAPPED_REGATTA_LEADERBOARD_NAME.name());
|
||||
if (wrappedRegattaLeaderboardName != null) {
|
||||
result = loadRegattaLeaderboardWithEliminations(dbLeaderboard, leaderboardName, wrappedRegattaLeaderboardName, leaderboardRegistry);
|
||||
} else {
|
||||
result = loadRegattaLeaderboard(leaderboardName, regattaName, dbLeaderboard, resultDiscardingRule, regattaRegistry);
|
||||
if (groupForMetaLeaderboard != null) {
|
||||
result = new LeaderboardGroupMetaLeaderboard(groupForMetaLeaderboard,
|
||||
loadScoringScheme(dbLeaderboard), loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS));
|
||||
groupForMetaLeaderboard.setOverallLeaderboard(result);
|
||||
} else {
|
||||
String regattaName = (String) dbLeaderboard.get(FieldNames.REGATTA_NAME.name());
|
||||
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbLeaderboard, FieldNames.LEADERBOARD_DISCARDING_THRESHOLDS);
|
||||
if (regattaName == null) {
|
||||
result = loadFlexibleLeaderboard(dbLeaderboard, resultDiscardingRule);
|
||||
} else {
|
||||
result = loadRegattaLeaderboard(leaderboardName, regattaName, dbLeaderboard,
|
||||
resultDiscardingRule, regattaRegistry);
|
||||
}
|
||||
}
|
||||
if (result != null) {
|
||||
DelayedLeaderboardCorrections loadedLeaderboardCorrections = new DelayedLeaderboardCorrectionsImpl(result, baseDomainFactory);
|
||||
loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, result.getScoreCorrection());
|
||||
loadSuppressedCompetitors(dbLeaderboard, loadedLeaderboardCorrections);
|
||||
loadColumnFactors(dbLeaderboard, result);
|
||||
}
|
||||
}
|
||||
if (result != null) {
|
||||
final Leaderboard finalResult = result;
|
||||
finalResult.setDisplayName((String) dbLeaderboard.get(FieldNames.LEADERBOARD_DISPLAY_NAME.name()));
|
||||
DelayedLeaderboardCorrections loadedLeaderboardCorrections = new DelayedLeaderboardCorrectionsImpl(result, baseDomainFactory);
|
||||
loadLeaderboardCorrections(dbLeaderboard, loadedLeaderboardCorrections, result.getScoreCorrection());
|
||||
loadSuppressedCompetitors(dbLeaderboard, loadedLeaderboardCorrections);
|
||||
loadColumnFactors(dbLeaderboard, result);
|
||||
// add the leaderboard to the registry
|
||||
if (leaderboardRegistry != null) {
|
||||
leaderboardRegistry.addLeaderboard(result);
|
||||
|
||||
+2
-1
@@ -80,7 +80,8 @@ public enum FieldNames {
|
||||
LEADERBOARD_IS_MEDAL_RACE_COLUMN, LEADERBOARD_CARRIED_POINTS, LEADERBOARD_CARRIED_POINTS_BY_ID,
|
||||
LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS,
|
||||
LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON, LEADERBOARD_CORRECTED_SCORE, LEADERBOARD_SCORE_CORRECTION_TIMESTAMP,
|
||||
LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_COLUMN_FACTORS,
|
||||
LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME,
|
||||
ELMINATED_COMPETITORS,
|
||||
|
||||
// leaderboard group
|
||||
LEADERBOARD_GROUP_UUID, LEADERBOARD_GROUP_NAME, LEADERBOARD_GROUP_DESCRIPTION,
|
||||
|
||||
+28
-18
@@ -96,6 +96,7 @@ import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
@@ -294,26 +295,35 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
if (leaderboard.getDisplayName() != null) {
|
||||
dbLeaderboard.put(FieldNames.LEADERBOARD_DISPLAY_NAME.name(), leaderboard.getDisplayName());
|
||||
}
|
||||
BasicDBList dbSuppressedCompetitorIds = new BasicDBList();
|
||||
for (Competitor suppressedCompetitor : leaderboard.getSuppressedCompetitors()) {
|
||||
dbSuppressedCompetitorIds.add(suppressedCompetitor.getId());
|
||||
}
|
||||
dbLeaderboard.put(FieldNames.LEADERBOARD_SUPPRESSED_COMPETITOR_IDS.name(), dbSuppressedCompetitorIds);
|
||||
if (leaderboard instanceof FlexibleLeaderboard) {
|
||||
storeFlexibleLeaderboard((FlexibleLeaderboard) leaderboard, dbLeaderboard);
|
||||
} else if (leaderboard instanceof RegattaLeaderboard) {
|
||||
storeRegattaLeaderboard((RegattaLeaderboard) leaderboard, dbLeaderboard);
|
||||
if (leaderboard instanceof RegattaLeaderboardWithEliminations) {
|
||||
dbLeaderboard.put(FieldNames.WRAPPED_REGATTA_LEADERBOARD_NAME.name(), ((RegattaLeaderboardWithEliminations) leaderboard).getRegatta().getName());
|
||||
BasicDBList eliminatedCompetitorIds = new BasicDBList();
|
||||
for (final Competitor c : ((RegattaLeaderboardWithEliminations) leaderboard).getEliminatedCompetitors()) {
|
||||
eliminatedCompetitorIds.add(c.getId());
|
||||
}
|
||||
dbLeaderboard.put(FieldNames.ELMINATED_COMPETITORS.name(), eliminatedCompetitorIds);
|
||||
} else {
|
||||
// at least store the scoring scheme
|
||||
dbLeaderboard.put(FieldNames.SCORING_SCHEME_TYPE.name(), leaderboard.getScoringScheme().getType().name());
|
||||
BasicDBList dbSuppressedCompetitorIds = new BasicDBList();
|
||||
for (Competitor suppressedCompetitor : leaderboard.getSuppressedCompetitors()) {
|
||||
dbSuppressedCompetitorIds.add(suppressedCompetitor.getId());
|
||||
}
|
||||
dbLeaderboard.put(FieldNames.LEADERBOARD_SUPPRESSED_COMPETITOR_IDS.name(), dbSuppressedCompetitorIds);
|
||||
if (leaderboard instanceof FlexibleLeaderboard) {
|
||||
storeFlexibleLeaderboard((FlexibleLeaderboard) leaderboard, dbLeaderboard);
|
||||
} else if (leaderboard instanceof RegattaLeaderboard) {
|
||||
storeRegattaLeaderboard((RegattaLeaderboard) leaderboard, dbLeaderboard);
|
||||
} else {
|
||||
// at least store the scoring scheme
|
||||
dbLeaderboard.put(FieldNames.SCORING_SCHEME_TYPE.name(), leaderboard.getScoringScheme().getType().name());
|
||||
}
|
||||
if (leaderboard.getDefaultCourseArea() != null) {
|
||||
dbLeaderboard.put(FieldNames.COURSE_AREA_ID.name(), leaderboard.getDefaultCourseArea().getId().toString());
|
||||
} else {
|
||||
dbLeaderboard.put(FieldNames.COURSE_AREA_ID.name(), null);
|
||||
}
|
||||
storeColumnFactors(leaderboard, dbLeaderboard);
|
||||
storeLeaderboardCorrectionsAndDiscards(leaderboard, dbLeaderboard);
|
||||
}
|
||||
if (leaderboard.getDefaultCourseArea() != null) {
|
||||
dbLeaderboard.put(FieldNames.COURSE_AREA_ID.name(), leaderboard.getDefaultCourseArea().getId().toString());
|
||||
} else {
|
||||
dbLeaderboard.put(FieldNames.COURSE_AREA_ID.name(), null);
|
||||
}
|
||||
storeColumnFactors(leaderboard, dbLeaderboard);
|
||||
storeLeaderboardCorrectionsAndDiscards(leaderboard, dbLeaderboard);
|
||||
leaderboardCollection.update(query, dbLeaderboard, /* upsrt */ true, /* multi */ false, WriteConcern.SAFE);
|
||||
}
|
||||
|
||||
|
||||
+1
-7
@@ -10,9 +10,6 @@ import java.util.UUID;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
@@ -54,20 +51,17 @@ import com.sap.sailing.server.gateway.deserialization.impl.DeviceAndSessionIdent
|
||||
import com.sap.sailing.server.impl.RacingEventServiceImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class AbstractGPSFixStoreTest {
|
||||
public class AbstractGPSFixStoreTest extends RaceLogTrackingTestHelper {
|
||||
protected RacingEventService service;
|
||||
protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
|
||||
DeviceAndSessionIdentifierWithGPSFixesDeserializer deserializer =
|
||||
new MockDeviceAndSessionIdentifierWithGPSFixesDeserializer();
|
||||
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
|
||||
protected RaceLog raceLog;
|
||||
protected RegattaLog regattaLog;
|
||||
protected SensorFixStore store;
|
||||
protected final Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", null, null, null, null, null, /* timeOnTimeFactor */ null, /* timeOnDistanceAllowanceInSecondsPerNauticalMile */ null, null);
|
||||
protected final Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark");
|
||||
|
||||
protected final AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0);
|
||||
|
||||
protected GPSFixMoving createFix(long millis, double lat, double lng, double knots, double degrees) {
|
||||
return new GPSFixMovingImpl(new DegreePosition(lat, lng),
|
||||
new MillisecondsTimePoint(millis), new KnotSpeedWithBearingImpl(knots, new DegreeBearingImpl(degrees)));
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package com.sap.sailing.domain.racelogtracking.test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRaceStatusEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartTimeEventImpl;
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
public abstract class RaceLogTrackingTestHelper {
|
||||
protected RaceLog raceLog;
|
||||
protected AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0);
|
||||
|
||||
protected void setStartAndEndOfTrackingInRaceLog(TimePoint startOfTrackingInRacelog, TimePoint endOfTrackingInRacelog) {
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(startOfTrackingInRacelog, author, 0));
|
||||
raceLog.add(new RaceLogEndOfTrackingEventImpl(endOfTrackingInRacelog, author, 0));
|
||||
}
|
||||
|
||||
protected void setStartAndEndOfRaceInRaceLog(TimePoint startTimeInRaceLog, TimePoint endTimeInRaceLog) {
|
||||
raceLog.add(new RaceLogStartTimeEventImpl(startTimeInRaceLog, author, 0, startTimeInRaceLog));
|
||||
raceLog.add(new RaceLogRaceStatusEventImpl(endTimeInRaceLog, endTimeInRaceLog, author, UUID.randomUUID(), 0, RaceLogRaceStatus.FINISHED));
|
||||
}
|
||||
|
||||
protected void setManualTrackingTimesOnTrackedRace(DynamicTrackedRace trackedRace, TimePoint startTime, TimePoint endTime) {
|
||||
trackedRace.setStartOfTrackingReceived(startTime);
|
||||
trackedRace.setEndOfTrackingReceived(endTime);
|
||||
}
|
||||
}
|
||||
+50
-6
@@ -7,18 +7,19 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.Timeout;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.tracking.impl.RaceLogRegisterCompetitorEventImpl;
|
||||
@@ -50,6 +51,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
|
||||
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
|
||||
import com.sap.sailing.domain.racelogtracking.test.RaceLogTrackingTestHelper;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
@@ -60,7 +62,7 @@ import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class CreateAndTrackWithRaceLogTest {
|
||||
public class CreateAndTrackWithRaceLogTest extends RaceLogTrackingTestHelper {
|
||||
private RacingEventService service;
|
||||
|
||||
private final Fleet fleet = new FleetImpl("fleet");
|
||||
@@ -68,7 +70,6 @@ public class CreateAndTrackWithRaceLogTest {
|
||||
private RegattaLeaderboard leaderboard;
|
||||
private RaceLogTrackingAdapter adapter;
|
||||
private Regatta regatta;
|
||||
private AbstractLogEventAuthor author;
|
||||
private SensorFixStore sensorFixStore;
|
||||
|
||||
private long time = 0;
|
||||
@@ -85,12 +86,17 @@ public class CreateAndTrackWithRaceLogTest {
|
||||
Series series = new SeriesImpl("series", /* isMedal */ false, /* isFleetsCanRunInParallel */ true, Collections.singletonList(fleet), Collections.emptySet(),
|
||||
service);
|
||||
regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser",
|
||||
/* startDate */null, /* endDate */null, UUID.randomUUID(), Collections.<Series> singletonList(series), false,
|
||||
/* startDate */null, /* endDate */null, UUID.randomUUID(), Collections.<Series> singletonList(series), /* persistent */ true,
|
||||
new HighPoint(), UUID.randomUUID(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
|
||||
series.addRaceColumn(columnName, /* trackedRegattaRegistry */null);
|
||||
leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
|
||||
adapter = RaceLogTrackingAdapterFactory.INSTANCE.getAdapter(DomainFactory.INSTANCE);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws MalformedURLException, IOException, InterruptedException {
|
||||
service.removeRegatta(regatta);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasRaceLog() {
|
||||
@@ -206,13 +212,51 @@ public class CreateAndTrackWithRaceLogTest {
|
||||
raceLogFixTrackerManager.stop(false);
|
||||
addFixes3(race, comp1, dev1);
|
||||
}
|
||||
|
||||
/**
|
||||
* See bug 4114: when the regatta is set to control the tracking times from start / finish times,
|
||||
* we don't want this to override start/end tracking times set explicitly in the race log unless
|
||||
* they have low priority. This could happen when the start time is adjusted manually or when
|
||||
* the race log is detached / attached again with a start time event in the race log.
|
||||
*/
|
||||
@Test
|
||||
public void testStartOfTrackingTimes() throws NotDenotedForRaceLogTrackingException, Exception {
|
||||
regatta.setControlTrackingFromStartAndFinishTimes(true);
|
||||
RaceColumn column = leaderboard.getRaceColumnByName(columnName);
|
||||
raceLog = column.getRaceLog(fleet);
|
||||
adapter.denoteRaceForRaceLogTracking(service, leaderboard, column, fleet, "race");
|
||||
// start tracking
|
||||
adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false);
|
||||
// now there is a trackedrace
|
||||
TrackedRace race = column.getTrackedRace(fleet);
|
||||
assertNotNull(race);
|
||||
final TimePoint startTimeInRaceLog = new MillisecondsTimePoint(123456);
|
||||
final TimePoint endTimeInRaceLog = new MillisecondsTimePoint(234567);
|
||||
setStartAndEndOfRaceInRaceLog(startTimeInRaceLog, endTimeInRaceLog);
|
||||
assertEquals(race.getStartOfRace().minus(TrackedRace.START_TRACKING_THIS_MUCH_BEFORE_RACE_START), race.getStartOfTracking());
|
||||
final TimePoint explicitStartOfTracking = new MillisecondsTimePoint(123000); // just a bit before start of race
|
||||
final TimePoint explicitEndOfTracking = new MillisecondsTimePoint(234600); // just a bit after end of race
|
||||
setStartAndEndOfTrackingInRaceLog(explicitStartOfTracking, explicitEndOfTracking);
|
||||
assertEquals(explicitStartOfTracking, race.getStartOfTracking());
|
||||
assertEquals(explicitEndOfTracking, race.getEndOfTracking());
|
||||
column.releaseTrackedRace(fleet);
|
||||
column.setTrackedRace(fleet, race);
|
||||
assertEquals(explicitStartOfTracking, race.getStartOfTracking());
|
||||
assertEquals(explicitEndOfTracking, race.getEndOfTracking());
|
||||
|
||||
// now remove the tracked race and track again; again, the explicit tracking times in the race log must prevail
|
||||
service.removeRace(regatta, race.getRace());
|
||||
adapter.startTracking(service, leaderboard, column, fleet, /* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ false);
|
||||
TrackedRace race2 = column.getTrackedRace(fleet);
|
||||
assertEquals(explicitStartOfTracking, race2.getStartOfTracking());
|
||||
assertEquals(explicitEndOfTracking, race2.getEndOfTracking());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void useEventsInRegattaLog() throws NotDenotedForRaceLogTrackingException, Exception {
|
||||
RaceColumn column = leaderboard.getRaceColumnByName(columnName);
|
||||
RegattaLog regattaLog = leaderboard.getRegattaLike().getRegattaLog();
|
||||
RaceLog raceLog = column.getRaceLog(fleet);
|
||||
|
||||
raceLog = column.getRaceLog(fleet);
|
||||
adapter.denoteRaceForRaceLogTracking(service, leaderboard, column, fleet, "race");
|
||||
|
||||
// add a mapping and one fix in, one out of mapping
|
||||
|
||||
+2
@@ -756,6 +756,8 @@ public class SensorFixStoreAndLoadTest {
|
||||
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
|
||||
addBravoFixes();
|
||||
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
||||
// creating a new RaceLog instance to ensure that startOfTracking insn't set initially
|
||||
raceLog = new RaceLogImpl("racelog");
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 0);
|
||||
|
||||
+46
-3
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
@@ -266,6 +267,38 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression test for bug 4145: when fixes are mapped while startOfTracking is still null, fixes are expected
|
||||
* to not make it into the race. However, when startOfTracking is then set to a value after the fix's time point,
|
||||
* that fix is expected to be loaded if there is no better fix within the tracking interval, just like it already
|
||||
* happens when starting the tracker for the race or adding pings while startOfTracking is already set to a
|
||||
* non-null value.
|
||||
*/
|
||||
@Test
|
||||
public void testFixesBeforeStartOfTrackingForMarkAreLoadedWhenSettingStartOfTracking() throws InterruptedException {
|
||||
testFixesInternal(/* start of tracking */ null, /* end of tracking */ null, /* mappings and fixes */ () -> {
|
||||
map(mark, device, 0, 200);
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
store.storeFix(device, createFix(150, 10, 20));
|
||||
store.storeFix(device, createFix(250, 10, 20));
|
||||
map(mark, device, 350, 500);
|
||||
store.storeFix(device, createFix(400, 10, 20));
|
||||
store.storeFix(device, createFix(450, 10, 20));
|
||||
}, /* tests track is initially empty */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 0); // still no start of tracking set
|
||||
}, /* now set start of tracking */ trackedRace -> trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(600)),
|
||||
/* now check the last fix has arrived */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1); // one fix
|
||||
trackedRace.getOrCreateTrack(mark).lockForRead();
|
||||
try {
|
||||
final GPSFix fix = trackedRace.getOrCreateTrack(mark).getRawFixes().iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(450), fix.getTimePoint());
|
||||
} finally {
|
||||
trackedRace.getOrCreateTrack(mark).unlockAfterRead();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 <br>
|
||||
* Updated for bug 4009 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4009
|
||||
@@ -456,13 +489,23 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@SafeVarargs
|
||||
private final void testFixes(long startOfTracking, long endOfTracking, Runnable beforeTrackingStarted,
|
||||
Consumer<DynamicTrackedRace>... afterTrackingStartedConsumers) throws InterruptedException {
|
||||
testFixesInternal(Long.valueOf(startOfTracking), Long.valueOf(endOfTracking), beforeTrackingStarted, afterTrackingStartedConsumers);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private final void testFixesInternal(Long startOfTracking, Long endOfTracking, Runnable beforeTrackingStarted,
|
||||
Consumer<DynamicTrackedRace>... afterTrackingStartedConsumers) throws InterruptedException {
|
||||
defineMarksOnRegattaLog(mark, mark2);
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(new MillisecondsTimePoint(startOfTracking), author, 0));
|
||||
raceLog.add(new RaceLogEndOfTrackingEventImpl(new MillisecondsTimePoint(endOfTracking), author, 0));
|
||||
if (startOfTracking != null) {
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(new MillisecondsTimePoint(startOfTracking), author, 0));
|
||||
}
|
||||
if (endOfTracking != null) {
|
||||
raceLog.add(new RaceLogEndOfTrackingEventImpl(new MillisecondsTimePoint(endOfTracking), author, 0));
|
||||
}
|
||||
beforeTrackingStarted.run();
|
||||
DynamicTrackedRace trackedRace = createDynamicTrackedRace(boatClass, raceDefinition);
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
|
||||
Regular → Executable
+46
-56
@@ -36,7 +36,6 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
|
||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
|
||||
@@ -77,42 +76,52 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
|
||||
/* useMarkPassingCalculator */ false,
|
||||
OneDesignRankingMetric::new, mock(RaceLogResolver.class));
|
||||
|
||||
setStartAndEndOfRaceInRaceLog(10000, 20000);
|
||||
MillisecondsTimePoint startOfRaceInRaceLog = new MillisecondsTimePoint(10000);
|
||||
MillisecondsTimePoint endOfRaceInRaceLog = new MillisecondsTimePoint(20000);
|
||||
setStartAndEndOfRaceInRaceLog(startOfRaceInRaceLog, endOfRaceInRaceLog);
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
|
||||
// test inference via race start/end time in racelog
|
||||
MillisecondsTimePoint startOfRaceInRaceLog = new MillisecondsTimePoint(10000);
|
||||
TimePoint expectedStartOfTracking = startOfRaceInRaceLog.minus(TrackedRace.START_TRACKING_THIS_MUCH_BEFORE_RACE_START);
|
||||
MillisecondsTimePoint endOfRaceInRaceLog = new MillisecondsTimePoint(20000);
|
||||
TimePoint expectedEndOfTracking = endOfRaceInRaceLog.plus(TrackedRace .STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH);
|
||||
TimePoint expectedEndOfTracking = endOfRaceInRaceLog.plus(TrackedRace.STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH);
|
||||
assertEquals(expectedStartOfTracking, trackedRace.getStartOfTracking());
|
||||
assertEquals(expectedEndOfTracking, trackedRace.getEndOfTracking());
|
||||
|
||||
// test inference via manually set start/end of tracking
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, 30000, 40000);
|
||||
assertEquals(new MillisecondsTimePoint(30000), trackedRace.getStartOfTracking());
|
||||
assertEquals(new MillisecondsTimePoint(40000), trackedRace.getEndOfTracking());
|
||||
final MillisecondsTimePoint manualStartOfTracking = new MillisecondsTimePoint(30000);
|
||||
final MillisecondsTimePoint manualEndOfTracking = new MillisecondsTimePoint(40000);
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, manualStartOfTracking, manualEndOfTracking);
|
||||
assertEquals(manualStartOfTracking, trackedRace.getStartOfTracking());
|
||||
assertEquals(manualEndOfTracking, trackedRace.getEndOfTracking());
|
||||
|
||||
// shouldn't change when setting time in raceLog again
|
||||
setStartAndEndOfRaceInRaceLog(100000, 200000);
|
||||
assertEquals(new MillisecondsTimePoint(30000), trackedRace.getStartOfTracking());
|
||||
assertEquals(new MillisecondsTimePoint(40000), trackedRace.getEndOfTracking());
|
||||
setStartAndEndOfRaceInRaceLog(new MillisecondsTimePoint(100000), new MillisecondsTimePoint(200000));
|
||||
assertEquals(manualStartOfTracking, trackedRace.getStartOfTracking());
|
||||
assertEquals(manualEndOfTracking, trackedRace.getEndOfTracking());
|
||||
|
||||
// test inference via start/end of tracking in RaceLog
|
||||
setStartAndEndOfTrackingInRaceLog(50000, 60000);
|
||||
MillisecondsTimePoint startOfTrackingInRacelog = new MillisecondsTimePoint(50000);
|
||||
MillisecondsTimePoint endOfTrackingInRacelog = new MillisecondsTimePoint(60000);
|
||||
setStartAndEndOfTrackingInRaceLog(startOfTrackingInRacelog, endOfTrackingInRacelog);
|
||||
assertEquals(startOfTrackingInRacelog, trackedRace.getStartOfTracking());
|
||||
assertEquals(endOfTrackingInRacelog, trackedRace.getEndOfTracking());
|
||||
|
||||
// shouldn't change when setting time in raceLog again
|
||||
setStartAndEndOfRaceInRaceLog(300000, 400000);
|
||||
setStartAndEndOfRaceInRaceLog(new MillisecondsTimePoint(300000), new MillisecondsTimePoint(400000));
|
||||
assertEquals(startOfTrackingInRacelog, trackedRace.getStartOfTracking());
|
||||
assertEquals(endOfTrackingInRacelog, trackedRace.getEndOfTracking());
|
||||
|
||||
// shouldn't change when setting start/end of tracking
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, 500000, 600000);
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, new MillisecondsTimePoint(500000), new MillisecondsTimePoint(600000));
|
||||
assertEquals(startOfTrackingInRacelog, trackedRace.getStartOfTracking());
|
||||
assertEquals(endOfTrackingInRacelog, trackedRace.getEndOfTracking());
|
||||
|
||||
// bug 4114: now detach the race log and attach it again; this is expected to set the
|
||||
// start of race from the race log which now is no longer expected to update the race log
|
||||
// with new start/end of tracking times when there are already such explicit events:
|
||||
trackedRace.detachRaceLog(raceLog.getId());
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
assertEquals(startOfTrackingInRacelog, trackedRace.getStartOfTracking());
|
||||
assertEquals(endOfTrackingInRacelog, trackedRace.getEndOfTracking());
|
||||
}
|
||||
@@ -241,7 +250,8 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
|
||||
trackedRace.updateMarkPassings(comp, Collections.singleton(new MarkPassingImpl(startMarkPassingTimePoint, trackedRace.getRace().getCourse().getFirstWaypoint(), comp)));
|
||||
assertNotNull(newStartAndEndOfTrackingNotifiedByRace[0]);
|
||||
assertTrue(trackedRace.getStartOfTracking().before(startMarkPassingTimePoint));
|
||||
assertTrue(trackedRace.getStartOfTracking().after(new MillisecondsTimePoint(123456)));
|
||||
final MillisecondsTimePoint startOfRaceInRaceLog = new MillisecondsTimePoint(123456);
|
||||
assertTrue(trackedRace.getStartOfTracking().after(startOfRaceInRaceLog));
|
||||
|
||||
// test inference from finished time change by new blue flag down event
|
||||
newStartAndEndOfTrackingNotifiedByRace[0] = null;
|
||||
@@ -254,40 +264,45 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
|
||||
// verify that setting a start and finished time through the race log adjusts the start/end of tracking times
|
||||
newStartAndEndOfTrackingNotifiedByRace[0] = null;
|
||||
newStartAndEndOfTrackingNotifiedByRace[1] = null;
|
||||
setStartAndEndOfRaceInRaceLog(123456, 234567);
|
||||
final MillisecondsTimePoint endOfRaceInRaceLog = new MillisecondsTimePoint(234567);
|
||||
setStartAndEndOfRaceInRaceLog(startOfRaceInRaceLog, endOfRaceInRaceLog);
|
||||
assertNotNull(trackedRace.getStartOfTracking());
|
||||
assertNotNull(trackedRace.getEndOfTracking());
|
||||
assertTrue(trackedRace.getStartOfTracking().before(new MillisecondsTimePoint(123456)));
|
||||
assertTrue(trackedRace.getEndOfTracking().after(new MillisecondsTimePoint(234567)));
|
||||
assertTrue(trackedRace.getStartOfTracking().before(startOfRaceInRaceLog));
|
||||
assertTrue(trackedRace.getEndOfTracking().after(endOfRaceInRaceLog));
|
||||
|
||||
newStartAndEndOfTrackingNotifiedByRace[0] = null;
|
||||
newStartAndEndOfTrackingNotifiedByRace[1] = null;
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, 1111, 2222); // wrong values; race log should take precedence when available
|
||||
final MillisecondsTimePoint manualStartOfTracking = new MillisecondsTimePoint(1111);
|
||||
final MillisecondsTimePoint manualEndOfTracking = new MillisecondsTimePoint(2222);
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, manualStartOfTracking, manualEndOfTracking); // wrong values; race log should take precedence when available
|
||||
// assert that the event listener was triggered
|
||||
assertEquals(new MillisecondsTimePoint(1111), newStartAndEndOfTrackingNotifiedByRace[0]);
|
||||
assertEquals(new MillisecondsTimePoint(2222), newStartAndEndOfTrackingNotifiedByRace[1]);
|
||||
assertEquals(manualStartOfTracking, newStartAndEndOfTrackingNotifiedByRace[0]);
|
||||
assertEquals(manualEndOfTracking, newStartAndEndOfTrackingNotifiedByRace[1]);
|
||||
// test values set immediately
|
||||
assertEquals(new MillisecondsTimePoint(1111), trackedRace.getStartOfTracking());
|
||||
assertEquals(new MillisecondsTimePoint(2222), trackedRace.getEndOfTracking());
|
||||
assertEquals(manualStartOfTracking, trackedRace.getStartOfTracking());
|
||||
assertEquals(manualEndOfTracking, trackedRace.getEndOfTracking());
|
||||
|
||||
newStartAndEndOfTrackingNotifiedByRace[0] = null;
|
||||
newStartAndEndOfTrackingNotifiedByRace[1] = null;
|
||||
setStartAndEndOfTrackingInRaceLog(10000, 20000); // correct values, taking precedence
|
||||
final MillisecondsTimePoint newStartOfTrackingInRaceLog = new MillisecondsTimePoint(10000);
|
||||
final MillisecondsTimePoint newEndOfTrackingInRaceLog = new MillisecondsTimePoint(20000);
|
||||
setStartAndEndOfTrackingInRaceLog(newStartOfTrackingInRaceLog, newEndOfTrackingInRaceLog); // correct values, taking precedence
|
||||
// assert that the event listener was triggered
|
||||
assertEquals(new MillisecondsTimePoint(10000), newStartAndEndOfTrackingNotifiedByRace[0]);
|
||||
assertEquals(new MillisecondsTimePoint(20000), newStartAndEndOfTrackingNotifiedByRace[1]);
|
||||
assertEquals(newStartOfTrackingInRaceLog, newStartAndEndOfTrackingNotifiedByRace[0]);
|
||||
assertEquals(newEndOfTrackingInRaceLog, newStartAndEndOfTrackingNotifiedByRace[1]);
|
||||
// test inference via racelog
|
||||
assertEquals(new MillisecondsTimePoint(10000), trackedRace.getStartOfTracking());
|
||||
assertEquals(new MillisecondsTimePoint(20000), trackedRace.getEndOfTracking());
|
||||
assertEquals(newStartOfTrackingInRaceLog, trackedRace.getStartOfTracking());
|
||||
assertEquals(newEndOfTrackingInRaceLog, trackedRace.getEndOfTracking());
|
||||
|
||||
// shouldn't change anymore when setting explicitly because race log takes precedence
|
||||
newStartAndEndOfTrackingNotifiedByRace[0] = null;
|
||||
newStartAndEndOfTrackingNotifiedByRace[1] = null;
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, 1111, 2222);
|
||||
setManualTrackingTimesOnTrackedRace(trackedRace, manualStartOfTracking, manualEndOfTracking);
|
||||
assertNull(newStartAndEndOfTrackingNotifiedByRace[0]);
|
||||
assertNull(newStartAndEndOfTrackingNotifiedByRace[1]);
|
||||
assertEquals(new MillisecondsTimePoint(10000), trackedRace.getStartOfTracking());
|
||||
assertEquals(new MillisecondsTimePoint(20000), trackedRace.getEndOfTracking());
|
||||
assertEquals(newStartOfTrackingInRaceLog, trackedRace.getStartOfTracking());
|
||||
assertEquals(newEndOfTrackingInRaceLog, trackedRace.getEndOfTracking());
|
||||
|
||||
// test inference when setting null in RaceLog; RaceLog should still take precedence with its null values
|
||||
newStartAndEndOfTrackingNotifiedByRace[0] = MillisecondsTimePoint.now();
|
||||
@@ -337,29 +352,4 @@ public class TrackedRaceStartTimeInferenceTest extends AbstractGPSFixStoreTest {
|
||||
assertEquals(newStartOfRace, oldAndNewStartTimeNotifiedByRace[1]);
|
||||
assertEquals(newStartOfRace, trackedRace.getStartOfRace());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public void setStartAndEndOfTrackingInRaceLog(int startTime, int endTime) {
|
||||
MillisecondsTimePoint startOfTrackingInRacelog = new MillisecondsTimePoint(startTime);
|
||||
MillisecondsTimePoint endOfTrackingInRacelog = new MillisecondsTimePoint(endTime);
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(startOfTrackingInRacelog, author, 0));
|
||||
raceLog.add(new RaceLogEndOfTrackingEventImpl(endOfTrackingInRacelog, author, 0));
|
||||
}
|
||||
|
||||
public void setStartAndEndOfRaceInRaceLog(int startTime, int endTime) {
|
||||
MillisecondsTimePoint startTimeInRaceLog2 = new MillisecondsTimePoint(startTime);
|
||||
MillisecondsTimePoint endTimeInRaceLog2 = new MillisecondsTimePoint(endTime);
|
||||
raceLog.add(new RaceLogStartTimeEventImpl(startTimeInRaceLog2, author, 0, startTimeInRaceLog2));
|
||||
raceLog.add(new RaceLogRaceStatusEventImpl(endTimeInRaceLog2, endTimeInRaceLog2, author, UUID.randomUUID(), 0, RaceLogRaceStatus.FINISHED));
|
||||
}
|
||||
|
||||
public void setManualTrackingTimesOnTrackedRace(DynamicTrackedRace trackedRace, int startTime, int endTime) {
|
||||
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(startTime));
|
||||
trackedRace.setEndOfTrackingReceived(new MillisecondsTimePoint(endTime));
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -1,14 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file datatype="javapropertyresourcebundle" date="2017-03-03T16:45:41Z" original="/usr/sap/ljs/app/Converter_7335190261158680380/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="es">
|
||||
<file datatype="javapropertyresourcebundle" date="2017-04-19T13:57:38Z" original="/usr/sap/ljs/app/Converter_429408536936316109/java/com.sap.sailing.domain.racelogtrackingadapter/resources/stringmessages/TrackingStringMessages.properties" source-language="en" target-language="es">
|
||||
<header>
|
||||
<skl>
|
||||
<internal-file crc="064477f4b22ea4cc8cbdaab36795b9a4" form="base64">H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbs7vfaejvaoM/AWjeDl8DxJFCz5pHCtxPupgj/qtpJkYhXTxl/t/ZvpMC8XKyhj6lTBY7I40plErHAl7Vv9vwoTnv0Cs+RzW3sCAAA=</internal-file>
|
||||
<internal-file crc="55a81e913c44ad945c6ad9615879c2ce" form="base64">H4sIAAAAAAAAAF1RQU7DQAy85xW+cakQ50o5FCSkSEBLk8J5m7iN1c16tXZbIsTf2U1SWnHzzjjjmYkGUx/I7Qt3IjVK7J455NWEwhWGHYdMauPe10/c4DJ8kJBugq14jXsSxbCQ/NUcEOQYEHo+QmtOCNoifD/8wMJ7ICdqrMXmHqoWHSS9YWHH1vI5XRzlgQPUlurDwFpyBzhKoqNsAOlMUN+yi+oMYToPRrIz2po7rDj/HKe4kNGy3AgGyUuN30F8JjOZcU1gam6pxQiNtI2aLmY/oe2HsFVL8hKt5MVuiGdizICmSb40ctAZssBjogZPVCOcSdvhneIbm9b7aw0zEOq87aesW4wlpEiS3Myz7ZH7Cl2DIU8j6DBnxvtSOWAxylT4pRdLDbu7O70Wn672qDPwFo3g5fI8aRQs+SRx7cT7qYM/6raT5GIV48Z/7f2b6TAvFysoY+zUwWPyONKZRKxwJe1b/b8KE579Ah7aV3t8AgAA</internal-file>
|
||||
</skl>
|
||||
<phase-group>
|
||||
<phase company-name="SAP AG" date="2016-12-16T08:45:26Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
|
||||
<phase company-name="SAP AG" date="2017-04-06T12:41:23Z" phase-name="file-creation" process-name="file-creation" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter"/>
|
||||
</phase-group>
|
||||
<tool tool-company="SAP AG" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter" tool-name="TEW: Javascript .properties Flexible File Converter" tool-version="1.1.0"/>
|
||||
<tool tool-company="SAP AG" tool-id="urn:x-sap:sls-mlt:java:properties-flexible-converter" tool-name="TEW: Java properties Flexible File Converter" tool-version="1.1.0"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="trackingInvitationFor" resname="trackingInvitationFor">
|
||||
@@ -40,7 +40,7 @@
|
||||
<target state="signed-off">balizador</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appStoreInstallText" resname="appStoreInstallText">
|
||||
<source>If you don't have the App yet, please install:</source>
|
||||
<source>If you don''t have the App yet, please install:</source>
|
||||
<target state="signed-off">Si todavía no tiene instalada la aplicación, instálela:</target>
|
||||
</trans-unit>
|
||||
<trans-unit datatype="x-urn:x-sap:sls-mlt:java:flexible-converter:messageformat" id="appIos" resname="appIos">
|
||||
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEndOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* A listener for finished time changes that---when {@link Regatta#isControlTrackingFromStartAndFinishTimes() enabled},
|
||||
* updates the end of tracking time for a race through the race log whenever the race's finished time has been changed,
|
||||
* e.g., by a blue flag down event on the race log.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class EndOfTrackingController extends AbstractRaceChangeListener {
|
||||
private final Regatta regatta;
|
||||
private final TrackedRace trackedRace;
|
||||
private final RaceLog raceLog;
|
||||
private final AbstractLogEventAuthor raceLogEventAuthor;
|
||||
|
||||
|
||||
/**
|
||||
* Initially checks the {@code trackedRace} for a {@link TrackedRace#getFinishedTime() finished time}. If one is found and
|
||||
* no {@link TrackedRace#getEndOfTracking() end of tracking} is defined yet, immediately invokes {@link #finishedTimeChanged(TimePoint, TimePoint)}
|
||||
* which will set an end of tracking time in the race log.
|
||||
*/
|
||||
public EndOfTrackingController(TrackedRace trackedRace, RaceLog raceLog, AbstractLogEventAuthor raceLogEventAuthor) {
|
||||
super();
|
||||
this.regatta = trackedRace.getTrackedRegatta().getRegatta();
|
||||
this.trackedRace = trackedRace;
|
||||
this.raceLog = raceLog;
|
||||
this.raceLogEventAuthor = raceLogEventAuthor;
|
||||
final TimePoint finishedTime = trackedRace.getFinishedTime();
|
||||
if (finishedTime != null) {
|
||||
finishedTimeChanged(/* old finished time */ null, finishedTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishedTimeChanged(TimePoint oldFinishedTime, TimePoint newFinishedTime) {
|
||||
if (regatta.isControlTrackingFromStartAndFinishTimes()) {
|
||||
final TimePoint newEndOfTracking = newFinishedTime.plus(TrackedRace.STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH);
|
||||
if (!Util.equalsWithNull(trackedRace.getEndOfTracking(), newEndOfTracking)) {
|
||||
raceLog.add(new RaceLogEndOfTrackingEventImpl(newEndOfTracking,
|
||||
raceLogEventAuthor, raceLog.getCurrentPassId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-13
@@ -93,8 +93,6 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
|
||||
private final RaceLogResolver raceLogResolver;
|
||||
|
||||
private volatile DynamicTrackedRace trackedRace;
|
||||
private StartOfTrackingController startOfTrackingController;
|
||||
private EndOfTrackingController endOfTrackingController;
|
||||
|
||||
public RaceLogRaceTracker(DynamicTrackedRegatta regatta, RaceLogConnectivityParams params, WindStore windStore,
|
||||
RaceLogResolver raceLogResolver, RaceLogConnectivityParams connectivityParams) {
|
||||
@@ -177,13 +175,6 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
|
||||
for (Entry<AbstractLog<?, ?>, Object> visitor : visitors.entrySet()) {
|
||||
visitor.getKey().removeListener(visitor.getValue());
|
||||
}
|
||||
if (startOfTrackingController != null && trackedRace != null) {
|
||||
trackedRace.removeStartTimeChangedListener(startOfTrackingController);
|
||||
}
|
||||
if (endOfTrackingController != null && trackedRace != null) {
|
||||
trackedRace.removeListener(endOfTrackingController);
|
||||
}
|
||||
|
||||
logger.info(String.format("Stopped tracking race-log race %s %s %s", params.getLeaderboard(),
|
||||
params.getRaceColumn(), params.getFleet()));
|
||||
}
|
||||
@@ -308,10 +299,6 @@ public class RaceLogRaceTracker extends AbstractRaceTrackerBaseImpl {
|
||||
trackedRace = trackedRegatta.createTrackedRace(raceDef, sidelines, windStore,
|
||||
params.getDelayToLiveInMillis(), WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND,
|
||||
boatClass.getApproximateManeuverDurationInMilliseconds(), null, /*useMarkPassingCalculator*/ true, raceLogResolver);
|
||||
startOfTrackingController = new StartOfTrackingController(trackedRace, raceLog, raceLogEventAuthor);
|
||||
trackedRace.addStartTimeChangedListener(startOfTrackingController);
|
||||
endOfTrackingController = new EndOfTrackingController(trackedRace, raceLog, raceLogEventAuthor);
|
||||
trackedRace.addListener(endOfTrackingController);
|
||||
notifyRaceCreationListeners();
|
||||
logger.info(String.format("Started tracking race-log race (%s)", raceLog));
|
||||
// this wakes up all waiting race handles
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogStartOfTrackingEventImpl;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.tracking.StartTimeChangedListener;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* A listener for start time changes that---when {@link Regatta#isControlTrackingFromStartAndFinishTimes() enabled},
|
||||
* updates the start of tracking time for a race through the race log whenever the race's start time has been changed.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class StartOfTrackingController implements StartTimeChangedListener {
|
||||
private final Regatta regatta;
|
||||
private final TrackedRace trackedRace;
|
||||
private final RaceLog raceLog;
|
||||
private final AbstractLogEventAuthor raceLogEventAuthor;
|
||||
|
||||
/**
|
||||
* Initially checks the {@code trackedRace} for a {@link TrackedRace#getStartOfRace() start time}. If one is found and
|
||||
* no {@link TrackedRace#getStartOfTracking() start of tracking} is defined yet, immediately invokes {@link #startTimeChanged(TimePoint)}
|
||||
* which will set a start of tracking time in the race log.
|
||||
*/
|
||||
public StartOfTrackingController(TrackedRace trackedRace, RaceLog raceLog, AbstractLogEventAuthor raceLogEventAuthor) {
|
||||
super();
|
||||
this.regatta = trackedRace.getTrackedRegatta().getRegatta();
|
||||
this.raceLog = raceLog;
|
||||
this.trackedRace = trackedRace;
|
||||
this.raceLogEventAuthor = raceLogEventAuthor;
|
||||
final TimePoint startOfRace = trackedRace.getStartOfRace();
|
||||
if (startOfRace != null) {
|
||||
startTimeChanged(startOfRace);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTimeChanged(TimePoint newTimePoint) {
|
||||
if (regatta.isControlTrackingFromStartAndFinishTimes()) {
|
||||
final TimePoint newStartOfTracking = newTimePoint.minus(TrackedRace.START_TRACKING_THIS_MUCH_BEFORE_RACE_START);
|
||||
if (!Util.equalsWithNull(trackedRace.getStartOfTracking(), newStartOfTracking)) {
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(newStartOfTracking,
|
||||
raceLogEventAuthor, raceLog.getCurrentPassId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-24
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@@ -103,6 +104,8 @@ import com.sap.sse.util.ThreadPoolUtil;
|
||||
*/
|
||||
public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
private static final Logger logger = Logger.getLogger(FixLoaderAndTracker.class.getName());
|
||||
private static final ScheduledExecutorService executor = ThreadPoolUtil.INSTANCE.createForegroundTaskThreadPoolExecutor(
|
||||
FixLoaderAndTracker.class.getSimpleName());
|
||||
protected final DynamicTrackedRace trackedRace;
|
||||
private final SensorFixStore sensorFixStore;
|
||||
private RegattaLogDeviceMappings<WithID> deviceMappings;
|
||||
@@ -124,7 +127,7 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
if (newStartOfTracking != null) {
|
||||
if (oldStartOfTracking == null) {
|
||||
// Fixes wheren't loaded while startOfTracking was null. So we need to load all fixes in the tracking interval now.
|
||||
loadFixesForExtendedTimeRange(getTrackingTimeRange());
|
||||
loadFixesWhenStartOfTrackingIsReceived();
|
||||
} else if (newStartOfTracking.before(oldStartOfTracking)) {
|
||||
loadFixesForExtendedTimeRange(new TimeRangeImpl(newStartOfTracking, oldStartOfTracking));
|
||||
}
|
||||
@@ -403,7 +406,6 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
|
||||
|
||||
final GPSFix lastFixAtOrBeforeStartOfTracking = track.getLastFixAtOrBefore(trackingTimeRange.from());
|
||||
// A better fix before start of tracking must be after the current best fix
|
||||
final MultiTimeRange beforeRange = coveredTimeRanges
|
||||
@@ -475,9 +477,6 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
}
|
||||
sensorFixStore.removeListener(listener);
|
||||
if (preemptiveStopRequested.get()) {
|
||||
waitForLoadingToFinishRunning();
|
||||
}
|
||||
}
|
||||
|
||||
private void startTracking() {
|
||||
@@ -487,22 +486,15 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
trackedRace.getRace().getName());
|
||||
}
|
||||
|
||||
private void waitForLoadingToFinishRunning() {
|
||||
synchronized (loadingJobs) {
|
||||
try {
|
||||
while (!loadingJobs.isEmpty()) {
|
||||
loadingJobs.wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.WARNING, "Interrupted while waiting for Fixes to be loaded", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFixesForExtendedTimeRange(final TimeRange extendedTimeRange) {
|
||||
deviceMappings.forEachItemAndCoveredTimeRanges((item, mappingsAndCoveredTimeRanges) -> addLoadingJob(
|
||||
new LoadFixesInTrackingTimeRangeJob(mappingsAndCoveredTimeRanges, extendedTimeRange)));
|
||||
}
|
||||
|
||||
private void loadFixesWhenStartOfTrackingIsReceived() {
|
||||
deviceMappings.forEachItemAndCoveredTimeRanges((item, mappingsAndCoveredTimeRanges) -> addLoadingJob(
|
||||
new LoadFixesForNewlyCoveredTimeRangesJob(item, mappingsAndCoveredTimeRanges)));
|
||||
}
|
||||
|
||||
private void setStatusAndProgress(TrackedRaceStatusEnum status, double progress) {
|
||||
trackedRace.onStatusChanged(this, new TrackedRaceStatusImpl(status, progress));
|
||||
@@ -562,7 +554,7 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
loadingJobs.add(job);
|
||||
updateStatusAndProgress();
|
||||
}
|
||||
ThreadPoolUtil.INSTANCE.getDefaultForegroundTaskThreadPoolExecutor().execute(job);
|
||||
executor.execute(job);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -622,11 +614,10 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads fixes for an item's mappings in a defined tracking {@link TimeRange}. This is used when:
|
||||
* <ul>
|
||||
* <li>Initially loading fixes into tracks</li>
|
||||
* <li>The tracking {@link TimeRange} changes</li>
|
||||
* </ul>
|
||||
* Loads fixes for an item's mappings in a defined tracking {@link TimeRange}. This is used when the tracking
|
||||
* {@link TimeRange} is extended. No better fixes for {@link Mark} are being loaded because if available, these must
|
||||
* have either already been loaded or the best fix is inside of the extended {@link TimeRange} that is completely loaded by
|
||||
* this job.
|
||||
*/
|
||||
private class LoadFixesInTrackingTimeRangeJob extends AbstractLoadingJob {
|
||||
|
||||
@@ -646,7 +637,9 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used when device mappings for an item changed so that fixes in a new {@link TimeRange} are covered.
|
||||
* This is used when device mappings for an item changed so that fixes in a new {@link TimeRange} are covered. This is also used when initially loading fixes due to startOfTracking being initially set. If
|
||||
* the mapping is a {@link Mark}, best fixes outside of the tracking {@link TimeRange} are loaded if none is
|
||||
* available in the tracking {@link TimeRange}.
|
||||
*/
|
||||
private class LoadFixesForNewlyCoveredTimeRangesJob extends AbstractLoadingJob {
|
||||
private final WithID item;
|
||||
|
||||
+2
-2
@@ -181,12 +181,12 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
private Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> calculateCoveredTimeRanges(
|
||||
final List<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForItem) {
|
||||
final Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> coveredTimeRanges = new HashMap<>();
|
||||
if(mappingsForItem != null) {
|
||||
if (mappingsForItem != null) {
|
||||
Map<Pair<DeviceIdentifier, Class<?>>, Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> groupedMappings = groupMappingsByDeviceIdAndMappingType(mappingsForItem);
|
||||
groupedMappings.entrySet().forEach(entry -> {
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForDeviceIdAndMappingType = entry.getValue();
|
||||
final MultiTimeRange coveredTimeRange = getCoveredTimeRange(mappingsForDeviceIdAndMappingType);
|
||||
if(!coveredTimeRange.isEmpty()) {
|
||||
if (!coveredTimeRange.isEmpty()) {
|
||||
coveredTimeRanges.put(Util.get(mappingsForDeviceIdAndMappingType, 0).getRegattaLogEvent(), coveredTimeRange);
|
||||
}
|
||||
});
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public class DependentStartTimeResolver {
|
||||
return internalResolve(event, dependingOnRaces);
|
||||
}
|
||||
|
||||
StartTimeFinderResult internalResolve(RaceLogDependentStartTimeEvent event,Iterable<SimpleRaceLogIdentifier> dependingOnRaces) {
|
||||
StartTimeFinderResult internalResolve(RaceLogDependentStartTimeEvent event, Iterable<SimpleRaceLogIdentifier> dependingOnRaces) {
|
||||
SimpleRaceLogIdentifier identifier = event.getDependentOnRaceIdentifier();
|
||||
Duration startTimeDifference = event.getStartTimeDifference();
|
||||
RaceLog raceLog = raceLogResolver.resolve(identifier);
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ public class StartTimeFinderResult {
|
||||
private final ResolutionFailed resolutionFailed;
|
||||
|
||||
private Duration startTimeDiff;
|
||||
|
||||
|
||||
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime, Duration startTimeDiff) {
|
||||
this(racesDependingOn, startTime, startTimeDiff, /* resolutionFailed */ null);
|
||||
}
|
||||
|
||||
+2
@@ -41,6 +41,7 @@ public class SwissTimingRaceConfigurationTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
|
||||
@Test
|
||||
public void testRaceConfig_446483_no_detail_config() throws Exception {
|
||||
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "446483"));
|
||||
@@ -58,6 +59,7 @@ public class SwissTimingRaceConfigurationTest {
|
||||
|
||||
}
|
||||
|
||||
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
|
||||
@Test
|
||||
public void testRaceConfig_6260_with_detail_config() throws Exception {
|
||||
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "6260"));
|
||||
|
||||
+2
@@ -74,6 +74,7 @@ public class SwissTimingReplayAdapterServiceTest {
|
||||
assertEquals(321920, replayCountListener.rankingMarkCount);
|
||||
}
|
||||
|
||||
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
|
||||
@Test
|
||||
public void testRaceData_SAW005905_20120805_EqualsOnlineVersion() throws Exception {
|
||||
byte[] localCopy = read(getClass().getResourceAsStream("/SAW005905.20120805.replay"));
|
||||
@@ -113,6 +114,7 @@ public class SwissTimingReplayAdapterServiceTest {
|
||||
assertEquals(9500, replayCountListener.rankingMarkCount);
|
||||
}
|
||||
|
||||
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
|
||||
@Test
|
||||
public void testRaceData_SAW005905_20120805_online() throws Exception {
|
||||
SwissTimingReplayTestListener replayCountListener = new SwissTimingReplayTestListener();
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
|
||||
import com.sap.sailing.domain.test.LeaderboardCourseChangeTest;
|
||||
|
||||
public class LeaderboardCourseChangeWithEliminationTest extends LeaderboardCourseChangeTest {
|
||||
@Override
|
||||
protected RegattaLeaderboardWithEliminations createRegattaLeaderboard(Regatta regatta,
|
||||
ThresholdBasedResultDiscardingRule discardingRule) {
|
||||
return new DelegatingRegattaLeaderboardWithCompetitorElimination(
|
||||
()->super.createRegattaLeaderboard(regatta, discardingRule), "Test leaderboard with elimination");
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -42,6 +42,7 @@ import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
|
||||
@@ -115,7 +116,7 @@ public class LeaderboardCourseChangeTest {
|
||||
|
||||
int[] ruleRaw = { 5, 3, 2 };
|
||||
ThresholdBasedResultDiscardingRule rule = new ThresholdBasedResultDiscardingRuleImpl(ruleRaw);
|
||||
Leaderboard leaderboard = new RegattaLeaderboardImpl(mockedRegatta, rule);
|
||||
Leaderboard leaderboard = createRegattaLeaderboard(mockedRegatta, rule);
|
||||
|
||||
DomainFactory baseDomainFactory = new DomainFactoryImpl((srlid)->null);
|
||||
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, raceColumnNames, false,
|
||||
@@ -134,6 +135,11 @@ public class LeaderboardCourseChangeTest {
|
||||
|
||||
}
|
||||
|
||||
protected RegattaLeaderboard createRegattaLeaderboard(Regatta mockedRegatta,
|
||||
ThresholdBasedResultDiscardingRule rule) {
|
||||
return new RegattaLeaderboardImpl(mockedRegatta, rule);
|
||||
}
|
||||
|
||||
private TrackedRace createSpyedTrackedRace(Regatta regatta, Course course, TimePoint timePoint, BoatClass boatClass) {
|
||||
TrackedRegatta mockedTrackedRegatta = createMockedTrackedRegatta(regatta);
|
||||
RaceDefinition mockedRace = createMockedRace(course, boatClass);
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class LeaderboardRankingAndScoringWithEliminationTest extends LeaderboardScoringAndRankingTest {
|
||||
@Override
|
||||
protected RegattaLeaderboardWithEliminations createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||
return new DelegatingRegattaLeaderboardWithCompetitorElimination(
|
||||
()->super.createLeaderboard(regatta, discardingThresholds), "Test leaderboard with elimination");
|
||||
}
|
||||
|
||||
/**
|
||||
* See bug 4110: for partial leaderboards that are a "projection" of another leaderboard the suppressed
|
||||
* competitors will receive their scores for all races as usual but won't be shown in the resulting
|
||||
* leaderboard panel, except for score editing / import. Suppressed competitors, however, will not receive
|
||||
* a regatta rank and shall not be part of the set of competitors shown in a leaderboard panel.<p>
|
||||
*
|
||||
* This test asserts that suppressing competitors will let worse competitors advance in the total ranking.
|
||||
* @throws NoWindException
|
||||
*/
|
||||
@Test
|
||||
public void testEliminationAdvancesWorseCompetitorsInRegattaRankButNotInRaces() throws NoWindException {
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
TimePoint earlier = now.minus(1000000);
|
||||
TimePoint later = now.plus(1000000); // first race from "earlier" to "now", second from "now" to "later", third from "later" to "finish"
|
||||
TimePoint finish = later.plus(1000000);
|
||||
Competitor[] c = createCompetitors(3).toArray(new Competitor[0]);
|
||||
Competitor[] f1 = new Competitor[] { c[0], c[1], c[2] };
|
||||
Regatta regatta = createRegatta(/* qualifying */0, new String[] { "Default" }, /* final */3, new String[] { "Default" },
|
||||
/* medal */ false, /* medal */ 0, "testTotalTimeNotCountedForRacesStartedLaterThanTimePointReqeusted",
|
||||
DomainFactory.INSTANCE.getOrCreateBoatClass("49er", /* typicallyStartsUpwind */true), DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT));
|
||||
RegattaLeaderboardWithEliminations leaderboard = createLeaderboard(regatta, /* discarding thresholds */ new int[0]);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Competitor, TimePoint>[] lastMarkPassingTimesForCompetitors = (Map<Competitor, TimePoint>[]) new HashMap<?, ?>[3];
|
||||
lastMarkPassingTimesForCompetitors[0] = new HashMap<>();
|
||||
lastMarkPassingTimesForCompetitors[0].put(c[0], now);
|
||||
lastMarkPassingTimesForCompetitors[0].put(c[1], now);
|
||||
lastMarkPassingTimesForCompetitors[0].put(c[2], now);
|
||||
lastMarkPassingTimesForCompetitors[1] = new HashMap<>();
|
||||
lastMarkPassingTimesForCompetitors[1].put(c[0], later);
|
||||
lastMarkPassingTimesForCompetitors[1].put(c[1], later);
|
||||
lastMarkPassingTimesForCompetitors[1].put(c[2], later);
|
||||
lastMarkPassingTimesForCompetitors[2] = new HashMap<>();
|
||||
lastMarkPassingTimesForCompetitors[2].put(c[0], finish);
|
||||
lastMarkPassingTimesForCompetitors[2].put(c[1], finish);
|
||||
lastMarkPassingTimesForCompetitors[2].put(c[2], finish);
|
||||
createAndAttachTrackedRacesWithStartTimeAndLastMarkPassingTimes(series.get(1), "Default",
|
||||
new Competitor[][] { f1 }, new TimePoint[] { earlier, now, later }, lastMarkPassingTimesForCompetitors);
|
||||
leaderboard.setEliminated(c[1], true);
|
||||
assertEquals(1.0, leaderboard.getTotalPoints(c[0], leaderboard.getRaceColumns().iterator().next(), later), 0.000001);
|
||||
assertEquals(2.0, leaderboard.getTotalPoints(c[1], leaderboard.getRaceColumns().iterator().next(), later), 0.000001);
|
||||
assertEquals(3.0, leaderboard.getTotalPoints(c[2], leaderboard.getRaceColumns().iterator().next(), later), 0.000001);
|
||||
assertEquals(1, leaderboard.getTotalRankOfCompetitor(c[0], later));
|
||||
assertEquals(0, leaderboard.getTotalRankOfCompetitor(c[1], later));
|
||||
assertEquals(2, leaderboard.getTotalRankOfCompetitor(c[2], later));
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -56,6 +56,7 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
|
||||
import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
|
||||
@@ -64,7 +65,6 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets10Or8AndLastBre
|
||||
import com.sap.sailing.domain.leaderboard.impl.HighPointFirstGets12Or8AndLastBreaksTie2017;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LeaderboardGroupImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
|
||||
import com.sap.sailing.domain.leaderboard.meta.LeaderboardGroupMetaLeaderboard;
|
||||
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
@@ -83,9 +83,9 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||
|
||||
public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRankingTestBase {
|
||||
protected Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
|
||||
return new RegattaLeaderboardImpl(regatta, discardingRules);
|
||||
protected DelegatingRegattaLeaderboardWithCompetitorElimination createDelegatingRegattaLeaderboardWithCompetitorElimination(
|
||||
Regatta regatta, String leaderboardName, int[] discardingThresholds) {
|
||||
return new DelegatingRegattaLeaderboardWithCompetitorElimination(()->createLeaderboard(regatta, discardingThresholds), leaderboardName);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1818,7 +1818,7 @@ public class LeaderboardScoringAndRankingTest extends LeaderboardScoringAndRanki
|
||||
assertEquals(1. + .9, leaderboard1.getNetPoints(c1[0], afterEndOfR1), 0.00000001);
|
||||
assertEquals(1, leaderboard1.getTotalRankOfCompetitor(c1[0], afterEndOfR1));
|
||||
// now assert that the competitor c1[0] is best in r2 because of the score correction, although c1[0] doesn't have a fleet
|
||||
assertEquals(0, leaderboard1.getCompetitorsFromBestToWorst(r2, afterEndOfR1).indexOf(c1[0]));
|
||||
assertEquals(0, Util.indexOf(leaderboard1.getCompetitorsFromBestToWorst(r2, afterEndOfR1), c1[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ 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.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
|
||||
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
|
||||
@@ -42,7 +43,7 @@ import com.sap.sse.util.impl.ArrayListNavigableSet;
|
||||
public class LeaderboardScoringAndRankingTestBase extends AbstractLeaderboardTest {
|
||||
protected ArrayList<Series> series;
|
||||
|
||||
protected Leaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||
protected RegattaLeaderboard createLeaderboard(Regatta regatta, int[] discardingThresholds) {
|
||||
ThresholdBasedResultDiscardingRuleImpl discardingRules = new ThresholdBasedResultDiscardingRuleImpl(discardingThresholds);
|
||||
return new RegattaLeaderboardImpl(regatta, discardingRules);
|
||||
}
|
||||
|
||||
+6
-2
@@ -159,7 +159,7 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa
|
||||
@Test
|
||||
public void testAddCompetitorToRegattaAndEnsureCacheInvalidation() throws NoWindException, InterruptedException, ExecutionException {
|
||||
Regatta regatta = createRegatta();
|
||||
RegattaLeaderboard leaderboard = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
|
||||
RegattaLeaderboard leaderboard = createRegattaLeaderboard(regatta);
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
leaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(now);
|
||||
LeaderboardDTO dto = leaderboard.getLeaderboardDTO(now.plus(10), Collections.emptySet(), /* addOverallDetails */ false,
|
||||
@@ -190,7 +190,7 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa
|
||||
public void testAddCompetitorToRegattaAndEnsureCacheInvalidationOnDeserializedRegattaLeaderboard() throws NoWindException,
|
||||
InterruptedException, ExecutionException, ClassNotFoundException, IOException {
|
||||
Regatta regatta = createRegatta();
|
||||
RegattaLeaderboard leaderboard = new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
|
||||
RegattaLeaderboard leaderboard = createRegattaLeaderboard(regatta);
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
leaderboard.getScoreCorrection().setTimePointOfLastCorrectionsValidity(now);
|
||||
RegattaLeaderboard deserializedLeaderboard = cloneBySerialization(leaderboard, DomainFactory.INSTANCE);
|
||||
@@ -208,4 +208,8 @@ public class RegattaLogEventNotificationForwardingTest extends AbstractSerializa
|
||||
assertFalse(dto3.competitors.isEmpty());
|
||||
assertEquals(event.getCompetitor().getName(), dto3.competitors.get(0).getName());
|
||||
}
|
||||
|
||||
protected RegattaLeaderboard createRegattaLeaderboard(Regatta regatta) {
|
||||
return new RegattaLeaderboardImpl(regatta, new ThresholdBasedResultDiscardingRuleImpl(new int[0]));
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.leaderboard.impl.DelegatingRegattaLeaderboardWithCompetitorElimination;
|
||||
|
||||
/**
|
||||
* Serializability is required because this instance forms the outer object for the serializable
|
||||
* lambda required for the regatta leaderboard provider for the
|
||||
* {@link DelegatingRegattaLeaderboardWithCompetitorElimination} constructor call.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class RegattaLogEventNotificationForwardingWithEliminationTest
|
||||
extends RegattaLogEventNotificationForwardingTest implements Serializable {
|
||||
private static final long serialVersionUID = -8184610575644911786L;
|
||||
|
||||
@Override
|
||||
protected RegattaLeaderboardWithEliminations createRegattaLeaderboard(Regatta regatta) {
|
||||
return new DelegatingRegattaLeaderboardWithCompetitorElimination(
|
||||
()->super.createRegattaLeaderboard(regatta), "Test leaderboard with elimination");
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -8,8 +8,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -32,6 +30,8 @@ import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class MarkPassingCalculatorPerformanceTest extends AbstractMockedRaceMarkPassingTest {
|
||||
|
||||
private static LinkedHashMap<String, Long> result = new LinkedHashMap<>();
|
||||
@@ -75,7 +75,7 @@ public class MarkPassingCalculatorPerformanceTest extends AbstractMockedRaceMark
|
||||
final long time = timeToAddCandidatesToChooser(500, 1, 25);
|
||||
System.out.println(time);
|
||||
result.put("ChooserPerformance", time);
|
||||
assertTrue(time < 5000);
|
||||
assertTrue(time < 15000);
|
||||
}
|
||||
|
||||
private long timeToAddCandidatesToChooser(int numberOfTimesAdding, int numberToAddEachTime, int numberOfRepititions) {
|
||||
|
||||
@@ -66,7 +66,7 @@ public interface DomainFactory extends SharedDomainFactory {
|
||||
|
||||
PlacemarkDTO convertToPlacemarkDTO(Placemark placemark);
|
||||
|
||||
List<CompetitorDTO> getCompetitorDTOList(List<Competitor> competitors);
|
||||
List<CompetitorDTO> getCompetitorDTOList(Iterable<Competitor> competitors);
|
||||
|
||||
TrackedRaceDTO createTrackedRaceDTO(TrackedRace trackedRace);
|
||||
|
||||
|
||||
+1
-1
@@ -338,7 +338,7 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CompetitorDTO> getCompetitorDTOList(List<Competitor> competitors) {
|
||||
public List<CompetitorDTO> getCompetitorDTOList(Iterable<Competitor> competitors) {
|
||||
List<CompetitorDTO> result = new ArrayList<CompetitorDTO>();
|
||||
for (Competitor competitor : competitors) {
|
||||
result.add(convertToCompetitorDTO(competitor));
|
||||
|
||||
+22
-10
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnListener;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.LeaderboardType;
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
@@ -114,6 +115,8 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
|
||||
/**
|
||||
* Convenience method which returns the difference between {@link #getAllCompetitors()} and {@link #getCompetitors()}.
|
||||
* The collection is a copy that can safely be held and modified, but it is not "live" in the sense that it does not
|
||||
* reflect changes that occur to the set of suppressed competitors after calling this method, nor vice-versa.
|
||||
*/
|
||||
Iterable<Competitor> getSuppressedCompetitors();
|
||||
|
||||
@@ -204,19 +207,21 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
Map<Competitor, Double> getCompetitorsForWhichThereAreCarriedPoints();
|
||||
|
||||
/**
|
||||
* Shorthand for {@link TrackedRace#getRank(Competitor, com.sap.sse.common.TimePoint)} with the
|
||||
* additional logic that in case the <code>race</code> hasn't {@link TrackedRace#hasStarted(TimePoint) started} yet
|
||||
* or no {@link TrackedRace} exists for <code>race</code>, 0 will be returned for all those competitors. The tracked
|
||||
* race for the correct {@link Fleet} is determined using {@link RaceColumn#getTrackedRace(Competitor)}.<p>
|
||||
* Shorthand for {@link TrackedRace#getRank(Competitor, com.sap.sse.common.TimePoint)} with the additional logic
|
||||
* that in case the <code>race</code> hasn't {@link TrackedRace#hasStarted(TimePoint) started} yet or no
|
||||
* {@link TrackedRace} exists for <code>race</code>, 0 will be returned for all those competitors. The tracked race
|
||||
* for the correct {@link Fleet} is determined using {@link RaceColumn#getTrackedRace(Competitor)}.
|
||||
* <p>
|
||||
*
|
||||
* For each competitor tracking-wise ranking better than <code>competitor</code> but with
|
||||
* a {@link #getMaxPointsReason(Competitor, RaceColumn, TimePoint) disqualification reason} given, <code>competitor</code>'s
|
||||
* rank is improved by one.
|
||||
* For each competitor tracking-wise ranking better than <code>competitor</code> but with a
|
||||
* {@link #getMaxPointsReason(Competitor, RaceColumn, TimePoint) disqualification reason} given,
|
||||
* <code>competitor</code>'s rank is improved by one.
|
||||
*
|
||||
* @param competitor
|
||||
* a competitor contained in the {@link #getCompetitors()} result
|
||||
* @param race
|
||||
* a race that is contained in the {@link #getRaceColumns()} result
|
||||
* @return a 1-based rank, or 0 if no rank can be determined for the {@code competitor} in {@code race}
|
||||
*/
|
||||
int getTrackedRank(Competitor competitor, RaceColumn race, TimePoint timePoint);
|
||||
|
||||
@@ -293,7 +298,7 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
* {@link #getTotalPoints(Competitor, RaceColumn, TimePoint)} and not on
|
||||
* {@link #getNetPoints(Competitor, RaceColumn, TimePoint)}.
|
||||
*/
|
||||
List<Competitor> getCompetitorsFromBestToWorst(RaceColumn raceColumn, TimePoint timePoint) throws NoWindException;
|
||||
Iterable<Competitor> getCompetitorsFromBestToWorst(RaceColumn raceColumn, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Sorts the competitors according to the overall regatta standings, considering the sorting rules for
|
||||
@@ -304,7 +309,8 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Returns the total rank of the given competitor.
|
||||
* Returns the total rank of the given competitor or {@code 0} if no rank can be determined for
|
||||
* the {@code competitor} in this leaderboard.
|
||||
*/
|
||||
int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
@@ -355,6 +361,10 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
boolean hasCarriedPoints(Competitor competitor);
|
||||
|
||||
SettableScoreCorrection getScoreCorrection();
|
||||
|
||||
void addScoreCorrectionListener(ScoreCorrectionListener listener);
|
||||
|
||||
void removeScoreCorrectionListener(ScoreCorrectionListener listener);
|
||||
|
||||
Competitor getCompetitorByName(String competitorName);
|
||||
|
||||
@@ -494,7 +504,7 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
CourseArea getDefaultCourseArea();
|
||||
|
||||
/**
|
||||
* Must be called when the leaderboard is removed from its server, becoming in accessible. This will give the leaderboard
|
||||
* Must be called when the leaderboard is removed from its server, becoming inaccessible. This will give the leaderboard
|
||||
* a chance to release all its resources that won't be collected or freed automatically. In particular, a leaderboard may
|
||||
* hold on to listeners which in turn are registered with {@link TrackedRace}s and therefore won't be released to the garbage
|
||||
* collector unless the tracked race becomes unreferenced. This may take long because the tracked race can generally
|
||||
@@ -548,4 +558,6 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
|
||||
* the boat class will be determined based on the most frequently occurring boat class when iterating across the competitors.
|
||||
*/
|
||||
BoatClass getBoatClass();
|
||||
|
||||
LeaderboardType getLeaderboardType();
|
||||
}
|
||||
+1
-1
@@ -291,7 +291,7 @@ public class LeaderboardCacheManager {
|
||||
final CacheInvalidationUponCompetitorChangeListener competitorChangeListener = new CacheInvalidationUponCompetitorChangeListener(
|
||||
leaderboard);
|
||||
final NameChangeListener nameChangeListener = new NameChangeListener(leaderboard);
|
||||
leaderboard.getScoreCorrection().addScoreCorrectionListener(scoreCorrectionListener);
|
||||
leaderboard.addScoreCorrectionListener(scoreCorrectionListener);
|
||||
leaderboard.addLeaderboardChangeListener(nameChangeListener);
|
||||
scoreCorrectionListeners.put(leaderboard, scoreCorrectionListener);
|
||||
competitorChangeListeners.put(leaderboard, competitorChangeListener);
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.sap.sailing.domain.leaderboard;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
|
||||
/**
|
||||
* A regatta leaderboard that is derived from another regatta leaderboard by eliminating a subset of the competitors and
|
||||
* that provides its own, unique name and optionally its own display name. This generally implements a "delegate"
|
||||
* pattern for a {@link RegattaLeaderboard}. It therefore does not maintain its own score corrections or set of
|
||||
* suppressed competitors. Note: "suppressed" is different from "eliminated" in that suppressed competitors do not show
|
||||
* in any race and are not assigned any rank in any race, but eliminated competitors are; they only don't receive a
|
||||
* regatta ("total") rank, and all competitors advance by as many ranks compared to the original leaderboard as there
|
||||
* are eliminated competitors ranking better in the original leaderboard.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*/
|
||||
public interface RegattaLeaderboardWithEliminations extends RegattaLeaderboard {
|
||||
|
||||
void setEliminated(Competitor competitor, boolean eliminated);
|
||||
|
||||
boolean isEliminated(Competitor competitor);
|
||||
|
||||
default Set<Competitor> getEliminatedCompetitors() {
|
||||
final Set<Competitor> result = new HashSet<>();
|
||||
for (final Competitor c : getAllCompetitors()) {
|
||||
if (isEliminated(c)) {
|
||||
result.add(c);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+1108
File diff suppressed because it is too large.
Load diff
+92
-1190
File diff suppressed because it is too large.
Load diff
+457
@@ -0,0 +1,457 @@
|
||||
package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.race.RaceLog;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CourseArea;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnListener;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.LeaderboardType;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboardWithEliminations;
|
||||
import com.sap.sailing.domain.leaderboard.ResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.ScoreCorrectionListener;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.regattalike.IsRegattaLike;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.ObscuringIterable;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
/**
|
||||
* A regatta leaderboard that is derived from another regatta leaderboard by eliminating a subset of the competitors and
|
||||
* that provides its own, unique name and optionally its own display name. The class generally implements a "delegate"
|
||||
* pattern for a {@link RegattaLeaderboard}. It therefore does not maintain its own score corrections or set of
|
||||
* suppressed competitors. Note: "suppressed" is different from "eliminated" in that suppressed competitors do not show
|
||||
* in any race and are not assigned any rank in any race, but eliminated competitors are; they only don't receive a
|
||||
* regatta ("total") rank, and all competitors advance by as many ranks compared to the original leaderboard as there
|
||||
* are eliminated competitors ranking better in the original leaderboard.
|
||||
* <p>
|
||||
*
|
||||
* This behavior is achieved by overriding any method returning a collection of {@link Competitor}s, such as
|
||||
* {@link #getCompetitors()}, such that the eliminated competitors are removed from the result which should let any
|
||||
* leaderboard panel displaying the contents of this leaderboard list only the non-eliminated competitors. This includes
|
||||
* {@link #getCompetitorsFromBestToWorst(TimePoint)} which also leads the implementation of
|
||||
* {@link #getTotalRankOfCompetitor(Competitor, TimePoint)} to calculate the ranks based on the competitor list without
|
||||
* those eliminated.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*/
|
||||
public class DelegatingRegattaLeaderboardWithCompetitorElimination extends AbstractLeaderboardWithCache implements RegattaLeaderboardWithEliminations {
|
||||
private static final long serialVersionUID = 8331154893189722924L;
|
||||
private final String name;
|
||||
private RegattaLeaderboard fullLeaderboard;
|
||||
|
||||
private transient final Supplier<RegattaLeaderboard> fullLeaderboardSupplier;
|
||||
|
||||
/**
|
||||
* The particular use case for which this field is introduced is registering score correction
|
||||
* listeners at a point in time when the full leaderboard hasn't been resolved yet. Instead of
|
||||
* letting this listener registration attempt the resolution without success the request can
|
||||
* be queued here, and each time the {@link #getFullLeaderboard()} successfully resolves a
|
||||
* leaderboard, all consumers in this set will be triggered.
|
||||
*/
|
||||
private final ConcurrentHashMap<Consumer<RegattaLeaderboard>, Boolean> triggerWhenFullLeaderboardIsResolved;
|
||||
|
||||
/**
|
||||
* Competitors eliminated from this leaderboard for regatta ranking; those competitors are not part of
|
||||
* {@link #getCompetitors()} but appear in {@link #getAllCompetitors()}. They may have an overlap with
|
||||
* {@link #getSuppressedCompetitors()}, but while suppressed competitors cannot receive a score in a single race,
|
||||
* eliminated competitors can, and their scores are relevant for computing the regatta ranks, but ultimately, an
|
||||
* eliminated competitor's regatta rank is defined as {@code 0} for this leaderboard, and competitors ranking worse
|
||||
* in the {@link #fullLeaderboard original leaderboard} will advance by one rank per eliminated competitor ranking
|
||||
* better.
|
||||
*/
|
||||
private final ConcurrentHashMap<Competitor, Boolean> eliminatedCompetitors;
|
||||
|
||||
/**
|
||||
* The leaderboard wrapper starts out with an empty set of eliminated competitors
|
||||
*/
|
||||
public DelegatingRegattaLeaderboardWithCompetitorElimination(Supplier<RegattaLeaderboard> fullLeaderboardSupplier,
|
||||
String name) {
|
||||
this.name = name;
|
||||
this.fullLeaderboardSupplier = fullLeaderboardSupplier;
|
||||
this.eliminatedCompetitors = new ConcurrentHashMap<>();
|
||||
this.triggerWhenFullLeaderboardIsResolved = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String newName) {
|
||||
getFullLeaderboard().setName(newName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Competitor> getCompetitors() {
|
||||
return new ObscuringIterable<>(getFullLeaderboard().getCompetitors(), eliminatedCompetitors.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEliminated(Competitor competitor, boolean eliminated) {
|
||||
if (eliminated) {
|
||||
eliminatedCompetitors.put(competitor, true);
|
||||
} else {
|
||||
eliminatedCompetitors.remove(competitor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEliminated(Competitor competitor) {
|
||||
return eliminatedCompetitors.containsKey(competitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Competitor> getEliminatedCompetitors() {
|
||||
return new HashSet<Competitor>(eliminatedCompetitors.keySet());
|
||||
}
|
||||
|
||||
public Map<RaceColumn, List<Competitor>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint)
|
||||
throws NoWindException {
|
||||
Map<RaceColumn, List<Competitor>> preResult = getFullLeaderboard().getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(timePoint);
|
||||
for (final List<Competitor> e : preResult.values()) {
|
||||
e.removeAll(eliminatedCompetitors.keySet());
|
||||
}
|
||||
return preResult;
|
||||
}
|
||||
|
||||
public Map<Competitor, Double> getCompetitorsForWhichThereAreCarriedPoints() {
|
||||
final Map<Competitor, Double> result = new HashMap<>();
|
||||
for (final java.util.Map.Entry<Competitor, Double> e : getFullLeaderboard().getCompetitorsForWhichThereAreCarriedPoints().entrySet()) {
|
||||
if (!isEliminated(e.getKey())) {
|
||||
result.put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Iterable<Competitor> getCompetitorsFromBestToWorst(RaceColumn raceColumn, TimePoint timePoint)
|
||||
throws NoWindException {
|
||||
return new ObscuringIterable<>(getFullLeaderboard().getCompetitorsFromBestToWorst(raceColumn, timePoint), eliminatedCompetitors.keySet());
|
||||
}
|
||||
|
||||
public List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint) {
|
||||
final List<Competitor> result = new ArrayList<>();
|
||||
for (final Competitor c : getFullLeaderboard().getCompetitorsFromBestToWorst(timePoint)) {
|
||||
if (!isEliminated(c)) {
|
||||
result.add(c);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<Pair<Competitor, RaceColumn>, Entry> getContent(TimePoint timePoint) throws NoWindException {
|
||||
final Map<Pair<Competitor, RaceColumn>, Entry> result = new HashMap<>();
|
||||
for (final java.util.Map.Entry<Pair<Competitor, RaceColumn>, Entry> e : getFullLeaderboard().getContent(timePoint).entrySet()) {
|
||||
if (!isEliminated(e.getKey().getA())) {
|
||||
result.put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LeaderboardType getLeaderboardType() {
|
||||
return LeaderboardType.RegattaLeaderboardWithEliminations;
|
||||
}
|
||||
|
||||
// --------------------- Delegate Pattern Implementation ----------------------
|
||||
public CompetitorProviderFromRaceColumnsAndRegattaLike getOrCreateCompetitorsProvider() {
|
||||
return getFullLeaderboard().getOrCreateCompetitorsProvider();
|
||||
}
|
||||
|
||||
public Regatta getRegatta() {
|
||||
return getFullLeaderboard().getRegatta();
|
||||
}
|
||||
|
||||
public Iterable<Competitor> getCompetitorsRegisteredInRegattaLog() {
|
||||
return getFullLeaderboard().getCompetitorsRegisteredInRegattaLog();
|
||||
}
|
||||
|
||||
public IsRegattaLike getRegattaLike() {
|
||||
return getFullLeaderboard().getRegattaLike();
|
||||
}
|
||||
|
||||
public RaceLog getRacelog(String raceColumnName, String fleetName) {
|
||||
return getFullLeaderboard().getRacelog(raceColumnName, fleetName);
|
||||
}
|
||||
|
||||
public void registerCompetitor(Competitor competitor) {
|
||||
getFullLeaderboard().registerCompetitor(competitor);
|
||||
}
|
||||
|
||||
public void registerCompetitors(Iterable<Competitor> competitor) {
|
||||
getFullLeaderboard().registerCompetitors(competitor);
|
||||
}
|
||||
|
||||
public void deregisterCompetitor(Competitor competitor) {
|
||||
getFullLeaderboard().deregisterCompetitor(competitor);
|
||||
}
|
||||
|
||||
public void deregisterCompetitors(Iterable<Competitor> competitor) {
|
||||
getFullLeaderboard().deregisterCompetitors(competitor);
|
||||
}
|
||||
|
||||
public Iterable<Competitor> getAllCompetitors() {
|
||||
return getFullLeaderboard().getAllCompetitors();
|
||||
}
|
||||
|
||||
public Iterable<Competitor> getAllCompetitors(RaceColumn raceColumn, Fleet fleet) {
|
||||
return getFullLeaderboard().getAllCompetitors(raceColumn, fleet);
|
||||
}
|
||||
|
||||
public Iterable<Competitor> getCompetitors(RaceColumn raceColumn, Fleet fleet) {
|
||||
return getFullLeaderboard().getCompetitors(raceColumn, fleet);
|
||||
}
|
||||
|
||||
public Iterable<Competitor> getSuppressedCompetitors() {
|
||||
return getFullLeaderboard().getSuppressedCompetitors();
|
||||
}
|
||||
|
||||
public boolean isSuppressed(Competitor competitor) {
|
||||
return getFullLeaderboard().isSuppressed(competitor);
|
||||
}
|
||||
|
||||
public void setSuppressed(Competitor competitor, boolean suppressed) {
|
||||
getFullLeaderboard().setSuppressed(competitor, suppressed);
|
||||
}
|
||||
|
||||
public Fleet getFleet(String fleetName) {
|
||||
return getFullLeaderboard().getFleet(fleetName);
|
||||
}
|
||||
|
||||
public Entry getEntry(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException {
|
||||
return getFullLeaderboard().getEntry(competitor, race, timePoint);
|
||||
}
|
||||
|
||||
public Entry getEntry(Competitor competitor, RaceColumn race, TimePoint timePoint,
|
||||
Set<RaceColumn> discardedRaceColumns) throws NoWindException {
|
||||
return getFullLeaderboard().getEntry(competitor, race, timePoint, discardedRaceColumns);
|
||||
}
|
||||
|
||||
public Map<RaceColumn, Map<Competitor, Double>> getNetPointsSumAfterRaceColumn(TimePoint timePoint)
|
||||
throws NoWindException {
|
||||
return getFullLeaderboard().getNetPointsSumAfterRaceColumn(timePoint);
|
||||
}
|
||||
|
||||
public double getCarriedPoints(Competitor competitor) {
|
||||
return getFullLeaderboard().getCarriedPoints(competitor);
|
||||
}
|
||||
|
||||
public int getTrackedRank(Competitor competitor, RaceColumn race, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getTrackedRank(competitor, race, timePoint);
|
||||
}
|
||||
|
||||
public Double getTotalPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getTotalPoints(competitor, raceColumn, timePoint);
|
||||
}
|
||||
|
||||
public MaxPointsReason getMaxPointsReason(Competitor competitor, RaceColumn race, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getMaxPointsReason(competitor, race, timePoint);
|
||||
}
|
||||
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException {
|
||||
return getFullLeaderboard().getNetPoints(competitor, race, timePoint);
|
||||
}
|
||||
|
||||
public boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
|
||||
return getFullLeaderboard().isDiscarded(competitor, raceColumn, timePoint);
|
||||
}
|
||||
|
||||
public Double getNetPoints(Competitor competitor, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getNetPoints(competitor, timePoint);
|
||||
}
|
||||
|
||||
public Double getNetPoints(Competitor competitor, Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint)
|
||||
throws NoWindException {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumnsToConsider, timePoint);
|
||||
}
|
||||
|
||||
public Iterable<RaceColumn> getRaceColumns() {
|
||||
return getFullLeaderboard().getRaceColumns();
|
||||
}
|
||||
|
||||
public RaceColumn getRaceColumnByName(String name) {
|
||||
return getFullLeaderboard().getRaceColumnByName(name);
|
||||
}
|
||||
|
||||
public void setCarriedPoints(Competitor competitor, double carriedPoints) {
|
||||
getFullLeaderboard().setCarriedPoints(competitor, carriedPoints);
|
||||
}
|
||||
|
||||
public void unsetCarriedPoints(Competitor competitor) {
|
||||
getFullLeaderboard().unsetCarriedPoints(competitor);
|
||||
}
|
||||
|
||||
public boolean hasCarriedPoints() {
|
||||
return getFullLeaderboard().hasCarriedPoints();
|
||||
}
|
||||
|
||||
public boolean hasCarriedPoints(Competitor competitor) {
|
||||
return getFullLeaderboard().hasCarriedPoints(competitor);
|
||||
}
|
||||
|
||||
public SettableScoreCorrection getScoreCorrection() {
|
||||
return getFullLeaderboard().getScoreCorrection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addScoreCorrectionListener(ScoreCorrectionListener listener) {
|
||||
if (fullLeaderboard != null) {
|
||||
fullLeaderboard.addScoreCorrectionListener(listener);
|
||||
} else {
|
||||
triggerWhenFullLeaderboardIsResolved.put(leaderboard->leaderboard.addScoreCorrectionListener(listener), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScoreCorrectionListener(ScoreCorrectionListener listener) {
|
||||
if (fullLeaderboard != null) {
|
||||
fullLeaderboard.removeScoreCorrectionListener(listener);
|
||||
} else {
|
||||
triggerWhenFullLeaderboardIsResolved.put(leaderboard->leaderboard.removeScoreCorrectionListener(listener), true);
|
||||
}
|
||||
}
|
||||
|
||||
public Competitor getCompetitorByName(String competitorName) {
|
||||
return getFullLeaderboard().getCompetitorByName(competitorName);
|
||||
}
|
||||
|
||||
public void setDisplayName(Competitor competitor, String displayName) {
|
||||
getFullLeaderboard().setDisplayName(competitor, displayName);
|
||||
}
|
||||
|
||||
public String getDisplayName(Competitor competitor) {
|
||||
return getFullLeaderboard().getDisplayName(competitor);
|
||||
}
|
||||
|
||||
public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn,
|
||||
TimePoint timePoint) {
|
||||
return getFullLeaderboard().countRaceForComparisonWithDiscardingThresholds(competitor, raceColumn, timePoint);
|
||||
}
|
||||
|
||||
public ResultDiscardingRule getResultDiscardingRule() {
|
||||
return getFullLeaderboard().getResultDiscardingRule();
|
||||
}
|
||||
|
||||
public void setCrossLeaderboardResultDiscardingRule(ThresholdBasedResultDiscardingRule discardingRule) {
|
||||
getFullLeaderboard().setCrossLeaderboardResultDiscardingRule(discardingRule);
|
||||
}
|
||||
|
||||
public Competitor getCompetitorByIdAsString(String idAsString) {
|
||||
return getFullLeaderboard().getCompetitorByIdAsString(idAsString);
|
||||
}
|
||||
|
||||
public void addRaceColumnListener(RaceColumnListener listener) {
|
||||
getFullLeaderboard().addRaceColumnListener(listener);
|
||||
}
|
||||
|
||||
public void removeRaceColumnListener(RaceColumnListener listener) {
|
||||
getFullLeaderboard().removeRaceColumnListener(listener);
|
||||
}
|
||||
|
||||
public Long getDelayToLiveInMillis() {
|
||||
return getFullLeaderboard().getDelayToLiveInMillis();
|
||||
}
|
||||
|
||||
public Iterable<TrackedRace> getTrackedRaces() {
|
||||
return getFullLeaderboard().getTrackedRaces();
|
||||
}
|
||||
|
||||
public ScoringScheme getScoringScheme() {
|
||||
return getFullLeaderboard().getScoringScheme();
|
||||
}
|
||||
|
||||
public TimePoint getTimePointOfLatestModification() {
|
||||
return getFullLeaderboard().getTimePointOfLatestModification();
|
||||
}
|
||||
|
||||
public Pair<GPSFixMoving, Speed> getMaximumSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getMaximumSpeedOverGround(competitor, timePoint);
|
||||
}
|
||||
|
||||
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return getFullLeaderboard().getAverageSpeedOverGround(competitor, timePoint);
|
||||
}
|
||||
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, Iterable<RaceColumn> raceColumnsToConsider,
|
||||
TimePoint timePoint) throws NoWindException {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumn, raceColumnsToConsider, timePoint);
|
||||
}
|
||||
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
|
||||
Set<RaceColumn> discardedRaceColumns) {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
|
||||
}
|
||||
|
||||
public TimePoint getNowMinusDelay() {
|
||||
return getFullLeaderboard().getNowMinusDelay();
|
||||
}
|
||||
|
||||
public CourseArea getDefaultCourseArea() {
|
||||
return getFullLeaderboard().getDefaultCourseArea();
|
||||
}
|
||||
|
||||
public NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher() {
|
||||
return getFullLeaderboard().getNumberOfCompetitorsInLeaderboardFetcher();
|
||||
}
|
||||
|
||||
public Pair<RaceColumn, Fleet> getRaceColumnAndFleet(TrackedRace trackedRace) {
|
||||
return getFullLeaderboard().getRaceColumnAndFleet(trackedRace);
|
||||
}
|
||||
|
||||
public BoatClass getBoatClass() {
|
||||
return getFullLeaderboard().getBoatClass();
|
||||
}
|
||||
|
||||
private RegattaLeaderboard getFullLeaderboard() {
|
||||
if (fullLeaderboard == null) {
|
||||
if (fullLeaderboardSupplier == null) {
|
||||
throw new NullPointerException("Internal error: Regatta leaderboard supplier is null; this can only happen upon premature serialization");
|
||||
}
|
||||
fullLeaderboard = fullLeaderboardSupplier.get();
|
||||
if (fullLeaderboard != null) {
|
||||
for (Iterator<Consumer<RegattaLeaderboard>> i=triggerWhenFullLeaderboardIsResolved.keySet().iterator(); i.hasNext(); ) {
|
||||
final Consumer<RegattaLeaderboard> toTrigger = i.next();
|
||||
toTrigger.accept(fullLeaderboard);
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return fullLeaderboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Before being serialized, ensure that the leaderboard supplier has been used to tru
|
||||
* to resolve the leaderboard.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream oos) throws IOException {
|
||||
getFullLeaderboard();
|
||||
oos.defaultWriteObject();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -363,7 +363,7 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardType getLeaderboardType() {
|
||||
public LeaderboardType getLeaderboardType() {
|
||||
return LeaderboardType.FlexibleLeaderboard;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardType getLeaderboardType() {
|
||||
public LeaderboardType getLeaderboardType() {
|
||||
return LeaderboardType.RegattaLeaderboard;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -194,7 +194,7 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI
|
||||
leaderboard.addRaceColumnListener(this);
|
||||
final ScoreCorrectionChangeForwarder listener = new ScoreCorrectionChangeForwarder();
|
||||
scoreCorrectionChangeForwardersByLeaderboard.put(leaderboard, listener);
|
||||
leaderboard.getScoreCorrection().addScoreCorrectionListener(listener);
|
||||
leaderboard.addScoreCorrectionListener(listener);
|
||||
}
|
||||
|
||||
protected void unregisterScoreCorrectionChangeForwarder(Leaderboard leaderboard) {
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ public class FlexibleMetaLeaderboard extends AbstractMetaLeaderboard {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardType getLeaderboardType() {
|
||||
public LeaderboardType getLeaderboardType() {
|
||||
return LeaderboardType.FlexibleMetaLeaderboard;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -92,7 +92,7 @@ public class LeaderboardGroupMetaLeaderboard extends AbstractMetaLeaderboard imp
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardType getLeaderboardType() {
|
||||
public LeaderboardType getLeaderboardType() {
|
||||
return LeaderboardType.RegattaMetaLeaderboard;
|
||||
}
|
||||
}
|
||||
+99
-32
@@ -14,6 +14,7 @@ import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
@@ -68,7 +69,7 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
/**
|
||||
* Distance ratios of actual distance traveled and leg length above this threshold will receive
|
||||
* penalties on their probability. Ratios below 1.0 receive the ratio as the penalty.
|
||||
* See {@link #getDistanceEstimationBasedProbability(Competitor, Candidate, Candidate)}.
|
||||
* See {@link #getDistanceEstimationBasedProbability(Competitor, Candidate, Candidate, Distance)}.
|
||||
*/
|
||||
private static final double MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH = 2.0;
|
||||
|
||||
@@ -321,8 +322,11 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
continue; // don't create edge from/to same waypoint
|
||||
}
|
||||
|
||||
final Supplier<Double> estimatedDistanceProbabilitySupplier;
|
||||
final double estimatedDistanceProbability;
|
||||
final double startTimingProbability;
|
||||
// when null, don't create an edge; when a valid distance, use this as the totalGreatCircleDistance value
|
||||
final Distance ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping;
|
||||
if (early == start) {
|
||||
// An edge starting at the start proxy node. If the late candidate is for a start mark passing,
|
||||
// determine a probability not based on distance traveled but based on the
|
||||
@@ -332,7 +336,9 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
// the race's start time.
|
||||
if (isGateStart == Boolean.TRUE || start.getTimePoint() == null) { // TODO for gate start read gate timing and scale probability accordingly
|
||||
startTimingProbability = 1; // no start time point known; all candidate time points equally likely
|
||||
estimatedDistanceProbability = 1.0; // can't tell distance sailed either because we don't know the start time
|
||||
estimatedDistanceProbability = 1.0;
|
||||
ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping = Distance.NULL;
|
||||
estimatedDistanceProbabilitySupplier = null; // can't tell distance sailed either because we don't know the start time
|
||||
} else {
|
||||
// no gate start and we know the race start time
|
||||
if (late.getWaypoint() != null && late.getWaypoint() == race.getRace().getCourse().getFirstWaypoint()) {
|
||||
@@ -347,10 +353,20 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
DELAY_AFTER_WHICH_PROBABILITY_OF_START_HALVES.plus(
|
||||
timeGapBetweenStartOfRaceAndCandidateTimePoint));
|
||||
estimatedDistanceProbability = 1.0;
|
||||
ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping = Distance.NULL;
|
||||
estimatedDistanceProbabilitySupplier = null;
|
||||
} else {
|
||||
startTimingProbability = 1; // can't really tell how well the start time was matched when
|
||||
// we don't have a start candidate
|
||||
estimatedDistanceProbability = late == end ? 1.0 : getDistanceEstimationBasedProbability(c, early, late);
|
||||
// we don't have a start candidate
|
||||
if (late == end) {
|
||||
estimatedDistanceProbability = 1.0;
|
||||
ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping = Distance.NULL;
|
||||
estimatedDistanceProbabilitySupplier = null;
|
||||
} else {
|
||||
estimatedDistanceProbability = 0.0;
|
||||
ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping = getIgnoreDueToTimingInducedEstimatedSpeeds(c, early, late);
|
||||
estimatedDistanceProbabilitySupplier = ()->getDistanceEstimationBasedProbability(c, early, late, ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -358,8 +374,12 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
if (late == end) {
|
||||
// final edge; we don't know anything about distances for the end proxy node
|
||||
estimatedDistanceProbability = 1.0;
|
||||
ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping = Distance.NULL;
|
||||
estimatedDistanceProbabilitySupplier = null;
|
||||
} else {
|
||||
estimatedDistanceProbability = getDistanceEstimationBasedProbability(c, early, late);
|
||||
estimatedDistanceProbability = 0.0;
|
||||
ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping = getIgnoreDueToTimingInducedEstimatedSpeeds(c, early, late);
|
||||
estimatedDistanceProbabilitySupplier = ()->getDistanceEstimationBasedProbability(c, early, late, ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping);
|
||||
}
|
||||
}
|
||||
// If one of the candidates is fixed, the edge is always created unless they travel backwards in time.
|
||||
@@ -368,9 +388,16 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
final NavigableSet<Candidate> fixed = fixedPassings.get(c);
|
||||
// TODO this comparison does not exactly implement the condition "if distance is more likely than skipping"
|
||||
if (travelingForwardInTimeOrUnknown(early, late) &&
|
||||
(fixed.contains(early) || fixed.contains(late) || estimatedDistanceProbability > MINIMUM_PROBABILITY)) {
|
||||
addEdge(edges, new Edge(early, late,
|
||||
startTimingProbability * estimatedDistanceProbability, race.getRace().getCourse().getNumberOfWaypoints()));
|
||||
(fixed.contains(early) || fixed.contains(late) || ignoreEdgeDueToProbabilityLowerThanMinimumForSkipping != null)) {
|
||||
final Edge edge;
|
||||
if (estimatedDistanceProbabilitySupplier != null) {
|
||||
edge = new Edge(early, late,
|
||||
()->startTimingProbability * estimatedDistanceProbabilitySupplier.get(), race.getRace().getCourse().getNumberOfWaypoints());
|
||||
} else {
|
||||
edge = new Edge(early, late,
|
||||
startTimingProbability * estimatedDistanceProbability, race.getRace().getCourse().getNumberOfWaypoints());
|
||||
}
|
||||
addEdge(edges, edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,7 +455,7 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
boolean endFound = false;
|
||||
currentEdgesMoreLikelyFirst.add(new Util.Pair<Edge, Double>(new Edge(
|
||||
new CandidateImpl(-1, null, /* estimated distance probability */ 1, null), startOfFixedInterval,
|
||||
1, race.getRace().getCourse().getNumberOfWaypoints()), 1.0));
|
||||
()->1.0, race.getRace().getCourse().getNumberOfWaypoints()), 1.0));
|
||||
while (!endFound) {
|
||||
Util.Pair<Edge, Double> mostLikelyEdgeWithProbability = currentEdgesMoreLikelyFirst.pollFirst();
|
||||
if (mostLikelyEdgeWithProbability == null) {
|
||||
@@ -503,24 +530,72 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The timing of the candidates is put in relation to the distance between the waypoints they connect. This implies
|
||||
* a rough speed estimate. If this is completely "out of whack" (way too low or way too high), we can assume that if
|
||||
* ultimately carrying out the potentially expensive (for long-distance races) calculation of the distance sailed,
|
||||
* we would only find that the distance-based probability would be below the {@link #MINIMUM_PROBABILITY} threshold
|
||||
* such that that edge would be ignored.
|
||||
*
|
||||
* @return {@code null} if the edge between {@code c1} and {@code c2} shall be ignored; the
|
||||
* {@link #getMinimumTotalGreatCircleDistanceBetweenWaypoints(Waypoint, Waypoint, TimePoint) straight-line
|
||||
* distance between the corresponding waypoints} otherwise. This distance can be used for a subsequent call
|
||||
* to {@link #getDistanceEstimationBasedProbability(Competitor, Candidate, Candidate, Distance)} so it doesn't have
|
||||
* to be re-calculated there.
|
||||
*/
|
||||
private Distance getIgnoreDueToTimingInducedEstimatedSpeeds(Competitor c, Candidate c1, Candidate c2) {
|
||||
final boolean ignore;
|
||||
assert c1.getOneBasedIndexOfWaypoint() < c2.getOneBasedIndexOfWaypoint();
|
||||
assert c2 != end;
|
||||
final TimePoint middleOfc1Andc2 = new MillisecondsTimePoint(c1.getTimePoint().plus(c2.getTimePoint().asMillis()).asMillis() / 2);
|
||||
Waypoint first = getFirstWaypoint(c1);
|
||||
final Waypoint second = c2.getWaypoint();
|
||||
final Distance totalGreatCircleDistance = getMinimumTotalGreatCircleDistanceBetweenWaypoints(first, second, middleOfc1Andc2);
|
||||
if (totalGreatCircleDistance == null) {
|
||||
ignore = true; // no distance known; cannot tell, so ignore the edge
|
||||
} else {
|
||||
// Computing the distance traveled can be quite expensive, especially for candidates very far apart.
|
||||
// As a quick approximation let's look at how long the time between the candidates was and relate that to the minimum distance
|
||||
// between the waypoints. This leads to a speed estimation; if we take the minimum distance times two, we
|
||||
// get an upper bound for a reasonable distance sailed between the waypoints and therefore an estimation
|
||||
// for the maximum speed at which the competitor would have had to sail:
|
||||
Speed estimatedMaxSpeed = totalGreatCircleDistance.scale(2).inTime(c1.getTimePoint().until(c2.getTimePoint()));
|
||||
final double estimatedMinSpeedBasedProbability = Math.max(0, estimatedMaxSpeed.divide(MINIMUM_REASONABLE_SPEED));
|
||||
final double estimatedMaxSpeedBasedProbability = Math.max(0, MAXIMUM_REASONABLE_SPEED.divide(estimatedMaxSpeed));
|
||||
final double estimatedSpeedBasedProbabilityMinimum = Math.min(estimatedMaxSpeedBasedProbability, estimatedMinSpeedBasedProbability);
|
||||
ignore = estimatedSpeedBasedProbabilityMinimum < MINIMUM_PROBABILITY;
|
||||
}
|
||||
return ignore ? null : totalGreatCircleDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the candidate has no waypoint associated, return the course's first waypoint; otherwise, return the candidates
|
||||
* {@link Candidate#getWaypoint() waypoint}.
|
||||
*/
|
||||
private Waypoint getFirstWaypoint(Candidate candidate) {
|
||||
Waypoint first;
|
||||
if (candidate.getOneBasedIndexOfWaypoint() == 0) {
|
||||
first = race.getRace().getCourse().getFirstWaypoint();
|
||||
} else {
|
||||
first = candidate.getWaypoint();
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* The distance between waypoints is used to estimate the distance that should be covered between these two
|
||||
* candidates. This estimation is then compared to the distance actually sailed. A distance smaller than the
|
||||
* estimation is (aside from a small tolerance) impossible, a distance larger get increasingly unlikely.
|
||||
*
|
||||
* @param totalGreatCircleDistance
|
||||
* the result of a previous
|
||||
* {@link #getMinimumTotalGreatCircleDistanceBetweenWaypoints(Waypoint, Waypoint, TimePoint)} call for
|
||||
* the two waypoints of {@code c1} and {@code c2}
|
||||
*/
|
||||
private double getDistanceEstimationBasedProbability(Competitor c, Candidate c1, Candidate c2) {
|
||||
private double getDistanceEstimationBasedProbability(Competitor c, Candidate c1, Candidate c2, Distance totalGreatCircleDistance) {
|
||||
final double result;
|
||||
assert c1.getOneBasedIndexOfWaypoint() < c2.getOneBasedIndexOfWaypoint();
|
||||
assert c2 != end;
|
||||
Waypoint first;
|
||||
final TimePoint middleOfc1Andc2 = new MillisecondsTimePoint(c1.getTimePoint().plus(c2.getTimePoint().asMillis()).asMillis() / 2);
|
||||
if (c1.getOneBasedIndexOfWaypoint() == 0) {
|
||||
first = race.getRace().getCourse().getFirstWaypoint();
|
||||
} else {
|
||||
first = c1.getWaypoint();
|
||||
}
|
||||
final Waypoint second = c2.getWaypoint();
|
||||
final Distance totalGreatCircleDistance = getMinimumTotalGreatCircleDistanceBetweenWaypoints(first, second, middleOfc1Andc2);
|
||||
if (totalGreatCircleDistance == null) {
|
||||
result = 0.0; // no distance known; cannot tell
|
||||
} else {
|
||||
@@ -529,19 +604,11 @@ public class CandidateChooserImpl implements CandidateChooser {
|
||||
// between the waypoints. This leads to a speed estimation; if we take the minimum distance times two, we
|
||||
// get an upper bound for a reasonable distance sailed between the waypoints and therefore an estimation
|
||||
// for the maximum speed at which the competitor would have had to sail:
|
||||
Speed estimatedMaxSpeed = totalGreatCircleDistance.scale(2).inTime(c1.getTimePoint().until(c2.getTimePoint()));
|
||||
final double estimatedMinSpeedBasedProbability = Math.max(0, estimatedMaxSpeed.divide(MINIMUM_REASONABLE_SPEED));
|
||||
final double estimatedMaxSpeedBasedProbability = Math.max(0, estimatedMaxSpeed.divide(MAXIMUM_REASONABLE_SPEED));
|
||||
final double estimatedSpeedBasedProbabilityMinimum = Math.min(estimatedMaxSpeedBasedProbability, estimatedMinSpeedBasedProbability);
|
||||
if (estimatedSpeedBasedProbabilityMinimum < MINIMUM_PROBABILITY) {
|
||||
result = estimatedSpeedBasedProbabilityMinimum;
|
||||
} else {
|
||||
final Distance actualDistanceTraveled = race.getTrack(c).getDistanceTraveled(c1.getTimePoint(), c2.getTimePoint());
|
||||
final double probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength =
|
||||
c2.getWaypoint() == race.getRace().getCourse().getLastWaypoint() ? PENALTY_FOR_LATEST_FINISH_PASSING : 1.0;
|
||||
result = getProbabilityOfActualDistanceGivenGreatCircleDistance(totalGreatCircleDistance, actualDistanceTraveled,
|
||||
probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength);
|
||||
}
|
||||
final Distance actualDistanceTraveled = race.getTrack(c).getDistanceTraveled(c1.getTimePoint(), c2.getTimePoint());
|
||||
final double probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength =
|
||||
c2.getWaypoint() == race.getRace().getCourse().getLastWaypoint() ? PENALTY_FOR_LATEST_FINISH_PASSING : 1.0;
|
||||
result = getProbabilityOfActualDistanceGivenGreatCircleDistance(totalGreatCircleDistance, actualDistanceTraveled,
|
||||
probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+24
-15
@@ -602,10 +602,8 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
List<Distance> waypointDistances = fixDistances.get(w);
|
||||
List<Distance> waypointDistancesBefore = fixDistancesBefore.get(w);
|
||||
List<Distance> waypointDistancesAfter = fixDistancesAfter.get(w);
|
||||
// due to course changes, waypoints that exist in the waypoints collection may not have a
|
||||
// corresponding
|
||||
// key in passingInstructions' key set which is the basis for the waypoints for which
|
||||
// getDistances(...)
|
||||
// due to course changes, waypoints that exist in the waypoints collection may not have a corresponding
|
||||
// key in passingInstructions' key set which is the basis for the waypoints for which getDistances(...)
|
||||
// computes results; so we have to check for null here:
|
||||
if (waypointDistances != null && waypointDistancesBefore != null && waypointDistancesAfter != null) {
|
||||
Iterator<Distance> disIter = waypointDistances.iterator();
|
||||
@@ -871,7 +869,8 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
final Distance d = portMark ? distances.get(0) : distances.get(1);
|
||||
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, t);
|
||||
final double sidePenalty = getSidePenalty(w, p, t, portMark, markPositionCache);
|
||||
double probability = getDistanceBasedProbability(w, t, d, markPositionCache) * sidePenalty;
|
||||
final Double distanceBasedProbability = getDistanceBasedProbability(w, t, d, markPositionCache);
|
||||
double probability = distanceBasedProbability == null ? sidePenalty : distanceBasedProbability * sidePenalty;
|
||||
final Double passesInTheRightDirectionProbability = passesInTheRightDirection(w, xte1, xte2, portMark);
|
||||
// null would mean "unknown"; no penalty for those cases
|
||||
probability = passesInTheRightDirectionProbability == null ? probability : probability * passesInTheRightDirectionProbability;
|
||||
@@ -1202,20 +1201,31 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a probability based on the distance to <code>w</code> and the average leg lengths before and after.
|
||||
* @return a probability based on the distance to <code>w</code>; for single marks the average leg lengths before
|
||||
* and after the waypoint {@code w} is also taken into account; for two-mark waypoints such as gates and
|
||||
* lines it seems fair to assume that the length of the adjacent legs should not play a role in how accurate
|
||||
* the competitor needs to pass the waypoint.
|
||||
*/
|
||||
private Double getDistanceBasedProbability(Waypoint w, TimePoint t, Distance distance, MarkPositionAtTimePointCache markPositionCache) {
|
||||
assert t.equals(markPositionCache.getTimePoint());
|
||||
assert race == markPositionCache.getTrackedRace();
|
||||
assert distance.getMeters() >= 0;
|
||||
final Double result;
|
||||
Distance legLength = getAverageLengthOfAdjacentLegs(t, w, markPositionCache);
|
||||
if (legLength != null) {
|
||||
if (Util.size(w.getControlPoint().getMarks())>1) {
|
||||
result = 1 / (STRICTNESS_OF_DISTANCE_BASED_PROBABILITY/* Raising this will make it stricter */
|
||||
// reduce distance by 2x the typical HDOP, accounting for the possibility that some distance from the mark
|
||||
// may have been caused by inaccurate GPS tracking
|
||||
* Math.abs(Math.max(0.0, distance.add(GPSFix.TYPICAL_HDOP.scale(-2)).divide(legLength))) + 1);
|
||||
// for a two-mark control point such as a gate or a line only consider the relation of
|
||||
// the distance to 150x the typical HDOP error;
|
||||
* Math.abs(Math.max(0.0, distance.divide(GPSFix.TYPICAL_HDOP.scale(150)))) + 1);
|
||||
} else {
|
||||
result = null;
|
||||
Distance legLength = getAverageLengthOfAdjacentLegs(t, w, markPositionCache);
|
||||
if (legLength != null) {
|
||||
result = 1 / (STRICTNESS_OF_DISTANCE_BASED_PROBABILITY/* Raising this will make it stricter */
|
||||
// reduce distance by 2x the typical HDOP, accounting for the possibility that some distance from the mark
|
||||
// may have been caused by inaccurate GPS tracking
|
||||
* Math.abs(Math.max(0.0, distance.add(GPSFix.TYPICAL_HDOP.scale(-2)).divide(legLength))) + 1);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1328,12 +1338,11 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
PassingInstruction instruction = getPassingInstructions(w);
|
||||
if (instruction == PassingInstruction.Line) {
|
||||
Util.Pair<Mark, Mark> marks = getPortAndStarboardMarks(t, w, markPositionCache);
|
||||
Position portPosition = null;
|
||||
Bearing b = null;
|
||||
Mark portMark = marks.getA();
|
||||
Mark starBoardMark = marks.getB();
|
||||
if (portMark != null && starBoardMark != null) {
|
||||
portPosition = markPositionCache.getEstimatedPosition(portMark);
|
||||
Position portPosition = markPositionCache.getEstimatedPosition(portMark);
|
||||
Position starboardPosition = markPositionCache.getEstimatedPosition(starBoardMark);
|
||||
if (portPosition != null && starboardPosition != null) {
|
||||
b = portPosition.getBearingGreatCircle(starboardPosition);
|
||||
@@ -1414,7 +1423,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
||||
}
|
||||
markPositions.add(estimatedMarkPosition);
|
||||
}
|
||||
if (markPositions.size() != 2){
|
||||
if (markPositions.size() != 2) {
|
||||
return new Util.Pair<Mark, Mark>(null, null);
|
||||
}
|
||||
final List<Leg> legs = race.getRace().getCourse().getLegs();
|
||||
|
||||
+30
-5
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.domain.markpassingcalculation.impl;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.markpassingcalculation.Candidate;
|
||||
|
||||
@@ -19,12 +21,21 @@ public class Edge implements Comparable<Edge> {
|
||||
|
||||
/**
|
||||
* The probability-reducing factor for skipping a waypoint is {@link #PENALTY_FOR_SKIPPED} but is reduced to
|
||||
* {@link #PENALTY_FOR_SKIPPED_TO_END} when skipping to the end. The reason for preferring skips to the end proxy node
|
||||
* is that in live situations where the course hasn't been completed yet it is required to skip to the end. The main
|
||||
* probability comes from the probability of the product of the start node, end node and distance-based
|
||||
* {@link #PENALTY_FOR_SKIPPED_TO_END} when skipping to the end. The reason for preferring skips to the end proxy
|
||||
* node is that in live situations where the course hasn't been completed yet it is required to skip to the end. The
|
||||
* main probability comes from the probability of the product of the start node, end node and distance-based
|
||||
* probabilities.
|
||||
* <p>
|
||||
*
|
||||
* Once used, the result is stored in {@link #estimatedDistanceAndStartTimingProbability}, and this field is set to
|
||||
* {@code null}.
|
||||
*/
|
||||
private final double estimatedDistanceAndStartTimingProbability;
|
||||
private Supplier<Double> estimatedDistanceAndStartTimingProbabilitySupplier;
|
||||
|
||||
/**
|
||||
* Either provided at construction time or calculated by the {@link #estimatedDistanceAndStartTimingProbabilitySupplier}.
|
||||
*/
|
||||
private double estimatedDistanceAndStartTimingProbability;
|
||||
|
||||
/**
|
||||
* The penalty for an edge's probability in case the edge skips one or more waypoints and does not
|
||||
@@ -48,6 +59,13 @@ public class Edge implements Comparable<Edge> {
|
||||
|
||||
private final int numberOfWaypoints;
|
||||
|
||||
public Edge(Candidate start, Candidate end, Supplier<Double> estimatedDistanceAndStartTimingProbabilitySupplier, int numberOfWaypoints) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.estimatedDistanceAndStartTimingProbabilitySupplier = estimatedDistanceAndStartTimingProbabilitySupplier;
|
||||
this.numberOfWaypoints = numberOfWaypoints;
|
||||
}
|
||||
|
||||
public Edge(Candidate start, Candidate end, double estimatedDistanceAndStartTimingProbability, int numberOfWaypoints) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
@@ -70,10 +88,17 @@ public class Edge implements Comparable<Edge> {
|
||||
final double penalty = getEnd().getOneBasedIndexOfWaypoint() == numberOfWaypoints + 1 ? PENALTY_FOR_SKIPPED_TO_END : getPenaltyForSkipping();
|
||||
// See bug 3241 comment #38: only use the edge's end candidate's probability; the start candidate's probability is the
|
||||
// previous edge's end probability. The only probability we'll miss this way is that of the start proxy node and that is always 1.
|
||||
return getEnd().getProbability() * estimatedDistanceAndStartTimingProbability *
|
||||
return getEnd().getProbability() * getEstimatedDistanceAndStartTimingProbability() *
|
||||
Math.pow(penalty, (getEnd().getOneBasedIndexOfWaypoint() - getStart().getOneBasedIndexOfWaypoint() - 1));
|
||||
}
|
||||
|
||||
private double getEstimatedDistanceAndStartTimingProbability() {
|
||||
if (estimatedDistanceAndStartTimingProbabilitySupplier != null) {
|
||||
estimatedDistanceAndStartTimingProbability = estimatedDistanceAndStartTimingProbabilitySupplier.get();
|
||||
estimatedDistanceAndStartTimingProbabilitySupplier = null;
|
||||
}
|
||||
return estimatedDistanceAndStartTimingProbability;
|
||||
}
|
||||
|
||||
public Candidate getStart() {
|
||||
return start;
|
||||
|
||||
+5
@@ -627,6 +627,11 @@ public abstract class AbstractRankingMetric implements RankingMetric {
|
||||
final Position estimatedPosition = getTrackedRace().getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolate */ true);
|
||||
if (estimatedPosition != null) {
|
||||
final Distance windwardDistanceFromLegStart = trackedLeg.getWindwardDistanceFromLegStart(estimatedPosition, cache);
|
||||
if (windwardDistanceFromLegStart == null) {
|
||||
// probably the leg start position is not known; therefore, distance cannot be determined; return null:
|
||||
d = null;
|
||||
break;
|
||||
}
|
||||
final Distance legWindwardDistance = trackedLeg.getWindwardDistance(cache);
|
||||
if (legWindwardDistance != null && legWindwardDistance.compareTo(windwardDistanceFromLegStart) < 0) {
|
||||
d = d.add(legWindwardDistance);
|
||||
|
||||
+1
@@ -548,6 +548,7 @@ public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends
|
||||
if (fromPos == null) {
|
||||
result = Distance.NULL;
|
||||
} else {
|
||||
// TODO bug 4122: idea: use the subset to determine fromPos and toPos in one go, eliminating two expensive getEstimatedPosition calls
|
||||
NavigableSet<GPSFix> subset = getGPSFixes().subSet(new DummyGPSFix(from),
|
||||
/* fromInclusive */false, new DummyGPSFix(to),
|
||||
/* toInclusive */false);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="66"
|
||||
height="66"
|
||||
viewBox="0 0 17.462499 17.4625"
|
||||
version="1.1"
|
||||
id="svg3788"
|
||||
sodipodi:docname="liveraces.svg"
|
||||
style="enable-background:new"
|
||||
inkscape:version="0.92.1 r15371">
|
||||
<defs
|
||||
id="defs3782" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="10"
|
||||
inkscape:cx="44.577827"
|
||||
inkscape:cy="33.336717"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer5"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="1072"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata3785">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer5"
|
||||
inkscape:label="Ebene 3"
|
||||
style="display:inline"
|
||||
transform="translate(0.2381256,0.29108406)">
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 0.05291608,0.05291787 15.857454,2.1960429 V 6.4029173 L 0.05291608,8.281459 Z"
|
||||
id="path3848"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458332"
|
||||
d="M 3.4925001,0.58208334 6.4822917,1.031875 v 6.4558328 l -2.9897916,0.3175 z"
|
||||
id="rect3852"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458332"
|
||||
d="M 9.8424996,1.42875 12.699999,1.825625 V 6.6674998 L 9.8424996,6.9320831 Z"
|
||||
id="rect3855"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.25730553"
|
||||
width="8.7120695"
|
||||
height="5.3254027"
|
||||
x="5.5261526"
|
||||
y="9.4949026"
|
||||
id="rect3870" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 2.5399991,8.0168736 v 8.8899584 0 0 H 16.95979 V 7.3554154 H 7.831666"
|
||||
id="path3872"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
+1
-1
@@ -249,7 +249,7 @@ public class DesktopStartView extends Composite implements StartView {
|
||||
EventDTO result = null;
|
||||
int selectedIndex = eventSelectionBox.getSelectedIndex();
|
||||
if (events != null && selectedIndex > 0) {
|
||||
String selectedItemText = eventSelectionBox.getItemText(selectedIndex);
|
||||
String selectedItemText = eventSelectionBox.getValue(selectedIndex);
|
||||
for (EventDTO event : events) {
|
||||
if (event.getName().equals(selectedItemText)) {
|
||||
result = event;
|
||||
|
||||
+4
-4
@@ -9,8 +9,8 @@ import com.sap.sailing.gwt.home.server.EventActionUtil;
|
||||
import com.sap.sailing.gwt.home.server.RaceListDataCalculator;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.gwt.dispatch.shared.caching.IsClientCacheable;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.ListResult;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.ResultWithTTL;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.SortedSetResult;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -24,7 +24,7 @@ import com.sap.sse.gwt.dispatch.shared.commands.SortedSetResult;
|
||||
* state} using a duration of <i>5 minutes</i> for currently running events.
|
||||
* </p>
|
||||
*/
|
||||
public class GetFinishedRacesAction implements SailingAction<ResultWithTTL<SortedSetResult<RaceListRaceDTO>>>,
|
||||
public class GetFinishedRacesAction implements SailingAction<ResultWithTTL<ListResult<RaceListRaceDTO>>>,
|
||||
IsClientCacheable {
|
||||
|
||||
private UUID eventId;
|
||||
@@ -47,12 +47,12 @@ public class GetFinishedRacesAction implements SailingAction<ResultWithTTL<Sorte
|
||||
|
||||
@Override
|
||||
@GwtIncompatible
|
||||
public ResultWithTTL<SortedSetResult<RaceListRaceDTO>> execute(SailingDispatchContext context) {
|
||||
public ResultWithTTL<ListResult<RaceListRaceDTO>> execute(SailingDispatchContext context) {
|
||||
RaceListDataCalculator raceListDataCalculator = new RaceListDataCalculator();
|
||||
EventActionUtil.forRacesOfRegatta(context, eventId, regattaId, raceListDataCalculator);
|
||||
|
||||
return new ResultWithTTL<>(EventActionUtil.getEventStateDependentTTL(context, eventId,
|
||||
Duration.ONE_MINUTE.times(5)), new SortedSetResult<>(raceListDataCalculator.getResult()));
|
||||
Duration.ONE_MINUTE.times(5)), new ListResult<>(raceListDataCalculator.getResult()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+19
-31
@@ -2,6 +2,7 @@ package com.sap.sailing.gwt.home.communication.event;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
@@ -14,11 +15,9 @@ import com.sap.sailing.gwt.home.communication.SailingDispatchContext;
|
||||
import com.sap.sailing.gwt.home.communication.eventview.RegattaMetadataDTO;
|
||||
import com.sap.sailing.gwt.home.server.EventActionUtil;
|
||||
import com.sap.sailing.gwt.home.server.EventActionUtil.LeaderboardCallback;
|
||||
import com.sap.sailing.gwt.home.server.EventActionUtil.RaceCallback;
|
||||
import com.sap.sailing.gwt.home.server.LeaderboardContext;
|
||||
import com.sap.sailing.gwt.home.server.RaceContext;
|
||||
import com.sap.sailing.gwt.server.HomeServiceUtil;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sailing.gwt.home.server.RaceRefreshCalculator;
|
||||
import com.sap.sse.gwt.dispatch.shared.caching.IsClientCacheable;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.ResultWithTTL;
|
||||
|
||||
@@ -53,39 +52,27 @@ public class GetRegattasAndLiveRacesForEventAction implements SailingAction<Resu
|
||||
@Override
|
||||
@GwtIncompatible
|
||||
public ResultWithTTL<RegattasAndLiveRacesDTO> execute(SailingDispatchContext context) {
|
||||
Event event = context.getRacingEventService().getEvent(eventId);
|
||||
final Map<String, RegattaMetadataDTO> regattas = mapRegattas(context, event);
|
||||
final Map<String, RegattaMetadataDTO> regattas = mapRegattas(context);
|
||||
final TreeMap<RegattaMetadataDTO, TreeSet<LiveRaceDTO>> regattasWithRaces = new TreeMap<>();
|
||||
EventState eventState = HomeServiceUtil.calculateEventState(event);
|
||||
|
||||
final Duration ttl;
|
||||
if (eventState == EventState.RUNNING || eventState == EventState.UPCOMING) {
|
||||
EventActionUtil.forRacesOfEvent(context, eventId, new RaceCallback() {
|
||||
@Override
|
||||
public void doForRace(RaceContext context) {
|
||||
LiveRaceDTO liveRace = context.getLiveRaceOrNull();
|
||||
if(liveRace != null) {
|
||||
ensureRegatta(context, regattas, regattasWithRaces).add(liveRace);
|
||||
}
|
||||
}
|
||||
});
|
||||
ttl = Duration.ONE_MINUTE.times(2);
|
||||
} else {
|
||||
ttl = EventActionUtil.calculateTtlForNonLiveEvent(event, eventState);
|
||||
}
|
||||
final TreeSet<RegattaMetadataDTO> regattasWithoutRaces = new TreeSet<>();
|
||||
for (RegattaMetadataDTO regatta : regattas.values()) {
|
||||
if (!regattasWithRaces.containsKey(regatta)) {
|
||||
regattasWithoutRaces.add(regatta);
|
||||
}
|
||||
}
|
||||
return new ResultWithTTL<RegattasAndLiveRacesDTO>(ttl, new RegattasAndLiveRacesDTO(regattasWithRaces, regattasWithoutRaces));
|
||||
final RaceRefreshCalculator refreshCalculator = new RaceRefreshCalculator();
|
||||
|
||||
EventActionUtil.forRacesOfEvent(context, eventId, raceContent -> {
|
||||
Optional<LiveRaceDTO> liveRace = Optional.ofNullable(raceContent.getLiveRaceOrNull());
|
||||
liveRace.ifPresent(r -> ensureRegatta(raceContent, regattas, regattasWithRaces).add(r));
|
||||
refreshCalculator.doForRace(raceContent);
|
||||
});
|
||||
regattas.values().stream().filter(regatta -> !regattasWithRaces.containsKey(regatta))
|
||||
.forEach(regattasWithoutRaces::add);
|
||||
RegattasAndLiveRacesDTO dto = new RegattasAndLiveRacesDTO(regattasWithRaces, regattasWithoutRaces);
|
||||
ResultWithTTL<RegattasAndLiveRacesDTO> result = new ResultWithTTL<>(refreshCalculator.getTTL(), dto);
|
||||
return EventActionUtil.withLiveRaceOrDefaultSchedule(context, eventId, event -> result, dto);
|
||||
}
|
||||
|
||||
@GwtIncompatible
|
||||
private Map<String, RegattaMetadataDTO> mapRegattas(SailingDispatchContext context, Event event) {
|
||||
private Map<String, RegattaMetadataDTO> mapRegattas(SailingDispatchContext context) {
|
||||
final Map<String, RegattaMetadataDTO> result = new HashMap<>();
|
||||
EventActionUtil.forLeaderboardsOfEvent(context, event, new LeaderboardCallback() {
|
||||
EventActionUtil.forLeaderboardsOfEvent(context, eventId, new LeaderboardCallback() {
|
||||
@Override
|
||||
public void doForLeaderboard(LeaderboardContext context) {
|
||||
result.put(context.getLeaderboardName(), context.asRegattaMetadataDTO());
|
||||
@@ -95,7 +82,8 @@ public class GetRegattasAndLiveRacesForEventAction implements SailingAction<Resu
|
||||
}
|
||||
|
||||
@GwtIncompatible
|
||||
protected Set<LiveRaceDTO> ensureRegatta(RaceContext context, Map<String, RegattaMetadataDTO> regattas, Map<RegattaMetadataDTO, TreeSet<LiveRaceDTO>> regattasWithRaces) {
|
||||
protected Set<LiveRaceDTO> ensureRegatta(RaceContext context, Map<String, RegattaMetadataDTO> regattas,
|
||||
Map<RegattaMetadataDTO, TreeSet<LiveRaceDTO>> regattasWithRaces) {
|
||||
String regattaName = context.getRegattaName();
|
||||
RegattaMetadataDTO regatta = regattas.get(regattaName);
|
||||
TreeSet<LiveRaceDTO> races = regattasWithRaces.get(regatta);
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.sap.sailing.gwt.home.communication.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.TreeSet;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.gwt.home.communication.race.FleetMetadataDTO;
|
||||
@@ -11,7 +11,7 @@ public class RaceCompetitionFormatFleetDTO implements IsSerializable {
|
||||
|
||||
private FleetMetadataDTO fleet;
|
||||
private int competitorCount = 0;
|
||||
private TreeSet<SimpleRaceMetadataDTO> races = new TreeSet<>();
|
||||
private LinkedHashSet<SimpleRaceMetadataDTO> races = new LinkedHashSet<>();
|
||||
|
||||
protected RaceCompetitionFormatFleetDTO() {
|
||||
}
|
||||
|
||||
+5
@@ -49,6 +49,11 @@ public class LiveRacesList extends Composite {
|
||||
this.regattaNameColumn.setShowDetails(showRegattaDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SortableRaceListColumn<LiveRaceDTO, ?> getDefaultSortColumn() {
|
||||
return startTimeColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setTableData(Collection<LiveRaceDTO> data) {
|
||||
boolean hasFleets = RaceListDataUtil.hasFleets(data);
|
||||
|
||||
+9
-7
@@ -13,16 +13,17 @@ import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicatorResources;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
|
||||
|
||||
public abstract class AbstractLeaderboardFullscreenViewer<T extends Widget> extends FullscreenContainer<T> implements
|
||||
LeaderboardDelegate<T> {
|
||||
public abstract class AbstractLeaderboardFullscreenViewer extends FullscreenContainer<Widget> implements
|
||||
LeaderboardDelegate {
|
||||
|
||||
private final Image autoRefreshControl = new Image("images/home/reload.svg");
|
||||
private final Image settingsControl = new Image("images/home/settings.svg");
|
||||
protected final Image autoRefreshControl = new Image("images/home/reload.svg");
|
||||
protected final Image settingsControl = new Image("images/home/settings.svg");
|
||||
|
||||
private final static BusyIndicatorResources RESOURCES = GWT.create(BusyIndicatorResources.class);
|
||||
|
||||
private final Label lastScoringUpdateTime = new Label();
|
||||
private final Label lastScoringUpdateText = new Label();
|
||||
protected final Widget lastScoringUpdatePanel;
|
||||
protected final Label lastScoringComment = new Label();
|
||||
protected final Label scoringScheme = new Label();
|
||||
protected final BusyIndicator busyIndicator = new SimpleBusyIndicator(false, 0.9f, RESOURCES.busyIndicatorCircleInverted());
|
||||
@@ -31,7 +32,7 @@ public abstract class AbstractLeaderboardFullscreenViewer<T extends Widget> exte
|
||||
showLogo();
|
||||
showBorder();
|
||||
addToolbarBusyIndicator(busyIndicator);
|
||||
addToolbarInfo(createPanel(lastScoringUpdateText, lastScoringUpdateTime));
|
||||
addToolbarInfo(lastScoringUpdatePanel = createPanel(lastScoringUpdateText, lastScoringUpdateTime));
|
||||
addToolbarAction(autoRefreshControl);
|
||||
addToolbarAction(settingsControl);
|
||||
}
|
||||
@@ -56,13 +57,14 @@ public abstract class AbstractLeaderboardFullscreenViewer<T extends Widget> exte
|
||||
|
||||
protected Widget createPanel(Widget... contentWidgets) {
|
||||
FlowPanel panel = new FlowPanel();
|
||||
for (Widget widget : contentWidgets)
|
||||
for (Widget widget : contentWidgets) {
|
||||
panel.add(widget);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeaderboardPanel(T leaderboardPanel) {
|
||||
public void setLeaderboardPanel(Widget leaderboardPanel) {
|
||||
showContent(leaderboardPanel);
|
||||
}
|
||||
|
||||
|
||||
+12
-3
@@ -83,7 +83,9 @@
|
||||
.regattaleaderboard_meta_update {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
vertical-align: top;
|
||||
}
|
||||
.regattaleaderboard_meta_liveraces_button,
|
||||
.regattaleaderboard_meta_reload,
|
||||
.regattaleaderboard_meta_settings_button,
|
||||
.regattaleaderboard_meta_fullscreen_button {
|
||||
@@ -109,6 +111,7 @@
|
||||
.regattaleaderboard_meta_busyindicator {
|
||||
display: inline-block;
|
||||
margin: 0 0.666666666666667em;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.regattaleaderboard_meta_liverace {
|
||||
@@ -127,6 +130,11 @@
|
||||
.regattaleaderboard_meta_settings {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.regattaleaderboard_meta_settings * {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.regattaleaderboard_meta_settings_button {
|
||||
background-color: #9f9f9f;
|
||||
@@ -228,9 +236,6 @@
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.marginFix {
|
||||
margin-top:0.5rem;
|
||||
}
|
||||
.letItScroll {
|
||||
overflow-x: auto;
|
||||
margin: 10px 0px;
|
||||
@@ -239,3 +244,7 @@
|
||||
background-color: #9f9f9f;
|
||||
background-image: url("images/home/fullscreen.svg");
|
||||
}
|
||||
.regattaleaderboard_meta_liveraces_button {
|
||||
background-color: #9f9f9f;
|
||||
background-image: url("images/home/liveraces.svg");
|
||||
}
|
||||
+1
-1
@@ -64,9 +64,9 @@ public interface EventRegattaLeaderboardResources extends ClientBundle {
|
||||
String leaderboardcompetitor_item_table();
|
||||
String leaderboardcompetitor_itemcenter_table();
|
||||
String leaderboardcompetitorshort();
|
||||
String marginFix();
|
||||
String letItScroll();
|
||||
|
||||
String regattaleaderboard_meta_fullscreen_button();
|
||||
String regattaleaderboard_meta_liveraces_button();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -6,9 +6,9 @@ import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
|
||||
public interface LeaderboardDelegate<T extends Widget> {
|
||||
public interface LeaderboardDelegate {
|
||||
|
||||
void setLeaderboardPanel(T leaderboardPanel);
|
||||
void setLeaderboardPanel(Widget leaderboardPanel);
|
||||
|
||||
void setBusyState(boolean isBusy);
|
||||
|
||||
|
||||
+23
-6
@@ -20,6 +20,7 @@ import com.google.gwt.uibinder.client.UiHandler;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.FocusWidget;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
@@ -49,9 +50,10 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
|
||||
}
|
||||
|
||||
@UiField HTMLPanel oldLeaderboardPanel;
|
||||
|
||||
|
||||
@UiField Anchor settingsAnchor;
|
||||
@UiField Anchor autoRefreshAnchor;
|
||||
@UiField Anchor showLiveRacesAnchor;
|
||||
@UiField Anchor fullscreenAnchor;
|
||||
@UiField DivElement lastScoringUpdateTimeDiv;
|
||||
@UiField DivElement lastScoringUpdateTextDiv;
|
||||
@@ -65,16 +67,13 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
|
||||
private Timer autoRefreshTimer;
|
||||
private final OldLeaderboardDelegate delegate;
|
||||
|
||||
public OldLeaderboard() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public OldLeaderboard(final OldLeaderboardDelegate delegate) {
|
||||
this.leaderboardPanel = null;
|
||||
EventRegattaLeaderboardResources.INSTANCE.css().ensureInjected();
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
settingsAnchor.setTitle(StringMessages.INSTANCE.settings());
|
||||
autoRefreshAnchor.setTitle(StringMessages.INSTANCE.refresh());
|
||||
showLiveRacesAnchor.setTitle(StringMessages.INSTANCE.showLiveNow());
|
||||
fullscreenAnchor.setTitle(StringMessages.INSTANCE.openFullscreenView());
|
||||
this.delegate = delegate;
|
||||
this.setupFullscreenDelegate();
|
||||
@@ -206,6 +205,24 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the {@link OldLeaderboard leaderboard}'s control to show live races.
|
||||
*
|
||||
* @return the {@link FocusWidget control} for showing live races.
|
||||
*/
|
||||
public FocusWidget getShowLiveRacesControl() {
|
||||
return showLiveRacesAnchor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the {@link OldLeaderboard leaderboard}'s control for fullscreen mode.
|
||||
*
|
||||
* @return the {@link FocusWidget control} for fullscreen mode.
|
||||
*/
|
||||
public FocusWidget getFullscreenControl() {
|
||||
return fullscreenAnchor;
|
||||
}
|
||||
|
||||
public void setLeaderboard(LeaderboardPanel leaderboardPanel, final Timer timer) {
|
||||
this.autoRefreshTimer = timer;
|
||||
this.leaderboardPanel = leaderboardPanel;
|
||||
@@ -274,7 +291,7 @@ public class OldLeaderboard extends Composite implements BusyStateChangeListener
|
||||
}
|
||||
}
|
||||
|
||||
public interface OldLeaderboardDelegate extends LeaderboardDelegate<LeaderboardPanel> {
|
||||
public interface OldLeaderboardDelegate extends LeaderboardDelegate {
|
||||
Element getHasLiveRaceElement();
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -4,7 +4,7 @@
|
||||
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages"/>
|
||||
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
|
||||
<ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.old.EventRegattaLeaderboardResources" />
|
||||
<g:HTMLPanel styleName=" {local_res.css.marginFix}">
|
||||
<g:HTMLPanel styleName="{res.mainCss.spacermargintopsmall}">
|
||||
<div class="{res.mediaCss.grid}">
|
||||
<div class="{res.mediaCss.small6} {res.mediaCss.column} {local_res.css.regattaleaderboard_meta_scoring}">
|
||||
<div ui:field="lastScoringCommentDiv" class="{local_res.css.regattaleaderboard_meta_scoring_comment}"></div>
|
||||
@@ -17,9 +17,10 @@
|
||||
<div ui:field="lastScoringUpdateTextDiv" class="{local_res.css.regattaleaderboard_meta_update_text}"></div>
|
||||
<div ui:field="lastScoringUpdateTimeDiv" class="{local_res.css.regattaleaderboard_meta_update_timestamp}"></div>
|
||||
</div>
|
||||
<g:Anchor ui:field="autoRefreshAnchor" href="javascript:;" styleName="{res.mainCss.button} {local_res.css.regattaleaderboard_meta_reload}"></g:Anchor>
|
||||
<g:Anchor ui:field="settingsAnchor" href="javascript:;" styleName="{res.mainCss.button} {local_res.css.regattaleaderboard_meta_settings_button}"></g:Anchor>
|
||||
<g:Anchor ui:field="fullscreenAnchor" href="javascript:;" styleName="{res.mainCss.button} {local_res.css.regattaleaderboard_meta_fullscreen_button}"></g:Anchor>
|
||||
<g:Anchor ui:field="autoRefreshAnchor" href="javascript:;" styleName="{res.mainCss.button} {local_res.css.regattaleaderboard_meta_reload}" />
|
||||
<g:Anchor ui:field="settingsAnchor" href="javascript:;" styleName="{res.mainCss.button} {local_res.css.regattaleaderboard_meta_settings_button}" />
|
||||
<g:Anchor ui:field="showLiveRacesAnchor" href="javascript:;" styleName="{res.mainCss.button} {local_res.css.regattaleaderboard_meta_liveraces_button}" />
|
||||
<g:Anchor ui:field="fullscreenAnchor" href="javascript:;" styleName="{res.mainCss.button} {local_res.css.regattaleaderboard_meta_fullscreen_button}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+89
-6
@@ -1,23 +1,106 @@
|
||||
package com.sap.sailing.gwt.home.desktop.partials.old.leaderboard;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
import com.google.gwt.uibinder.client.UiField;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Image;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.old.AbstractLeaderboardFullscreenViewer;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.old.leaderboard.OldLeaderboard.OldLeaderboardDelegate;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
|
||||
public class OldLeaderboardDelegateFullscreenViewer extends AbstractLeaderboardFullscreenViewer<LeaderboardPanel>
|
||||
public class OldLeaderboardDelegateFullscreenViewer extends AbstractLeaderboardFullscreenViewer
|
||||
implements OldLeaderboardDelegate {
|
||||
|
||||
|
||||
interface OldLeaderboardDelegateFullscreenViewerUiBinder
|
||||
extends UiBinder<Widget, OldLeaderboardDelegateFullscreenViewer.ContentLayout> {
|
||||
}
|
||||
|
||||
private static OldLeaderboardDelegateFullscreenViewerUiBinder uiBinder = GWT
|
||||
.create(OldLeaderboardDelegateFullscreenViewerUiBinder.class);
|
||||
|
||||
private final Image showLiveRacesControl = new Image("images/home/liveraces.svg");
|
||||
private final Label hasLiveRace = new Label();
|
||||
|
||||
private final Widget headerWidget;
|
||||
private final ContentLayout contentLayout = new ContentLayout();
|
||||
private final Widget content = uiBinder.createAndBindUi(this.contentLayout);
|
||||
|
||||
class ContentLayout {
|
||||
@UiField SimplePanel liveRacesContainerUi, leaderboardContainerUi;
|
||||
@UiField FlowPanel subHeaderUi, subHeaderContentUi, subHeaderToolbarUi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link OldLeaderboardDelegateFullscreenViewer} instance including live races information.
|
||||
*
|
||||
* @see #OldLeaderboardDelegateFullscreenViewer(boolean)
|
||||
*/
|
||||
public OldLeaderboardDelegateFullscreenViewer() {
|
||||
setHeaderWidget(createPanel(lastScoringComment, hasLiveRace, scoringScheme));
|
||||
this(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link OldLeaderboardDelegateFullscreenViewer} instance optionally including live races information
|
||||
* which expand when they are selected and available.
|
||||
*
|
||||
* @param includeLiveRaces
|
||||
* <code>true</code> to include live races information, <code>false</code> to not include
|
||||
*/
|
||||
public OldLeaderboardDelegateFullscreenViewer(boolean includeLiveRaces) {
|
||||
setHeaderWidget(headerWidget = createPanel(lastScoringComment, hasLiveRace, scoringScheme));
|
||||
if (includeLiveRaces) {
|
||||
addToolbarAction(showLiveRacesControl);
|
||||
} else {
|
||||
contentLayout.liveRacesContainerUi.removeFromParent();
|
||||
contentLayout.subHeaderUi.removeFromParent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShow() {
|
||||
super.onShow();
|
||||
getContentWidget().getElement().getFirstChildElement().getStyle().clearMarginTop();
|
||||
}
|
||||
|
||||
public void setLiveRacesPanel(Widget liveRaces) {
|
||||
contentLayout.liveRacesContainerUi.setWidget(liveRaces);
|
||||
}
|
||||
|
||||
public Image getShowLiveRacesControl() {
|
||||
return showLiveRacesControl;
|
||||
}
|
||||
|
||||
public void setShowLiveRaces(boolean show) {
|
||||
if (show) {
|
||||
contentLayout.subHeaderContentUi.add(headerWidget);
|
||||
contentLayout.subHeaderToolbarUi.add(busyIndicator);
|
||||
contentLayout.subHeaderToolbarUi.add(lastScoringUpdatePanel);
|
||||
contentLayout.subHeaderToolbarUi.add(autoRefreshControl);
|
||||
contentLayout.subHeaderToolbarUi.add(settingsControl);
|
||||
} else {
|
||||
setHeaderWidget(headerWidget);
|
||||
addToolbarBusyIndicator(busyIndicator);
|
||||
addToolbarInfo(lastScoringUpdatePanel);
|
||||
addToolbarAction(autoRefreshControl);
|
||||
addToolbarAction(settingsControl);
|
||||
addToolbarAction(showLiveRacesControl);
|
||||
}
|
||||
contentLayout.liveRacesContainerUi.setVisible(show);
|
||||
contentLayout.subHeaderUi.setVisible(show);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeaderboardPanel(Widget leaderboardPanel) {
|
||||
contentLayout.leaderboardContainerUi.setWidget(leaderboardPanel);
|
||||
showContent(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Element getHasLiveRaceElement() {
|
||||
return hasLiveRace.getElement();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
|
||||
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
|
||||
<ui:style>
|
||||
.liveRacesContainer {
|
||||
margin-top: .5em;
|
||||
}
|
||||
.subHeader {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
color: #ffffff;
|
||||
}
|
||||
.subHeaderContent {
|
||||
padding: .5em .1em;
|
||||
}
|
||||
.subHeaderToolbar {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
.leaderboardContainer {
|
||||
margin-top: -10px;
|
||||
}
|
||||
</ui:style>
|
||||
<g:FlowPanel>
|
||||
<g:SimplePanel ui:field="liveRacesContainerUi" addStyleNames="{style.liveRacesContainer}"/>
|
||||
<g:FlowPanel ui:field="subHeaderUi" addStyleNames="{style.subHeader}">
|
||||
<g:FlowPanel ui:field="subHeaderToolbarUi" addStyleNames="{style.subHeaderToolbar}"/>
|
||||
<g:FlowPanel ui:field="subHeaderContentUi" addStyleNames="{style.subHeaderContent}"/>
|
||||
</g:FlowPanel>
|
||||
<g:SimplePanel ui:field="leaderboardContainerUi" addStyleNames="{style.leaderboardContainer}"/>
|
||||
</g:FlowPanel>
|
||||
</ui:UiBinder>
|
||||
+1
-1
@@ -240,6 +240,6 @@ public class OldMultiLeaderboard extends Composite implements SelectedLeaderboar
|
||||
lastSelectedLeaderboardPanel = selectedLeaderboard;
|
||||
}
|
||||
|
||||
public interface OldMultiLeaderboardDelegate extends LeaderboardDelegate<MultiLeaderboardProxyPanel>{
|
||||
public interface OldMultiLeaderboardDelegate extends LeaderboardDelegate {
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages"/>
|
||||
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
|
||||
<ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.old.EventRegattaLeaderboardResources" />
|
||||
<g:HTMLPanel styleName=" {local_res.css.marginFix}">
|
||||
<g:HTMLPanel styleName="{res.mainCss.spacermargintopsmall}">
|
||||
<div class="{res.mediaCss.grid}">
|
||||
<div class="{res.mediaCss.small6} {res.mediaCss.column} {local_res.css.regattaleaderboard_meta_scoring}">
|
||||
<div ui:field="lastScoringCommentDiv" class="{local_res.css.regattaleaderboard_meta_scoring_comment}"></div>
|
||||
|
||||
+1
-2
@@ -2,10 +2,9 @@ package com.sap.sailing.gwt.home.desktop.partials.old.multileaderboard;
|
||||
|
||||
import com.sap.sailing.gwt.home.desktop.partials.old.AbstractLeaderboardFullscreenViewer;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.old.multileaderboard.OldMultiLeaderboard.OldMultiLeaderboardDelegate;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.MultiLeaderboardProxyPanel;
|
||||
|
||||
public class OldMultiLeaderboardDelegateFullscreenViewer extends
|
||||
AbstractLeaderboardFullscreenViewer<MultiLeaderboardProxyPanel> implements OldMultiLeaderboardDelegate {
|
||||
AbstractLeaderboardFullscreenViewer implements OldMultiLeaderboardDelegate {
|
||||
|
||||
public OldMultiLeaderboardDelegateFullscreenViewer() {
|
||||
setHeaderWidget(createPanel(lastScoringComment, scoringScheme));
|
||||
|
||||
+9
-3
@@ -57,9 +57,11 @@ public abstract class AbstractRaceList<T extends RaceMetadataDTO<? extends Abstr
|
||||
protected void setTableData(Collection<T> data) {
|
||||
this.ensureInitTableColumns();
|
||||
this.columnSet.updateColumnVisibilities();
|
||||
List<T> dataList = new ArrayList<>(data);
|
||||
for (int i = 0; i < this.cellTable.getColumnCount(); i++) {
|
||||
SortableRaceListColumn<T, ?> column = (SortableRaceListColumn<T, ?>) this.cellTable.getColumn(i);
|
||||
column.updateHeaderAndCellStyles();
|
||||
column.setRacesInNaturalOrder(dataList);
|
||||
}
|
||||
this.cellTable.setPageSize(data.size());
|
||||
this.cellTable.setList(data);
|
||||
@@ -90,8 +92,8 @@ public abstract class AbstractRaceList<T extends RaceMetadataDTO<? extends Abstr
|
||||
ColumnSortList sortList = this.cellTable.getColumnSortList();
|
||||
List<ColumnSortInfo> oldSortInfos;
|
||||
if (sortList.size() == 0) {
|
||||
boolean ascending = startTimeColumn.getPreferredSortingOrder().isAscending();
|
||||
oldSortInfos = Collections.singletonList(new ColumnSortInfo(startTimeColumn, ascending));
|
||||
boolean ascending = getDefaultSortColumn().getPreferredSortingOrder().isAscending();
|
||||
oldSortInfos = Collections.singletonList(new ColumnSortInfo(getDefaultSortColumn(), ascending));
|
||||
} else {
|
||||
oldSortInfos = new ArrayList<ColumnSortList.ColumnSortInfo>(sortList.size());
|
||||
for (int i = sortList.size() - 1; i >= 0; i--) {
|
||||
@@ -104,6 +106,10 @@ public abstract class AbstractRaceList<T extends RaceMetadataDTO<? extends Abstr
|
||||
}
|
||||
}
|
||||
|
||||
protected SortableRaceListColumn<T, ?> getDefaultSortColumn() {
|
||||
return raceNameColumn;
|
||||
}
|
||||
|
||||
protected void add(SortableRaceListColumn<T, ?> column) {
|
||||
Header<?> header = column.getHeader();
|
||||
boolean ascending = column.getPreferredSortingOrder().isAscending();
|
||||
@@ -133,7 +139,7 @@ public abstract class AbstractRaceList<T extends RaceMetadataDTO<? extends Abstr
|
||||
}
|
||||
return filterableValues;
|
||||
}
|
||||
|
||||
|
||||
public abstract boolean hasWind();
|
||||
|
||||
public abstract boolean hasVideos();
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.sap.sailing.gwt.home.desktop.partials.racelist;
|
||||
|
||||
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
|
||||
|
||||
public class DefaultRaceListColumnComparator<T extends RaceMetadataDTO<?>> extends RaceListColumnComparator<T, String> {
|
||||
@Override
|
||||
public int compare(T o1, T o2) {
|
||||
return compareByNaturalOrder(o1, o2);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is unused by the overridden {@link #compare(RaceMetadataDTO, RaceMetadataDTO)} implementation; still
|
||||
* it needs an implementation because the super class declares it as abstract.
|
||||
*/
|
||||
@Override
|
||||
public String getValue(T object) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.sap.sailing.gwt.home.desktop.partials.racelist;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
|
||||
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* Special Comparator with following precedences:
|
||||
* <ol>
|
||||
* <li>The value of the column</li>
|
||||
* <li>Natural order of races</li>
|
||||
* <li>Race names</li>
|
||||
* </ol>
|
||||
*
|
||||
* Following constrains apply to the sorting order:
|
||||
* <ul>
|
||||
* <li>If a sorting value is null/empty, it gets always sorted below the non-empty values</li>
|
||||
* <li>The presendences 2) und 3) for sorting between empty values still apply</li>
|
||||
* <li>Ascending and descending order is also considered for empty values</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Vladislav Chumak
|
||||
*
|
||||
* @param <R>
|
||||
* DTO type
|
||||
* @param <S>
|
||||
* type of the value to be sorted
|
||||
*/
|
||||
public abstract class RaceListColumnComparator<R extends RaceMetadataDTO<?>, S extends Comparable<S>>
|
||||
extends InvertibleComparatorAdapter<R> {
|
||||
|
||||
private List<R> racesInNaturalOrder = null;
|
||||
private final Map<R, Integer> positionInNaturalOrder = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public int compare(R o1, R o2) {
|
||||
int compareResult;
|
||||
if (getValue(o1) == getValue(o2)) {
|
||||
compareResult = 0;
|
||||
} else if (getValue(o1) == null) {
|
||||
return isAscending() ? 1 : -1;
|
||||
} else if (getValue(o2) == null) {
|
||||
return isAscending() ? -1 : 1;
|
||||
} else {
|
||||
compareResult = getValue(o1).compareTo(getValue(o2));
|
||||
}
|
||||
if (compareResult == 0) {
|
||||
compareResult = compareByNaturalOrder(o1, o2);
|
||||
}
|
||||
if (compareResult == 0) {
|
||||
// RaceListRaceDTO implements Comparable which considers race names for its comparison
|
||||
compareResult = o1.compareTo(o2);
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
|
||||
protected int compareByNaturalOrder(R o1, R o2) {
|
||||
final int compareResult;
|
||||
if (racesInNaturalOrder != null) {
|
||||
getAndCacheIndexInNaturalOrder(o1);
|
||||
compareResult = Util.compareToWithNull(getAndCacheIndexInNaturalOrder(o1), getAndCacheIndexInNaturalOrder(o2), /* nullIsLess */ !isAscending());
|
||||
} else {
|
||||
compareResult = 0;
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
|
||||
private Integer getAndCacheIndexInNaturalOrder(R o) {
|
||||
Integer i = positionInNaturalOrder.get(o);
|
||||
if (i == null) {
|
||||
i = racesInNaturalOrder.indexOf(o);
|
||||
positionInNaturalOrder.put(o, i);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public abstract S getValue(R object);
|
||||
|
||||
/**
|
||||
* Sets the comparator to use the order of the passed {@link List} as second comparison criteria.
|
||||
*
|
||||
* @param racesInNaturalOrder
|
||||
* The list with items which order should be considered as second comparison criteria
|
||||
*/
|
||||
public void setRacesInNaturalOrder(List<R> racesInNaturalOrder) {
|
||||
this.racesInNaturalOrder = racesInNaturalOrder;
|
||||
positionInNaturalOrder.clear();
|
||||
}
|
||||
}
|
||||
+46
-59
@@ -1,6 +1,5 @@
|
||||
package com.sap.sailing.gwt.home.desktop.partials.racelist;
|
||||
|
||||
import static com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceTrackingState.TRACKED_VALID_DATA;
|
||||
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.shortTimeFormatter;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -21,9 +20,7 @@ import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
import com.google.gwt.safehtml.shared.SafeUri;
|
||||
import com.google.gwt.safehtml.shared.UriUtils;
|
||||
import com.google.gwt.user.cellview.client.SafeHtmlHeader;
|
||||
import com.sap.sailing.domain.common.InvertibleComparator;
|
||||
import com.sap.sailing.domain.common.SortingOrder;
|
||||
import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
|
||||
import com.sap.sailing.gwt.home.communication.event.LiveRaceDTO;
|
||||
import com.sap.sailing.gwt.home.communication.event.RaceListRaceDTO;
|
||||
import com.sap.sailing.gwt.home.communication.event.SimpleCompetitorDTO;
|
||||
@@ -36,17 +33,15 @@ import com.sap.sailing.gwt.home.communication.race.wind.AbstractWindDTO;
|
||||
import com.sap.sailing.gwt.home.communication.race.wind.WindStatisticsDTO;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.racelist.RaceListResources.LocalCss;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPadCell;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPadController;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.EventView;
|
||||
import com.sap.sailing.gwt.home.shared.utils.HomeSailingFlagsBuilder;
|
||||
import com.sap.sailing.gwt.regattaoverview.client.FlagsMeaningExplanator;
|
||||
import com.sap.sailing.gwt.ui.client.FlagImageResolver;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
|
||||
import com.sap.sailing.gwt.ui.shared.util.NullSafeComparableComparator;
|
||||
import com.sap.sailing.gwt.ui.shared.util.NullSafeComparatorWrapper;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.util.NaturalComparator;
|
||||
|
||||
public class RaceListColumnFactory {
|
||||
|
||||
@@ -81,8 +76,7 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
}
|
||||
};
|
||||
InvertibleComparator<T> comparator = null;
|
||||
return new SortableRaceListColumn<T, FleetMetadataDTO>("", cell, comparator) {
|
||||
return new SortableRaceListColumn<T, FleetMetadataDTO>("", cell, null) {
|
||||
@Override
|
||||
public String getColumnStyle() {
|
||||
return CSS.race_fleetcorner();
|
||||
@@ -97,9 +91,9 @@ public class RaceListColumnFactory {
|
||||
|
||||
public static <T extends RaceMetadataDTO<?>> SortableRaceListColumn<T, String> getRegattaNameColumn() {
|
||||
Cell<String> cell = new TextCell();
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, String>(new NaturalComparator(false)) {
|
||||
RaceListColumnComparator<T, String> comparator = new RaceListColumnComparator<T, String>() {
|
||||
@Override
|
||||
protected String getComparisonValue(T object) {
|
||||
public String getValue(T object) {
|
||||
return object.getRegattaDisplayName();
|
||||
}
|
||||
};
|
||||
@@ -123,13 +117,8 @@ public class RaceListColumnFactory {
|
||||
|
||||
public static <T extends RaceMetadataDTO<?>> SortableRaceListColumn<T, String> getRaceNameColumn() {
|
||||
Cell<String> cell = new TextCell();
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, String>(new NaturalComparator(false)) {
|
||||
@Override
|
||||
protected String getComparisonValue(T object) {
|
||||
return object.getRaceName();
|
||||
}
|
||||
};
|
||||
return new SortableRaceListColumn<T, String>(I18N.race(), cell, comparator) {
|
||||
DefaultRaceListColumnComparator<T> comparator = new DefaultRaceListColumnComparator<>();
|
||||
return new SortableRaceListColumn<T, String>(I18N.race(), cell, comparator, SortingOrder.DESCENDING) {
|
||||
@Override
|
||||
public String getHeaderStyle() {
|
||||
return CSS.raceslist_head_item();
|
||||
@@ -149,9 +138,9 @@ public class RaceListColumnFactory {
|
||||
|
||||
public static <T extends RaceMetadataDTO<?>> SortableRaceListColumn<T, String> getFleetNameColumn() {
|
||||
Cell<String> cell = new TextCell();
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, String>(new NaturalComparator(false)) {
|
||||
RaceListColumnComparator<T, String> comparator = new RaceListColumnComparator<T, String>() {
|
||||
@Override
|
||||
protected String getComparisonValue(T object) {
|
||||
public String getValue(T object) {
|
||||
return object.getFleet() != null ? object.getFleet().getFleetName() : null;
|
||||
}
|
||||
};
|
||||
@@ -174,9 +163,10 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
|
||||
public static <T extends RaceMetadataDTO<?>> SortableRaceListStartTimeColumn<T> getStartTimeColumn() {
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Date>(new NullSafeComparableComparator<Date>(true)) {
|
||||
RaceListColumnComparator<T, Date> comparator = new RaceListColumnComparator<T, Date>() {
|
||||
|
||||
@Override
|
||||
protected Date getComparisonValue(T object) {
|
||||
public Date getValue(T object) {
|
||||
return object.getStart();
|
||||
}
|
||||
};
|
||||
@@ -185,9 +175,10 @@ public class RaceListColumnFactory {
|
||||
|
||||
public static <T extends RaceListRaceDTO> SortableRaceListColumn<T, String> getDurationColumn() {
|
||||
Cell<String> cell = new TextCell();
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Duration>(new NullSafeComparableComparator<Duration>(false)) {
|
||||
RaceListColumnComparator<T, Duration> comparator = new RaceListColumnComparator<T, Duration>() {
|
||||
|
||||
@Override
|
||||
protected Duration getComparisonValue(T object) {
|
||||
public Duration getValue(T object) {
|
||||
return object.getDuration();
|
||||
}
|
||||
};
|
||||
@@ -219,8 +210,7 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
}
|
||||
};
|
||||
InvertibleComparator<T> comparator = null;
|
||||
return new SortableRaceListColumn<T, FlagStateDTO>(I18N.flags(), cell, comparator) {
|
||||
return new SortableRaceListColumn<T, FlagStateDTO>(I18N.flags(), cell, null) {
|
||||
@Override
|
||||
public String getHeaderStyle() {
|
||||
return CSS.raceslist_head_item();
|
||||
@@ -239,9 +229,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
|
||||
public static <T extends LiveRaceDTO> SortableRaceListColumn<T, String> getWindSpeedColumn() {
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Double>( new NullSafeComparableComparator<Double>()) {
|
||||
RaceListColumnComparator<T, Double> comparator = new RaceListColumnComparator<T, Double>() {
|
||||
@Override
|
||||
protected Double getComparisonValue(T object) {
|
||||
public Double getValue(T object) {
|
||||
return object.getWind() != null ? object.getWind().getTrueWindSpeedInKnots() : null;
|
||||
}
|
||||
};
|
||||
@@ -254,9 +244,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
|
||||
public static <T extends RaceListRaceDTO> SortableRaceListColumn<T, String> getWindRangeColumn() {
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Double>(new NullSafeComparableComparator<Double>()) {
|
||||
RaceListColumnComparator<T, Double> comparator = new RaceListColumnComparator<T, Double>() {
|
||||
@Override
|
||||
protected Double getComparisonValue(T object) {
|
||||
public Double getValue(T object) {
|
||||
WindStatisticsDTO wind = object.getWind();
|
||||
return wind != null ? (wind.getTrueUpperboundWindInKnots() + wind .getTrueLowerboundWindInKnots()) : null;
|
||||
}
|
||||
@@ -288,9 +278,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
}
|
||||
};
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Double>( new NullSafeComparableComparator<Double>()) {
|
||||
RaceListColumnComparator<T, Double> comparator = new RaceListColumnComparator<T, Double>() {
|
||||
@Override
|
||||
protected Double getComparisonValue(T object) {
|
||||
public Double getValue(T object) {
|
||||
return object.getWind() != null ? object.getWind().getTrueWindFromDeg() : null;
|
||||
}
|
||||
};
|
||||
@@ -314,9 +304,9 @@ public class RaceListColumnFactory {
|
||||
|
||||
public static <T extends RaceMetadataDTO<?>> SortableRaceListColumn<T, String> getCourseAreaColumn() {
|
||||
Cell<String> cell = new TextCell();
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, String>(new NaturalComparator(false)) {
|
||||
RaceListColumnComparator<T, String> comparator = new RaceListColumnComparator<T, String>() {
|
||||
@Override
|
||||
protected String getComparisonValue(T object) {
|
||||
public String getValue(T object) {
|
||||
return object.getCourseArea();
|
||||
}
|
||||
};
|
||||
@@ -340,9 +330,9 @@ public class RaceListColumnFactory {
|
||||
|
||||
public static <T extends RaceMetadataDTO<?>> SortableRaceListColumn<T, String> getCourseColumn() {
|
||||
Cell<String> cell = new TextCell();
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, String>(new NaturalComparator(false)) {
|
||||
RaceListColumnComparator<T, String> comparator = new RaceListColumnComparator<T, String>() {
|
||||
@Override
|
||||
protected String getComparisonValue(T object) {
|
||||
public String getValue(T object) {
|
||||
return object.getCourse();
|
||||
}
|
||||
};
|
||||
@@ -365,9 +355,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
|
||||
public static <T extends RaceListRaceDTO> SortableRaceListColumn<T, Number> getWindSourcesCountColumn() {
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Integer>(new NullSafeComparableComparator<Integer>(false)) {
|
||||
RaceListColumnComparator<T, Integer> comparator = new RaceListColumnComparator<T, Integer>() {
|
||||
@Override
|
||||
protected Integer getComparisonValue(T object) {
|
||||
public Integer getValue(T object) {
|
||||
return object.getWindSourcesCount();
|
||||
}
|
||||
};
|
||||
@@ -380,9 +370,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
|
||||
public static <T extends RaceListRaceDTO> SortableRaceListColumn<T, Number> getVideoCountColumn() {
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Integer>(new NullSafeComparableComparator<Integer>(false)) {
|
||||
RaceListColumnComparator<T, Integer> comparator = new RaceListColumnComparator<T, Integer>() {
|
||||
@Override
|
||||
protected Integer getComparisonValue(T object) {
|
||||
public Integer getValue(T object) {
|
||||
return object.getVideoCount();
|
||||
}
|
||||
};
|
||||
@@ -395,9 +385,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
|
||||
public static <T extends RaceListRaceDTO> SortableRaceListColumn<T, Number> getAudioCountColumn() {
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, Integer>(new NullSafeComparableComparator<Integer>(false)) {
|
||||
RaceListColumnComparator<T, Integer> comparator = new RaceListColumnComparator<T, Integer>() {
|
||||
@Override
|
||||
protected Integer getComparisonValue(T object) {
|
||||
public Integer getValue(T object) {
|
||||
return object.getAudioCount();
|
||||
}
|
||||
};
|
||||
@@ -428,9 +418,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
}
|
||||
};
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, RaceViewState>(new NullSafeComparableComparator<RaceViewState>()) {
|
||||
RaceListColumnComparator<T, RaceViewState> comparator = new RaceListColumnComparator<T, RaceViewState>() {
|
||||
@Override
|
||||
protected RaceViewState getComparisonValue(T object) {
|
||||
public RaceViewState getValue(T object) {
|
||||
return object.getViewState();
|
||||
}
|
||||
};
|
||||
@@ -463,10 +453,9 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
}
|
||||
};
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorWrapper<T, String>(
|
||||
new NullSafeComparatorWrapper<String>(new NaturalComparator(false), false)) {
|
||||
RaceListColumnComparator<T, String> comparator = new RaceListColumnComparator<T, String>() {
|
||||
@Override
|
||||
protected String getComparisonValue(T object) {
|
||||
public String getValue(T object) {
|
||||
return object.getWinner() != null ? object.getWinner().getName() : null;
|
||||
}
|
||||
};
|
||||
@@ -490,21 +479,19 @@ public class RaceListColumnFactory {
|
||||
|
||||
public static <T extends RaceMetadataDTO<?>> SortableRaceListColumn<T, T> getRaceViewerButtonColumn(
|
||||
final EventView.Presenter presenter, final boolean showNotTracked) {
|
||||
InvertibleComparator<T> comparator = new InvertibleComparatorAdapter<T>() {
|
||||
final RaceviewerLaunchPadController lpPresenter = new RaceviewerLaunchPadController(presenter::getRaceViewerURL);
|
||||
final RaceviewerLaunchPadCell<T> lpadCell = new RaceviewerLaunchPadCell<T>(lpPresenter, showNotTracked);
|
||||
DefaultRaceListColumnComparator<T> comparator = new DefaultRaceListColumnComparator<T>() {
|
||||
@Override
|
||||
public int compare(T o1, T o2) {
|
||||
if (o1.getTrackingState() == TRACKED_VALID_DATA && o2.getTrackingState() == TRACKED_VALID_DATA) {
|
||||
if (o1.getViewState() == RaceViewState.FINISHED && o2.getViewState() != RaceViewState.FINISHED) {
|
||||
return 1;
|
||||
}
|
||||
if (o1.getViewState() != RaceViewState.FINISHED && o2.getViewState() == RaceViewState.FINISHED) {
|
||||
return -1;
|
||||
}
|
||||
int compareResult = lpPresenter.getRenderingStyle(o1).compareTo(lpPresenter.getRenderingStyle(o2));
|
||||
if (compareResult == 0) {
|
||||
compareResult = super.compare(o1, o2);
|
||||
}
|
||||
return -o1.getTrackingState().compareTo(o2.getTrackingState());
|
||||
return compareResult;
|
||||
}
|
||||
};
|
||||
return new SortableRaceListColumn<T, T>("", new RaceviewerLaunchPadCell<T>(presenter, showNotTracked), comparator) {
|
||||
return new SortableRaceListColumn<T, T>("", lpadCell, comparator) {
|
||||
@Override
|
||||
public String getHeaderStyle() {
|
||||
return getStyleNamesString(CSS.raceslist_head_item(), CSS.raceslist_head_itembutton());
|
||||
@@ -523,7 +510,7 @@ public class RaceListColumnFactory {
|
||||
}
|
||||
|
||||
public static class SortableRaceListStartTimeColumn<T extends RaceMetadataDTO<?>> extends SortableRaceListColumn<T, Date> {
|
||||
protected SortableRaceListStartTimeColumn(InvertibleComparator<T> comparator) {
|
||||
protected SortableRaceListStartTimeColumn(RaceListColumnComparator<T, ?> comparator) {
|
||||
super(I18N.start(), new StartTimeCell(), comparator, SortingOrder.DESCENDING);
|
||||
}
|
||||
|
||||
@@ -560,7 +547,7 @@ public class RaceListColumnFactory {
|
||||
|
||||
|
||||
private static abstract class WindSpeedOrRangeColumn<T extends RaceMetadataDTO<?>> extends SortableRaceListColumn<T, String> {
|
||||
protected WindSpeedOrRangeColumn(InvertibleComparator<T> comparator) {
|
||||
protected WindSpeedOrRangeColumn(RaceListColumnComparator<T, ?> comparator) {
|
||||
super(I18N.wind(), new TextCell(), comparator);
|
||||
}
|
||||
|
||||
@@ -580,7 +567,7 @@ public class RaceListColumnFactory {
|
||||
private static final SafeUri ICON_VIDEO = UriUtils.fromTrustedString("images/home/icon-video.png");
|
||||
private static final SafeUri ICON_AUDIO = UriUtils.fromTrustedString("images/home/icon-audio.png");
|
||||
|
||||
private DataCountColumn(final SafeUri imageUri, InvertibleComparator<T> comparator) {
|
||||
private DataCountColumn(final SafeUri imageUri, RaceListColumnComparator<T, ?> comparator) {
|
||||
super(new SafeHtmlHeader(TEMPLATE.imageHeader(CSS.raceslist_head_itemflag(), imageUri)), new AbstractCell<Number>() {
|
||||
@Override
|
||||
public void render(Context context, Number value, SafeHtmlBuilder sb) {
|
||||
|
||||
+16
-7
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.gwt.home.desktop.partials.racelist;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.cell.client.Cell;
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
|
||||
import com.google.gwt.user.cellview.client.Header;
|
||||
@@ -7,10 +9,11 @@ import com.google.gwt.user.cellview.client.SafeHtmlHeader;
|
||||
import com.sap.sailing.domain.common.InvertibleComparator;
|
||||
import com.sap.sailing.domain.common.SortingOrder;
|
||||
import com.sap.sailing.gwt.common.client.SharedResources;
|
||||
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
|
||||
import com.sap.sailing.gwt.ui.client.shared.controls.SortableColumn;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public abstract class SortableRaceListColumn<T, C> extends SortableColumn<T, C> {
|
||||
public abstract class SortableRaceListColumn<T extends RaceMetadataDTO<?>, C> extends SortableColumn<T, C> {
|
||||
|
||||
enum ColumnVisibility {
|
||||
ALWAYS {
|
||||
@@ -33,28 +36,34 @@ public abstract class SortableRaceListColumn<T, C> extends SortableColumn<T, C>
|
||||
}
|
||||
|
||||
private final Header<?> header;
|
||||
private final InvertibleComparator<T> comparator;
|
||||
private final RaceListColumnComparator<T, ?> comparator;
|
||||
private boolean showDetails = true;
|
||||
private ColumnVisibility columnVisibility = ColumnVisibility.ALWAYS;
|
||||
|
||||
protected SortableRaceListColumn(String headerText, Cell<C> cell, InvertibleComparator<T> comparator) {
|
||||
protected SortableRaceListColumn(String headerText, Cell<C> cell, RaceListColumnComparator<T, ?> comparator) {
|
||||
this(new WrappedTextHeader(headerText), cell, comparator);
|
||||
}
|
||||
|
||||
protected SortableRaceListColumn(String headerText, Cell<C> cell, InvertibleComparator<T> comparator, SortingOrder preferredSortingOrder) {
|
||||
protected SortableRaceListColumn(String headerText, Cell<C> cell, RaceListColumnComparator<T, ?> comparator, SortingOrder preferredSortingOrder) {
|
||||
this(new WrappedTextHeader(headerText), cell, comparator, preferredSortingOrder);
|
||||
}
|
||||
|
||||
private SortableRaceListColumn(Header<?> header, Cell<C> cell, InvertibleComparator<T> comparator) {
|
||||
private SortableRaceListColumn(Header<?> header, Cell<C> cell, RaceListColumnComparator<T, ?> comparator) {
|
||||
this(header, cell, comparator, SortingOrder.ASCENDING);
|
||||
}
|
||||
|
||||
protected SortableRaceListColumn(Header<?> header, Cell<C> cell, InvertibleComparator<T> comparator, SortingOrder preferredSortingOrder) {
|
||||
protected SortableRaceListColumn(Header<?> header, Cell<C> cell, RaceListColumnComparator<T, ?> comparator, SortingOrder preferredSortingOrder) {
|
||||
super(cell, preferredSortingOrder);
|
||||
this.header = header;
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
|
||||
public void setRacesInNaturalOrder(List<T> racesInNaturalOrder) {
|
||||
if (this.comparator != null) {
|
||||
this.comparator.setRacesInNaturalOrder(racesInNaturalOrder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final InvertibleComparator<T> getComparator() {
|
||||
return comparator;
|
||||
|
||||
+18
-52
@@ -11,15 +11,8 @@ import com.google.gwt.safehtml.shared.SafeHtml;
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
import com.google.gwt.safehtml.shared.SafeUri;
|
||||
import com.google.gwt.safehtml.shared.UriUtils;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
|
||||
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
|
||||
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.EventView;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.EventView.Presenter;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.raceboard.RaceBoardModes;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends AbstractCell<T> {
|
||||
@@ -45,33 +38,23 @@ public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends Abstr
|
||||
private final String liveStyleNames = Util.join(" ", analyzeStyleNames, local_res.css().raceviewerlaunchpadlive());
|
||||
private final String iconStyleNames = local_res.css().raceviewerlaunchpad_icon();
|
||||
|
||||
private final EventView.Presenter presenter;
|
||||
private final PopupPanel panel = new PopupPanel(true, false);
|
||||
private final RaceviewerLaunchPadController launchPadController;
|
||||
private final boolean showNotTracked;
|
||||
|
||||
public RaceviewerLaunchPadCell(Presenter presenter, boolean showNotTracked) {
|
||||
public RaceviewerLaunchPadCell(RaceviewerLaunchPadController launchPadPresenter, boolean showNotTracked) {
|
||||
super(BrowserEvents.CLICK);
|
||||
this.showNotTracked = showNotTracked;
|
||||
local_res.css().ensureInjected();
|
||||
this.presenter = presenter;
|
||||
this.launchPadController = launchPadPresenter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBrowserEvent(Context context, final Element parent, T data, NativeEvent event,
|
||||
ValueUpdater<T> valueUpdater) {
|
||||
if (!renderAsDirectLinkButton(data)) {
|
||||
if (!launchPadController.renderAsDirectLink(data)) {
|
||||
if (data.hasValidTrackingData() && BrowserEvents.CLICK.equals(event.getType())
|
||||
&& parent.getFirstChildElement().isOrHasChild(Element.as(event.getEventTarget()))) {
|
||||
panel.setWidget(createLaunchPad(parent.getFirstChildElement(), data));
|
||||
panel.setPopupPositionAndShow(new PositionCallback() {
|
||||
@Override
|
||||
public void setPosition(int offsetWidth, int offsetHeight) {
|
||||
int alignBottom = parent.getAbsoluteTop() + parent.getOffsetHeight() - offsetHeight;
|
||||
int top = (alignBottom - Window.getScrollTop() < 0 ? parent.getAbsoluteTop() - 1 : alignBottom + 1);
|
||||
panel.setPopupPosition(parent.getAbsoluteRight() + 1 - offsetWidth, top);
|
||||
panel.getElement().scrollIntoView();
|
||||
}
|
||||
});
|
||||
launchPadController.showRaceviewerLaunchPad(data, parent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -80,38 +63,21 @@ public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends Abstr
|
||||
|
||||
@Override
|
||||
public void render(Context context, T data, SafeHtmlBuilder sb) {
|
||||
if (data.hasValidTrackingData()) {
|
||||
if (renderAsDirectLinkButton(data)) {
|
||||
switch (launchPadController.getRenderingStyle(data)) {
|
||||
case WATCH_LIVE_ONLY:
|
||||
sb.append(TEMPLATE.standaloneButton(plannedStyleNames, iconStyleNames, I18N.watchLive(),
|
||||
"launch-play",
|
||||
UriUtils.fromString(presenter.getRaceViewerURL(data, RaceBoardModes.PLAYER.name()))));
|
||||
} else {
|
||||
String styleNames = data.isFinished() ? analyzeStyleNames : liveStyleNames;
|
||||
sb.append(TEMPLATE.raceviewerLaunchPad(styleNames, iconStyleNames, I18N.raceDetailsToShow()));
|
||||
}
|
||||
} else if (showNotTracked) {
|
||||
sb.append(TEMPLATE.raceNotTracked(notTrackedStyleNames, I18N.eventRegattaRaceNotTracked()));
|
||||
} else {
|
||||
sb.append(TEMPLATE.raceNotTracked(notTrackedStyleNames, ""));
|
||||
"launch-play", UriUtils.fromString(launchPadController.getDirectLinkUrl(data))));
|
||||
break;
|
||||
case WATCH_LIVE_OR_ANALYZE:
|
||||
sb.append(TEMPLATE.raceviewerLaunchPad(liveStyleNames, iconStyleNames, I18N.raceDetailsToShow()));
|
||||
break;
|
||||
case ANALYZE:
|
||||
sb.append(TEMPLATE.raceviewerLaunchPad(analyzeStyleNames, iconStyleNames, I18N.raceDetailsToShow()));
|
||||
break;
|
||||
case NOT_TRACKED:
|
||||
sb.append(TEMPLATE.raceNotTracked(notTrackedStyleNames, showNotTracked ? I18N.eventRegattaRaceNotTracked() : ""));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link RaceviewerLaunchPad} for the given data object.
|
||||
*/
|
||||
private RaceviewerLaunchPad createLaunchPad(Element button, T data) {
|
||||
RaceviewerLaunchPad launchPad = new RaceviewerLaunchPad(data, panel) {
|
||||
@Override
|
||||
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String raceBoardMode) {
|
||||
return presenter.getRaceViewerURL(data, raceBoardMode);
|
||||
}
|
||||
};
|
||||
return launchPad;
|
||||
}
|
||||
|
||||
/** Determine whether a direct link button should be/is rendered instead of a menu popup. */
|
||||
private boolean renderAsDirectLinkButton(T data) {
|
||||
return !data.isFinished() && !data.isRunning();
|
||||
}
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.google.gwt.core.client.Scheduler;
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
|
||||
import com.sap.sailing.gwt.ui.raceboard.RaceBoardModes;
|
||||
|
||||
public class RaceviewerLaunchPadController {
|
||||
|
||||
public static enum RenderingStyle {
|
||||
WATCH_LIVE_ONLY, WATCH_LIVE_OR_ANALYZE, ANALYZE, NOT_TRACKED
|
||||
}
|
||||
|
||||
private final PopupPanel panel = new PopupPanel(true, false);
|
||||
private final BiFunction<SimpleRaceMetadataDTO, String, String> raceboardUrlFactory;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RaceviewerLaunchPadController} instance with the given URL factory.
|
||||
*
|
||||
* @param raceviewerUrlFactory
|
||||
* {@link BiFunction Factory method} to get the raceboard URL for given {@link SimpleRaceMetadataDTO
|
||||
* race} and {@link RaceBoardModes raceboard mode} information
|
||||
*/
|
||||
public RaceviewerLaunchPadController(BiFunction<SimpleRaceMetadataDTO, String, String> raceboardUrlFactory) {
|
||||
this.raceboardUrlFactory = raceboardUrlFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the direct link URL for the given {@link SimpleRaceMetadataDTO race}.
|
||||
*
|
||||
* @param data
|
||||
* the {@link SimpleRaceMetadataDTO race} to get the link URL for
|
||||
* @return the direct link URL (as {@link String})
|
||||
*/
|
||||
public <T extends SimpleRaceMetadataDTO> String getDirectLinkUrl(T data) {
|
||||
return raceboardUrlFactory.apply(data, RaceBoardModes.PLAYER.name());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link RaceviewerLaunchPad} for the given {@link SimpleRaceMetadataDTO race} and {@link PopupPanel}
|
||||
* instance.
|
||||
*
|
||||
* @param data
|
||||
* the {@link SimpleRaceMetadataDTO race} to create a launch pad for
|
||||
* @param parent
|
||||
* the {@link PopupPanel} which will contain the launch pad
|
||||
* @return the newly created {@link RaceviewerLaunchPad} instance
|
||||
*/
|
||||
public <T extends SimpleRaceMetadataDTO> RaceviewerLaunchPad getRaceviewerLaunchPad(T data, PopupPanel parent) {
|
||||
return new RaceviewerLaunchPad(data, parent) {
|
||||
@Override
|
||||
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String raceBoardMode) {
|
||||
return raceboardUrlFactory.apply(data, raceBoardMode);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a {@link RaceviewerLaunchPad} for the given {@link SimpleRaceMetadataDTO race} relative to the also
|
||||
* provided element.
|
||||
*
|
||||
* @param data
|
||||
* the {@link SimpleRaceMetadataDTO race} to create a launch pad for
|
||||
* @param relTo
|
||||
* the {@link Element element} to show the launch pad relative to
|
||||
*/
|
||||
public <T extends SimpleRaceMetadataDTO> void showRaceviewerLaunchPad(T data, Element relTo) {
|
||||
relTo.scrollIntoView();
|
||||
panel.setWidget(new RaceviewerLaunchPad(data, panel) {
|
||||
@Override
|
||||
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String raceBoardMode) {
|
||||
return raceboardUrlFactory.apply(data, raceBoardMode);
|
||||
}
|
||||
});
|
||||
panel.setVisible(false);
|
||||
panel.show();
|
||||
Scheduler.get().scheduleDeferred(() -> {
|
||||
Widget panelContent = panel.getWidget();
|
||||
int alignRight = relTo.getAbsoluteLeft() + relTo.getOffsetWidth() - panelContent.getOffsetWidth();
|
||||
int left = (alignRight - Window.getScrollLeft() < 0 ? relTo.getAbsoluteLeft() - 1 : alignRight + 1);
|
||||
int alignBottom = relTo.getAbsoluteTop() + relTo.getOffsetHeight() - panelContent.getOffsetHeight();
|
||||
int top = (alignBottom - Window.getScrollTop() < 0 ? relTo.getAbsoluteTop() - 1 : alignBottom + 1);
|
||||
panel.setPopupPosition(left, top);
|
||||
panel.setVisible(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a direct link should be/is rendered for the given {@link SimpleRaceMetadataDTO race} instead of
|
||||
* a menu popup.
|
||||
*
|
||||
* @param data
|
||||
* the {@link SimpleRaceMetadataDTO race} which should be/is rendered
|
||||
* @return <code>true</code> if a direct link should be/is rendered, <code>false</code> otherwise
|
||||
*/
|
||||
public <T extends SimpleRaceMetadataDTO> boolean renderAsDirectLink(T data) {
|
||||
return !data.isRunning() && !data.isFinished();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the {@link RenderingStyle} to use for the given {@link SimpleRaceMetadataDTO race}.
|
||||
*
|
||||
* @param data
|
||||
* the {@link SimpleRaceMetadataDTO race} to get the {@link RenderingStyle} for
|
||||
* @return the calculated {@link RenderingStyle}
|
||||
*/
|
||||
public <T extends SimpleRaceMetadataDTO> RenderingStyle getRenderingStyle(T data) {
|
||||
final RenderingStyle result;
|
||||
if (data.hasValidTrackingData()) {
|
||||
if (renderAsDirectLink(data)) {
|
||||
result = RenderingStyle.WATCH_LIVE_ONLY;
|
||||
} else {
|
||||
result = data.isFinished() ? RenderingStyle.ANALYZE : RenderingStyle.WATCH_LIVE_OR_ANALYZE;
|
||||
}
|
||||
} else {
|
||||
result = RenderingStyle.NOT_TRACKED;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+4
-1
@@ -95,7 +95,10 @@
|
||||
<li>
|
||||
<ui:text from='{i18n.contentSapInSailing12}' />
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<ui:text from='{i18n.contentSapInSailing13}' />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-2
@@ -89,8 +89,7 @@ public class StandingsList extends Widget implements RefreshableWidget<GetMiniLe
|
||||
}
|
||||
if (data.getLastScoreUpdate() != null) {
|
||||
DivElement lastUpdateElement = Document.get().createDivElement();
|
||||
String lastUpdate = DateAndTimeFormatterUtil.longDateFormatter.render(data.getLastScoreUpdate()) + " "
|
||||
+ DateAndTimeFormatterUtil.formatElapsedTime(data.getLastScoreUpdate().getTime());
|
||||
String lastUpdate = DateAndTimeFormatterUtil.formatLongDateAndTimeGMT(data.getLastScoreUpdate());
|
||||
lastUpdateElement.setInnerText(StringMessages.INSTANCE.lastScoreUpdate() + ": " + lastUpdate);
|
||||
scoreInformationUi.appendChild(lastUpdateElement);
|
||||
}
|
||||
|
||||
+120
-20
@@ -1,16 +1,23 @@
|
||||
package com.sap.sailing.gwt.home.desktop.places.event.regatta.leaderboardtab;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
import com.google.gwt.uibinder.client.UiField;
|
||||
import com.google.gwt.uibinder.client.UiHandler;
|
||||
import com.google.gwt.user.client.ui.AcceptsOneWidget;
|
||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.gwt.common.client.controls.tabbar.TabView;
|
||||
import com.sap.sailing.gwt.home.communication.event.EventState;
|
||||
import com.sap.sailing.gwt.home.communication.event.GetLiveRacesForRegattaAction;
|
||||
import com.sap.sailing.gwt.home.communication.event.LiveRaceDTO;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.liveraces.LiveRacesList;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.old.leaderboard.OldLeaderboard;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.old.leaderboard.OldLeaderboardDelegateFullscreenViewer;
|
||||
import com.sap.sailing.gwt.home.desktop.places.Consumer;
|
||||
@@ -19,32 +26,40 @@ import com.sap.sailing.gwt.home.desktop.places.event.regatta.EventRegattaView.Pr
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.RegattaAnalyticsDataManager;
|
||||
import com.sap.sailing.gwt.home.desktop.places.event.regatta.SharedLeaderboardRegattaTabView;
|
||||
import com.sap.sailing.gwt.home.shared.partials.placeholder.InfoPlaceholder;
|
||||
import com.sap.sailing.gwt.home.shared.refresh.RefreshManager;
|
||||
import com.sap.sailing.gwt.home.shared.refresh.RefreshManagerWithErrorAndBusy;
|
||||
import com.sap.sailing.gwt.home.shared.refresh.RefreshableWidget;
|
||||
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings;
|
||||
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateProvider;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
import com.sap.sse.gwt.client.shared.perspective.DefaultOnSettingsLoadedCallback;
|
||||
import com.sap.sse.gwt.dispatch.shared.commands.CollectionResult;
|
||||
|
||||
/**
|
||||
* Created by pgtaboada on 25.11.14.
|
||||
*/
|
||||
public class RegattaLeaderboardTabView extends SharedLeaderboardRegattaTabView<RegattaLeaderboardPlace> {
|
||||
interface MyBinder extends UiBinder<HTMLPanel, RegattaLeaderboardTabView> {
|
||||
interface MyBinder extends UiBinder<FlowPanel, RegattaLeaderboardTabView> {
|
||||
}
|
||||
|
||||
interface Style extends CssResource {
|
||||
String showLiveRacesActive();
|
||||
}
|
||||
|
||||
private static MyBinder ourUiBinder = GWT.create(MyBinder.class);
|
||||
|
||||
private Presenter currentPresenter;
|
||||
|
||||
@UiField(provided = true)
|
||||
protected OldLeaderboard leaderboard;
|
||||
@UiField Style style;
|
||||
@UiField FlowPanel buttonContainer;
|
||||
@UiField FlowPanel liveRacesContainer;
|
||||
@UiField(provided = true) LiveRacesList liveRaces;
|
||||
@UiField(provided = true) protected OldLeaderboard leaderboard;
|
||||
|
||||
private LeaderboardUpdateProvider leaderboardUpdateProvider = null;
|
||||
private OldLeaderboardAndLiveRacesPresenter leaderboardAndLiveRacesPresenter;
|
||||
|
||||
public RegattaLeaderboardTabView() {
|
||||
leaderboard = new OldLeaderboard(new OldLeaderboardDelegateFullscreenViewer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<RegattaLeaderboardPlace> getPlaceClassForActivation() {
|
||||
return RegattaLeaderboardPlace.class;
|
||||
@@ -73,37 +88,53 @@ public class RegattaLeaderboardTabView extends SharedLeaderboardRegattaTabView<R
|
||||
if (regattaId != null && !regattaId.isEmpty()) {
|
||||
String leaderboardName = regattaId;
|
||||
RegattaAnalyticsDataManager regattaAnalyticsManager = currentPresenter.getCtx().getRegattaAnalyticsManager();
|
||||
LeaderboardPanel leaderboardPanel = regattaAnalyticsManager.getLeaderboardPanel();
|
||||
LeaderboardPanel leaderboardPanel = regattaAnalyticsManager.getLeaderboardPanel();
|
||||
|
||||
final Consumer<LeaderboardPanel> leaderboardConsumer = new Consumer<LeaderboardPanel>() {
|
||||
@Override
|
||||
public void consume(LeaderboardPanel leaderboardPanel) {
|
||||
leaderboardUpdateProvider = leaderboardPanel;
|
||||
leaderboardUpdateProvider.addLeaderboardUpdateListener(RegattaLeaderboardTabView.this);
|
||||
OldLeaderboardDelegateFullscreenViewer leaderboardDelegate = new OldLeaderboardDelegateFullscreenViewer();
|
||||
leaderboard = new OldLeaderboard(leaderboardDelegate);
|
||||
liveRaces = new LiveRacesList(currentPresenter, false);
|
||||
initWidget(ourUiBinder.createAndBindUi(RegattaLeaderboardTabView.this));
|
||||
style.ensureInjected();
|
||||
leaderboardAndLiveRacesPresenter = new OldLeaderboardAndLiveRacesPresenter(leaderboard, liveRaces,
|
||||
buttonContainer, liveRacesContainer, leaderboardDelegate);
|
||||
RefreshManager refreshManager = new RefreshManagerWithErrorAndBusy(RegattaLeaderboardTabView.this,
|
||||
contentArea, currentPresenter.getDispatch(),
|
||||
currentPresenter.getErrorAndBusyClientFactory());
|
||||
refreshManager.add(leaderboardAndLiveRacesPresenter.getLiveRacesRefreshableWrapper(),
|
||||
new GetLiveRacesForRegattaAction(currentPresenter.getEventDTO().getId(), regattaId));
|
||||
leaderboard.setLeaderboard(leaderboardPanel, currentPresenter.getAutoRefreshTimer());
|
||||
if (currentPresenter.getEventDTO().getState() == EventState.RUNNING) {
|
||||
// TODO: start autorefresh?
|
||||
}
|
||||
regattaAnalyticsManager.hideCompetitorChart();
|
||||
contentArea.setWidget(RegattaLeaderboardTabView.this);
|
||||
if(leaderboardPanel.getLeaderboard() != null) {
|
||||
if (leaderboardPanel.getLeaderboard() != null) {
|
||||
leaderboard.updatedLeaderboard(leaderboardPanel.getLeaderboard());
|
||||
}
|
||||
}
|
||||
};
|
||||
if(leaderboardPanel == null) {
|
||||
createSharedLeaderboardPanel(leaderboardName, regattaAnalyticsManager, currentPresenter.getUserService(), /*FIXME placeToken */ null, leaderboardConsumer);
|
||||
} else if( /*FIXME placeToken not empty */ false) {
|
||||
createLeaderboardComponentContext(leaderboardName, currentPresenter.getUserService(), /*FIXME placeToken */ null).initInitialSettings(new DefaultOnSettingsLoadedCallback<LeaderboardSettings>() {
|
||||
@Override
|
||||
public void onSuccess(LeaderboardSettings settings) {
|
||||
leaderboardPanel.updateSettings(settings);
|
||||
leaderboardConsumer.consume(leaderboardPanel);
|
||||
}
|
||||
});
|
||||
if (leaderboardPanel == null) {
|
||||
createSharedLeaderboardPanel(leaderboardName, regattaAnalyticsManager,
|
||||
currentPresenter.getUserService(), /* FIXME placeToken */ null, leaderboardConsumer);
|
||||
} else if ( /* FIXME placeToken not empty */ false) {
|
||||
createLeaderboardComponentContext(leaderboardName, currentPresenter.getUserService(),
|
||||
/* FIXME placeToken */ null)
|
||||
.initInitialSettings(new DefaultOnSettingsLoadedCallback<LeaderboardSettings>() {
|
||||
@Override
|
||||
public void onSuccess(LeaderboardSettings settings) {
|
||||
leaderboardPanel.updateSettings(settings);
|
||||
leaderboardConsumer.consume(leaderboardPanel);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
leaderboardConsumer.consume(leaderboardPanel);
|
||||
}
|
||||
|
||||
} else {
|
||||
contentArea.setWidget(new Label("No leaderboard specified, cannot proceed to leaderboardpage"));
|
||||
new com.google.gwt.user.client.Timer() {
|
||||
@@ -140,4 +171,73 @@ public class RegattaLeaderboardTabView extends SharedLeaderboardRegattaTabView<R
|
||||
leaderboardUpdateProvider.removeLeaderboardUpdateListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@UiHandler("collapseLiveRacesButton")
|
||||
void onCollapseLiveRacesButtonClicked(ClickEvent event) {
|
||||
leaderboardAndLiveRacesPresenter.setShowLiveRaces(false);
|
||||
}
|
||||
|
||||
@UiHandler("fullscreenButton")
|
||||
void onFullscreenButtonClicked(ClickEvent event) {
|
||||
leaderboard.getFullscreenControl().fireEvent(event);
|
||||
}
|
||||
|
||||
private class OldLeaderboardAndLiveRacesPresenter {
|
||||
private final OldLeaderboard leaderboard;
|
||||
private final LiveRacesList liveRaces;
|
||||
private final FlowPanel buttonContainer, liveRacesContainer;
|
||||
private final OldLeaderboardDelegateFullscreenViewer leaderboardDelegate;
|
||||
boolean show = false, available = false;
|
||||
|
||||
private OldLeaderboardAndLiveRacesPresenter(OldLeaderboard leaderboard, LiveRacesList liveRaces,
|
||||
FlowPanel buttonContainer, FlowPanel liveRacesContainer,
|
||||
OldLeaderboardDelegateFullscreenViewer leaderboardDelegate) {
|
||||
this.leaderboard = leaderboard;
|
||||
this.liveRaces = liveRaces;
|
||||
this.buttonContainer = buttonContainer;
|
||||
this.liveRacesContainer = liveRacesContainer;
|
||||
this.leaderboardDelegate = leaderboardDelegate;
|
||||
|
||||
ClickHandler showLivesRacesClickHandler = event -> setShowLiveRaces(!show);
|
||||
this.leaderboard.getShowLiveRacesControl().addClickHandler(showLivesRacesClickHandler);
|
||||
this.leaderboardDelegate.getShowLiveRacesControl().addClickHandler(showLivesRacesClickHandler);
|
||||
this.leaderboardDelegate.getShowLiveRacesControl().setTitle(StringMessages.INSTANCE.showLiveNow());
|
||||
|
||||
this.leaderboardDelegate.addCloseHandler(event -> {
|
||||
liveRaces.removeFromParent();
|
||||
liveRacesContainer.add(liveRaces);
|
||||
});
|
||||
this.leaderboard.getFullscreenControl().addClickHandler(event -> {
|
||||
liveRaces.removeFromParent();
|
||||
leaderboardDelegate.setLiveRacesPanel(liveRaces);
|
||||
});
|
||||
this.setShowLiveRaces(false);
|
||||
}
|
||||
|
||||
private RefreshableWidget<CollectionResult<LiveRaceDTO>> getLiveRacesRefreshableWrapper() {
|
||||
return new RefreshableWidget<CollectionResult<LiveRaceDTO>>() {
|
||||
@Override
|
||||
public void setData(CollectionResult<LiveRaceDTO> data) {
|
||||
available = data != null && !data.getValues().isEmpty();
|
||||
OldLeaderboardAndLiveRacesPresenter.this.updateLiveRaces();
|
||||
liveRaces.getRefreshable().setData(data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void setShowLiveRaces(boolean show) {
|
||||
this.show = show;
|
||||
leaderboard.getShowLiveRacesControl().setStyleName(style.showLiveRacesActive(), show);
|
||||
leaderboardDelegate.getShowLiveRacesControl().setStyleName(style.showLiveRacesActive(), show);
|
||||
this.updateLiveRaces();
|
||||
}
|
||||
|
||||
private void updateLiveRaces() {
|
||||
leaderboard.getShowLiveRacesControl().setVisible(!available || !show);
|
||||
leaderboard.getFullscreenControl().setVisible(!available || !show);
|
||||
buttonContainer.setVisible(available && show);
|
||||
liveRacesContainer.setVisible(available && show);
|
||||
leaderboardDelegate.setShowLiveRaces(available && show);
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-8
@@ -1,11 +1,32 @@
|
||||
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui'
|
||||
xmlns:dp="urn:import:com.sap.sailing.gwt.home.desktop.partials">
|
||||
<ui:style>
|
||||
.outerBox {
|
||||
padding-top: 10px;
|
||||
}
|
||||
xmlns:dp="urn:import:com.sap.sailing.gwt.home.desktop.partials">
|
||||
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages"/>
|
||||
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
|
||||
<ui:with field="lb_res" type="com.sap.sailing.gwt.home.desktop.partials.old.EventRegattaLeaderboardResources" />
|
||||
<ui:style type="com.sap.sailing.gwt.home.desktop.places.event.regatta.leaderboardtab.RegattaLeaderboardTabView.Style">
|
||||
.mainPanel {
|
||||
position: relative;
|
||||
}
|
||||
.buttons {
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
}
|
||||
.showLiveRacesActive {
|
||||
background-color: red;
|
||||
}
|
||||
</ui:style>
|
||||
<g:HTMLPanel styleName="{style.outerBox}">
|
||||
<dp:old.leaderboard.OldLeaderboard ui:field='leaderboard'/>
|
||||
</g:HTMLPanel>
|
||||
<g:FlowPanel addStyleNames="{res.mediaCss.grid} {style.mainPanel}">
|
||||
<g:FlowPanel ui:field="liveRacesContainer" addStyleNames="{res.mediaCss.column} {res.mediaCss.small12} {res.mainCss.spacermargintopmedium}">
|
||||
<dp:liveraces.LiveRacesList ui:field="liveRaces" />
|
||||
</g:FlowPanel>
|
||||
<g:FlowPanel ui:field="buttonContainer" addStyleNames="{res.mediaCss.column} {res.mediaCss.small12} {res.mainCss.spacermargintopsmall} {style.buttons}">
|
||||
<g:Anchor href="javascript:;" ui:field="collapseLiveRacesButton" title="{i18n.showLiveNow}"
|
||||
addStyleNames="{res.mainCss.button} {lb_res.css.regattaleaderboard_meta_liveraces_button} {style.showLiveRacesActive}" />
|
||||
<g:Anchor href="javascript:;" ui:field="fullscreenButton" title="{i18n.openFullscreenView}"
|
||||
addStyleNames="{res.mainCss.button} {lb_res.css.regattaleaderboard_meta_fullscreen_button}" />
|
||||
</g:FlowPanel>
|
||||
<g:FlowPanel styleName="{res.mediaCss.column} {res.mediaCss.small12}">
|
||||
<dp:old.leaderboard.OldLeaderboard ui:field='leaderboard'/>
|
||||
</g:FlowPanel>
|
||||
</g:FlowPanel>
|
||||
</ui:UiBinder>
|
||||
+2
-1
@@ -36,7 +36,8 @@ public class EventSeriesOverallLeaderboardTabView extends SharedLeaderboardEvent
|
||||
protected OldLeaderboard leaderboard;
|
||||
|
||||
public EventSeriesOverallLeaderboardTabView() {
|
||||
leaderboard = new OldLeaderboard(new OldLeaderboardDelegateFullscreenViewer());
|
||||
leaderboard = new OldLeaderboard(new OldLeaderboardDelegateFullscreenViewer(false));
|
||||
leaderboard.getShowLiveRacesControl().removeFromParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-2
@@ -105,8 +105,7 @@ public class MinileaderboardBox extends Composite implements RefreshableWidget<G
|
||||
scoreInformation.add(new Label(data.getScoreCorrectionText()));
|
||||
}
|
||||
if (data.getLastScoreUpdate() != null) {
|
||||
String lastUpdate = DateAndTimeFormatterUtil.longDateFormatter.render(data.getLastScoreUpdate()) + " "
|
||||
+ DateAndTimeFormatterUtil.formatElapsedTime(data.getLastScoreUpdate().getTime());
|
||||
String lastUpdate = DateAndTimeFormatterUtil.formatLongDateAndTimeGMT(data.getLastScoreUpdate());
|
||||
scoreInformation.add(new Label(StringMessages.INSTANCE.lastScoreUpdate() + ": " + lastUpdate));
|
||||
}
|
||||
scoreInformation.getElement().getStyle().setBackgroundColor("#f2f2f2");
|
||||
|
||||
+4
-1
@@ -67,7 +67,10 @@
|
||||
<li>
|
||||
<ui:text from='{i18n.contentSapInSailing12}' />
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<ui:text from='{i18n.contentSapInSailing13}' />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div ui:field="sapSailingAnalyticsUi"
|
||||
|
||||
+9
-3
@@ -108,11 +108,17 @@ public final class EventActionUtil {
|
||||
return calculateTtlForNonLiveEvent(event, eventState);
|
||||
}
|
||||
|
||||
public static <T extends DTO> ResultWithTTL<T> withLiveRaceOrDefaultSchedule(SailingDispatchContext context, UUID eventId, CalculationWithEvent<T> callback) {
|
||||
public static <T extends DTO> ResultWithTTL<T> withLiveRaceOrDefaultSchedule(SailingDispatchContext context,
|
||||
UUID eventId, CalculationWithEvent<T> callback) {
|
||||
return withLiveRaceOrDefaultSchedule(context, eventId, callback, null);
|
||||
}
|
||||
|
||||
public static <T extends DTO> ResultWithTTL<T> withLiveRaceOrDefaultSchedule(SailingDispatchContext context,
|
||||
UUID eventId, CalculationWithEvent<T> callback, T defaultResult) {
|
||||
Event event = context.getRacingEventService().getEvent(eventId);
|
||||
EventState eventState = HomeServiceUtil.calculateEventState(event);
|
||||
if (eventState != EventState.RUNNING) {
|
||||
return new ResultWithTTL<T>(calculateTtlForNonLiveEvent(event, eventState), null);
|
||||
if (eventState == EventState.FINISHED) {
|
||||
return new ResultWithTTL<T>(calculateTtlForNonLiveEvent(event, eventState), defaultResult);
|
||||
}
|
||||
return callback.calculateWithEvent(event);
|
||||
}
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ public class RaceCompetitionFormatDataCalculator implements RaceCallback {
|
||||
}
|
||||
|
||||
private class SeriesFleetData {
|
||||
private final Set<SimpleRaceMetadataDTO> races = new HashSet<>();
|
||||
private final Set<SimpleRaceMetadataDTO> races = new LinkedHashSet<>();
|
||||
private final Set<SimpleCompetitorDTO> competitors = new HashSet<>();
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -48,6 +48,7 @@ import com.sap.sailing.domain.leaderboard.ScoreCorrection;
|
||||
import com.sap.sailing.domain.tracking.RaceLogWindFixDeclinationHelper;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.gwt.home.communication.event.EventState;
|
||||
import com.sap.sailing.gwt.home.communication.event.LiveRaceDTO;
|
||||
import com.sap.sailing.gwt.home.communication.event.RaceListRaceDTO;
|
||||
import com.sap.sailing.gwt.home.communication.event.SimpleCompetitorDTO;
|
||||
@@ -475,12 +476,12 @@ public class RaceContext {
|
||||
// TODO do not calculate the winner if the blue flag is currently shown.
|
||||
try {
|
||||
TimePoint finishTime = getLiveTimePoint();
|
||||
List<Competitor> competitors = leaderboard.getCompetitorsFromBestToWorst(raceColumn, finishTime);
|
||||
if (competitors == null || competitors.isEmpty()) {
|
||||
Iterable<Competitor> competitors = leaderboard.getCompetitorsFromBestToWorst(raceColumn, finishTime);
|
||||
if (competitors == null || Util.isEmpty(competitors)) {
|
||||
return null;
|
||||
}
|
||||
if (Util.size(raceColumn.getFleets()) == 1) {
|
||||
return new SimpleCompetitorDTO(competitors.get(0));
|
||||
return new SimpleCompetitorDTO(competitors.iterator().next());
|
||||
}
|
||||
for (Competitor competitor : competitors) {
|
||||
if (isCompetitorInFleet(competitor)) {
|
||||
@@ -522,6 +523,9 @@ public class RaceContext {
|
||||
}
|
||||
|
||||
public boolean isLiveOrOfPublicInterest() {
|
||||
if (HomeServiceUtil.calculateEventState(event) == EventState.FINISHED) {
|
||||
return false;
|
||||
}
|
||||
boolean isLive = false;
|
||||
boolean isOfPublicInterest = false;
|
||||
if (trackedRace != null) {
|
||||
|
||||
+4
-3
@@ -1,7 +1,8 @@
|
||||
package com.sap.sailing.gwt.home.server;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.TreeSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gwt.core.shared.GwtIncompatible;
|
||||
import com.sap.sailing.gwt.home.communication.event.RaceListRaceDTO;
|
||||
@@ -13,12 +14,12 @@ import com.sap.sailing.gwt.home.server.EventActionUtil.RaceCallback;
|
||||
*/
|
||||
@GwtIncompatible
|
||||
public class RaceListDataCalculator implements RaceCallback {
|
||||
private final TreeSet<RaceListRaceDTO> races = new TreeSet<>();
|
||||
private final Set<RaceListRaceDTO> races = new LinkedHashSet<>();
|
||||
|
||||
@Override
|
||||
public void doForRace(RaceContext context) {
|
||||
RaceListRaceDTO finishedRace = context.getFinishedRaceOrNull();
|
||||
if(finishedRace != null) {
|
||||
if (finishedRace != null) {
|
||||
races.add(finishedRace);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
* TTL} can be used for as refresh interval of "Live races" sections.
|
||||
*/
|
||||
@GwtIncompatible
|
||||
final class RaceRefreshCalculator implements RaceCallback {
|
||||
public final class RaceRefreshCalculator implements RaceCallback {
|
||||
private long ttl = Duration.ONE_MINUTE.times(3).asMillis();
|
||||
|
||||
@Override
|
||||
|
||||
+7
-34
@@ -6,34 +6,28 @@ import static com.sap.sse.common.impl.MillisecondsTimePoint.now;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.core.client.Scheduler;
|
||||
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||
import com.google.gwt.dom.client.Element;
|
||||
import com.google.gwt.text.client.DateTimeFormatRenderer;
|
||||
import com.google.gwt.user.client.Event;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.PopupPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.common.client.DateUtil;
|
||||
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
|
||||
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceTrackingState;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPad;
|
||||
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPadController;
|
||||
import com.sap.sailing.gwt.home.shared.partials.regattacompetition.RegattaCompetitionView.RegattaCompetitionRaceView;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.raceboard.RaceBoardModes;
|
||||
|
||||
public abstract class AbstractRegattaCompetitionFleetRace extends Widget implements RegattaCompetitionRaceView {
|
||||
|
||||
private static final StringMessages I18N = StringMessages.INSTANCE;
|
||||
private final RaceviewerLaunchPadController launchPadController;
|
||||
protected final Element mainElement;
|
||||
private final SimpleRaceMetadataDTO race;
|
||||
private final RegattaCompetitionPresenter presenter;
|
||||
private final PopupPanel panel = new PopupPanel(true, false);
|
||||
|
||||
protected AbstractRegattaCompetitionFleetRace(final SimpleRaceMetadataDTO race,
|
||||
RegattaCompetitionPresenter presenter) {
|
||||
this.race = race;
|
||||
this.presenter = presenter;
|
||||
this.launchPadController = new RaceviewerLaunchPadController(presenter::getRaceViewerURL);
|
||||
this.mainElement = getMainUiElement();
|
||||
setupRaceState(race);
|
||||
getRaceNameUiElement().setInnerText(race.getRaceName());
|
||||
@@ -52,37 +46,16 @@ public abstract class AbstractRegattaCompetitionFleetRace extends Widget impleme
|
||||
}
|
||||
|
||||
// If rendered as direct link button, open link in new tab directly instead of showing the menu popup
|
||||
if (!race.isFinished() && !race.isRunning()) {
|
||||
Window.open(presenter.getRaceViewerURL(race, RaceBoardModes.PLAYER.name()), "_blank", "");
|
||||
if (launchPadController.renderAsDirectLink(race)) {
|
||||
Window.open(launchPadController.getDirectLinkUrl(race), "_blank", "");
|
||||
return;
|
||||
}
|
||||
|
||||
// Show menu popup relative to button by ensuring it stays with the viewport
|
||||
this.getElement().scrollIntoView();
|
||||
panel.setWidget(new RaceviewerLaunchPad(race, panel) {
|
||||
@Override
|
||||
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String mode) {
|
||||
return presenter.getRaceViewerURL(data, mode);
|
||||
}
|
||||
});
|
||||
panel.setVisible(false);
|
||||
panel.show();
|
||||
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
Widget button = AbstractRegattaCompetitionFleetRace.this, panelContent = panel.getWidget();
|
||||
int alignRight = button.getAbsoluteLeft() + button.getOffsetWidth() - panelContent.getOffsetWidth();
|
||||
int left = (alignRight - Window.getScrollLeft() < 0 ? button.getAbsoluteLeft() - 1 : alignRight + 1);
|
||||
int alignBottom = button.getAbsoluteTop() + button.getOffsetHeight() - panelContent.getOffsetHeight();
|
||||
int top = (alignBottom - Window.getScrollTop() < 0 ? button.getAbsoluteTop() - 1 : alignBottom + 1);
|
||||
panel.setPopupPosition(left, top);
|
||||
panel.setVisible(true);
|
||||
}
|
||||
});
|
||||
launchPadController.showRaceviewerLaunchPad(race, this.getElement());
|
||||
}
|
||||
|
||||
private void setupRaceState(SimpleRaceMetadataDTO race) {
|
||||
boolean isUntrackedRace = race.getTrackingState() != RaceTrackingState.TRACKED_VALID_DATA;
|
||||
final boolean isUntrackedRace = !race.hasValidTrackingData();
|
||||
if (race.isRunning()) {
|
||||
mainElement.addClassName(getRaceLiveStyleName());
|
||||
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.live() : I18N.actionWatch());
|
||||
|
||||
+2
-1
@@ -62,7 +62,8 @@ public class LeaderboardPanelLifecycle
|
||||
currentLeaderboardSettings.isShowAddedScores(),
|
||||
currentLeaderboardSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
|
||||
currentLeaderboardSettings.isShowCompetitorSailIdColumn(),
|
||||
currentLeaderboardSettings.isShowCompetitorFullNameColumn());
|
||||
currentLeaderboardSettings.isShowCompetitorFullNameColumn(),
|
||||
currentLeaderboardSettings.isShowCompetitorNationality());
|
||||
globalLeaderboardSettings = LeaderboardSettingsFactory.getInstance().keepDefaults(currentLeaderboardSettings,
|
||||
globalLeaderboardSettings);
|
||||
return globalLeaderboardSettings;
|
||||
|
||||
+12
-1
@@ -54,6 +54,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
private boolean autoExpandPreSelectedRace = false;
|
||||
private LongSetting delayBetweenAutoAdvancesInMilliseconds;
|
||||
private BooleanSetting updateUponPlayStateChange;
|
||||
private BooleanSetting isShowCompetitorNationality;
|
||||
|
||||
/**
|
||||
* There are two ways to select race columns.
|
||||
@@ -85,6 +86,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
|
||||
@Override
|
||||
protected void addChildSettings() {
|
||||
isShowCompetitorNationality = new BooleanSetting("showCompetitorNationality", this, false);
|
||||
namesOfRaceColumnsToShow = new StringListSetting("namesOfRaceColumnsToShow", this);
|
||||
namesOfRacesToShow = new StringListSetting("namesOfRacesToShow", this, null);
|
||||
numberOfLastRacesToShow = new IntegerSetting("numberOfLastRacesToShow", this, null);
|
||||
@@ -155,7 +157,8 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
boolean autoExpandPreSelectedRace, Long delayBetweenAutoAdvancesInMilliseconds, String nameOfRaceToSort,
|
||||
boolean sortAscending, boolean updateUponPlayStateChange, RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy,
|
||||
boolean showAddedScores, boolean showOverallColumnWithNumberOfRacesCompletedPerCompetitor,
|
||||
boolean showCompetitorSailIdColumn, boolean showCompetitorFullNameColumn) {
|
||||
boolean showCompetitorSailIdColumn, boolean showCompetitorFullNameColumn,
|
||||
boolean isCompetitorNationalityColumnVisible) {
|
||||
if (namesOfRacesToShow != null && namesOfRaceColumnsToShow != null) {
|
||||
throw new IllegalArgumentException("You can identify races either only by their race or by their column names, not both");
|
||||
}
|
||||
@@ -176,6 +179,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
this.showCompetitorSailIdColumn.setValue(showCompetitorSailIdColumn);
|
||||
this.showCompetitorFullNameColumn.setValue(showCompetitorFullNameColumn);
|
||||
this.showOverallColumnWithNumberOfRacesCompletedPerCompetitor.setValue(showOverallColumnWithNumberOfRacesCompletedPerCompetitor);
|
||||
this.isShowCompetitorNationality.setValue(isCompetitorNationalityColumnVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,6 +284,10 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
return showCompetitorFullNameColumn.getValue();
|
||||
}
|
||||
|
||||
public boolean isShowCompetitorNationality() {
|
||||
return isShowCompetitorNationality.getValue();
|
||||
}
|
||||
|
||||
void overrideDefaultValues(LeaderboardSettings newDefaults) {
|
||||
autoExpandPreSelectedRace = newDefaults.autoExpandPreSelectedRace;
|
||||
boolean namesOfRaceColumnsToShowWasDefault = Util.equals(namesOfRaceColumnsToShow.getValues(), namesOfRaceColumnsToShow.getDefaultValues());
|
||||
@@ -306,6 +314,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
showCompetitorSailIdColumn.setDefaultValue(newDefaults.showCompetitorSailIdColumn.getValue());
|
||||
showCompetitorFullNameColumn.setDefaultValue(newDefaults.showCompetitorFullNameColumn.getValue());
|
||||
showOverallColumnWithNumberOfRacesCompletedPerCompetitor.setDefaultValue(newDefaults.showOverallColumnWithNumberOfRacesCompletedPerCompetitor.getValue());
|
||||
isShowCompetitorNationality.setDefaultValue(newDefaults.isShowCompetitorNationality.getValue());
|
||||
}
|
||||
|
||||
void setValues(LeaderboardSettings settingsWithCustomValues) {
|
||||
@@ -325,6 +334,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
this.showAddedScores.setValue(settingsWithCustomValues.isShowAddedScores());
|
||||
this.showCompetitorSailIdColumn.setValue(settingsWithCustomValues.isShowCompetitorSailIdColumn());
|
||||
this.showCompetitorFullNameColumn.setValue(settingsWithCustomValues.isShowCompetitorFullNameColumn());
|
||||
this.isShowCompetitorNationality.setValue(settingsWithCustomValues.isShowCompetitorNationality());
|
||||
this.showOverallColumnWithNumberOfRacesCompletedPerCompetitor.setValue(settingsWithCustomValues.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor());
|
||||
}
|
||||
|
||||
@@ -347,6 +357,7 @@ public class LeaderboardSettings extends AbstractGenericSerializableSettings {
|
||||
leaderboardSettings.showCompetitorSailIdColumn.setDefaultValue(showCompetitorSailIdColumn.getDefaultValue());
|
||||
leaderboardSettings.showCompetitorFullNameColumn.setDefaultValue(showCompetitorFullNameColumn.getDefaultValue());
|
||||
leaderboardSettings.showOverallColumnWithNumberOfRacesCompletedPerCompetitor.setDefaultValue(showOverallColumnWithNumberOfRacesCompletedPerCompetitor.getDefaultValue());
|
||||
leaderboardSettings.isShowCompetitorNationality.setDefaultValue(isShowCompetitorNationality.getDefaultValue());
|
||||
return leaderboardSettings;
|
||||
}
|
||||
|
||||
|
||||
Executable → Regular
+15
-3
@@ -52,6 +52,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
private CheckBox showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox;
|
||||
private CheckBox showCompetitorSailIdColumnheckBox;
|
||||
private CheckBox showCompetitorFullNameColumnCheckBox;
|
||||
private CheckBox isCompetitorNationalityColumnVisible;
|
||||
private LeaderboardSettings initialSettings;
|
||||
|
||||
public LeaderboardSettingsDialogComponent(LeaderboardSettings initialSettings, List<String> allRaceColumnNames, StringMessages stringMessages) {
|
||||
@@ -193,13 +194,23 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
dialog.addTooltip(showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox, stringMessages.showNumberOfRacesScored());
|
||||
showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox.setValue(initialSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor());
|
||||
overallDetailDialogContent.add(showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox);
|
||||
|
||||
FlowPanel overallDetailDialogContentSecondLine = new FlowPanel();
|
||||
overallDetailDialogContentSecondLine.addStyleName("dialogInnerContent");
|
||||
showCompetitorSailIdColumnheckBox = dialog.createCheckbox(stringMessages.showCompetitorSailIdColumn());
|
||||
showCompetitorSailIdColumnheckBox.setTitle(stringMessages.showCompetitorSailIdColumnTooltip(stringMessages.alwaysShowCompetitorNationalityColumn()));
|
||||
showCompetitorSailIdColumnheckBox.setValue(initialSettings.isShowCompetitorSailIdColumn());
|
||||
overallDetailDialogContent.add(showCompetitorSailIdColumnheckBox);
|
||||
overallDetailDialogContentSecondLine.add(showCompetitorSailIdColumnheckBox);
|
||||
isCompetitorNationalityColumnVisible = dialog.createCheckbox(stringMessages.alwaysShowCompetitorNationalityColumn());
|
||||
isCompetitorNationalityColumnVisible.setTitle(stringMessages.alwaysShowCompetitorNationalityColumnTooltip());
|
||||
isCompetitorNationalityColumnVisible.setValue(initialSettings.isShowCompetitorNationality());
|
||||
overallDetailDialogContentSecondLine.add(isCompetitorNationalityColumnVisible);
|
||||
showCompetitorFullNameColumnCheckBox = dialog.createCheckbox(stringMessages.showCompetitorFullNameColumn());
|
||||
showCompetitorFullNameColumnCheckBox.setValue(initialSettings.isShowCompetitorFullNameColumn());
|
||||
overallDetailDialogContent.add(showCompetitorFullNameColumnCheckBox);
|
||||
overallDetailDialogContentSecondLine.add(showCompetitorFullNameColumnCheckBox);
|
||||
|
||||
overallDetailDialog.add(overallDetailDialogContent);
|
||||
overallDetailDialog.add(overallDetailDialogContentSecondLine);
|
||||
return overallDetailDialog;
|
||||
}
|
||||
|
||||
@@ -389,7 +400,8 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
true, /* updateUponPlayStateChange */ true, activeRaceColumnSelectionStrategy,
|
||||
/*showAddedScores*/ showAddedScoresCheckBox.getValue().booleanValue(),
|
||||
/*showOverallColumnWithNumberOfRacesSailedPerCompetitor*/ showOverallColumnWithNumberOfRacesSailedPerCompetitorCheckBox.getValue().booleanValue(),
|
||||
showCompetitorSailIdColumnheckBox.getValue(), showCompetitorFullNameColumnCheckBox.getValue());
|
||||
showCompetitorSailIdColumnheckBox.getValue(), showCompetitorFullNameColumnCheckBox.getValue(),
|
||||
isCompetitorNationalityColumnVisible.getValue());
|
||||
return LeaderboardSettingsFactory.getInstance().keepDefaults(initialSettings, newSettings);
|
||||
}
|
||||
|
||||
|
||||
+7
-4
@@ -83,7 +83,7 @@ public class LeaderboardSettingsFactory {
|
||||
/* refresh interval */ null, /* name of race to sort */ nameOfRaceToSort,
|
||||
/* ascending */ true, /* updateUponPlayStateChange */ true, raceColumnSelectionType,
|
||||
/*showAddedScores*/ false, /*showOverallRacesCompleted*/ false,
|
||||
showCompetitorSailIdColumn, showCompetitorNameColumn);
|
||||
showCompetitorSailIdColumn, showCompetitorNameColumn, false);
|
||||
break;
|
||||
case Replay:
|
||||
settings = createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, nameOfRaceToSort, /* autoExpandFirstRace */
|
||||
@@ -172,7 +172,8 @@ public class LeaderboardSettingsFactory {
|
||||
existingSettings.isShowAddedScores(),
|
||||
existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
|
||||
existingSettings.isShowCompetitorSailIdColumn(),
|
||||
existingSettings.isShowCompetitorFullNameColumn());
|
||||
existingSettings.isShowCompetitorFullNameColumn(),
|
||||
existingSettings.isShowCompetitorNationality());
|
||||
|
||||
final LeaderboardSettings existingDefaultSettings = existingSettings.getDefaultSettings();
|
||||
|
||||
@@ -190,7 +191,8 @@ public class LeaderboardSettingsFactory {
|
||||
existingDefaultSettings.isShowAddedScores(),
|
||||
existingDefaultSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
|
||||
existingDefaultSettings.isShowCompetitorSailIdColumn(),
|
||||
existingDefaultSettings.isShowCompetitorFullNameColumn());
|
||||
existingDefaultSettings.isShowCompetitorFullNameColumn(),
|
||||
existingSettings.isShowCompetitorNationality());
|
||||
|
||||
newDefaultSettings = LeaderboardSettingsFactory.getInstance().createNewSettingsWithCustomDefaults(newDefaultSettings);
|
||||
|
||||
@@ -225,7 +227,8 @@ public class LeaderboardSettingsFactory {
|
||||
return new LeaderboardSettings(maneuverDetails, legDetails, raceDetails, overallDetailsToShow,
|
||||
namesOfRaceColumnsToShow, namesOfRacesToShow, numberOfLastRacesToShow, autoExpandPreSelectedRace, refreshIntervalInMs,
|
||||
nameOfRaceToSort, sortAscending, updateUponPlayStateChange, strategy, /*showAddedScores*/ false,
|
||||
/*showOverallRacesCompleted*/ false, showCompetitorSailIdColumn, showCompetitorFullNameColumns);
|
||||
/* showOverallRacesCompleted */ false, showCompetitorSailIdColumn, showCompetitorFullNameColumns,
|
||||
settingsWithRaceSelection.isShowCompetitorNationality());
|
||||
}
|
||||
|
||||
private List<DetailType> copyDetailTypes(List<DetailType> detailTypes) {
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public abstract class AbstractCompetitorRegistrationDialog extends DataEntryDialog<Set<CompetitorDTO>> {
|
||||
protected final ErrorReporter errorReporter;
|
||||
protected final SailingServiceAsync sailingService;
|
||||
protected final StringMessages stringMessages;
|
||||
protected final String leaderboardName;
|
||||
protected final CompetitorRegistrationsPanel competitorRegistrationsPanel;
|
||||
|
||||
public AbstractCompetitorRegistrationDialog(SailingServiceAsync sailingService, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter, boolean editable,
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<Set<CompetitorDTO>> callback,
|
||||
String leaderboardName, String boatClass, Validator<Set<CompetitorDTO>> validator) {
|
||||
super(stringMessages.registerCompetitors(), /* messsage */null, stringMessages.save(), stringMessages.cancel(),
|
||||
validator, callback);
|
||||
this.errorReporter = errorReporter;
|
||||
this.stringMessages = stringMessages;
|
||||
this.sailingService = sailingService;
|
||||
this.leaderboardName = leaderboardName;
|
||||
this.competitorRegistrationsPanel = new CompetitorRegistrationsPanel(sailingService, stringMessages, errorReporter, editable, leaderboardName, boatClass,
|
||||
()->validateAndUpdate(), getRegisteredCompetitorsRetriever(), /* restrictPoolToLeaderboard */ false, getAdditionalWidgetsToInsertAboveCompetitorTables(stringMessages));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
return competitorRegistrationsPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<CompetitorDTO> getResult() {
|
||||
return competitorRegistrationsPanel.getResult();
|
||||
}
|
||||
|
||||
protected abstract Consumer<AsyncCallback<Collection<CompetitorDTO>>> getRegisteredCompetitorsRetriever();
|
||||
|
||||
protected abstract Widget[] getAdditionalWidgetsToInsertAboveCompetitorTables(StringMessages stringMessages);
|
||||
}
|
||||
+5
-5
@@ -8,24 +8,24 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public abstract class AbstractLeaderboardDialog extends DataEntryDialog<LeaderboardDescriptor> {
|
||||
public abstract class AbstractLeaderboardDialog<LD extends LeaderboardDescriptor> extends DataEntryDialog<LD> {
|
||||
protected final StringMessages stringMessages;
|
||||
protected TextBox nameTextBox;
|
||||
protected TextBox displayNameTextBox;
|
||||
protected LeaderboardDescriptor leaderboardDescriptor;
|
||||
protected LD leaderboardDescriptor;
|
||||
|
||||
protected DiscardThresholdBoxes discardThresholdBoxes;
|
||||
protected static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4;
|
||||
|
||||
public AbstractLeaderboardDialog(String title, LeaderboardDescriptor leaderboardDescriptor, StringMessages stringMessages,
|
||||
Validator<LeaderboardDescriptor> validator, DialogCallback<LeaderboardDescriptor> callback) {
|
||||
public AbstractLeaderboardDialog(String title, LD leaderboardDescriptor, StringMessages stringMessages,
|
||||
Validator<LD> validator, DialogCallback<LD> callback) {
|
||||
super(title, null, stringMessages.ok(), stringMessages.cancel(), validator, callback);
|
||||
this.stringMessages = stringMessages;
|
||||
this.leaderboardDescriptor = leaderboardDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LeaderboardDescriptor getResult() {
|
||||
protected LD getResult() {
|
||||
leaderboardDescriptor.setName(nameTextBox.getValue().trim()); // avoid trailing blank issues; leaderboard names may appear in URLs
|
||||
leaderboardDescriptor.setDisplayName(displayNameTextBox.getValue().trim().isEmpty() ? null : displayNameTextBox.getValue());
|
||||
leaderboardDescriptor.setDiscardThresholds(discardThresholdBoxes==null?null:discardThresholdBoxes.getDiscardThresholds());
|
||||
|
||||
+77
-31
@@ -1,7 +1,10 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.gwt.core.client.Callback;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
@@ -19,6 +22,7 @@ import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.CompetitorDescriptor;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTOImpl;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorWithToolTipDTO;
|
||||
import com.sap.sailing.gwt.ui.adminconsole.CompetitorImportProviderSelectionDialog.MatchImportedCompetitorsDialogFactory;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
@@ -28,51 +32,65 @@ import com.sap.sse.gwt.client.celltable.RefreshableMultiSelectionModel;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.BusyDisplay;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.BusyIndicator;
|
||||
import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDialog<Set<CompetitorDTO>> implements BusyDisplay {
|
||||
/**
|
||||
* Shows two competitor tables next to each other; the table on the left is that of the "registered" competitors, the table
|
||||
* on the right is the "pool" of all competitors with those already "registered" removed. Between the tables are buttons
|
||||
* to move competitors left and right, thereby assigning them to the "registered" set or moving them back to the pool.
|
||||
* The {@link #getResult result} consists of all {@link CompetitorDTO competitors} in the table of "registered" competitors.<p>
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDisplay {
|
||||
protected CompetitorTableWrapper<RefreshableMultiSelectionModel<CompetitorDTO>> allCompetitorsTable;
|
||||
protected CompetitorTableWrapper<RefreshableMultiSelectionModel<CompetitorDTO>> registeredCompetitorsTable;
|
||||
protected final boolean filterByLeaderBoardInitially = false;
|
||||
final StringMessages stringMessages;
|
||||
protected final SailingServiceAsync sailingService;
|
||||
final ErrorReporter errorReporter;
|
||||
private boolean editable;
|
||||
private Button registerBtn;
|
||||
private Button unregisterBtn;
|
||||
private Button addCompetitorButton;
|
||||
private CheckBox showOnlyCompetitorsOfLogCheckBox;
|
||||
|
||||
private String boatClass;
|
||||
|
||||
protected String leaderboardName;
|
||||
private final BusyIndicator busyIndicator;
|
||||
private final ImportCompetitorCallback importCompetitorCallback;
|
||||
private final Runnable validator;
|
||||
private final Consumer<AsyncCallback<Collection<CompetitorDTO>>> registeredCompetitorsRetriever;
|
||||
private final boolean restrictPoolToLeaderboard;
|
||||
|
||||
/**
|
||||
* @param boatClass
|
||||
* The <code>boatClass</code> parameter describes the default shown boat class for new competitors. The
|
||||
* <code>boatClass</code> parameter is <code>null</code>, if you want to edit a competitor or there is no
|
||||
* boat class for the new competitor.
|
||||
* @param validator
|
||||
* when this panel is used to modify the resulting competitor set, this callback's {@link Runnable#run}
|
||||
* method is invoked if a non-{@code null} value is passed here.
|
||||
* @param registeredCompetitorsRetriever
|
||||
* although declared as a consumer, this is a provider of the set of competitors to be shown in the
|
||||
* "registered" competitors table. Technically, it "consumes" a callback to which to pass the competitors
|
||||
* retrieved as the "registered" ones.
|
||||
* @param restrictPoolToLeaderboard
|
||||
* whether the pool of "all" competitors is to be restricted to those obtained from the leaderboard, or
|
||||
* to all competitors in the server's competitor store
|
||||
* @param additionalWidgetsBeforeTables widgets to be inserted above / before the competitor tables; may be {@code null} or empty
|
||||
*/
|
||||
protected AbstractCompetitorRegistrationsDialog(final SailingServiceAsync sailingService,
|
||||
protected CompetitorRegistrationsPanel(final SailingServiceAsync sailingService,
|
||||
final StringMessages stringMessages, final ErrorReporter errorReporter, boolean editable,
|
||||
DialogCallback<Set<CompetitorDTO>> callback, String leaderboardName, String boatClass,
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator<Set<CompetitorDTO>> validator) {
|
||||
super(stringMessages.registerCompetitors(), /* messsage */null, stringMessages.save(), stringMessages.cancel(), validator, callback);
|
||||
String leaderboardName, final String boatClass, Runnable validator,
|
||||
Consumer<AsyncCallback<Collection<CompetitorDTO>>> registeredCompetitorsRetriever,
|
||||
boolean restrictPoolToLeaderboard, Widget... additionalWidgetsBeforeTables) {
|
||||
this.stringMessages = stringMessages;
|
||||
this.sailingService = sailingService;
|
||||
this.errorReporter = errorReporter;
|
||||
this.editable = editable;
|
||||
this.restrictPoolToLeaderboard = restrictPoolToLeaderboard;
|
||||
this.validator = validator;
|
||||
this.leaderboardName = leaderboardName;
|
||||
this.boatClass = boatClass;
|
||||
this.busyIndicator = new SimpleBusyIndicator();
|
||||
this.registeredCompetitorsRetriever = registeredCompetitorsRetriever;
|
||||
this.importCompetitorCallback = new RaceOrRegattaImportCompetitorCallback(this, sailingService, errorReporter, stringMessages);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
final FlowPanel mainPanel = new FlowPanel();
|
||||
final HorizontalPanel buttonPanel = new HorizontalPanel();
|
||||
addCompetitorButton = new Button(stringMessages.add(stringMessages.competitor()));
|
||||
addCompetitorButton.addClickHandler(new ClickHandler() {
|
||||
@@ -102,7 +120,7 @@ public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDia
|
||||
MatchImportedCompetitorsDialogFactory matchCompetitorsDialogFactory = getMatchCompetitorsDialogFactory(
|
||||
sailingService, stringMessages, errorReporter);
|
||||
CompetitorImportProviderSelectionDialog dialog = new CompetitorImportProviderSelectionDialog(
|
||||
matchCompetitorsDialogFactory, AbstractCompetitorRegistrationsDialog.this, providerNames, sailingService,
|
||||
matchCompetitorsDialogFactory, CompetitorRegistrationsPanel.this, providerNames, sailingService,
|
||||
stringMessages, errorReporter);
|
||||
dialog.show();
|
||||
}
|
||||
@@ -151,7 +169,7 @@ public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDia
|
||||
buttonPanel.add(inviteCompetitorsButton);
|
||||
buttonPanel.add(competitorImportButton);
|
||||
buttonPanel.add(busyIndicator);
|
||||
mainPanel.add(buttonPanel);
|
||||
this.add(buttonPanel);
|
||||
showOnlyCompetitorsOfLogCheckBox = new CheckBox(stringMessages.showOnlyCompetitorsOfLog());
|
||||
showOnlyCompetitorsOfLogCheckBox.setValue(false);
|
||||
showOnlyCompetitorsOfLogCheckBox.addClickHandler(new ClickHandler() {
|
||||
@@ -164,11 +182,14 @@ public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDia
|
||||
}
|
||||
}
|
||||
});
|
||||
mainPanel.add(showOnlyCompetitorsOfLogCheckBox);
|
||||
addAdditionalWidgets(mainPanel);
|
||||
mainPanel.add(competitorRegistrationPanel);
|
||||
this.add(showOnlyCompetitorsOfLogCheckBox);
|
||||
if (additionalWidgetsBeforeTables != null) {
|
||||
for (final Widget additionalWidget : additionalWidgetsBeforeTables) {
|
||||
add(additionalWidget);
|
||||
}
|
||||
}
|
||||
this.add(competitorRegistrationPanel);
|
||||
refreshCompetitors();
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
private MatchImportedCompetitorsDialogFactory getMatchCompetitorsDialogFactory(
|
||||
@@ -190,8 +211,6 @@ public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDia
|
||||
busyIndicator.setBusy(isBusy);
|
||||
}
|
||||
|
||||
public abstract void addAdditionalWidgets(FlowPanel mainPanel);
|
||||
|
||||
protected void move(CompetitorTableWrapper<?> from, CompetitorTableWrapper<?> to, Iterable<CompetitorDTO> toMove) {
|
||||
from.getFilterField().removeAll(toMove);
|
||||
to.getFilterField().addAll(toMove);
|
||||
@@ -218,17 +237,27 @@ public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDia
|
||||
setRegisterableCompetitorsAndRegisteredCompetitors();
|
||||
}
|
||||
|
||||
protected abstract void setRegisteredCompetitors();
|
||||
|
||||
private void setRegisterableCompetitorsAndRegisteredCompetitors() {
|
||||
allCompetitorsTable.refreshCompetitorList(null, new Callback<Iterable<CompetitorDTO>, Throwable>() {
|
||||
allCompetitorsTable.refreshCompetitorList(this.restrictPoolToLeaderboard ? leaderboardName : null, new Callback<Iterable<CompetitorDTO>, Throwable>() {
|
||||
@Override
|
||||
public void onSuccess(Iterable<CompetitorDTO> result) {
|
||||
setRegisteredCompetitors();
|
||||
registeredCompetitorsRetriever.accept(new AsyncCallback<Collection<CompetitorDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<CompetitorDTO> registeredCompetitors) {
|
||||
moveFromPoolToRegistered(registeredCompetitors);
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable reason) {
|
||||
errorReporter.reportError("Could not load already registered competitors: " + reason.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable reason) {
|
||||
errorReporter.reportError("Could not load pool of competitors: " + reason.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -243,6 +272,12 @@ public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDia
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
private void validateAndUpdate() {
|
||||
if (validator != null) {
|
||||
validator.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void activateRegistrationButtons(){
|
||||
registerBtn.setEnabled(true);
|
||||
unregisterBtn.setEnabled(true);
|
||||
@@ -253,10 +288,21 @@ public abstract class AbstractCompetitorRegistrationsDialog extends DataEntryDia
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<CompetitorDTO> getResult() {
|
||||
public Set<CompetitorDTO> getResult() {
|
||||
final Set<CompetitorDTO> registeredCompetitors = new HashSet<>();
|
||||
Util.addAll(registeredCompetitorsTable.getAllCompetitors(), registeredCompetitors);
|
||||
return registeredCompetitors;
|
||||
}
|
||||
|
||||
public void grayOutCompetitorsFromRegistered(List<CompetitorWithToolTipDTO> competitors) {
|
||||
registeredCompetitorsTable.grayOutCompetitors(competitors);
|
||||
}
|
||||
|
||||
public void grayOutCompetitorsFromPool(List<CompetitorWithToolTipDTO> competitors) {
|
||||
allCompetitorsTable.grayOutCompetitors(competitors);
|
||||
}
|
||||
|
||||
public void moveFromPoolToRegistered(Collection<CompetitorDTO> registeredCompetitors) {
|
||||
move(allCompetitorsTable, registeredCompetitorsTable, registeredCompetitors);
|
||||
}
|
||||
}
|
||||
+32
-35
@@ -34,6 +34,18 @@ import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
|
||||
import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel;
|
||||
|
||||
/**
|
||||
* A filterable competitor table. The data model is managed by the {@link #getFilterField() filter field}. In
|
||||
* order to set an initial set of competitors to display by this table, use {@link #refreshCompetitorList(Iterable)}.
|
||||
* The selected competitors can be obtained from the {@link #getSelectionModel() selection model}. The competitor
|
||||
* set can also be updated to that of a leaderboard by using {@link #refreshCompetitorList(String)}, providing the
|
||||
* leaderboard name as parameter. The competitors currently in the table (regardless of the current filter settings)
|
||||
* are returned by {@link #getAllCompetitors()}.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
* @param <S>
|
||||
*/
|
||||
public class CompetitorTableWrapper<S extends RefreshableSelectionModel<CompetitorDTO>> extends TableWrapper<CompetitorDTO, S> {
|
||||
private final LabeledAbstractFilterablePanel<CompetitorDTO> filterField;
|
||||
|
||||
@@ -285,43 +297,28 @@ public class CompetitorTableWrapper<S extends RefreshableSelectionModel<Competit
|
||||
*/
|
||||
public void refreshCompetitorList(String leaderboardName, final Callback<Iterable<CompetitorDTO>,
|
||||
Throwable> callback) {
|
||||
final AsyncCallback<Iterable<CompetitorDTO>> myCallback = new AsyncCallback<Iterable<CompetitorDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Remote Procedure Call getCompetitors() - Failure: " + caught.getMessage());
|
||||
if (callback != null) {
|
||||
callback.onFailure(caught);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Iterable<CompetitorDTO> result) {
|
||||
getFilteredCompetitors(result);
|
||||
refreshCompetitorList(result);
|
||||
if (callback != null) {
|
||||
callback.onSuccess(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (leaderboardName != null) {
|
||||
sailingService.getCompetitorsOfLeaderboard(leaderboardName, new AsyncCallback<Iterable<CompetitorDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Remote Procedure Call getCompetitors() - Failure: " + caught.getMessage());
|
||||
if (callback != null) {
|
||||
callback.onFailure(caught);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Iterable<CompetitorDTO> result) {
|
||||
refreshCompetitorList(result);
|
||||
if (callback != null) {
|
||||
callback.onSuccess(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
sailingService.getCompetitorsOfLeaderboard(leaderboardName, myCallback);
|
||||
} else {
|
||||
sailingService.getCompetitors(new AsyncCallback<Iterable<CompetitorDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Remote Procedure Call getCompetitors() - Failure: " + caught.getMessage());
|
||||
if (callback != null) {
|
||||
callback.onFailure(caught);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Iterable<CompetitorDTO> result) {
|
||||
getFilteredCompetitors(result);
|
||||
refreshCompetitorList(result);
|
||||
if (callback != null) {
|
||||
callback.onSuccess(result);
|
||||
}
|
||||
}
|
||||
});
|
||||
sailingService.getCompetitors(myCallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialog {
|
||||
public abstract class FlexibleLeaderboardDialog extends AbstractLeaderboardDialog<LeaderboardDescriptor> {
|
||||
protected ListBox scoringSchemeListBox;
|
||||
protected ListBox sailingEventsListBox;
|
||||
protected Collection<EventDTO> existingEvents;
|
||||
|
||||
+131
-38
@@ -33,6 +33,7 @@ import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.view.client.ListDataProvider;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaName;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
@@ -123,6 +124,16 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
}
|
||||
});
|
||||
|
||||
Button createRegattaLeaderboardWithEliminationsBtn = new Button(stringMessages.createRegattaLeaderboardWithEliminations() + "...");
|
||||
createRegattaLeaderboardWithEliminationsBtn.ensureDebugId("CreateRegattaLeaderboardWithEliminationsButton");
|
||||
controlsPanel.add(createRegattaLeaderboardWithEliminationsBtn);
|
||||
createRegattaLeaderboardWithEliminationsBtn.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent clickEvent) {
|
||||
createRegattaLeaderboardWithEliminations();
|
||||
}
|
||||
});
|
||||
|
||||
leaderboardRemoveButton = new Button(stringMessages.remove());
|
||||
leaderboardRemoveButton.ensureDebugId("LeaderboardsRemoveButton");
|
||||
leaderboardRemoveButton.setEnabled(false);
|
||||
@@ -227,11 +238,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
TextColumn<StrippedLeaderboardDTO> leaderboardTypeColumn = new TextColumn<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
public String getValue(StrippedLeaderboardDTO leaderboard) {
|
||||
String result = leaderboard.type.isRegattaLeaderboard() ? "Regatta" : "Flexible";
|
||||
if (leaderboard.type.isMetaLeaderboard()) {
|
||||
result += " , Meta";
|
||||
}
|
||||
return result;
|
||||
return leaderboard.type.toString();
|
||||
}
|
||||
};
|
||||
leaderboardTypeColumn.setSortable(true);
|
||||
@@ -285,37 +292,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
removeLeaderboard(leaderboardDTO);
|
||||
}
|
||||
} else if (LeaderboardConfigImagesBarCell.ACTION_EDIT.equals(value)) {
|
||||
final String oldLeaderboardName = leaderboardDTO.name;
|
||||
List<StrippedLeaderboardDTO> otherExistingLeaderboard = new ArrayList<StrippedLeaderboardDTO>();
|
||||
otherExistingLeaderboard.addAll(availableLeaderboardList);
|
||||
otherExistingLeaderboard.remove(leaderboardDTO);
|
||||
if (leaderboardDTO.type.isMetaLeaderboard()) {
|
||||
Window.alert(stringMessages.metaLeaderboardCannotBeChanged());
|
||||
} else {
|
||||
if (leaderboardDTO.type.isRegattaLeaderboard()) {
|
||||
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name,
|
||||
leaderboardDTO.displayName, /* scoring scheme provided by regatta */ null,
|
||||
leaderboardDTO.discardThresholds, leaderboardDTO.regattaName,
|
||||
leaderboardDTO.defaultCourseAreaId);
|
||||
AbstractLeaderboardDialog dialog = new RegattaLeaderboardEditDialog(Collections
|
||||
.unmodifiableCollection(otherExistingLeaderboard), Collections.unmodifiableCollection(allRegattas),
|
||||
descriptor, stringMessages, errorReporter,
|
||||
new DialogCallback<LeaderboardDescriptor>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(LeaderboardDescriptor result) {
|
||||
updateLeaderboard(oldLeaderboardName, result);
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
} else {
|
||||
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name, leaderboardDTO.displayName, leaderboardDTO.scoringScheme, leaderboardDTO.discardThresholds, leaderboardDTO.defaultCourseAreaId);
|
||||
openUpdateFlexibleLeaderboardDialog(leaderboardDTO, otherExistingLeaderboard, leaderboardDTO.name, descriptor);
|
||||
}
|
||||
}
|
||||
editLeaderboard(leaderboardDTO);
|
||||
} else if (LeaderboardConfigImagesBarCell.ACTION_EDIT_SCORES.equals(value)) {
|
||||
String leaderboardEditingUrl = EntryPointWithSettingsLinkFactory.createLeaderboardEditingLink(leaderboardDTO.name);
|
||||
Window.open(leaderboardEditingUrl, "_blank", null);
|
||||
@@ -357,6 +334,81 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
leaderboardTable.setSelectionModel(selectionCheckboxColumn.getSelectionModel(), selectionCheckboxColumn.getSelectionManager());
|
||||
}
|
||||
|
||||
private void editLeaderboard(StrippedLeaderboardDTO leaderboardDTO) {
|
||||
final String oldLeaderboardName = leaderboardDTO.name;
|
||||
List<StrippedLeaderboardDTO> otherExistingLeaderboard = new ArrayList<StrippedLeaderboardDTO>();
|
||||
otherExistingLeaderboard.addAll(availableLeaderboardList);
|
||||
otherExistingLeaderboard.remove(leaderboardDTO);
|
||||
if (leaderboardDTO.type.isMetaLeaderboard()) {
|
||||
Window.alert(stringMessages.metaLeaderboardCannotBeChanged());
|
||||
} else {
|
||||
AbstractLeaderboardDialog<?> dialog;
|
||||
switch (leaderboardDTO.type) {
|
||||
case RegattaLeaderboard:
|
||||
dialog = new RegattaLeaderboardEditDialog(Collections
|
||||
.unmodifiableCollection(otherExistingLeaderboard), Collections.unmodifiableCollection(allRegattas),
|
||||
createLeaderboardDescriptor(leaderboardDTO, /* scoring scheme is provided by regatta, not leaderboard */ null),
|
||||
stringMessages, errorReporter,
|
||||
new DialogCallback<LeaderboardDescriptor>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(LeaderboardDescriptor result) {
|
||||
updateLeaderboard(oldLeaderboardName, result);
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
break;
|
||||
case RegattaLeaderboardWithEliminations:
|
||||
dialog = new RegattaLeaderboardWithEliminationsEditDialog(sailingService, Collections
|
||||
.unmodifiableCollection(otherExistingLeaderboard),
|
||||
Collections.unmodifiableCollection(allRegattas),
|
||||
new LeaderboardDescriptorWithEliminations(
|
||||
createLeaderboardDescriptor(leaderboardDTO, /* scoring scheme is provided by regatta, not leaderboard */ null),
|
||||
/* eliminated competitors */ null), stringMessages,
|
||||
errorReporter, new DialogCallback<LeaderboardDescriptorWithEliminations>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(LeaderboardDescriptorWithEliminations result) {
|
||||
updateLeaderboard(oldLeaderboardName, result);
|
||||
sailingService.setEliminatedCompetitors(oldLeaderboardName, result.getEliminatedCompetitors(),
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error trying to update eliminated competitors for leaderboard "
|
||||
+ oldLeaderboardName + ": " + caught.getMessage());
|
||||
}
|
||||
@Override
|
||||
public void onSuccess(Void v) {
|
||||
// nothing to do for now; maybe if the table once will show the number of eliminated competitors or similar, update it
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
break;
|
||||
case FlexibleLeaderboard:
|
||||
openUpdateFlexibleLeaderboardDialog(leaderboardDTO, otherExistingLeaderboard, leaderboardDTO.name, createLeaderboardDescriptor(leaderboardDTO,
|
||||
leaderboardDTO.scoringScheme));
|
||||
break;
|
||||
default:
|
||||
Window.alert(stringMessages.unknownLeaderboardType(leaderboardDTO.type.name()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LeaderboardDescriptor createLeaderboardDescriptor(StrippedLeaderboardDTO leaderboardDTO, ScoringSchemeType scoringScheme) {
|
||||
return new LeaderboardDescriptor(leaderboardDTO.name,
|
||||
leaderboardDTO.displayName, scoringScheme,
|
||||
leaderboardDTO.discardThresholds, leaderboardDTO.regattaName,
|
||||
leaderboardDTO.defaultCourseAreaId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addColumnsToRacesTable(CellTable<RaceColumnDTOAndFleetDTOWithNameBasedEquality> racesTable) {
|
||||
TextColumn<RaceColumnDTOAndFleetDTOWithNameBasedEquality> explicitFactorColumn = new TextColumn<RaceColumnDTOAndFleetDTOWithNameBasedEquality>() {
|
||||
@@ -742,8 +794,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
}
|
||||
|
||||
private void createFlexibleLeaderboard(List<EventDTO> existingEvents) {
|
||||
|
||||
AbstractLeaderboardDialog dialog = new FlexibleLeaderboardCreateDialog(Collections.unmodifiableCollection(availableLeaderboardList),
|
||||
final FlexibleLeaderboardCreateDialog dialog = new FlexibleLeaderboardCreateDialog(Collections.unmodifiableCollection(availableLeaderboardList),
|
||||
stringMessages, Collections.unmodifiableCollection(existingEvents), errorReporter, new DialogCallback<LeaderboardDescriptor>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
@@ -801,6 +852,48 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void createRegattaLeaderboardWithEliminations() {
|
||||
RegattaLeaderboardWithEliminationsCreateDialog dialog = new RegattaLeaderboardWithEliminationsCreateDialog(
|
||||
sailingService, Collections.unmodifiableCollection(availableLeaderboardList),
|
||||
Collections.unmodifiableCollection(allRegattas), stringMessages, errorReporter,
|
||||
new DialogCallback<LeaderboardDescriptorWithEliminations>() {
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ok(final LeaderboardDescriptorWithEliminations newLeaderboard) {
|
||||
sailingService.createRegattaLeaderboardWithEliminations(newLeaderboard.getName(), newLeaderboard.getDisplayName(),
|
||||
newLeaderboard.getRegattaName(), new AsyncCallback<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
errorReporter.reportError("Error trying to create the new regatta leaderboard " + newLeaderboard.getName()
|
||||
+ ": " + t.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(StrippedLeaderboardDTO result) {
|
||||
addLeaderboard(result);
|
||||
sailingService.setEliminatedCompetitors(newLeaderboard.getName(), newLeaderboard.getEliminatedCompetitors(), new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error trying to set the eliminated competitors for leaderboard "+newLeaderboard.getName()+
|
||||
": "+caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
// nothing to do for now until any elimination properties are shown in the leaderboard table...
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
dialog.ensureDebugId("RegattaLeaderboardCreateDialog");
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void addLeaderboard(StrippedLeaderboardDTO result) {
|
||||
filteredLeaderboardList.getList().add(result);
|
||||
availableLeaderboardList.add(result);
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
|
||||
public class LeaderboardDescriptorWithEliminations extends LeaderboardDescriptor {
|
||||
private final Set<CompetitorDTO> eliminatedCompetitors;
|
||||
|
||||
public LeaderboardDescriptorWithEliminations(LeaderboardDescriptor base, Set<CompetitorDTO> eliminatedCompetitors) {
|
||||
super(base.getName(), base.getDisplayName(), base.getScoringScheme(), base.getDiscardThresholds(), base.getRegattaName(), base.getCourseAreaId());
|
||||
this.eliminatedCompetitors = eliminatedCompetitors;
|
||||
}
|
||||
|
||||
public Set<CompetitorDTO> getEliminatedCompetitors() {
|
||||
return eliminatedCompetitors;
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -4,7 +4,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.sap.sailing.domain.common.LeaderboardType;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.controls.ImagesBarCell;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
@@ -34,7 +33,7 @@ public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell {
|
||||
result.add(new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())));
|
||||
result.add(new ImageSpec(ACTION_UNLINK, stringMessages.actionRaceUnlink(), makeImagePrototype(resources.unlinkIcon())));
|
||||
StrippedLeaderboardDTO selectedLeaderboard = selectedLeaderboardProvider.getSelectedLeaderboard();
|
||||
if (selectedLeaderboard != null && selectedLeaderboard.type != LeaderboardType.RegattaLeaderboard && selectedLeaderboard.type != LeaderboardType.RegattaMetaLeaderboard) {
|
||||
if (selectedLeaderboard != null && !selectedLeaderboard.type.isRegattaLeaderboard()) {
|
||||
// race columns cannot be removed from a regatta leaderboard; they need to be removed from the regatta instead
|
||||
result.add(new ImageSpec(ACTION_REMOVE, stringMessages.actionRaceRemove(), makeImagePrototype(IconResources.INSTANCE.removeIcon())));
|
||||
}
|
||||
|
||||
+48
-57
@@ -10,12 +10,13 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorWithToolTipDTO;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
@@ -26,12 +27,15 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegistrationsDialog {
|
||||
public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegistrationDialog {
|
||||
|
||||
private String fleetName;
|
||||
private String raceColumnName;
|
||||
private CheckBox competitorRegistrationInRaceLogCheckBox;
|
||||
private final Map<String, Set<CompetitorDTO>> fleetNameWithCompetitors;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final SailingServiceAsync sailingService;
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
private static class Validator implements com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator<Set<CompetitorDTO>> {
|
||||
private CheckBox competitorRegistrationInRaceLogCheckBox;
|
||||
@@ -102,14 +106,33 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<Set<CompetitorDTO>> callback,
|
||||
String leaderboardName, String boatClass, String raceColumnName, String fleetName, List<FleetDTO> fleets, Validator validator) {
|
||||
super(sailingService, stringMessages, errorReporter, editable, callback, leaderboardName, boatClass, validator);
|
||||
this.errorReporter = errorReporter;
|
||||
this.stringMessages = stringMessages;
|
||||
this.sailingService = sailingService;
|
||||
this.raceColumnName = raceColumnName;
|
||||
this.fleetName = fleetName;
|
||||
fleetNameWithCompetitors = findCompetitorsFromTheSameRaceColumn(fleets);
|
||||
competitorRegistrationInRaceLogCheckBox = createCheckbox(stringMessages.registerCompetitorsOnRace());
|
||||
validator.setCompetitorRegistrationInRaceLogCheckBox(competitorRegistrationInRaceLogCheckBox);
|
||||
validator.setCompetitorRegistrationInRaceLogCheckBox(getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages));
|
||||
validator.setFleetWithCompetitors(fleetNameWithCompetitors);
|
||||
setupCompetitorRegistationsOnRaceCheckbox();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget[] getAdditionalWidgetsToInsertAboveCompetitorTables(StringMessages stringMessages) {
|
||||
return new Widget[] { getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages) };
|
||||
}
|
||||
|
||||
private CheckBox getOrCreateCompetitorRegistrationInRaceLogCheckBox(StringMessages stringMessages) {
|
||||
if (competitorRegistrationInRaceLogCheckBox == null) {
|
||||
competitorRegistrationInRaceLogCheckBox = createCheckbox(stringMessages.registerCompetitorsOnRace());
|
||||
}
|
||||
return competitorRegistrationInRaceLogCheckBox;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Consumer<AsyncCallback<Collection<CompetitorDTO>>> getRegisteredCompetitorsRetriever() {
|
||||
return (callback)->getRegisteredCompetitors(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* For all {@code fleets} passed and not equal to {@link #fleetName} retrieves the competitor-per-race registrations
|
||||
@@ -165,60 +188,28 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis
|
||||
}
|
||||
}
|
||||
}
|
||||
allCompetitorsTable.grayOutCompetitors(competitors);
|
||||
registeredCompetitorsTable.grayOutCompetitors(competitors);
|
||||
competitorRegistrationsPanel.grayOutCompetitorsFromPool(competitors);
|
||||
competitorRegistrationsPanel.grayOutCompetitorsFromRegistered(competitors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setRegisteredCompetitors() {
|
||||
if (showOnlyCompetitorsOfLog()) {
|
||||
sailingService.getCompetitorRegistrationsInRaceLog(leaderboardName, raceColumnName, fleetName,
|
||||
new AsyncCallback<Collection<CompetitorDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<CompetitorDTO> registeredCompetitors) {
|
||||
move(allCompetitorsTable, registeredCompetitorsTable, registeredCompetitors);
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable reason) {
|
||||
errorReporter.reportError("Could not load already registered competitors: "
|
||||
+ reason.getMessage());
|
||||
}
|
||||
});
|
||||
private void getRegisteredCompetitors(AsyncCallback<Collection<CompetitorDTO>> callback) {
|
||||
if (competitorRegistrationsPanel.showOnlyCompetitorsOfLog()) {
|
||||
sailingService.getCompetitorRegistrationsInRaceLog(leaderboardName, raceColumnName, fleetName, callback);
|
||||
} else {
|
||||
sailingService.getCompetitorRegistrationsForRace(leaderboardName, raceColumnName, fleetName,
|
||||
new AsyncCallback<Collection<CompetitorDTO>>() {
|
||||
@Override
|
||||
public void onSuccess(Collection<CompetitorDTO> registeredCompetitors) {
|
||||
move(allCompetitorsTable, registeredCompetitorsTable, registeredCompetitors);
|
||||
validateAndUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable reason) {
|
||||
errorReporter.reportError("Could not load already registered competitors: "
|
||||
+ reason.getMessage());
|
||||
}
|
||||
});
|
||||
sailingService.getCompetitorRegistrationsForRace(leaderboardName, raceColumnName, fleetName, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalWidgets(FlowPanel mainPanel) {
|
||||
mainPanel.add(competitorRegistrationInRaceLogCheckBox);
|
||||
}
|
||||
|
||||
|
||||
private void setupCompetitorRegistationsOnRaceCheckbox() {
|
||||
sailingService.areCompetitorRegistrationsEnabledForRace(leaderboardName, raceColumnName, fleetName,
|
||||
new AsyncCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean isEnabled) {
|
||||
competitorRegistrationInRaceLogCheckBox.setValue(isEnabled);
|
||||
getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).setValue(isEnabled);
|
||||
if (isEnabled) {
|
||||
activateRegistrationButtons();
|
||||
competitorRegistrationsPanel.activateRegistrationButtons();
|
||||
} else {
|
||||
deactivateRegistrationButtons(stringMessages.competitorRegistrationsOnRaceDisabled());
|
||||
competitorRegistrationsPanel.deactivateRegistrationButtons(stringMessages.competitorRegistrationsOnRaceDisabled());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,12 +219,12 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis
|
||||
+ reason.getMessage());
|
||||
}
|
||||
});
|
||||
competitorRegistrationInRaceLogCheckBox.addClickHandler(new ClickHandler() {
|
||||
getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
final String title;
|
||||
final String message;
|
||||
if (competitorRegistrationInRaceLogCheckBox.getValue()) {
|
||||
if (getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).getValue()) {
|
||||
title = stringMessages.doYouWantToRegisterCompetitorsDirectlyOnTheRace();
|
||||
message = stringMessages.warningDirectCompetitorRegistration();
|
||||
} else {
|
||||
@@ -244,14 +235,14 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis
|
||||
new DialogCallback<Void>() {
|
||||
@Override
|
||||
public void ok(Void editedObject) {
|
||||
if (competitorRegistrationInRaceLogCheckBox.getValue()) {
|
||||
if (getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).getValue()) {
|
||||
sailingService.enableCompetitorRegistrationsForRace(leaderboardName,
|
||||
raceColumnName, fleetName, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(Void isEnabled) {
|
||||
competitorRegistrationInRaceLogCheckBox.setValue(true);
|
||||
activateRegistrationButtons();
|
||||
refreshCompetitors();
|
||||
getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).setValue(true);
|
||||
competitorRegistrationsPanel.activateRegistrationButtons();
|
||||
competitorRegistrationsPanel.refreshCompetitors();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -267,10 +258,10 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis
|
||||
raceColumnName, fleetName, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onSuccess(Void isEnabled) {
|
||||
competitorRegistrationInRaceLogCheckBox.setValue(false);
|
||||
deactivateRegistrationButtons(stringMessages
|
||||
getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).setValue(false);
|
||||
competitorRegistrationsPanel.deactivateRegistrationButtons(stringMessages
|
||||
.competitorRegistrationsOnRaceDisabled());
|
||||
refreshCompetitors();
|
||||
competitorRegistrationsPanel.refreshCompetitors();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -285,7 +276,7 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
competitorRegistrationInRaceLogCheckBox.setValue(!competitorRegistrationInRaceLogCheckBox.getValue());
|
||||
getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).setValue(!getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).getValue());
|
||||
}
|
||||
}) {
|
||||
@Override
|
||||
@@ -299,7 +290,7 @@ public class RaceLogCompetitorRegistrationDialog extends AbstractCompetitorRegis
|
||||
|
||||
@Override
|
||||
protected Set<CompetitorDTO> getResult() {
|
||||
if (competitorRegistrationInRaceLogCheckBox.getValue()) {
|
||||
if (getOrCreateCompetitorRegistrationInRaceLogCheckBox(stringMessages).getValue()) {
|
||||
return super.getResult();
|
||||
} else {
|
||||
return Collections.emptySet();
|
||||
|
||||
+2
-2
@@ -8,9 +8,9 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public class RaceOrRegattaImportCompetitorCallback extends ImportCompetitorCallback {
|
||||
private final AbstractCompetitorRegistrationsDialog registrationsDialog;
|
||||
private final CompetitorRegistrationsPanel registrationsDialog;
|
||||
|
||||
public RaceOrRegattaImportCompetitorCallback(AbstractCompetitorRegistrationsDialog registrationsDialog, SailingServiceAsync sailingService, ErrorReporter errorReporter,
|
||||
public RaceOrRegattaImportCompetitorCallback(CompetitorRegistrationsPanel registrationsDialog, SailingServiceAsync sailingService, ErrorReporter errorReporter,
|
||||
StringMessages stringMessages) {
|
||||
super(sailingService, errorReporter, stringMessages);
|
||||
this.registrationsDialog = registrationsDialog;
|
||||
|
||||
+6
-4
@@ -11,10 +11,12 @@ import com.sap.sse.gwt.client.ErrorReporter;
|
||||
|
||||
public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
|
||||
|
||||
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards, Collection<RegattaDTO> existingRegattas, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter, DialogCallback<LeaderboardDescriptor> callback) {
|
||||
super(stringMessages.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringMessages, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
|
||||
|
||||
public RegattaLeaderboardCreateDialog(Collection<StrippedLeaderboardDTO> existingLeaderboards,
|
||||
Collection<RegattaDTO> existingRegattas, StringMessages stringMessages, ErrorReporter errorReporter,
|
||||
DialogCallback<LeaderboardDescriptor> callback) {
|
||||
super(stringMessages.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringMessages,
|
||||
errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards),
|
||||
callback);
|
||||
nameTextBox = createTextBox(null);
|
||||
nameTextBox.ensureDebugId("NameTextBox");
|
||||
nameTextBox.setVisibleLength(50);
|
||||
|
||||
Loaded 100 of 164 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user