diff --git a/configuration/aws-automation/launch-mongodb-replica.sh b/configuration/aws-automation/launch-mongodb-replica.sh index 63dfb6a3b59..931c343c024 100755 --- a/configuration/aws-automation/launch-mongodb-replica.sh +++ b/configuration/aws-automation/launch-mongodb-replica.sh @@ -13,18 +13,20 @@ # AVAILABILITY_ZONE=eu-west-1c INSTANCE_TYPE=i2.xlarge -IMAGE_ID=ami-0875074f93689aa3a +IMAGE_ID=ami-0e5f927ef86e00bd2 KEY_NAME=Axel REPLICA_SET_NAME= REPLICA_SET_PRIMARY= REPLICA_SET_PRIORITY= +REPLICA_SET_VOTES= if [ $# -eq 0 ]; then - echo "$0 [-r ] [-p :] [-P ] [-a ] [-i ] [-k ]" + echo "$0 [-r ] [-p :] [-P ] [-a ] [-i ] [-k ] [-v ]" echo "" echo "-r Replica set name, e.g., live" echo "-p Primary host:port, e.g., mongo0.internal.sapsailing.com:27017" echo "-P priority, e.g., 0 if the replica shall never become PRIMARY" + echo "-v number of votes, e.g., 0 to not let this affect the PRIMARY's votes" echo "-t Instance type; defaults to $INSTANCE_TYPE" echo "-a Availability zone, defaults to $AVAILABILITY_ZONE" echo "-i Amazon Machine Image (AMI) ID to use to launch the instance" @@ -39,13 +41,14 @@ if [ $# -eq 0 ]; then exit 2 fi -options='r:p:P:t:a:i:k:' +options='r:p:P:t:a:i:k:v:' while getopts $options option do case $option in r) REPLICA_SET_NAME=$OPTARG;; p) REPLICA_SET_PRIMARY=$OPTARG;; P) REPLICA_SET_PRIORITY=$OPTARG;; + v) REPLICA_SET_VOTES=$OPTARG;; t) INSTANCE_TYPE=$OPTARG;; a) AVAILABILITY_ZONE=$OPTARG;; i) IMAGE_ID=$OPTARG;; @@ -57,4 +60,5 @@ done aws ec2 run-instances --placement AvailabilityZone=$AVAILABILITY_ZONE --instance-type $INSTANCE_TYPE --security-group-ids sg-0a9bc2fb61f10a342 --image-id $IMAGE_ID --count 1 --user-data "REPLICA_SET_NAME=$REPLICA_SET_NAME REPLICA_SET_PRIMARY=$REPLICA_SET_PRIMARY -REPLICA_SET_PRIORITY=$REPLICA_SET_PRIORITY" --ebs-optimized --key-name $KEY_NAME --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY}]" "ResourceType=volume,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY}]" +REPLICA_SET_PRIORITY=$REPLICA_SET_PRIORITY +REPLICA_SET_VOTES=$REPLICA_SET_VOTES" --ebs-optimized --key-name $KEY_NAME --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY}]" "ResourceType=volume,Tags=[{Key=Name,Value=MongoDB Replica Set $REPLICA_SET_NAME P$REPLICA_SET_PRIORITY}]" diff --git a/configuration/launchhudsonslave b/configuration/launchhudsonslave index ce5b79e4054..963374c8835 100755 --- a/configuration/launchhudsonslave +++ b/configuration/launchhudsonslave @@ -1,7 +1,6 @@ #!/bin/bash AWS=/root/.local/bin/aws REGION=eu-west-1 -#HUDSON_SLAVE_AMI_ID=ami-09f0f1f11364aeddb HUDSON_SLAVE_AMI_ID=ami-027eed1bdca18b979 echo Launching instance... instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'` diff --git a/configuration/mongo_instance_setup/add-as-replica b/configuration/mongo_instance_setup/add-as-replica index 32996a8155b..2556fefcc73 100755 --- a/configuration/mongo_instance_setup/add-as-replica +++ b/configuration/mongo_instance_setup/add-as-replica @@ -11,7 +11,11 @@ REPLICA_SET_PRIORITY=$(ec2-metadata | grep REPLICA_SET_PRIORITY | sed -e 's/^use if [ -z "$REPLICA_SET_PRIORITY" ]; then REPLICA_SET_PRIORITY=1 fi +REPLICA_SET_VOTES=$(ec2-metadata | grep REPLICA_SET_VOTES | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_VOTES=//') +if [ -z "$REPLICA_SET_VOTES" ]; then + REPLICA_SET_VOTES=0 +fi if [ \! -z "REPLICA_SET_PRIMARY" ]; then IP=$(ec2-metadata -o | sed -e 's/^local-ipv4: //') - echo "rs.add({host: \"$IP:27017\", priority: $REPLICA_SET_PRIORITY})" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true" + echo "rs.add({host: \"$IP:27017\", priority: $REPLICA_SET_PRIORITY, votes: $REPLICA_SET_VOTES})" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true" fi diff --git a/configuration/mongo_instance_setup/mongo-replica-set.service b/configuration/mongo_instance_setup/mongo-replica-set.service index 7868305cef1..5cba8232875 100644 --- a/configuration/mongo_instance_setup/mongo-replica-set.service +++ b/configuration/mongo_instance_setup/mongo-replica-set.service @@ -11,3 +11,4 @@ Type=oneshot RemainAfterExit=true ExecStart=/usr/local/bin/add-as-replica ExecStop=/usr/local/bin/remove-as-replica +TimeoutStopSpec=120s diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java index 01b3ed75872..5e4366997d0 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/Leaderboard.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.BoatClass; @@ -539,6 +540,15 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns { Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, Set discardedRaceColumns); + /** + * Same as {@link #getNetPoints(Competitor, RaceColumn, TimePoint, Set)}, only that a supplier for + * the total points for the {@code competitor} in column {@code raceColumn} at time point {@code timePoint} + * is provided. This helps if a caller also needs to determine the total points anyway, saving redundant + * calculations. + */ + Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, + Set discardedRaceColumns, Supplier totalPointsProvider); + TimePoint getNowMinusDelay(); /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 8185529a029..e1c2add400f 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -13,6 +13,7 @@ import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -466,11 +467,17 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW @Override public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, Set discardedRaceColumns) { + return getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns, ()->getTotalPoints(competitor, raceColumn, timePoint)); + } + + @Override + public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, + Set discardedRaceColumns, Supplier totalPointsProvider) { Double result; if (isDiscarded(competitor, raceColumn, timePoint, discardedRaceColumns)) { result = 0.0; } else { - final Double totalPoints = getTotalPoints(competitor, raceColumn, timePoint); + final Double totalPoints = totalPointsProvider.get(); if (totalPoints == null) { result = null; } else { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java index d0dfd8cf9af..841e32df4c2 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/DelegatingRegattaLeaderboardWithCompetitorElimination.java @@ -495,4 +495,11 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr public void deregisterBoats(Iterable boats) { getFullLeaderboard().deregisterBoats(boats); } + + @Override + public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, + Set discardedRaceColumns, Supplier totalPointsProvider) { + return getFullLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns, + totalPointsProvider); + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java index 3b79eee114d..377cfbe0fd1 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardTotalRankComparator.java @@ -94,8 +94,10 @@ public class LeaderboardTotalRankComparator implements Comparator { this.discardedRaceColumnsPerCompetitor.put(competitor, discardedRaceColumns); for (RaceColumn raceColumn : raceColumnsToConsider) { Pair key = new Util.Pair(competitor, raceColumn); - netPointsCache.put(key, leaderboard.getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns)); - totalPointsCache.put(key, leaderboard.getTotalPoints(competitor, raceColumn, timePoint)); + final Double totalPoints = leaderboard.getTotalPoints(competitor, raceColumn, timePoint); + netPointsCache.put(key, leaderboard.getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns, + /* total points provider */ ()->totalPoints)); + totalPointsCache.put(key, totalPoints); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java index 79ef6af15c7..6a45fdf97c2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java @@ -61,6 +61,7 @@ import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; import com.sap.sse.gwt.client.celltable.SelectionCheckboxColumn; import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; import com.sap.sse.security.shared.dto.NamedDTO; import com.sap.sse.security.ui.client.UserService; import com.sap.sse.security.ui.client.component.AccessControlledButtonPanel; @@ -184,7 +185,7 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); leaderboardTable = new FlushableCellTable(/* pageSize */10000, tableRes); filterLeaderboardPanel = new LabeledAbstractFilterablePanel(lblFilterEvents, - availableLeaderboardList, filteredLeaderboardList) { + availableLeaderboardList, filteredLeaderboardList, stringMessages) { @Override public List getSearchableStrings(StrippedLeaderboardDTOWithSecurity t) { List strings = new ArrayList(); @@ -199,6 +200,8 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel } }; filterLeaderboardPanel.getTextBox().ensureDebugId("LeaderboardsFilterTextBox"); + filterLeaderboardPanel + .setCheckboxEnabledFilter(leaderboard -> userService.hasPermission(leaderboard, DefaultActions.UPDATE)); leaderboardsPanel.add(filterLeaderboardPanel); leaderboardTable.ensureDebugId("AvailableLeaderboardsTable"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java index 405dba290a0..9f27522ac85 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractTrackedRacesListComposite.java @@ -161,7 +161,7 @@ public abstract class AbstractTrackedRacesListComposite extends AbstractComposit filterPanel.add(lblFilterRaces); filterPanel.setCellVerticalAlignment(lblFilterRaces, HasVerticalAlignment.ALIGN_MIDDLE); raceList = raceTableWrapper.getDataProvider(); - filterablePanelRaces = new CustomizableFilterablePanel(allRaces, raceList) { + filterablePanelRaces = new CustomizableFilterablePanel(allRaces, raceList, stringMessages) { @Override public List getSearchableStrings(RaceDTO t) { List strings = new ArrayList(); @@ -205,6 +205,7 @@ public abstract class AbstractTrackedRacesListComposite extends AbstractComposit filterPanel.add(filterablePanelRaces); filterPanel.setCellVerticalAlignment(filterablePanelRaces, HasVerticalAlignment.ALIGN_MIDDLE); + filterablePanelRaces.setCheckboxEnabledFilter(race -> userService.hasPermission(race, DefaultActions.UPDATE)); refreshableSelectionModel = raceTableWrapper.getSelectionModel(); setupTableColumns(stringMessages, raceTableWrapper.getColumnSortHandler()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatTableWrapper.java index 264b99c7368..88baee4f89f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BoatTableWrapper.java @@ -151,7 +151,7 @@ public class BoatTableWrapper> exte }); filterField = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterBoats()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(BoatDTO boat) { List string = new ArrayList(); @@ -167,6 +167,7 @@ public class BoatTableWrapper> exte return table; } }; + filterField.setCheckboxEnabledFilter(boat -> userService.hasPermission(boat, DefaultActions.UPDATE)); registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); // BoatTable edit features diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactBoatTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactBoatTableWrapper.java index 4b5b08268f3..dee2696f1aa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactBoatTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactBoatTableWrapper.java @@ -132,7 +132,7 @@ public class CompactBoatTableWrapper(new ArrayList<>(), getDataProvider()) { + filterField = new CustomizableFilterablePanel(new ArrayList<>(), getDataProvider(), stringMessages) { @Override public Iterable getSearchableStrings(BoatDTO boat) { List string = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorTableWrapper.java index a2bdd211038..368a7e92cb8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompactCompetitorTableWrapper.java @@ -28,6 +28,8 @@ import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.celltable.EntityIdentityComparator; import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; +import com.sap.sse.security.shared.HasPermissions.DefaultActions; +import com.sap.sse.security.ui.client.UserService; /** * A compact filterable competitor table. The data model is managed by the {@link #getFilterField() filter field}. In @@ -44,7 +46,7 @@ public class CompactCompetitorTableWrapper boatsForCompetitors; public CompactCompetitorTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter, - boolean multiSelection, boolean enablePager) { + boolean multiSelection, boolean enablePager, UserService userService) { super(sailingService, stringMessages, errorReporter, multiSelection, enablePager, new EntityIdentityComparator() { @Override @@ -133,7 +135,7 @@ public class CompactCompetitorTableWrapper(new Label(stringMessages.filterCompetitors()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(CompetitorDTO t) { List string = new ArrayList(); @@ -148,6 +150,8 @@ public class CompactCompetitorTableWrapper userService.hasPermission(comp, DefaultActions.UPDATE)); + registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); mainPanel.insert(filterField, 0); table.addColumnSortHandler(competitorColumnListHandler); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorDescriptorTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorDescriptorTableWrapper.java index b18f591e867..3f10ad46039 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorDescriptorTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorDescriptorTableWrapper.java @@ -102,7 +102,7 @@ public class CompetitorDescriptorTableWrapper( new Label(stringMessages.filterImportedCompetitorsByNameSailRaceFleet()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(CompetitorDescriptor competitorDescriptor) { List string = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java index ee2270f901e..51b8b1b37e6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorTableWrapper.java @@ -301,7 +301,7 @@ public class CompetitorTableWrapper(new Label(getStringMessages().filterCompetitors()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(CompetitorDTO t) { List string = new ArrayList(); @@ -321,6 +321,7 @@ public class CompetitorTableWrapper userService.hasPermission(comp, DefaultActions.UPDATE)); registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); // CompetitorTableEditFeatures diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java index c50a16c26d5..da729623038 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java @@ -22,12 +22,13 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.ErrorReporter; -import com.sap.sse.gwt.client.celltable.ImagesBarColumn; import com.sap.sse.gwt.client.Notification; import com.sap.sse.gwt.client.Notification.NotificationType; +import com.sap.sse.gwt.client.celltable.ImagesBarColumn; import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; import com.sap.sse.gwt.client.dialog.DataEntryDialog; +import com.sap.sse.security.ui.client.UserService; /** * Allows an administrator to map a list of given competitors to boats for a race. @@ -60,11 +61,13 @@ public class CompetitorToBoatMappingsDialog extends DataEntryDialog competitorsAndBoats, DialogCallback> callback) { + final ErrorReporter errorReporter, String leaderboardName, Map competitorsAndBoats, + DialogCallback> callback, UserService userService) { super(stringMessages.actionEditCompetitorToBoatAssignments(), null, stringMessages.ok(), stringMessages.cancel(), new CompetitorToBoatMappingValidator(), callback); this.stringMessages = stringMessages; this.competitorToBoatMappings = new HashMap<>(competitorsAndBoats); - this.competitorTable = new CompactCompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); + this.competitorTable = new CompactCompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, + /* multiSelection */ false, /* enablePager */ true, userService); this.boatTable = new CompactBoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); ImagesBarColumn competitorActionColumn = new ImagesBarColumn<>(new CompactCompetitorConfigImagesBarCell(stringMessages)); competitorActionColumn.setFieldUpdater(new FieldUpdater() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsViewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsViewPanel.java index 07a2c90348a..1de886e2b12 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsViewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsViewPanel.java @@ -16,6 +16,7 @@ import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.async.MarkedAsyncCallback; import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; import com.sap.sse.gwt.client.celltable.RefreshableSingleSelectionModel; +import com.sap.sse.security.ui.client.UserService; /** * Allows an administrator to view the boats assigned to the competitors of a race. @@ -36,7 +37,8 @@ public class CompetitorToBoatMappingsViewPanel extends SimplePanel { private final RefreshableSelectionModel refreshableCompetitorSelectionModel; public CompetitorToBoatMappingsViewPanel(final SailingServiceAsync sailingService, final StringMessages stringMessages, - final ErrorReporter errorReporter, final String leaderboardName, final String raceColumnName, final String fleetName) { + final ErrorReporter errorReporter, final String leaderboardName, final String raceColumnName, + final String fleetName, UserService userService) { super(); this.sailingService = sailingService; this.stringMessages = stringMessages; @@ -44,7 +46,8 @@ public class CompetitorToBoatMappingsViewPanel extends SimplePanel { this.raceColumnName = raceColumnName; this.fleetName = fleetName; this.errorReporter = errorReporter; - this.competitorTable = new CompactCompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); + this.competitorTable = new CompactCompetitorTableWrapper<>(sailingService, stringMessages, errorReporter, + /* multiSelection */ false, /* enablePager */ true, userService); this.boatTable = new CompactBoatTableWrapper<>(sailingService, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true); refreshableCompetitorSelectionModel = competitorTable.getSelectionModel(); refreshableCompetitorSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java index 16749247f6d..42001790574 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventListComposite.java @@ -163,7 +163,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le eventListDataProvider = new ListDataProvider(); filterTextbox = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterEventsByName()), - allEvents, eventListDataProvider) { + allEvents, eventListDataProvider, stringMessages) { @Override public Iterable getSearchableStrings(EventDTO t) { List result = new ArrayList(); @@ -215,6 +215,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le panel.add(noEventsLabel); fillEvents(); initWidget(panel); + filterTextbox.setCheckboxEnabledFilter(event -> userService.hasPermission(event, DefaultActions.UPDATE)); } private CellTable createEventTable(UserDTO user) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionDeviceConfigurationsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionDeviceConfigurationsPanel.java index d665a883208..2b54159f9d8 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionDeviceConfigurationsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ExpeditionDeviceConfigurationsPanel.java @@ -88,7 +88,7 @@ public class ExpeditionDeviceConfigurationsPanel extends FlowPanel { filteredDeviceConfigurations.addDataDisplay(allDeviceConfigurations); final List emptyList = Collections.emptyList(); filterDeviceConfigurationsPanel = new LabeledAbstractFilterablePanel(new Label(stringMessages.expeditionDeviceConfigurations()), - emptyList, filteredDeviceConfigurations) { + emptyList, filteredDeviceConfigurations, stringMessages) { @Override public Iterable getSearchableStrings(ExpeditionDeviceConfiguration t) { Set strings = new HashSet<>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java index 931903bd935..cfa73e57946 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/IgtimiAccountsPanel.java @@ -105,7 +105,7 @@ public class IgtimiAccountsPanel extends FlowPanel { tableRes); final ListDataProvider filteredAccounts = new ListDataProvider<>(); filterAccountsPanel = new LabeledAbstractFilterablePanel( - new Label(stringMessages.igtimiAccounts()), Collections.emptyList(), filteredAccounts) { + new Label(stringMessages.igtimiAccounts()), Collections.emptyList(), filteredAccounts, stringMessages) { @Override public Iterable getSearchableStrings(AccountWithSecurityDTO t) { Set strings = Collections.singleton(t.getEmail()); @@ -124,6 +124,8 @@ public class IgtimiAccountsPanel extends FlowPanel { .getSelectionModel(); final Panel controlsPanel = new HorizontalPanel(); + filterAccountsPanel + .setCheckboxEnabledFilter(account -> userService.hasPermission(account, DefaultActions.UPDATE)); controlsPanel.add(filterAccountsPanel); final AccessControlledButtonPanel buttonPanel = new AccessControlledButtonPanel(userService, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java index 1cf3f9ab334..52e4c1447c8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java @@ -652,7 +652,8 @@ public class LeaderboardConfigPanel extends AbstractLeaderboardConfigPanel final String raceName = LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName) ? raceColumnName : raceColumnName + ", " + fleetName; ShowCompetitorToBoatMappingsDialog dialog = new ShowCompetitorToBoatMappingsDialog(sailingService, - stringMessages, errorReporter, selectedLeaderboardName, raceColumnName, fleetName, raceName); + stringMessages, errorReporter, selectedLeaderboardName, raceColumnName, fleetName, raceName, + userService); dialog.center(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java index 238be6e8bd4..4af19d4f721 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupConfigPanel.java @@ -220,7 +220,7 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel leaderboardsTable = new FlushableCellTable(10000, tableRes); leaderboardsTable.ensureDebugId("LeaderboardsCellTable"); leaderboardsFilterablePanel = new LabeledAbstractFilterablePanel( - filterLeaderboardsLabel, availableLeaderboards, leaderboardsProvider) { + filterLeaderboardsLabel, availableLeaderboards, leaderboardsProvider, stringMessages) { @Override public Iterable getSearchableStrings(StrippedLeaderboardDTO t) { List strings = new ArrayList(); @@ -484,7 +484,7 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel groupsTable = new FlushableCellTable(10000, tableRes); groupsTable.ensureDebugId("LeaderboardGroupsCellTable"); groupsFilterablePanel = new LabeledAbstractFilterablePanel(filterLeaderboardGroupsLbl, - availableLeaderboardGroups, groupsProvider) { + availableLeaderboardGroups, groupsProvider, stringMessages) { @Override public Iterable getSearchableStrings(LeaderboardGroupDTO t) { List string = new ArrayList(); @@ -499,6 +499,8 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel }; groupsFilterablePanel.getTextBox().ensureDebugId("LeaderboardGroupsFilterTextBox"); leaderboardGroupsContentPanel.add(groupsFilterablePanel); + groupsFilterablePanel.setCheckboxEnabledFilter( + leaderboardGroup -> userService.hasPermission(leaderboardGroup, DefaultActions.UPDATE)); final Button createButton = buttonPanel.addCreateAction(stringMessages.createNewLeaderboardGroup(), this::addNewGroup); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupSelectorComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupSelectorComposite.java index c17ba4bc277..19a95938b18 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupSelectorComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardGroupSelectorComposite.java @@ -145,7 +145,7 @@ public class LeaderboardGroupSelectorComposite extends Composite implements HasV availableLeaderboardGroupsProvider.addDataDisplay(availableLeaderboardGroupsTable); availableLeaderboardGroupsFilterablePanel = new LabeledAbstractFilterablePanel( new Label(stringMessages.filterLeaderboardGroupsByName()), - Collections. emptyList(), availableLeaderboardGroupsProvider) { + Collections. emptyList(), availableLeaderboardGroupsProvider, stringMessages) { @Override public Iterable getSearchableStrings(LeaderboardGroupDTO t) { List result = new ArrayList<>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java index 200e6ae1daa..6fe181066d4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java @@ -173,7 +173,7 @@ public class MediaPanel extends FlowPanel implements MediaTracksRefresher { this.filterableMediaTracks = new LabeledAbstractFilterablePanel(lblFilterRaces, allMediaTracks, - mediaTrackListDataProvider) { + mediaTrackListDataProvider, stringMessages) { @Override public List getSearchableStrings(MediaTrackWithSecurityDTO t) { List strings = new ArrayList(); @@ -194,6 +194,8 @@ public class MediaPanel extends FlowPanel implements MediaTracksRefresher { }; createMediaTracksTable(userService); filterableMediaTracks.getTextBox().ensureDebugId("MediaTracksFilterTextBox"); + filterableMediaTracks + .setCheckboxEnabledFilter(mediaTrack -> userService.hasPermission(mediaTrack, DefaultActions.UPDATE)); buttonAndFilterPanel.addUnsecuredWidget(filterableMediaTracks); refreshableSelectionModel.addSelectionChangeHandler(new Handler() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java index 1a6743eb6e7..cd6e7588046 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaListComposite.java @@ -108,7 +108,7 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer regattaListDataProvider = new ListDataProvider(); filterablePanelRegattas = new LabeledAbstractFilterablePanel(filterRegattasLabel, allRegattas, - regattaListDataProvider) { + regattaListDataProvider, stringMessages) { @Override public Iterable getSearchableStrings(RegattaDTO t) { List string = new ArrayList(); @@ -129,6 +129,8 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer regattaTable.ensureDebugId("RegattasCellTable"); refreshableRegattaMultiSelectionModel = (RefreshableMultiSelectionModel) regattaTable.getSelectionModel(); regattaTable.setVisible(false); + filterablePanelRegattas + .setCheckboxEnabledFilter(regatta -> userService.hasPermission(regatta, DefaultActions.UPDATE)); panel.add(filterablePanelRegattas); panel.add(regattaTable); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java index a56a2f7d33c..5223b66f7c4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java @@ -184,7 +184,7 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog( new Label(stringMessages.filterDeviceMappings()), - new ArrayList(), deviceMappingTable.getDataProvider()) { + new ArrayList(), deviceMappingTable.getDataProvider(), stringMessages) { @Override public Iterable getSearchableStrings(DeviceMappingDTO t) { List string = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RemoteServerInstancesManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RemoteServerInstancesManagementPanel.java index eb63e28f431..a0737083725 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RemoteServerInstancesManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RemoteServerInstancesManagementPanel.java @@ -70,7 +70,7 @@ public class RemoteServerInstancesManagementPanel extends SimplePanel { serverDataProvider = new ListDataProvider(); filteredServerTablePanel = new LabeledAbstractFilterablePanel( new Label(stringMessages.filterBy() + ":"), Collections. emptyList(), - serverDataProvider) { + serverDataProvider, stringMessages) { @Override public List getSearchableStrings(RemoteSailingServerReferenceDTO t) { List strings = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ShowCompetitorToBoatMappingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ShowCompetitorToBoatMappingsDialog.java index 78319335b3b..cbdd1d94c17 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ShowCompetitorToBoatMappingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ShowCompetitorToBoatMappingsDialog.java @@ -9,11 +9,12 @@ import com.google.gwt.user.client.ui.VerticalPanel; 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.security.ui.client.UserService; public class ShowCompetitorToBoatMappingsDialog extends DialogBox { public ShowCompetitorToBoatMappingsDialog(final SailingServiceAsync sailingService, final StringMessages stringMessages, final ErrorReporter errorReporter, String leaderboardName, final String raceColumnName, final String fleetName, - String raceName) { + String raceName, UserService userService) { super(); setText(stringMessages.actionShowCompetitorToBoatAssignments()); setAnimationEnabled(true); @@ -26,7 +27,7 @@ public class ShowCompetitorToBoatMappingsDialog extends DialogBox { }); VerticalPanel vPanel = new VerticalPanel(); CompetitorToBoatMappingsViewPanel competitorPanel = new CompetitorToBoatMappingsViewPanel(sailingService, - stringMessages, errorReporter, leaderboardName, raceColumnName, fleetName); + stringMessages, errorReporter, leaderboardName, raceColumnName, fleetName, userService); vPanel.add(new Label(stringMessages.race() + ": " + raceName)); vPanel.add(competitorPanel); vPanel.add(okButton); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java index 52c18c66cca..72f7b0b1a76 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java @@ -451,7 +451,7 @@ public class SmartphoneTrackingEventManagementPanel @Override public void cancel() { } - }).show(); + }, userService).show(); } @Override @@ -935,7 +935,7 @@ public class SmartphoneTrackingEventManagementPanel final String raceName = LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleetName) ? raceColumnName : raceColumnName + ", " + fleetName; ShowCompetitorToBoatMappingsDialog dialog = new ShowCompetitorToBoatMappingsDialog(sailingService, stringMessages, errorReporter, selectedLeaderboardName, raceColumnName, fleetName, - raceName); + raceName, userService); dialog.center(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java index 4de9e3fd461..5630453be38 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java @@ -200,7 +200,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane raceTable.setWidth("300px"); raceList = new ListDataProvider(); filterablePanelEvents = new LabeledAbstractFilterablePanel(lblFilterEvents, - availableSwissTimingRaces, raceList) { + availableSwissTimingRaces, raceList, stringMessages) { @Override public Iterable getSearchableStrings(SwissTimingRaceRecordDTO t) { List strings = new ArrayList<>(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java index e6e8c5ac587..da50f66ad5d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingReplayConnectorPanel.java @@ -177,7 +177,8 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane raceTable.addColumn(raceStartTrackingColumn, stringMessages.startTime()); raceTable.setWidth("300px"); raceList = new ListDataProvider(); - filterablePanelEvents = new LabeledAbstractFilterablePanel(lblFilterEvents, availableSwissTimingRaces, raceList) { + filterablePanelEvents = new LabeledAbstractFilterablePanel(lblFilterEvents, + availableSwissTimingRaces, raceList, stringMessages) { @Override public List getSearchableStrings(SwissTimingReplayRaceDTO t) { List strings = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java index f472a1f5501..52683733937 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java @@ -252,7 +252,8 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel { AdminConsoleTableResources tableResources = GWT.create(AdminConsoleTableResources.class); racesTable = new FlushableCellTable(10000, tableResources); racesTable.ensureDebugId("TrackableRacesCellTable"); - this.racesFilterablePanel = new LabeledAbstractFilterablePanel(racesFilterLabel, availableTracTracRaces, raceList) { + this.racesFilterablePanel = new LabeledAbstractFilterablePanel(racesFilterLabel, + availableTracTracRaces, raceList, stringMessages) { @Override public List getSearchableStrings(TracTracRaceRecordDTO t) { List strings = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingArchivedConnectionTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingArchivedConnectionTableWrapper.java index e124ae0bbaf..52fbc1cafb9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingArchivedConnectionTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingArchivedConnectionTableWrapper.java @@ -122,7 +122,7 @@ public class SwissTimingArchivedConnectionTableWrapper extends u -> configACL.openDialog(u)); filterField = new LabeledAbstractFilterablePanel( new Label(stringMessages.filterSwissTimingAchivedConnections()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(SwissTimingArchiveConfigurationWithSecurityDTO t) { List strings = new ArrayList(); @@ -137,6 +137,8 @@ public class SwissTimingArchivedConnectionTableWrapper extends } }; registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); + filterField + .setCheckboxEnabledFilter(connection -> userService.hasPermission(connection, DefaultActions.UPDATE)); mainPanel.insert(filterField, 0); table.addColumnSortHandler(swissTimingAccountColumnListHandler); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingConnectionTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingConnectionTableWrapper.java index 1adafbfc4f4..e6818dd97b2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingConnectionTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingConnectionTableWrapper.java @@ -140,7 +140,7 @@ public class SwissTimingConnectionTableWrapper extends filterField = new LabeledAbstractFilterablePanel( new Label(stringMessagesClient.filterSwissTimingConnections()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(SwissTimingConfigurationWithSecurityDTO t) { List strings = new ArrayList(); @@ -166,6 +166,8 @@ public class SwissTimingConnectionTableWrapper extends return table; } }; + filterField + .setCheckboxEnabledFilter(connection -> userService.hasPermission(connection, DefaultActions.UPDATE)); registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); mainPanel.insert(filterField, 0); table.addColumnSortHandler(swissTimingConectionColumnListHandler); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/tractrac/TracTracConnectionTableWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/tractrac/TracTracConnectionTableWrapper.java index 29660d5cca8..f84f1a517b9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/tractrac/TracTracConnectionTableWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/tractrac/TracTracConnectionTableWrapper.java @@ -134,7 +134,7 @@ public class TracTracConnectionTableWrapper extends u -> configACL.openDialog(u)); filterField = new LabeledAbstractFilterablePanel( new Label(stringMessages.filterTracTracConnections()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(TracTracConfigurationWithSecurityDTO t) { List strings = new ArrayList(); @@ -150,6 +150,8 @@ public class TracTracConnectionTableWrapper extends } }; registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); + filterField + .setCheckboxEnabledFilter(connection -> userService.hasPermission(connection, DefaultActions.UPDATE)); mainPanel.insert(filterField, 0); table.addColumnSortHandler(tracTracAccountColumnListHandler); table.addColumn(tracTracAccountNameColumn, getStringMessages().name()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index cc16cf3188d..0aca1ba8dab 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -475,8 +475,8 @@ scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Puntuación baja con scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=Una variante del esquema de puntuación baja que utiliza eliminaciones que constan de rondas.\nCada ronda es una serie de la regata y para la clasificación de la flota se cuenta hacia atrás\nempezando por el número más alto de la primera ronda.\nlf tiene lugar una final y una final de los perdedores, utilizan 1 y 2 para la clasificación. Empiezan con 3 para la ronda de semifinal. scoringSchemeLowPointForLeagueOverallLeaderboard=Puntuación baja para la tabla de clasificación general, los empates se resuelven por el total de puntos de las pruebas, luego la última prueba scoringSchemeLowPointForLeagueOverallLeaderboardDescription=En lugar del sistema habitual de recuento hacia atrás que se utiliza para los esquemas de puntuación baja para romper empates, para clasificar los competidores que tienen los mismos totales de puntos se compara el total de los puntos que han obtenido en todas las pruebas. Si aún persiste el empate, el empate se resuelve con la última prueba. -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Puntuación baja, los empates se resuelven por la última serie -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=En lugar del sistema habitual de recuento para los esquemas de puntuación baja para romper empates, se tiene en cuenta la puntuación de la serie final sin regatas excluidas si dos competidores tienen los mismos puntos. +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Puntuación baja, los empates se resuelven por la última serie sin medalla +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=En lugar del sistema habitual de recuento para los esquemas de puntuación baja para romper empates, se tiene en cuenta la puntuación de la serie sin medalla sin regatas excluidas si dos competidores tienen los mismos puntos. scoringSchemeLowPointWithAutomaticRdg=Sistema de puntuación baja, RDG obtiene una puntuación por defecto basada en la media de todas las puntuaciones que no sean de RDG scoringSchemeHighPointFirstGetsOne=Puntuación alta, el ganador obtiene 1 punto; el perdedor, 0 scoringSchemeHighPointFirstGetsOneDescription=Puntuación alta, el ganador obtiene 1 punto; el perdedor, 0; los empates se han resuelto basándose en la comparación directa con la última comparación directa resolviendo otro empate; si se producen ciclos en la comparación directa, los competidores se han tratado por igual; los competidores que no hayan competido entre sí (aún) se clasificarán basándose en su posición en el gráfico de victorias/derrotas. @@ -1284,6 +1284,7 @@ editMarkPassings=Editar superaciones de marca editMarkPositions=Editar posiciones de marca removeFixedPassings=Eliminar superación de marca corregida setFixedPassing=Fijar hora como superación de marca +chooseFixedPassing=Seleccione la hora como superación de marca setSuppressedPassing=Suprimir inicio de superación de marca con actual removeSuppressedPassing=Eliminar superación de marca suprimida warningSettingFixedPassing=La hora de superación {0} del punto de ruta no puede ser anterior a la hora del punto de ruta anterior o posterior a la hora del punto siguiente @@ -1293,6 +1294,7 @@ errorSuppressingPassing=Error al suprimir superaciones de marca: {0} errorRemovingSupressedPassing=Error al eliminar superaciones de marca suprimidas: {0} errorTryingToObtainMarkPassing=Error al intentar obtener superaciones de marca: {0} errorTryingToObtainRaceLogMarkPassingData=Error al intentar obtener datos de superación de marca de registro de carrera: {0} +warningMarkPassingChangesNoEffect=Advertencia: la carrera no está respaldada por el algoritmo de superación de marca interno. Las modificaciones no tendrán efecto. email=Correo electrónico pleaseEnterAnEmail=Indique un correo electrónico inviteCompetitors=Invitar a competidores diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index 8d965ea698d..b6bf4f4b726 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -475,8 +475,8 @@ scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Système de points à scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=Une variante du schéma de scores a minima qui utilise des éliminations sous forme de tours. \nChaque tour est organisé comme série dans la régate, et l''ordre de départ de la flotte est inversé, \ncommençant par le numéro le plus élevé pour le premier tour. \nSi une finale et une finale des perdants ont lieu, utilisez 1 et 2 comme ordre. Commencez à trois pour le tour des demi-finales. scoringSchemeLowPointForLeagueOverallLeaderboard=Système de points à minima pour le palmarès général, en cas d''égalité, le total des points de l''Act est utilisé, puis celui du dernier Act scoringSchemeLowPointForLeagueOverallLeaderboardDescription=À la place du système habituel de comptage pour schémas à minima en cas d''égalité, les concurrents dont le total des points est égal sont départagés en comparant le total des points qu''ils ont obtenus dans tous les Act. Si l''égalité persiste, la victoire est accordée en fonction du classement dans le dernier Act. -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Système de points a minima, en cas d''égalité, seule la dernière série compte -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=À la place du système habituel de comptage pour schémas à minima en cas d''égalité, le score de la dernière série sans exclusion de course est pris en compte si deux concurrents sont à égalité. +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Système de points a minima, en cas d''égalité, seule la dernière série non médaillée compte +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=À la place du système habituel de comptage pour schémas à minima en cas d''égalité, le score de la dernière série non médaillée sans exclusion de course est pris en compte si deux concurrents sont à égalité. scoringSchemeLowPointWithAutomaticRdg=Système de points a minima, reclassement à une manche avec score par défaut basé sur la moyenne de tous les scores non reclassés. scoringSchemeHighPointFirstGetsOne=Système de points avec bonus, le vainqueur obtient 1 point, le perdant 0 scoringSchemeHighPointFirstGetsOneDescription=Système de points avec bonus, le vainqueur obtient 1 point, le perdant 0. En cas d''égalité, l''égalité est départagée par comparaison directe avec la dernière comparaison directe où des concurrents à égalité ont été départagés. Si des cycles avec comparaison directe existent, les concurrents sont traités à égalités ; les concurrents qui ne se sont pas (encore) affrontés sont classés en fonction de leur position dans le classement des courses gagnées/perdues. @@ -1284,6 +1284,7 @@ editMarkPassings=Modifier passages des marques editMarkPositions=Modifier positions des marques removeFixedPassings=Supprimer passage de marque fixé setFixedPassing=Définir l''heure comme passage de marque +chooseFixedPassing=Sélectionner l''heure comme passage de marque setSuppressedPassing=Supprimer passage de marque en commençant par marque actuelle removeSuppressedPassing=Supprimer passage de marque supprimé warningSettingFixedPassing=L''heure à laquelle le point de cheminement {0} a été passé ne peut pas être antérieure à l''heure de passage du point de cheminement précédent, ni postérieure à l''heure de passage du point de cheminement suivant. @@ -1293,6 +1294,7 @@ errorSuppressingPassing=Erreur lors de la suppression des passages de marques : errorRemovingSupressedPassing=Erreur lors de la suppression des passages de marques supprimés : {0} errorTryingToObtainMarkPassing=Erreur lors de la tentative d''obtention des passages de marques : {0} errorTryingToObtainRaceLogMarkPassingData=Erreur lors de la tentative d''obtention des données de passages de marques du journal de course : {0} +warningMarkPassingChangesNoEffect=Avertissement : la course n''est pas gérée par un algorithme de passage de marque interne. Les modifications seront sans effet. email=E-mail pleaseEnterAnEmail=Saisissez un e-mail. inviteCompetitors=Inviter des concurrents diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties index cacbe67887c..d7385728b22 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties @@ -475,8 +475,8 @@ scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Punteggio minimo con scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=Una variante dello schema di calcolo del punteggio minimo che utilizza eliminazioni attraverso round.\nOgni round verrà configurato come una serie nella manifestazione e l''ordine delle flotte procede all''indietro\ncominciando dal numero più alto per il primo round.\nSe si svolgono una finale e una finale tra perdenti, utilizzare 1 e 2 per il relativo ordine. Iniziare con 3 per il round della semifinale. scoringSchemeLowPointForLeagueOverallLeaderboard=Punteggio minimo per classifica generale, parità risolta con la somma dei punti delle prove, quindi ultima prova scoringSchemeLowPointForLeagueOverallLeaderboardDescription=Invece dell''usuale sistema di count-back utilizzato per il punteggio minimo per risolvere le parità, i concorrenti con uguali punti totali vengono confrontati in base al totale di punti accumulati in tutte le prove. Se il pareggio ancora permane, viene risolto in base all''ultima prova. -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Punteggio minimo, parità risolta solo in base all''ultima serie -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=Invece dell''usuale sistema di count-back degli schemi di punteggio minimo per risolvere i pareggi, il punteggio della serie finale senza esclusione di prove viene considerato se i concorrenti hanno pari punti. +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Punteggio minimo, parità risolta solo in base all''ultima non-medal series +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=Invece dell''usuale sistema di count-back degli schemi di punteggio minimo per risolvere i pareggi, il punteggio dell''ultima non-medal series senza esclusione di prove viene considerato se i concorrenti hanno pari punti. scoringSchemeLowPointWithAutomaticRdg=Sistema di punteggio minimo, RDG ottiene un punteggio predefinito in base alla media di tutti i punteggi non RDG scoringSchemeHighPointFirstGetsOne=Punteggio massimo, il vincitore ottiene 1 punto, il perdente 0 scoringSchemeHighPointFirstGetsOneDescription=Punteggio massimo, il vincitore ottiene 1 punto, il perdente 0; i pareggi vengono risolti in base al confronto diretto con l''ultimo confronto diretto che ha risolto un altro pareggio; in caso di catene nei confronti diretti, i concorrenti vengono trattati alla pari; i concorrenti che non hanno (ancora) gareggiato fra loro vengono classificati in base alla loro posizione nel grafico vittorie/sconfitte. @@ -1284,6 +1284,7 @@ editMarkPassings=Modifica passaggi segnali editMarkPositions=Modifica posizioni segnali removeFixedPassings=Rimuovi passaggio segnale fisso setFixedPassing=Imposta ora come passaggio segnale +chooseFixedPassing=Scegli ora come passaggio segnale setSuppressedPassing=Annulla passaggio segnale a cominciare dall''attuale removeSuppressedPassing=Rimuovi passaggio segnale annullato warningSettingFixedPassing=l''ora del passaggio del waypoint {0} non può essere antecedente all''ora del precedente waypoint o posteriore all''ora del successivo @@ -1293,6 +1294,7 @@ errorSuppressingPassing=Errore in annullamento passaggi segnale: {0} errorRemovingSupressedPassing=Errore in rimozione passaggi segnali annullati: {0} errorTryingToObtainMarkPassing=Errore in tentativo di ottenere passaggi segnale: {0} errorTryingToObtainRaceLogMarkPassingData=Errore in tentativo di ottenere dati sul passaggio segnale dal registro gara: {0} +warningMarkPassingChangesNoEffect=Attenzione: la gara non è supportata dall''algoritmo interno del passaggio segnale. Le modifiche non saranno valide. email=E-mail pleaseEnterAnEmail=Inserire un''e-mail. inviteCompetitors=Invita concorrenti diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index 59db7b5fef3..156543f7801 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -475,8 +475,8 @@ scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=低得点方式、予 scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=ラウンドから構成される予選を用いた低得点方式のバリエーションです。\n各ラウンドがレガッタ内のシリーズとして形成されることになり、フリート順序は\n最初のラウンドで最も大きい数から開始してカウントダウンします。\n決勝や最下位決定戦が行われる場合は、その順序として 1 および 2 を使用します。準決勝ラウンドは 3 から始めます。 scoringSchemeLowPointForLeagueOverallLeaderboard=全体リーダーボードに対して低得点方式、アクト総得点、次に最終アクトによって順位を決定 scoringSchemeLowPointForLeagueOverallLeaderboardDescription=順位決定のための低得点方式に使用される通常のカウントダウン方式ではなく、総得点が同点の競技者が全アクト共通で得点した総得点で比較されます。これでも同順位となる場合は、最終アクトによって順位を決定します。 -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=低得点方式、最終シリーズのみに基づいて順位を決定 -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=順序を決定するための低得点方式の通常のカウントダウン方式ではなく、2 艇の競技者の得点が同一の場合に、除外するレースなしに最終シリーズの得点を考慮します。 +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=低得点方式、最後の非メダルシリーズのみに基づいて順位を決定 +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=順序を決定するための低得点方式の通常のカウントダウン方式ではなく、2 艇の競技者の得点が同一の場合に、除外するレースなしに最後の非メダルシリーズの得点を考慮します。 scoringSchemeLowPointWithAutomaticRdg=低得点方式、RDG (救済を受けた艇) はすべての非 RDG (Redress give) スコアの平均に基づくデフォルトスコアを獲得 scoringSchemeHighPointFirstGetsOne=高得点方式、勝者が 1 点、敗者が 0 点を獲得 scoringSchemeHighPointFirstGetsOneDescription=高得点方式、勝者が 1 点、敗者が 0 点を獲得します。順位の決定は、同順位が崩れた最終の直接比較に基づいて行われます。直接比較が繰り返されている場合、競技者は対等に扱われます。(まだ) 直接対戦していない競技者は勝敗グラフにおける位置に基づいて並べられます。 @@ -1284,6 +1284,7 @@ editMarkPassings=マーク通過編集 editMarkPositions=マーク位置編集 removeFixedPassings=固定マーク通過を削除 setFixedPassing=マーク通過の時刻を設定 +chooseFixedPassing=マーク通過の時刻を選択 setSuppressedPassing=現在のもので開始したマーク通過を非表示 removeSuppressedPassing=非表示のマーク通過を削除 warningSettingFixedPassing=変針点 {0} を通過する時刻は、前の変針点の時刻より前であってはならず、後の変針点の時刻より後であってはなりません。 @@ -1293,6 +1294,7 @@ errorSuppressingPassing=マーク通過の非表示でエラー発生: {0} errorRemovingSupressedPassing=非表示のマーク通過の削除でエラー発生: {0} errorTryingToObtainMarkPassing=マーク通過を取得しようとしてエラーが発生: {0} errorTryingToObtainRaceLogMarkPassingData=レースログのマーク通過データを取得しようとしてエラーが発生: {0} +warningMarkPassingChangesNoEffect=警告: レースはマーク通過の内部アルゴリズムによって裏付けられていません。変更は反映されません。 email=電子メール pleaseEnterAnEmail=電子メールを入力してください inviteCompetitors=競技者の招待 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index 52282e85741..8a140f54b47 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -475,8 +475,8 @@ scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Sistema linear com el scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=Uma variante do esquema de pontuação linear, utilizando eliminatórias compostas de rodadas.\nCada rodada será modelada como uma série na regata e a ordenação da flotilha com contagem regressiva\ncomeçando pelo número mais alto para a primeira rodada.\nSe for realizada uma final e uma final "B", utilize 1 e 2 como ordenação. Comece com 3 para a rodada da semifinal. scoringSchemeLowPointForLeagueOverallLeaderboard=Sistema linear para painel de classificação geral, desempates efetuados pela soma dos pontos das etapas, em seguida pela última etapa scoringSchemeLowPointForLeagueOverallLeaderboardDescription=Em vez do habitual sistema de contagem regressiva utilizado para esquemas lineares para efetuar os desempates, os competidores com somas de pontos iguais são comparados comparando a soma dos pontos obtidos em todas as etapas. Se o empate continuar, o desempate é efetuado pela última etapa. -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Sistema linear, desempates só com base na última série -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=Em vez do habitual sistema de contagem regressiva para esquemas lineares para efetuar os desempates, a pontuação da série final sem corridas excluídas é considerada se dois competidores tiverem pontuações iguais. +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Sistema linear, desempates só com base na última série sem medalhas +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=Em vez do habitual sistema de contagem regressiva para esquemas lineares para efetuar os desempates, a pontuação da série final sem medalhas sem corridas excluídas é considerada se dois competidores tiverem pontuações iguais. scoringSchemeLowPointWithAutomaticRdg=Sistema linear, a RDG recebe a pontuação padrão com base na média de todas as pontuações não RDG scoringSchemeHighPointFirstGetsOne=Sistema de bônus, o vencedor recebe 1 ponto, o perdedor 0 scoringSchemeHighPointFirstGetsOneDescription=Sistema de bônus, o vencedor recebe 1 ponto, o perdedor 0; os desempates são efetuados com base na comparação direta com a última comparação direta de outro desempate; se ocorrerem ciclos nas comparações diretas, os competidores são tratados da mesma forma; os competidores que (ainda) não competiram uns com os outros são ordenados com base na sua posição no diagrama de vitórias/derrotas. @@ -1284,6 +1284,7 @@ editMarkPassings=Processar passagens da marca editMarkPositions=Processar posições da marca removeFixedPassings=Remover passagem da marca fixa setFixedPassing=Definir hora como passagem da marca +chooseFixedPassing=Selecionar hora como passagem da marca setSuppressedPassing=Suprimir passagem da marca começando pela atual removeSuppressedPassing=Remover passagem da marca suprimida warningSettingFixedPassing=O tempo de passagem do waypoint {0} não pode ser anterior ao tempo do waypoint anterior ou posterior ao tempo do seguinte @@ -1293,6 +1294,7 @@ errorSuppressingPassing=Erro ao suprimir passagens da marca: {0} errorRemovingSupressedPassing=Erro ao remover passagens da marca suprimidas: {0} errorTryingToObtainMarkPassing=Erro ao tentar obter passagens da marca: {0} errorTryingToObtainRaceLogMarkPassingData=Erro ao tentar obter dados de passagem da marca do registro da corrida: {0} +warningMarkPassingChangesNoEffect=Aviso: a corrida não é apoiada pelo algoritmo de passagem da marca interno. As modificações não terão efeito. email=E-mail pleaseEnterAnEmail=Insira um e-mail. inviteCompetitors=Convidar competidores diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 52d408826fe..a00439e408c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -475,8 +475,8 @@ scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=Низкий балл scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=Вариант схемы оценки с низкими баллами, использующий туры на выбывание.\nКаждый тур моделируется как серия в регате, и суда ранжируются в обратном порядке,\nначиная с наибольшего числа за первый раунд.\nВ финале и при определении 3-го места используйте ранги 1 и 2. Для полуфинального тура начните с 3. scoringSchemeLowPointForLeagueOverallLeaderboard=Низкий балл по всей таблице лидеров, равенство разрешается по сумме актов, затем по последнему акту scoringSchemeLowPointForLeagueOverallLeaderboardDescription=Вместо обычной системы обратного отсчета, используемой для разрешения равенства в схемах низких баллов, участники с равными суммами баллов сравниваются по сумме баллов, полученных за все акты. Если равенство сохраняется, оно разрешается по последнему акту. -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Низкий балл, равенство разрешается только по последней серии -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=Вместо обычной системы обратного отсчета, используемой для разрешения равенства в системах низких баллов, при равенстве оценок двух участников учитывается оценка последней серии без исключенных гонок. +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=Низкий балл, равенство разрешается только по последней не медальной серии +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=Вместо обычной системы обратного отсчета, используемой для разрешения равенства в системах низких баллов, при равенстве оценок двух участников учитывается оценка последней не медальной серии без исключенных гонок. scoringSchemeLowPointWithAutomaticRdg=Система низких баллов, RDG получает оценку по умолчанию на основе среднего всех оценок без RDG scoringSchemeHighPointFirstGetsOne=Высокий балл, победитель получает 1 балл, проигравший получает 0 баллов scoringSchemeHighPointFirstGetsOneDescription=Высокий балл, победитель получает 1 балл, проигравший получает 0 баллов; равенство разрешается путем прямого сравнения с последним прямым сравнением при разрешении другого равенства; если в прямом сравнении обнаружен цикл, участники считаются равными; участники, (еще) не соревновавшиеся друг против друга, располагаются согласно позициям в графе побед/поражений. @@ -1284,6 +1284,7 @@ editMarkPassings=Изменить прохождения отметок editMarkPositions=Изменить позиции отметок removeFixedPassings=Удалить прохождение фиксированной отметки setFixedPassing=Установить время как прохождение отметки +chooseFixedPassing=Выбрать время как прохождение отметки setSuppressedPassing=Подавить прохождение отметки, начиная с текущей removeSuppressedPassing=Удалить подавленное прохождение отметки warningSettingFixedPassing=Время прохождения отметки {0} не может быть раньше времени предыдущей отметки или позже времени следующей @@ -1293,6 +1294,7 @@ errorSuppressingPassing=Ошибка при подавлении прохожд errorRemovingSupressedPassing=Ошибка при удалении подавленных прохождений отметок: {0} errorTryingToObtainMarkPassing=Ошибка при попытке получить прохождения отметок: {0} errorTryingToObtainRaceLogMarkPassingData=Ошибка при попытке получить данные журнала гонки о прохождении отметок: {0} +warningMarkPassingChangesNoEffect=Предупреждение: гонка не поддерживается внутренним алгоритмом прохождения отметок. Изменения не будут иметь влияния. email=Эл. почта pleaseEnterAnEmail=Введите электронную почту. inviteCompetitors=Пригласить участников diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 018a8860a42..61e4b7f969f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -475,8 +475,8 @@ scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07=采用多轮淘汰制 scoringSchemeLowPointWithEliminationsAndRoundsWinnerGets07Description=低分记分系统的一种,采用多轮淘汰制。\n每一轮应作为比赛系列,船队顺序\n从第一轮的最高数向后计。\n如果有最终一支舰队和最终败者,则使用 1 和 2 排序。 半决赛从第 3 位开始。 scoringSchemeLowPointForLeagueOverallLeaderboard=总积分榜的低分,分站赛得分总和,否则采用最后一个分站赛的得分打破平局 scoringSchemeLowPointForLeagueOverallLeaderboardDescription=与用于低分记分系统打破平局的常规倒算系统不同,对于总得分相同的参赛队,比较其在所有分站赛中的得分总和。 如果仍有平局,则以最后一个分站赛的得分打破平局。 -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=低分,仅根据上一系列赛打破平局。 -scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=与用于低分记分系统打破平局的常规倒算系统不同,如果两支参赛队的得分相同,则考虑不排除比赛轮次的总决赛系列赛的得分。 +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnly=低分,仅根据最后非奖牌轮系列赛打破平局。 +scoringSchemeLowPointTieBreakBasedOnLastSeriesOnlyDescription=与用于低分记分系统打破平局的常规倒算系统不同,如果两支参赛队的得分相同,则考虑不排除比赛轮次的最后非奖牌轮系列赛的得分。 scoringSchemeLowPointWithAutomaticRdg=低分系统,RDG 根据所有非 RDG 得分的平均值获得默认得分 scoringSchemeHighPointFirstGetsOne=高分,获胜者得 1 分,败者 0 分 scoringSchemeHighPointFirstGetsOneDescription=高分,获胜者得 1 分,败者 0 分;根据与打破其他平局的上次直接比较进行直接比较打破平局;如果直接比较中出现循环赛,公平对待参赛队;根据输赢图中的位置将(尚未)互相比赛的参赛队排序。 @@ -1284,6 +1284,7 @@ editMarkPassings=编辑绕标 editMarkPositions=编辑标位 removeFixedPassings=移除固定的绕标 setFixedPassing=设置绕标时间 +chooseFixedPassing=选择绕标时间 setSuppressedPassing=从当前开始禁止绕标 removeSuppressedPassing=移除禁止的绕标 warningSettingFixedPassing=通过{0}航路点的时间不能在上一航路点的时间之前或下一航路点的时间之后 @@ -1293,6 +1294,7 @@ errorSuppressingPassing=禁止绕标出错: {0} errorRemovingSupressedPassing=移除禁止的绕标出错: {0} errorTryingToObtainMarkPassing=尝试获取绕标出错: {0} errorTryingToObtainRaceLogMarkPassingData=尝试获取比赛轮次日志绕标数据出错: {0} +warningMarkPassingChangesNoEffect=警告:内部绕标算法不支持竞赛。更改将不会产生任何影响! email=电子邮件 pleaseEnterAnEmail=请输入电子邮件。 inviteCompetitors=邀请参赛队 diff --git a/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_it.properties b/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_it.properties index 082ef7fc8ab..db0fa7505c5 100644 --- a/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_it.properties +++ b/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_it.properties @@ -9,4 +9,4 @@ WindIsConfident=Affidabile misurazione del vento Rank=Posizione in classifica BackendPolars=Polari back-end TargetBeatAngle=Angolo target di bolina -TargetRunawayAngle=Angolo di fuga target +TargetRunawayAngle=Angolo di lasco target diff --git a/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_ja.properties b/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_ja.properties index fb48b7dfabe..8dfec87ebb4 100644 --- a/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_ja.properties +++ b/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_ja.properties @@ -9,4 +9,4 @@ WindIsConfident=風測定確度 Rank=ランク BackendPolars=バックエンドポーラー TargetBeatAngle=目標ビートアングル -TargetRunawayAngle=目標ランナウェイアングル +TargetRunawayAngle=目標ランアングル diff --git a/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_zh.properties b/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_zh.properties index 6f344bcf675..2455df4358c 100755 --- a/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_zh.properties +++ b/java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_zh.properties @@ -9,4 +9,4 @@ WindIsConfident=风力测量置信度 Rank=名次 BackendPolars=后端极坐标图 TargetBeatAngle=目标迎风航行角度 -TargetRunawayAngle=目标失控角度 +TargetRunawayAngle=目标顺风航行角度 diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/ApiContext.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/ApiContext.java index b7bf4e9e4ed..44995fc18ff 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/ApiContext.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/ApiContext.java @@ -112,7 +112,7 @@ public class ApiContext { String error = "API POST request " + url + " failed (rc=" + e.getResponse().getStatus() + "): " + e.getResponse().getEntity(String.class); logger.severe(error); - throw new RuntimeException(error); + throw HttpException.forResponse(e.getResponse(), error).orElse(e); } return (R) JSONValue.parse(result); } @@ -141,7 +141,7 @@ public class ApiContext { String error = "API POST request " + url + " failed (rc=" + e.getResponse().getStatus() + "): " + e.getResponse().getEntity(String.class); logger.severe(error); - throw new RuntimeException(error); + throw HttpException.forResponse(e.getResponse(), error).orElse(e); } return (R) JSONValue.parse(result); } @@ -167,7 +167,7 @@ public class ApiContext { String error = "API POST request " + url + " failed (rc=" + e.getResponse().getStatus() + "): " + e.getResponse().getEntity(String.class); logger.severe(error); - throw new RuntimeException(error); + throw HttpException.forResponse(e.getResponse(), error).orElse(e); } return (R) JSONValue.parse(result); } @@ -193,7 +193,7 @@ public class ApiContext { String error = "API PUT request " + url + " failed (rc=" + e.getResponse().getStatus() + "): " + e.getResponse().getEntity(String.class); logger.severe(error); - throw new RuntimeException(error); + throw HttpException.forResponse(e.getResponse(), error).orElse(e); } return (R) JSONValue.parse(result); } @@ -212,7 +212,7 @@ public class ApiContext { String error = "API DELETE request " + url + " failed (rc=" + e.getResponse().getStatus() + "): " + e.getResponse().getEntity(String.class); logger.severe(error); - throw new RuntimeException(error); + throw HttpException.forResponse(e.getResponse(), error).orElse(e); } } @@ -256,7 +256,7 @@ public class ApiContext { String error = "API GET request " + url + " failed (rc=" + rc + "): " + e.getResponse().getEntity(String.class); logger.severe(error); - throw new RuntimeException(error); + throw HttpException.forResponse(e.getResponse(), error).orElse(e); } } return JSONValue.parse(result); diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/DeviceStatus.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/DeviceStatus.java new file mode 100644 index 00000000000..7147936d827 --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/DeviceStatus.java @@ -0,0 +1,18 @@ +package com.sap.sailing.selenium.api.core; + +import org.json.simple.JSONObject; + +public class DeviceStatus extends JsonWrapper { + public DeviceStatus(JSONObject json) { + super(json); + } + + public String getDeviceId() { + return (String) ((JSONObject)get("deviceId")).get("id"); + } + + public GPSFixResponse getLastGPSFix() { + JSONObject jsonObject = get("lastGPSFix"); + return jsonObject == null ? null : new GPSFixResponse(jsonObject); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/GPSFixResponse.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/GPSFixResponse.java new file mode 100644 index 00000000000..0153fe1a6d6 --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/GPSFixResponse.java @@ -0,0 +1,13 @@ +package com.sap.sailing.selenium.api.core; + +import org.json.simple.JSONObject; + +public class GPSFixResponse extends JsonWrapper { + public GPSFixResponse(JSONObject json) { + super(json); + } + + public long getTime() { + return ((Number)get("unixtime")).longValue(); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/HttpException.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/HttpException.java new file mode 100644 index 00000000000..bfed76aed10 --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/core/HttpException.java @@ -0,0 +1,94 @@ +package com.sap.sailing.selenium.api.core; + +import java.util.Optional; + +import javax.ws.rs.core.Response; + +import com.sun.jersey.api.client.ClientResponse; + +public class HttpException extends RuntimeException { + + private static final long serialVersionUID = -7750172668418557820L; + + private final int httpStatusCode; + + public static Optional forResponse(final ClientResponse response) { + return forResponse(response, null); + } + + public static Optional forResponse(final ClientResponse response, String message) { + final int statusCode = response.getStatus(); + switch (statusCode) { + case 200: + return Optional.empty(); + case 401: + return Optional.of(new Unauthorized(message)); + case 403: + return Optional.of(new Forbidden(message)); + case 404: + return Optional.of(new NotFound(message)); + default: + return Optional.of(new HttpException(statusCode, message)); + } + } + + public HttpException(final int httpStatusCode) { + super(); + this.httpStatusCode = httpStatusCode; + } + + public HttpException(final int httpStatusCode, final String message) { + super(message); + this.httpStatusCode = httpStatusCode; + } + + public void rethrow() { + throw this; + } + + public int getHttpStatusCode() { + return httpStatusCode; + } + + public static final class NotFound extends HttpException { + + private static final long serialVersionUID = 1875842782488954242L; + + NotFound() { + super(Response.Status.NOT_FOUND.getStatusCode()); + } + + NotFound(String message) { + super(Response.Status.NOT_FOUND.getStatusCode(), message); + } + + } + + public static final class Unauthorized extends HttpException { + + private static final long serialVersionUID = 1875842782488954242L; + + Unauthorized() { + super(Response.Status.UNAUTHORIZED.getStatusCode()); + } + + Unauthorized(String message) { + super(Response.Status.UNAUTHORIZED.getStatusCode(), message); + } + + } + + public static final class Forbidden extends HttpException { + + private static final long serialVersionUID = 1875842782488954242L; + + Forbidden() { + super(Response.Status.FORBIDDEN.getStatusCode()); + } + + Forbidden(String message) { + super(Response.Status.FORBIDDEN.getStatusCode(), message); + } + + } +} diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/event/TrackingDeviceApi.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/event/TrackingDeviceApi.java new file mode 100644 index 00000000000..a769e8e8313 --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/event/TrackingDeviceApi.java @@ -0,0 +1,15 @@ +package com.sap.sailing.selenium.api.event; + +import java.util.UUID; + +import com.sap.sailing.selenium.api.core.ApiContext; +import com.sap.sailing.selenium.api.core.DeviceStatus; + +public class TrackingDeviceApi { + + private static final String BASE_PATH = "/api/v1/tracking_devices"; + + public DeviceStatus getDeviceStatus(ApiContext ctx, UUID deviceUUID) { + return new DeviceStatus(ctx.get(BASE_PATH + "/" + deviceUUID.toString())); + } +} diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/RegattaApi.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/RegattaApi.java index ad892d30766..f569c3c39ef 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/RegattaApi.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/RegattaApi.java @@ -4,9 +4,11 @@ import java.util.Map; import java.util.Optional; import java.util.TreeMap; import java.util.UUID; +import java.util.function.Predicate; import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import org.openqa.selenium.TimeoutException; import com.sap.sailing.selenium.api.core.ApiContext; @@ -19,6 +21,7 @@ public class RegattaApi { private static final String ADD = "/add"; private static final String ADD_RACE_COLUMN_URL = "/addracecolumns"; private static final String LIST_COMPETITORS = "/competitors"; + private static final String TRACKING_DEVICES = "/tracking_devices"; public Regatta getRegatta(ApiContext ctx, String regattaName) { return new Regatta(ctx.get(REGATTAS + "/" + regattaName)); @@ -28,6 +31,26 @@ public class RegattaApi { return new RegattaRaces(ctx.get(REGATTAS + "/" + regattaName + LIST_REGATTA_RACES)); } + public RegattaRaces getRegattaRaces(ApiContext ctx, String regattaName, Predicate predicateToCheck) + throws TimeoutException { + for (int i = 0; i < 20; i++) { + final RegattaRaces regattaRaces = getRegattaRaces(ctx, regattaName); + if (predicateToCheck.test(regattaRaces)) { + return regattaRaces; + } + if (i == 19) { + // skip unnecessary 500ms wait + break; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + throw new TimeoutException(e); + } + } + throw new TimeoutException("Expected condition failed, 20 tries, waited for 10 seconds."); + } + public Competitor createAndAddCompetitor(ApiContext ctx, String regattaName, String boatclass, String competitorEmail, String competitorName, String nationalityIOC, UUID deviceUuid) { return createAndAddCompetitor(ctx, regattaName, boatclass, competitorEmail, competitorName, nationalityIOC, @@ -96,5 +119,9 @@ public class RegattaApi { queryParams.put("secret", secret.orElse(null)); ctx.post(url, queryParams); } - + + public RegattaDeviceStatus getTrackingDeviceStatus(ApiContext ctx, String regattaName) { + final String url = REGATTAS + "/" + regattaName + TRACKING_DEVICES; + return new RegattaDeviceStatus(ctx.get(url)); + } } diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/RegattaDeviceStatus.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/RegattaDeviceStatus.java new file mode 100644 index 00000000000..93bbbb631de --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/regatta/RegattaDeviceStatus.java @@ -0,0 +1,77 @@ +package com.sap.sailing.selenium.api.regatta; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.selenium.api.core.DeviceStatus; +import com.sap.sailing.selenium.api.core.JsonWrapper; + +public class RegattaDeviceStatus extends JsonWrapper { + public abstract static class TrackedItemDeviceStatus extends JsonWrapper { + public TrackedItemDeviceStatus(JSONObject json) { + super(json); + } + + public List getDeviceStatuses() { + JSONArray statusArray = get("deviceStatuses"); + return statusArray.stream().map(o -> new DeviceStatus((JSONObject) o)).collect(Collectors.toList()); + } + } + + public static class CompetitorDeviceStatus extends TrackedItemDeviceStatus { + public CompetitorDeviceStatus(JSONObject json) { + super(json); + } + + public String getCompetitorId() { + return (String) get("competitorId"); + } + } + + public static class BoatDeviceStatus extends TrackedItemDeviceStatus { + public BoatDeviceStatus(JSONObject json) { + super(json); + } + + public String getBoatId() { + return (String) get("boatId"); + } + } + + public static class MarkDeviceStatus extends TrackedItemDeviceStatus { + public MarkDeviceStatus(JSONObject json) { + super(json); + } + + public String getMarkId() { + return (String) get("markId"); + } + } + + public RegattaDeviceStatus(JSONObject json) { + super(json); + } + + public List getCompetitors() { + Object jsonObjectOrNull = get("competitors"); + return jsonObjectOrNull instanceof JSONArray ? ((JSONArray) jsonObjectOrNull).stream() + .map(o -> new CompetitorDeviceStatus((JSONObject) o)).collect(Collectors.toList()) + : Collections.emptyList(); + } + + public List getBoats() { + Object jsonObjectOrNull = get("boats"); + return jsonObjectOrNull instanceof JSONArray ? ((JSONArray) jsonObjectOrNull).stream() + .map(o -> new BoatDeviceStatus((JSONObject) o)).collect(Collectors.toList()) : Collections.emptyList(); + } + + public List getMarks() { + Object jsonObjectOrNull = get("marks"); + return jsonObjectOrNull instanceof JSONArray ? ((JSONArray) jsonObjectOrNull).stream() + .map(o -> new MarkDeviceStatus((JSONObject) o)).collect(Collectors.toList()) : Collections.emptyList(); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/MarkApiTest.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/MarkApiTest.java index a2c7c9bc3f3..cd696e04b41 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/MarkApiTest.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/MarkApiTest.java @@ -1,7 +1,9 @@ package com.sap.sailing.selenium.api.test; +import static com.sap.sailing.selenium.api.core.ApiContext.SECURITY_CONTEXT; import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT; import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext; +import static com.sap.sailing.selenium.api.core.ApiContext.createApiContext; import static java.lang.System.currentTimeMillis; import static org.junit.Assert.assertNotNull; @@ -10,9 +12,11 @@ import org.junit.Test; import com.sap.sailing.domain.common.CompetitorRegistrationType; import com.sap.sailing.selenium.api.core.ApiContext; +import com.sap.sailing.selenium.api.core.HttpException; import com.sap.sailing.selenium.api.event.EventApi; import com.sap.sailing.selenium.api.event.MarkApi; import com.sap.sailing.selenium.api.event.MarkApi.Mark; +import com.sap.sailing.selenium.api.event.SecurityApi; import com.sap.sailing.selenium.api.regatta.RaceColumn; import com.sap.sailing.selenium.api.regatta.Regatta; import com.sap.sailing.selenium.api.regatta.RegattaApi; @@ -26,6 +30,7 @@ public class MarkApiTest extends AbstractSeleniumTest { private final EventApi eventApi = new EventApi(); private final RegattaApi regattaApi = new RegattaApi(); private final MarkApi markApi = new MarkApi(); + private final SecurityApi securityApi = new SecurityApi(); @Before public void setUp() { @@ -55,4 +60,21 @@ public class MarkApiTest extends AbstractSeleniumTest { markApi.addMarkFix(ctx, EVENT_NAME, race.getRaceName(), "Default", mark.getMarkId(), 9.12, .599, currentTimeMillis()); } + + @Test(expected = HttpException.NotFound.class) + public void testAddMarkToRegattaForNonExistingEvent() { + final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT); + markApi.addMarkToRegatta(ctx, "NONEVENT", "Startboat"); + } + + @Test(expected = HttpException.Unauthorized.class) + public void testAddMarkToRegattaWithoutPermission() { + final ApiContext adminSecurityCtx = createAdminApiContext(getContextRoot(), SECURITY_CONTEXT); + final ApiContext ownerCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT); + securityApi.createUser(adminSecurityCtx, "donald", "Donald Duck", null, "daisy0815"); + final ApiContext readerCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "donald", "daisy0815"); + + eventApi.createEvent(ownerCtx, EVENT_NAME, BOAT_CLASS, CompetitorRegistrationType.CLOSED, "default"); + markApi.addMarkToRegatta(readerCtx, EVENT_NAME, "Startboat"); + } } diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java index 4c675b433f9..c25ab0a2e72 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/RegattaApiTest.java @@ -1,9 +1,11 @@ package com.sap.sailing.selenium.api.test; import static com.sap.sailing.domain.common.CompetitorRegistrationType.CLOSED; +import static com.sap.sailing.domain.common.CompetitorRegistrationType.OPEN_UNMODERATED; import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT; import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext; import static com.sap.sailing.selenium.api.core.ApiContext.createApiContext; +import static com.sap.sailing.selenium.api.core.GpsFixMoving.createFix; import static com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage.goToPage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -11,8 +13,11 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -22,8 +27,10 @@ import org.junit.Test; import com.sap.sailing.domain.common.CompetitorRegistrationType; import com.sap.sailing.selenium.api.core.ApiContext; +import com.sap.sailing.selenium.api.core.DeviceStatus; import com.sap.sailing.selenium.api.event.EventApi; import com.sap.sailing.selenium.api.event.EventApi.Event; +import com.sap.sailing.selenium.api.event.GpsFixApi; import com.sap.sailing.selenium.api.event.LeaderboardApi; import com.sap.sailing.selenium.api.event.SecurityApi; import com.sap.sailing.selenium.api.event.UserGroupApi; @@ -32,6 +39,8 @@ import com.sap.sailing.selenium.api.regatta.Competitor; import com.sap.sailing.selenium.api.regatta.RaceColumn; import com.sap.sailing.selenium.api.regatta.Regatta; import com.sap.sailing.selenium.api.regatta.RegattaApi; +import com.sap.sailing.selenium.api.regatta.RegattaDeviceStatus; +import com.sap.sailing.selenium.api.regatta.RegattaDeviceStatus.CompetitorDeviceStatus; import com.sap.sailing.selenium.api.regatta.RegattaRaces; import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage; import com.sap.sailing.selenium.test.AbstractSeleniumTest; @@ -46,6 +55,7 @@ public class RegattaApiTest extends AbstractSeleniumTest { private final RegattaApi regattaApi = new RegattaApi(); private final LeaderboardApi leaderboardApi = new LeaderboardApi(); private final UserGroupApi usergroupApi = new UserGroupApi(); + private final GpsFixApi gpsFixApi = new GpsFixApi(); @Before public void setUp() { @@ -92,7 +102,7 @@ public class RegattaApiTest extends AbstractSeleniumTest { RaceColumn[] raceColumns = regattaApi.addRaceColumn(ctx, EVENT_NAME, "R", 1); leaderboardApi.startRaceLogTracking(ctx, EVENT_NAME, raceColumns[0].getRaceName(), "Default"); - regattaRaces = regattaApi.getRegattaRaces(ctx, EVENT_NAME); + regattaRaces = regattaApi.getRegattaRaces(ctx, EVENT_NAME, r -> r.getRaces().length > 0); assertEquals("read: reagtta.series should have 1 entries", 1, regattaRaces.getRaces().length); } @@ -228,4 +238,42 @@ public class RegattaApiTest extends AbstractSeleniumTest { assertEquals(0, competitors.length); } + + @Test + public void testTrackingDeviceStatus() { + final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT); + + Event event = eventApi.createEvent(ctx, EVENT_NAME, BOAT_CLASS, OPEN_UNMODERATED, "default"); + final UUID competitor1DeviceID = UUID.randomUUID(); + final Competitor competitor1 = regattaApi.createAndAddCompetitorWithSecret(ctx, EVENT_NAME, BOAT_CLASS, "test@de", "Max", + "USA", event.getSecret(), competitor1DeviceID); + final UUID competitor2DeviceID = UUID.randomUUID(); + final Competitor competitor2 = regattaApi.createAndAddCompetitorWithSecret(ctx, EVENT_NAME, BOAT_CLASS, "test@de", + "Peter", "USA", event.getSecret(), competitor2DeviceID); + + final long timeMillisCompetitor1 = System.currentTimeMillis(); + gpsFixApi.postGpsFix(ctx, competitor1DeviceID, createFix(49.121, 8.5987, timeMillisCompetitor1, 10.0, 180.0)); + final long timeMillisCompetitor2 = System.currentTimeMillis() - 50; + gpsFixApi.postGpsFix(ctx, competitor2DeviceID, createFix(49.120, 8.5988, timeMillisCompetitor2, 10.0, 180.0)); + + final RegattaDeviceStatus trackingDeviceStatus = regattaApi.getTrackingDeviceStatus(ctx, EVENT_NAME); + + final List competitorsTrackingDeviceStatus = trackingDeviceStatus.getCompetitors(); + assertEquals(2, competitorsTrackingDeviceStatus.size()); + final Map> statusByCompetitorUUID = competitorsTrackingDeviceStatus.stream() + .collect(Collectors.toMap(c -> UUID.fromString(c.getCompetitorId()), c -> c.getDeviceStatuses())); + assertTrue(statusByCompetitorUUID.containsKey(competitor1.getId())); + assertTrue(statusByCompetitorUUID.containsKey(competitor2.getId())); + + List competitor1DeviceStatuses = statusByCompetitorUUID.get(competitor1.getId()); + assertEquals(1, competitor1DeviceStatuses.size()); + DeviceStatus competitor1DeviceStatus = competitor1DeviceStatuses.iterator().next(); + assertEquals(competitor1DeviceID.toString(), competitor1DeviceStatus.getDeviceId()); + assertEquals(timeMillisCompetitor1, competitor1DeviceStatus.getLastGPSFix().getTime()); + List competitor2DeviceStatuses = statusByCompetitorUUID.get(competitor2.getId()); + assertEquals(1, competitor1DeviceStatuses.size()); + DeviceStatus competitor2DeviceStatus = competitor2DeviceStatuses.iterator().next(); + assertEquals(competitor2DeviceID.toString(), competitor2DeviceStatus.getDeviceId()); + assertEquals(timeMillisCompetitor2, competitor2DeviceStatus.getLastGPSFix().getTime()); + } } diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/TrackingDeviceStatusApiTest.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/TrackingDeviceStatusApiTest.java new file mode 100644 index 00000000000..7ee6e914eb6 --- /dev/null +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/TrackingDeviceStatusApiTest.java @@ -0,0 +1,55 @@ +package com.sap.sailing.selenium.api.test; + +import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT; +import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext; +import static com.sap.sailing.selenium.api.core.GpsFixMoving.createFix; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; + +import com.sap.sailing.selenium.api.core.ApiContext; +import com.sap.sailing.selenium.api.core.DeviceStatus; +import com.sap.sailing.selenium.api.core.GPSFixResponse; +import com.sap.sailing.selenium.api.core.GpsFixMoving; +import com.sap.sailing.selenium.api.event.GpsFixApi; +import com.sap.sailing.selenium.api.event.TrackingDeviceApi; +import com.sap.sailing.selenium.test.AbstractSeleniumTest; + +public class TrackingDeviceStatusApiTest extends AbstractSeleniumTest { + + private final GpsFixApi gpsFixApi = new GpsFixApi(); + private final TrackingDeviceApi trackingDeviceApi = new TrackingDeviceApi(); + + @Before + public void setUp() { + clearState(getContextRoot(), /* headless */ true); + } + + @Test + public void testDeviceStatusWithLastFix() { + final UUID deviceUUID = UUID.randomUUID(); + final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT); + final DeviceStatus statusBeforeFix = trackingDeviceApi.getDeviceStatus(ctx, deviceUUID); + assertEquals(deviceUUID.toString(), statusBeforeFix.getDeviceId()); + assertNull(statusBeforeFix.getLastGPSFix()); + + final long fix1Millis = System.currentTimeMillis() - 100; + final GpsFixMoving fix1 = createFix(49.121, 8.5987, fix1Millis, 10.0, 180.0); + gpsFixApi.postGpsFix(ctx, deviceUUID, fix1); + final DeviceStatus statusWithFix1 = trackingDeviceApi.getDeviceStatus(ctx, deviceUUID); + final GPSFixResponse lastFix1 = statusWithFix1.getLastGPSFix(); + assertNotNull(lastFix1); + assertEquals(fix1Millis, lastFix1.getTime()); + + final long fix2Millis = System.currentTimeMillis(); + final GpsFixMoving fix2 = createFix(49.12, 8.599, fix2Millis, 10.0, 180.0); + gpsFixApi.postGpsFix(ctx, deviceUUID, fix2); + final DeviceStatus statusWithFix2 = trackingDeviceApi.getDeviceStatus(ctx, deviceUUID); + assertEquals(fix2Millis, statusWithFix2.getLastGPSFix().getTime()); + } +} diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/LeaderboardDetailsPanelPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/LeaderboardDetailsPanelPO.java index a9ba620a57b..d8d4d510ca6 100644 --- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/LeaderboardDetailsPanelPO.java +++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/LeaderboardDetailsPanelPO.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Objects; import java.util.function.Function; +import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.WebDriverWait; @@ -96,6 +97,9 @@ public class LeaderboardDetailsPanelPO extends PageArea { @FindBy(how = BySeleniumId.class, using = "TrackedRacesFilterTextBox") private WebElement trackedRacesFilterTextBox; + @FindBy(how = BySeleniumId.class, using = "RefreshButton") + private WebElement refreshButton; + public LeaderboardDetailsPanelPO(WebDriver driver, WebElement element) { super(driver, element); } @@ -175,15 +179,30 @@ public class LeaderboardDetailsPanelPO extends PageArea { return findRace(race); } }); - DataEntryPO trackingRow = webDriverWait.until(new Function() { + DataEntryPO trackingRow; + + try { + trackingRow = findTrackingRow(tracking, webDriverWait); + } catch (TimeoutException e) { + // click the refresh button to refresh the tracking grid + refreshButton.click(); + trackingRow = findTrackingRow(tracking, webDriverWait); + } + + raceRow.select(); + trackingRow.select(); + waitForAjaxRequests(); // the selection will update elements of the cell table; wait until the callback has been + // received + } + + private DataEntryPO findTrackingRow(TrackedRaceDescriptor tracking, WebDriverWait webDriverWait) + throws TimeoutException { + return webDriverWait.until(new Function() { @Override public DataEntryPO apply(WebDriver t) { return findTracking(tracking); } }); - raceRow.select(); - trackingRow.select(); - waitForAjaxRequests(); // the selection will update elements of the cell table; wait until the callback has been received } private DataEntryPO findRace(RaceDescriptor race) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java index bfb8670b99a..45f24382036 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java @@ -16,7 +16,9 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.server.interfaces.RacingEventService; import com.sap.sse.InvalidDateException; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.TypeBasedServiceFinderFactory; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.osgi.CachedOsgiTypeBasedServiceFinderFactory; import com.sap.sse.replication.ReplicationService; import com.sap.sse.security.SecurityService; import com.sap.sse.util.DateParser; @@ -36,8 +38,7 @@ public abstract class AbstractSailingServerResource { } public T getService(Class clazz) { - BundleContext context = (BundleContext) servletContext - .getAttribute(RestServletContainer.OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME); + BundleContext context = getBundleContext(); ServiceTracker tracker = new ServiceTracker(context, clazz, null); tracker.open(); T service = tracker.getService(); @@ -45,6 +46,16 @@ public abstract class AbstractSailingServerResource { return service; } + protected BundleContext getBundleContext() { + BundleContext context = (BundleContext) servletContext + .getAttribute(RestServletContainer.OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME); + return context; + } + + protected TypeBasedServiceFinderFactory getServiceFinderFactory () { + return new CachedOsgiTypeBasedServiceFinderFactory(getBundleContext()); + } + public RacingEventService getService() { @SuppressWarnings("unchecked") ServiceTracker tracker = (ServiceTracker) servletContext.getAttribute(RestServletContainer.RACING_EVENT_SERVICE_TRACKER_NAME); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java old mode 100755 new mode 100644 index a118b0980f0..67d93b13225 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -17,6 +17,7 @@ import java.util.concurrent.Callable; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; @@ -46,6 +47,8 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl; import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl; +import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMappingFinder; +import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorWithBoat; import com.sap.sailing.domain.base.Course; @@ -77,6 +80,7 @@ import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; +import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.DynamicTrackedRace; @@ -107,6 +111,7 @@ import com.sap.sailing.server.gateway.serialization.impl.CompleteManeuverCurveWi import com.sap.sailing.server.gateway.serialization.impl.CompleteManeuverCurvesWithEstimationDataJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.DefaultWindTrackJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.DetailedBoatClassJsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.DeviceIdentifierJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.DistanceJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.FleetJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.GPSFixJsonSerializer; @@ -129,6 +134,7 @@ import com.sap.sailing.server.gateway.serialization.impl.TargetTimeInfoSerialize import com.sap.sailing.server.gateway.serialization.impl.TeamJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.TrackedRaceJsonSerializer; import com.sap.sailing.server.gateway.serialization.impl.WindJsonSerializer; +import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler; import com.sap.sailing.server.operationaltransformation.AddColumnToSeries; import com.sap.sailing.server.operationaltransformation.UpdateSeries; import com.sap.sse.InvalidDateException; @@ -139,6 +145,7 @@ import com.sap.sse.common.Speed; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; +import com.sap.sse.common.WithID; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.RGBColor; @@ -2277,4 +2284,54 @@ public class RegattasResource extends AbstractSailingServerResource { } return Response.ok().header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); } + + @GET + @Produces("application/json;charset=UTF-8") + @Path("{regattaName}/tracking_devices") + public Response getTrackingStatus(@PathParam("regattaName") String regattaName) { + final Regatta regatta = getService().getRegattaByName(regattaName); + if (regatta != null) { + // Deeper insights to tracking data is only available to users who can administer a regatta + SecurityUtils.getSubject() + .checkPermission(regatta.getIdentifier().getStringPermission(DefaultActions.UPDATE)); + + final TrackingDeviceStatusSerializer serializer = new TrackingDeviceStatusSerializer( + new DeviceIdentifierJsonSerializer( + getServiceFinderFactory().createServiceFinder(DeviceIdentifierJsonHandler.class))); + + final RegattaLogDeviceMappingFinder regattaLogDeviceMappingFinder = new RegattaLogDeviceMappingFinder( + regatta.getRegattaLog()); + final Map>> foundMappings = regattaLogDeviceMappingFinder + .analyze(); + + final JSONObject result = new JSONObject(); + foundMappings.forEach((item, mappings) -> { + final JSONArray deviceStatusesOfTrackedItem = new JSONArray(); + deviceStatusesOfTrackedItem + .addAll(mappings.stream().map(DeviceMappingWithRegattaLogEvent::getDevice).distinct() + .map(deviceIdentifier -> serializer.serialize( + TrackingDeviceStatus.calculateDeviceStatus(deviceIdentifier, getService()))) + .collect(Collectors.toList())); + final JSONObject itemObject = new JSONObject(); + itemObject.put("deviceStatuses", deviceStatusesOfTrackedItem); + if (item instanceof Competitor) { + itemObject.put("competitorId", item.getId().toString()); + ((JSONArray) result.computeIfAbsent("competitors", k -> new JSONArray())).add(itemObject); + } else if (item instanceof Boat) { + itemObject.put("boatId", item.getId().toString()); + ((JSONArray) result.computeIfAbsent("boats", k -> new JSONArray())).add(itemObject); + } else if (item instanceof Mark) { + itemObject.put("markId", item.getId().toString()); + ((JSONArray) result.computeIfAbsent("marks", k -> new JSONArray())).add(itemObject); + } else { + logger.log(Level.WARNING, "Unexpected tracked item found while calculating the tracker status. ID: " + + item.getId() + "; type: " + item.getClass().getName()); + } + }); + return Response.ok(result.toJSONString()) + .header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } else { + throw new IllegalStateException("Regatta named " + regattaName + " could not be resolved"); + } + } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java index 66469183084..5bb8c441830 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java @@ -36,6 +36,7 @@ public class RestApiApplication extends Application { classes.add(UserGroupsResource.class); classes.add(StatusResource.class); classes.add(TrackedEventsResource.class); + classes.add(TrackingDevicesResource.class); // Exception Mappers classes.add(ShiroAuthorizationExceptionTo401ResponseMapper.class); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatus.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatus.java new file mode 100644 index 00000000000..7fe01e9c867 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatus.java @@ -0,0 +1,48 @@ +package com.sap.sailing.server.gateway.jaxrs.api; + +import java.util.Collections; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; + +import com.sap.sailing.domain.common.DeviceIdentifier; +import com.sap.sailing.domain.common.tracking.GPSFix; +import com.sap.sailing.server.interfaces.RacingEventService; +import com.sap.sse.common.Timed; + +public final class TrackingDeviceStatus { + private static final Logger logger = Logger.getLogger(TrackingDeviceStatus.class.getName()); + private final DeviceIdentifier deviceId; + private final GPSFix lastFix; + + public TrackingDeviceStatus(DeviceIdentifier deviceId, GPSFix lastFix) { + this.deviceId = deviceId; + this.lastFix = lastFix; + } + + public DeviceIdentifier getDeviceId() { + return deviceId; + } + + public GPSFix getLastGPSFix() { + return lastFix; + } + + public static TrackingDeviceStatus calculateDeviceStatus(DeviceIdentifier deviceIdentifier, RacingEventService service) { + try { + Map lastFixMap = service.getSensorFixStore().getLastFix(Collections.singletonList(deviceIdentifier)); + Timed lastFix = lastFixMap.get(deviceIdentifier); + GPSFix lastGPSFix = lastFix instanceof GPSFix ? (GPSFix) lastFix : null; + return new TrackingDeviceStatus(deviceIdentifier, lastGPSFix); + } catch (Exception e) { + final String errorMessage = "Error while loading device status for device " + deviceIdentifier; + logger.log(Level.SEVERE, errorMessage, e); + throw new WebApplicationException( + Response.status(Status.INTERNAL_SERVER_ERROR.getStatusCode()).entity(errorMessage).build()); + } + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatusSerializer.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatusSerializer.java new file mode 100644 index 00000000000..37f0a23b7c3 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDeviceStatusSerializer.java @@ -0,0 +1,26 @@ +package com.sap.sailing.server.gateway.jaxrs.api; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.common.DeviceIdentifier; +import com.sap.sailing.server.gateway.serialization.JsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.GPSFixJsonSerializer; + +public final class TrackingDeviceStatusSerializer implements JsonSerializer { + private final GPSFixJsonSerializer gpsFixSerializer = new GPSFixJsonSerializer(); + private final JsonSerializer deviceIdSerializer; + + public TrackingDeviceStatusSerializer(JsonSerializer deviceIdSerializer) { + this.deviceIdSerializer = deviceIdSerializer; + } + + @Override + public JSONObject serialize(TrackingDeviceStatus deviceStatus) { + JSONObject result = new JSONObject(); + result.put("deviceId", deviceIdSerializer.serialize(deviceStatus.getDeviceId())); + if (deviceStatus.getLastGPSFix() != null) { + result.put("lastGPSFix", gpsFixSerializer.serialize(deviceStatus.getLastGPSFix())); + } + return result; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDevicesResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDevicesResource.java new file mode 100644 index 00000000000..53633cad4e6 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/TrackingDevicesResource.java @@ -0,0 +1,57 @@ +package com.sap.sailing.server.gateway.jaxrs.api; + +import java.util.Set; +import java.util.UUID; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.common.DeviceIdentifier; +import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; +import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; +import com.sap.sailing.server.gateway.serialization.JsonSerializer; +import com.sap.sailing.server.gateway.serialization.impl.DeviceIdentifierJsonSerializer; +import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler; + +@Path("/v1/tracking_devices") +public class TrackingDevicesResource extends AbstractSailingServerResource { + + @GET + @Produces("application/json;charset=UTF-8") + @Path("{deviceUUID}") + public Response getDeviceStatus(@PathParam("deviceUUID") UUID deviceUUID) { + final JsonSerializer serializer = new TrackingDeviceStatusSerializer( + new DeviceIdentifierJsonSerializer( + getServiceFinderFactory().createServiceFinder(DeviceIdentifierJsonHandler.class))); + final JSONObject result = serializer.serialize(calculateDeviceStatus(deviceUUID)); + return Response.ok(result.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8") + .build(); + } + + @GET + @Produces("application/json;charset=UTF-8") + public Response getDeviceStatuses(@QueryParam("deviceUUIDs") Set deviceUUIDs) { + final JsonSerializer serializer = new TrackingDeviceStatusSerializer( + new DeviceIdentifierJsonSerializer( + getServiceFinderFactory().createServiceFinder(DeviceIdentifierJsonHandler.class))); + final JSONArray result = new JSONArray(); + for (UUID deviceUUID : deviceUUIDs) { + result.add(serializer.serialize(calculateDeviceStatus(deviceUUID))); + } + return Response.ok(result.toJSONString()).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8") + .build(); + } + + private TrackingDeviceStatus calculateDeviceStatus(UUID deviceUUID) { + final DeviceIdentifier deviceIdentifier = new SmartphoneUUIDIdentifierImpl(deviceUUID); + return TrackingDeviceStatus.calculateDeviceStatus(deviceIdentifier, getService()); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html index 82a2df2eab7..dfeeccef5fb 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html @@ -161,6 +161,10 @@ name to make sure you get to the event you're interested in. /api/v1/regattas/{regattaname}/competitors/{competitorId}/remove Removes a competitor from a regatta (POST) + + /api/v1/regattas/{regattaName}/tracking_devices + Gets the status for all GPS trackers associated to a regatta (GET) + /api/v1/regattas/{regattaname}/races/{racename}/datamining/{queryidentifier} Executes the query for the race and gets the result @@ -356,6 +360,14 @@ name to make sure you get to the event you're interested in. /api/v1/trackedevents Delete a tracked event for the current user. + + /api/v1/tracking_devices/{deviceUUID} + Gets the status of an app based tracking device (GET) + + + /api/v1/tracking_devices + Gets the status of multiple app based tracking devices (GET) +
diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/regattasTrackingDevicesGet.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattasTrackingDevicesGet.html new file mode 100644 index 00000000000..bc1ab66ccb2 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattasTrackingDevicesGet.html @@ -0,0 +1,46 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/regattas/{regattaName}/tracking_devices

+ +Description: +

The service gets the status of all GPS tracking devices associated to the regatta. The user needs to have UPDATE permission granted to be allowed to access tracking device data.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Mandatory parameters:None
Optional parameters:None
Request method:GET
Example:http://www.sapsailing.com/sailingserver/api/v1/regattas/Kornati Cup 2019 - SUP Contest/tracking_devices
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/trackingDeviceStatusGet.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/trackingDeviceStatusGet.html new file mode 100644 index 00000000000..23fd993c46b --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/trackingDeviceStatusGet.html @@ -0,0 +1,46 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/tracking_devices/{deviceUUID}

+ +Description: +

The service gets the status of an app based tracker defined by the given UUID.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Mandatory parameters:None
Optional parameters:None
Request method:GET
Example:http://www.sapsailing.com/sailingserver/api/v1/tracking_devices/dc77e3d1-d405-435e-8699-ce7245a6fd4a
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/trackingDeviceStatusesGet.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/trackingDeviceStatusesGet.html new file mode 100644 index 00000000000..c34e36e1b96 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/trackingDeviceStatusesGet.html @@ -0,0 +1,46 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/tracking_devices/{deviceUUID}

+ +Description: +

The service gets the status of app based trackers defined by their respective UUID.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Mandatory parameters:deviceUUIDs: UUIDs of the devices to get the status for (multiple occurrences of the parameter are allowed)
Optional parameters:None
Request method:GET
Example:http://www.sapsailing.com/sailingserver/api/v1/tracking_devices?deviceUUIDs=dc77e3d1-d405-435e-8699-ce7245a6fd4a&deviceUUIDs=dc77e3d1-d405-435e-8699-ce7245a6fd4b
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java index e617930a836..0bc62c7239c 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java @@ -133,8 +133,7 @@ public class Activator implements BundleActivator { // only continue once we have the service, as some of the services require it to start properly securityServiceTracker.waitForService(0); internalStartBundle(context); - } catch (InterruptedException | MalformedObjectNameException | InstanceAlreadyExistsException - | MBeanRegistrationException | NotCompliantMBeanException | MalformedURLException e) { + } catch (Exception e) { logger.log(Level.SEVERE, "Could not start RacingEvent service properly", e); } }; @@ -204,39 +203,31 @@ public class Activator implements BundleActivator { InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { mailQueue = new ExecutorMailQueue(mailServiceTracker); notificationService = new SailingNotificationServiceImpl(context, mailQueue); - trackedRegattaListener = new OSGiBasedTrackedRegattaListener(context); - registrations.add(context.registerService(HasPermissionsProvider.class, (HasPermissionsProvider) SecuredDomainType::getAllInstances, null)); - registrations.add(context.registerService(SecurityInitializationCustomizer.class, (SecurityInitializationCustomizer) securityService -> { final RoleDefinition sailingViewerRoleDefinition = securityService.getOrCreateRoleDefinitionFromPrototype(SailingViewerRole.getInstance()); if (securityService.isInitialOrMigration()) { - // The server is initially set to be public by adding sailing_viewer role to the server group // with forAll=true securityService.putRoleDefinitionToUserGroup(securityService.getDefaultTenant(), sailingViewerRoleDefinition, true); - // sailing_viewer role is publicly readable securityService.addToAccessControlList(sailingViewerRoleDefinition.getIdentifier(), null, DefaultActions.READ.name()); } }, null)); - final TrackedRaceStatisticsCache trackedRaceStatisticsCache = new TrackedRaceStatisticsCacheImpl(); registrations.add(context.registerService(TrackedRaceStatisticsCache.class.getName(), trackedRaceStatisticsCache, null)); registrations.add(context.registerService(TrackedRegattaListener.class.getName(), trackedRaceStatisticsCache, null)); - // At this point the OSGi resolver is used as device type service finder. // In the case that we are not in an OSGi context (e.g. running a JUnit test instead), // this code block is not run, and the test case can inject some other type of finder // instead. serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context); - racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors, serviceFinderFactory, trackedRegattaListener, notificationService, trackedRaceStatisticsCache, restoreTrackedRaces, securityServiceTracker); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index fbaa74b3bf0..e88ce01abd4 100644 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -800,7 +800,6 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes } }); this.dataImportLock = new DataImportLockWithProgress(); - remoteSailingServerSet = new RemoteSailingServerSet(scheduler, baseDomainFactory); regattasByName = new ConcurrentHashMap(); regattasByNameLock = new NamedReentrantReadWriteLock("regattasByName for " + this, /* fair */false); diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index 581d67c76ea..91b4c7193e9 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -21,7 +21,19 @@

Release Notes - Administration Console

-

July 2019

+

August 2019

+
    +
  • New API endpoints were added to get the status of tracking devices. By now, just the last received GPS fix is returned in the status. + Under /sailingserver/api/v1/tracking_devices/[deviceUUID] the status of smartphone tracking devices can be read by using the device's UUID. + /sailingserver/api/v1/regattas/[regattaName]/tracking_devices the status for all tracking devices associated to a regatta is returned. + While the first requires the non public device UUID to be included in the request, the latter requires a user to have UPDATE permission for the regatta in question. +
  • +
  • Added a filter to hide all elements the current user is not allowed to edit. The filter can + be applied for each table in the AdminConsole and is active per default. +
  • +
+ +

July 2019

  • Result and competitor import URLs are now only considered when the requesting user has the permission to read that URL.
  • diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/DimensionFilterSelectionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/DimensionFilterSelectionProvider.java index 41a1b3fc3ea..8ebf0e17c6d 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/DimensionFilterSelectionProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/DimensionFilterSelectionProvider.java @@ -119,7 +119,7 @@ public class DimensionFilterSelectionProvider extends AbstractDataMiningComponen availableData = new HashSet<>(); filteredData = new ListDataProvider(this::elementAsString); - filterPanel = new AbstractFilterablePanel(null, filteredData) { + filterPanel = new AbstractFilterablePanel(null, filteredData, getDataMiningStringMessages()) { @Override public Iterable getSearchableStrings(Serializable element) { return Collections.singleton(elementAsString(element)); diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java index ec8846d6e0c..abc2b2be895 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java @@ -148,7 +148,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractDa filteredFilterDimensions.addDataDisplay(filterDimensionsList); filterFilterDimensionsPanel = new AbstractFilterablePanel( - null, filteredFilterDimensions) { + null, filteredFilterDimensions, stringMessages) { @Override public Iterable getSearchableStrings(DimensionWithContext dimension) { return dimension.getMatchingStrings(); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java index 07c042d24e7..525673adf0c 100755 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java @@ -100,4 +100,6 @@ public interface StringMessages extends Messages { String whatsNewDialogMessage(); String showChangelog(); + String hideElementsWithoutUpdateRights(); + } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties index 26ae65d7291..a0b4c76b86a 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties @@ -82,3 +82,4 @@ loadOrSaveQueries=Load or save query: whatsNewDialogTitle=What''s New? whatsNewDialogMessage=We added some features for you. If you are interested, click below to see what''s new. showChangelog=View Changelog +hideElementsWithoutUpdateRights=Hide elements without update rights diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties index fbac38ff8b4..2077e2e75ac 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties @@ -81,4 +81,5 @@ dataMiningStoredQueryRemovedFailed=Die gespeicherte Anfrage ''{0}'' konnte nicht loadOrSaveQueries=Anfrage laden oder speichern: whatsNewDialogTitle=Was gibt''s Neues? whatsNewDialogMessage=Wir haben einige neue Funktionen für Sie hinzugefügt. Klicken Sie unten, um zu sehen, was sich geändert hat. -showChangelog=Änderungen anzeigen \ No newline at end of file +showChangelog=Änderungen anzeigen +hideElementsWithoutUpdateRights=Elemente ohne Aktualisierungsberechtigung ausblenden diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java index 2256ae4c13b..904a66d64e7 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/AbstractFilterablePanel.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Function; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.dom.client.KeyUpEvent; @@ -11,6 +12,7 @@ import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.user.cellview.client.AbstractCellTable; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.ColumnSortEvent; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Panel; @@ -20,6 +22,7 @@ import com.sap.sse.common.Util; import com.sap.sse.common.filter.AbstractKeywordFilter; import com.sap.sse.common.filter.AbstractListFilter; import com.sap.sse.common.filter.Filter; +import com.sap.sse.gwt.client.StringMessages; import com.sap.sse.gwt.client.celltable.RefreshableSelectionModel; /** @@ -49,8 +52,43 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { protected ListDataProvider all; protected final ListDataProvider filtered; protected final TextBox textBox; + protected final CheckBox checkbox; private final Set> filters = new HashSet<>(); + private final CheckboxEnablableFilter checkboxFilter; + + private boolean added = false; + + static class CheckboxEnablableFilter implements Filter { + + private final CheckBox checkbox; + private Filter filterToApply; + + public CheckboxEnablableFilter(CheckBox checkbox) { + this.checkbox = checkbox; + } + + @Override + public boolean matches(T object) { + if (filterToApply == null) { + return true; + } + if (!this.checkbox.getValue()) { + return true; + } + return filterToApply.matches(object); + } + + @Override + public String getName() { + return filterToApply.getName(); + } + + public void setFilterToApply(Filter filterToApply) { + this.filterToApply = filterToApply; + } + + } protected final AbstractKeywordFilter filterer = new AbstractKeywordFilter() { @Override @@ -72,13 +110,18 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { * normal circumstances the text box will be empty in this case, not making filtering any stricter. */ public AbstractFilterablePanel(Iterable all, final ListDataProvider filtered, - boolean drawTextBox) { + boolean drawTextBox, final StringMessages stringMessages) { filters.add(filterer); setSpacing(5); this.all = new ListDataProvider<>(); this.filtered = filtered; this.textBox = new TextBox(); this.textBox.ensureDebugId("FilterTextBox"); + // TODO: i18n + this.checkbox = new CheckBox(stringMessages.hideElementsWithoutUpdateRights()); + checkbox.setValue(true); + checkboxFilter = new CheckboxEnablableFilter<>(checkbox); + filters.add(checkboxFilter); this.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); setAll(all); if (drawTextBox) { @@ -86,8 +129,9 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { } } - public AbstractFilterablePanel(Iterable all, final ListDataProvider filtered) { - this(all, filtered, /* show default filter text box */ true); + public AbstractFilterablePanel(Iterable all, final ListDataProvider filtered, + final StringMessages stringMessages) { + this(all, filtered, /* show default filter text box */ true, stringMessages); } private void setAll(Iterable all) { @@ -221,6 +265,14 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { }); } + public void addDefaultCheckBoxIfNecessary() { + if (!added) { + add(getCheckBox()); + getCheckBox().addClickHandler(e -> filter()); + added = true; + } + } + public void search(String searchString) { getTextBox().setText(searchString); filterer.setKeywords(Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(searchString)); @@ -231,6 +283,26 @@ public abstract class AbstractFilterablePanel extends HorizontalPanel { return textBox; } + public CheckBox getCheckBox() { + return checkbox; + } + + public void setCheckboxEnabledFilter(Function filterFunction) { + + this.checkboxFilter.setFilterToApply(new Filter() { + @Override + public boolean matches(T object) { + return filterFunction.apply(object); + } + + @Override + public String getName() { + return "Update Permission Filter"; + } + }); + addDefaultCheckBoxIfNecessary(); + } + public Iterable getAll() { return all.getList(); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java index 7b087250675..9f2a9b356fc 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/CustomizableFilterablePanel.java @@ -5,6 +5,7 @@ import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.ListDataProvider; import com.sap.sse.common.filter.Filter; +import com.sap.sse.gwt.client.StringMessages; /** * Customizable filter panel which allows to create a flexible filter panel with various number of filter inputs (e.g. @@ -19,8 +20,8 @@ public abstract class CustomizableFilterablePanel extends AbstractFilterableP * Does not add the default filter text box yet; use {@link #addDefaultTextBox()} to do so; you may * add other widgets before the default text filter box. */ - public CustomizableFilterablePanel(Iterable all, ListDataProvider filtered) { - super(all, filtered, /* show default filter text box */ false); + public CustomizableFilterablePanel(Iterable all, ListDataProvider filtered, StringMessages stringMessages) { + super(all, filtered, /* show default filter text box */ false, stringMessages); } public void add(Label label, Widget widget, Filter filter) { diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/LabeledAbstractFilterablePanel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/LabeledAbstractFilterablePanel.java index 1b58c83e575..e96196518f8 100755 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/LabeledAbstractFilterablePanel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/panels/LabeledAbstractFilterablePanel.java @@ -4,6 +4,7 @@ import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.client.ui.Label; import com.google.gwt.view.client.ListDataProvider; import com.sap.sse.common.filter.AbstractListFilter; +import com.sap.sse.gwt.client.StringMessages; /** * This Panel contains a label and a text box. Text entered into the text box filters the {@link CellTable} passed to @@ -29,8 +30,9 @@ public abstract class LabeledAbstractFilterablePanel extends AbstractFilterab /** */ - public LabeledAbstractFilterablePanel(Label label, Iterable all, final ListDataProvider filtered) { - super(all, filtered); + public LabeledAbstractFilterablePanel(Label label, Iterable all, final ListDataProvider filtered, + StringMessages stringMessages) { + super(all, filtered, stringMessages); insert(label, 0); } diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/RoleDefinitionsPanel.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/RoleDefinitionsPanel.java index d478a690e30..4d51b82af2f 100755 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/RoleDefinitionsPanel.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/RoleDefinitionsPanel.java @@ -80,7 +80,7 @@ public class RoleDefinitionsPanel extends VerticalPanel { this.userManagementService = userService.getUserManagementService(); rolesListDataProvider = new ListDataProvider<>(); filterablePanelRoleDefinitions = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterRoles()), new ArrayList<>(), - rolesListDataProvider) { + rolesListDataProvider, stringMessages) { @Override public Iterable getSearchableStrings(RoleDefinitionDTO roleDefinition) { return Arrays.asList(roleDefinition.getName(), roleDefinition.getId().toString(), roleDefinition.getPermissions().toString()); @@ -91,6 +91,8 @@ public class RoleDefinitionsPanel extends VerticalPanel { return roleDefinitionsTable; } }; + filterablePanelRoleDefinitions + .setCheckboxEnabledFilter(role -> userService.hasPermission(role, DefaultActions.UPDATE)); roleDefinitionsTable = createRoleDefinitionsTable(tableResources); roleDefinitionsTable.ensureDebugId("RolesCellTable"); filterablePanelRoleDefinitions.getTextBox().ensureDebugId("RolesFilterTextBox"); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserGroupTableWrapper.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserGroupTableWrapper.java index 9a711f64ab3..be37598d17d 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserGroupTableWrapper.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/UserGroupTableWrapper.java @@ -108,7 +108,7 @@ public class UserGroupTableWrapper extends u -> configACL.openDialog(u)); filterField = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterUserGroups()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(UserGroupDTO t) { List string = new ArrayList(); @@ -121,6 +121,7 @@ public class UserGroupTableWrapper extends return table; } }; + filterField.setCheckboxEnabledFilter(userGroup -> userService.hasPermission(userGroup, DefaultActions.UPDATE)); registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); mainPanel.insert(filterField, 0); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/usergroup/roles/RoleDefinitionTableWrapper.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/usergroup/roles/RoleDefinitionTableWrapper.java index a7899af2614..74da9d5a548 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/usergroup/roles/RoleDefinitionTableWrapper.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/usergroup/roles/RoleDefinitionTableWrapper.java @@ -114,7 +114,7 @@ public class RoleDefinitionTableWrapper extends // filter field configuration filterField = new LabeledAbstractFilterablePanel>( new Label(stringMessages.filterRoles()), new ArrayList>(), - dataProvider) { + dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(Pair t) { List string = new ArrayList(); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java index a18f6cbf456..fcb8b0b1b0a 100755 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserTableWrapper.java @@ -195,7 +195,7 @@ extends TableWrapper { u -> configACL.openDialog(u)); filterField = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterUsers()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(UserDTO t) { List string = new ArrayList(); @@ -213,6 +213,7 @@ extends TableWrapper { } }; registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); + filterField.setCheckboxEnabledFilter(user -> userService.hasPermission(user, DefaultActions.UPDATE)); mainPanel.insert(filterField, 0); table.addColumnSortHandler(userColumnListHandler); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionWithSecurityDTOTableWrapper.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionWithSecurityDTOTableWrapper.java index d31c5b3bd9c..907e66ac675 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionWithSecurityDTOTableWrapper.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionWithSecurityDTOTableWrapper.java @@ -119,7 +119,7 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends // filter field configuration filterField = new LabeledAbstractFilterablePanel( new Label(stringMessages.filterPermission()), new ArrayList(), - dataProvider) { + dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(WildcardPermissionWithSecurityDTO t) { List string = new ArrayList(); @@ -133,6 +133,8 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends } }; registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); + filterField + .setCheckboxEnabledFilter(permission -> userService.hasPermission(permission, DefaultActions.UPDATE)); mainPanel.insert(filterField, 0); diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/RoleWithSecurityDTOTableWrapper.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/RoleWithSecurityDTOTableWrapper.java index bb6201b8ba3..818ea6b2f25 100644 --- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/RoleWithSecurityDTOTableWrapper.java +++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/RoleWithSecurityDTOTableWrapper.java @@ -120,7 +120,7 @@ public class RoleWithSecurityDTOTableWrapper extends // filter field configuration filterField = new LabeledAbstractFilterablePanel(new Label(stringMessages.filterRoles()), - new ArrayList(), dataProvider) { + new ArrayList(), dataProvider, stringMessages) { @Override public Iterable getSearchableStrings(RoleWithSecurityDTO t) { List string = new ArrayList(); @@ -133,6 +133,7 @@ public class RoleWithSecurityDTOTableWrapper extends return table; } }; + filterField.setCheckboxEnabledFilter(role -> userService.hasPermission(role, DefaultActions.UPDATE)); registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); mainPanel.insert(filterField, 0); diff --git a/wiki/planning/orc.md b/wiki/planning/orc.md index f4da5c966d5..690fe053e34 100644 --- a/wiki/planning/orc.md +++ b/wiki/planning/orc.md @@ -10,6 +10,7 @@ The scoring itself allows to choose between using the simpler provided ToT/ToD s Currently the calculation for a constructed course with set TWAs does work with matching values to the official ORC tools and M2S. The internal calculations follow the ideas provided in the ORC pascal code, many calculating parts are using the predicted velocity value instead of the allowance. The calculation involves the following implemented parts: + - get Allowances for Leg with TWA - get Lagrange interpolated Allowance for TWA/TWS - get Allowances for whole Course out of leg parts @@ -21,6 +22,7 @@ The last two stated points have currently some issues regarding the accuracy of ### Mixed Course The next step is to extend the properties of an ORCPerformanceCurveLeg to not only be defined as a leg with a specified TWA and an fixed allowance calculated with Lagrange but also be defined as a leg as a predefined course. + - [ ] Add enums for predefined courses - [ ] Add or change ORCPerformanceCurveLeg implementation (and interface) - [ ] Adapt ORCPerformanceCurveImpl to handle Legs with predefined allowances @@ -33,9 +35,10 @@ After the functionality for the different course possibilities is added, there n This problem should be resolved by the usage of the RaceLog and by the usage of an adapter pattern to match the information given from a TrackedRace to the ORCPerformanceCurveCourse. For this purpose there are the following subtasks: + - [ ] UML for this concept -- [ ] create RaceLogEvent for TWA/Length information per Leg. This task additionally consists out of some design decisions regarding the revokability of events. -- [ ] create RaceLogEventAnalyzer for this Event +- [x] create RaceLogEvent for TWA/Length information per Leg. This task additionally consists out of some design decisions regarding the revokability of events. +- [x] create RaceLogEventAnalyzer for this Event *currently focussed on these tasks* - [ ] implement logic to get TWA/Length from a TrackedLeg - [ ] implement logic wether to use information from RaceLog or automatic generated from TrackedLeg - [ ] implement Listener on RaceLog