mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-03 09:39:38 +00:00
Merge branch 'master' into bug2842
This commit is contained in:
commit
daecd8c236
84 files changed
+1016
-104
No files matched your search
@@ -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 <replica-set-name>] [-p <host>:<port>] [-P <priority] [-t <instance-type>] [-a <availability-zone>] [-i <ami-id>] [-k <key-pair-name>]"
|
||||
echo "$0 [-r <replica-set-name>] [-p <host>:<port>] [-P <priority] [-t <instance-type>] [-a <availability-zone>] [-i <ami-id>] [-k <key-pair-name>] [-v <votes>]"
|
||||
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}]"
|
||||
@@ -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'`
|
||||
|
||||
@@ -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
|
||||
@@ -11,3 +11,4 @@ Type=oneshot
|
||||
RemainAfterExit=true
|
||||
ExecStart=/usr/local/bin/add-as-replica
|
||||
ExecStop=/usr/local/bin/remove-as-replica
|
||||
TimeoutStopSpec=120s
|
||||
@@ -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<RaceColumn> 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<RaceColumn> discardedRaceColumns, Supplier<Double> totalPointsProvider);
|
||||
|
||||
TimePoint getNowMinusDelay();
|
||||
|
||||
/**
|
||||
|
||||
+8
-1
@@ -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<RaceColumn> discardedRaceColumns) {
|
||||
return getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns, ()->getTotalPoints(competitor, raceColumn, timePoint));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
|
||||
Set<RaceColumn> discardedRaceColumns, Supplier<Double> 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 {
|
||||
|
||||
+7
@@ -495,4 +495,11 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
|
||||
public void deregisterBoats(Iterable<Boat> boats) {
|
||||
getFullLeaderboard().deregisterBoats(boats);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint,
|
||||
Set<RaceColumn> discardedRaceColumns, Supplier<Double> totalPointsProvider) {
|
||||
return getFullLeaderboard().getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns,
|
||||
totalPointsProvider);
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -94,8 +94,10 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
this.discardedRaceColumnsPerCompetitor.put(competitor, discardedRaceColumns);
|
||||
for (RaceColumn raceColumn : raceColumnsToConsider) {
|
||||
Pair<Competitor, RaceColumn> key = new Util.Pair<Competitor, RaceColumn>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -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<StrippedLeaderboardDTOWithSecurity>(/* pageSize */10000, tableRes);
|
||||
filterLeaderboardPanel = new LabeledAbstractFilterablePanel<StrippedLeaderboardDTOWithSecurity>(lblFilterEvents,
|
||||
availableLeaderboardList, filteredLeaderboardList) {
|
||||
availableLeaderboardList, filteredLeaderboardList, stringMessages) {
|
||||
@Override
|
||||
public List<String> getSearchableStrings(StrippedLeaderboardDTOWithSecurity t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
@@ -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");
|
||||
|
||||
+2
-1
@@ -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<RaceDTO>(allRaces, raceList) {
|
||||
filterablePanelRaces = new CustomizableFilterablePanel<RaceDTO>(allRaces, raceList, stringMessages) {
|
||||
@Override
|
||||
public List<String> getSearchableStrings(RaceDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
@@ -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());
|
||||
|
||||
+2
-1
@@ -151,7 +151,7 @@ public class BoatTableWrapper<S extends RefreshableSelectionModel<BoatDTO>> exte
|
||||
});
|
||||
|
||||
filterField = new LabeledAbstractFilterablePanel<BoatDTO>(new Label(stringMessages.filterBoats()),
|
||||
new ArrayList<BoatDTO>(), dataProvider) {
|
||||
new ArrayList<BoatDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(BoatDTO boat) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -167,6 +167,7 @@ public class BoatTableWrapper<S extends RefreshableSelectionModel<BoatDTO>> exte
|
||||
return table;
|
||||
}
|
||||
};
|
||||
filterField.setCheckboxEnabledFilter(boat -> userService.hasPermission(boat, DefaultActions.UPDATE));
|
||||
registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
|
||||
|
||||
// BoatTable edit features
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ public class CompactBoatTableWrapper<S extends RefreshableSelectionModel<BoatDTO
|
||||
return comparator.compare(o1.getIdAsString(), o2.getIdAsString());
|
||||
}
|
||||
});
|
||||
filterField = new CustomizableFilterablePanel<BoatDTO>(new ArrayList<>(), getDataProvider()) {
|
||||
filterField = new CustomizableFilterablePanel<BoatDTO>(new ArrayList<>(), getDataProvider(), stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(BoatDTO boat) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
|
||||
+6
-2
@@ -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<S extends RefreshableSelectionModel<C
|
||||
private final Map<CompetitorDTO, BoatDTO> 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<CompetitorDTO>() {
|
||||
@Override
|
||||
@@ -133,7 +135,7 @@ public class CompactCompetitorTableWrapper<S extends RefreshableSelectionModel<C
|
||||
};
|
||||
|
||||
filterField = new LabeledAbstractFilterablePanel<CompetitorDTO>(new Label(stringMessages.filterCompetitors()),
|
||||
new ArrayList<CompetitorDTO>(), dataProvider) {
|
||||
new ArrayList<CompetitorDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(CompetitorDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -148,6 +150,8 @@ public class CompactCompetitorTableWrapper<S extends RefreshableSelectionModel<C
|
||||
return table;
|
||||
}
|
||||
};
|
||||
filterField.setCheckboxEnabledFilter(comp -> userService.hasPermission(comp, DefaultActions.UPDATE));
|
||||
|
||||
registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
|
||||
mainPanel.insert(filterField, 0);
|
||||
table.addColumnSortHandler(competitorColumnListHandler);
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ public class CompetitorDescriptorTableWrapper<S extends RefreshableSelectionMode
|
||||
this.competitorImportMatcher = competitorImportMatcherParam;
|
||||
filterablePanelCompetitorDescriptor = new LabeledAbstractFilterablePanel<CompetitorDescriptor>(
|
||||
new Label(stringMessages.filterImportedCompetitorsByNameSailRaceFleet()),
|
||||
new ArrayList<CompetitorDescriptor>(), dataProvider) {
|
||||
new ArrayList<CompetitorDescriptor>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(CompetitorDescriptor competitorDescriptor) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
|
||||
+2
-1
@@ -301,7 +301,7 @@ public class CompetitorTableWrapper<S extends RefreshableSelectionModel<Competit
|
||||
});
|
||||
|
||||
filterField = new LabeledAbstractFilterablePanel<CompetitorDTO>(new Label(getStringMessages().filterCompetitors()),
|
||||
new ArrayList<CompetitorDTO>(), dataProvider) {
|
||||
new ArrayList<CompetitorDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(CompetitorDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -321,6 +321,7 @@ public class CompetitorTableWrapper<S extends RefreshableSelectionModel<Competit
|
||||
return table;
|
||||
}
|
||||
};
|
||||
filterField.setCheckboxEnabledFilter(comp -> userService.hasPermission(comp, DefaultActions.UPDATE));
|
||||
registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
|
||||
|
||||
// CompetitorTableEditFeatures
|
||||
|
||||
+6
-3
@@ -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<Map<Competit
|
||||
}
|
||||
|
||||
public CompetitorToBoatMappingsDialog(final SailingServiceAsync sailingService, final StringMessages stringMessages,
|
||||
final ErrorReporter errorReporter, String leaderboardName, Map<CompetitorDTO, BoatDTO> competitorsAndBoats, DialogCallback<Map<CompetitorDTO, BoatDTO>> callback) {
|
||||
final ErrorReporter errorReporter, String leaderboardName, Map<CompetitorDTO, BoatDTO> competitorsAndBoats,
|
||||
DialogCallback<Map<CompetitorDTO, BoatDTO>> 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<CompetitorDTO, CompactCompetitorConfigImagesBarCell> competitorActionColumn = new ImagesBarColumn<>(new CompactCompetitorConfigImagesBarCell(stringMessages));
|
||||
competitorActionColumn.setFieldUpdater(new FieldUpdater<CompetitorDTO, String>() {
|
||||
|
||||
+5
-2
@@ -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<CompetitorDTO> 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() {
|
||||
|
||||
+2
-1
@@ -163,7 +163,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
|
||||
|
||||
eventListDataProvider = new ListDataProvider<EventDTO>();
|
||||
filterTextbox = new LabeledAbstractFilterablePanel<EventDTO>(new Label(stringMessages.filterEventsByName()),
|
||||
allEvents, eventListDataProvider) {
|
||||
allEvents, eventListDataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(EventDTO t) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
@@ -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<EventDTO> createEventTable(UserDTO user) {
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ public class ExpeditionDeviceConfigurationsPanel extends FlowPanel {
|
||||
filteredDeviceConfigurations.addDataDisplay(allDeviceConfigurations);
|
||||
final List<ExpeditionDeviceConfiguration> emptyList = Collections.emptyList();
|
||||
filterDeviceConfigurationsPanel = new LabeledAbstractFilterablePanel<ExpeditionDeviceConfiguration>(new Label(stringMessages.expeditionDeviceConfigurations()),
|
||||
emptyList, filteredDeviceConfigurations) {
|
||||
emptyList, filteredDeviceConfigurations, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(ExpeditionDeviceConfiguration t) {
|
||||
Set<String> strings = new HashSet<>();
|
||||
|
||||
+3
-1
@@ -105,7 +105,7 @@ public class IgtimiAccountsPanel extends FlowPanel {
|
||||
tableRes);
|
||||
final ListDataProvider<AccountWithSecurityDTO> filteredAccounts = new ListDataProvider<>();
|
||||
filterAccountsPanel = new LabeledAbstractFilterablePanel<AccountWithSecurityDTO>(
|
||||
new Label(stringMessages.igtimiAccounts()), Collections.emptyList(), filteredAccounts) {
|
||||
new Label(stringMessages.igtimiAccounts()), Collections.emptyList(), filteredAccounts, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(AccountWithSecurityDTO t) {
|
||||
Set<String> 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,
|
||||
|
||||
+2
-1
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -220,7 +220,7 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel
|
||||
leaderboardsTable = new FlushableCellTable<StrippedLeaderboardDTO>(10000, tableRes);
|
||||
leaderboardsTable.ensureDebugId("LeaderboardsCellTable");
|
||||
leaderboardsFilterablePanel = new LabeledAbstractFilterablePanel<StrippedLeaderboardDTO>(
|
||||
filterLeaderboardsLabel, availableLeaderboards, leaderboardsProvider) {
|
||||
filterLeaderboardsLabel, availableLeaderboards, leaderboardsProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(StrippedLeaderboardDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
@@ -484,7 +484,7 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel
|
||||
groupsTable = new FlushableCellTable<LeaderboardGroupDTO>(10000, tableRes);
|
||||
groupsTable.ensureDebugId("LeaderboardGroupsCellTable");
|
||||
groupsFilterablePanel = new LabeledAbstractFilterablePanel<LeaderboardGroupDTO>(filterLeaderboardGroupsLbl,
|
||||
availableLeaderboardGroups, groupsProvider) {
|
||||
availableLeaderboardGroups, groupsProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(LeaderboardGroupDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ public class LeaderboardGroupSelectorComposite extends Composite implements HasV
|
||||
availableLeaderboardGroupsProvider.addDataDisplay(availableLeaderboardGroupsTable);
|
||||
availableLeaderboardGroupsFilterablePanel = new LabeledAbstractFilterablePanel<LeaderboardGroupDTO>(
|
||||
new Label(stringMessages.filterLeaderboardGroupsByName()),
|
||||
Collections.<LeaderboardGroupDTO> emptyList(), availableLeaderboardGroupsProvider) {
|
||||
Collections.<LeaderboardGroupDTO> emptyList(), availableLeaderboardGroupsProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(LeaderboardGroupDTO t) {
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
+3
-1
@@ -173,7 +173,7 @@ public class MediaPanel extends FlowPanel implements MediaTracksRefresher {
|
||||
|
||||
this.filterableMediaTracks = new LabeledAbstractFilterablePanel<MediaTrackWithSecurityDTO>(lblFilterRaces,
|
||||
allMediaTracks,
|
||||
mediaTrackListDataProvider) {
|
||||
mediaTrackListDataProvider, stringMessages) {
|
||||
@Override
|
||||
public List<String> getSearchableStrings(MediaTrackWithSecurityDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
@@ -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() {
|
||||
|
||||
+3
-1
@@ -108,7 +108,7 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer
|
||||
regattaListDataProvider = new ListDataProvider<RegattaDTO>();
|
||||
|
||||
filterablePanelRegattas = new LabeledAbstractFilterablePanel<RegattaDTO>(filterRegattasLabel, allRegattas,
|
||||
regattaListDataProvider) {
|
||||
regattaListDataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(RegattaDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -129,6 +129,8 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer
|
||||
regattaTable.ensureDebugId("RegattasCellTable");
|
||||
refreshableRegattaMultiSelectionModel = (RefreshableMultiSelectionModel<RegattaDTO>) regattaTable.getSelectionModel();
|
||||
regattaTable.setVisible(false);
|
||||
filterablePanelRegattas
|
||||
.setCheckboxEnabledFilter(regatta -> userService.hasPermission(regatta, DefaultActions.UPDATE));
|
||||
panel.add(filterablePanelRegattas);
|
||||
|
||||
panel.add(regattaTable);
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog<Void
|
||||
deviceMappingPanel.add(chart);
|
||||
filterField = new LabeledAbstractFilterablePanel<DeviceMappingDTO>(
|
||||
new Label(stringMessages.filterDeviceMappings()),
|
||||
new ArrayList<DeviceMappingDTO>(), deviceMappingTable.getDataProvider()) {
|
||||
new ArrayList<DeviceMappingDTO>(), deviceMappingTable.getDataProvider(), stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(DeviceMappingDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ public class RemoteServerInstancesManagementPanel extends SimplePanel {
|
||||
serverDataProvider = new ListDataProvider<RemoteSailingServerReferenceDTO>();
|
||||
filteredServerTablePanel = new LabeledAbstractFilterablePanel<RemoteSailingServerReferenceDTO>(
|
||||
new Label(stringMessages.filterBy() + ":"), Collections.<RemoteSailingServerReferenceDTO> emptyList(),
|
||||
serverDataProvider) {
|
||||
serverDataProvider, stringMessages) {
|
||||
@Override
|
||||
public List<String> getSearchableStrings(RemoteSailingServerReferenceDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
|
||||
+3
-2
@@ -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);
|
||||
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -200,7 +200,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
|
||||
raceTable.setWidth("300px");
|
||||
raceList = new ListDataProvider<SwissTimingRaceRecordDTO>();
|
||||
filterablePanelEvents = new LabeledAbstractFilterablePanel<SwissTimingRaceRecordDTO>(lblFilterEvents,
|
||||
availableSwissTimingRaces, raceList) {
|
||||
availableSwissTimingRaces, raceList, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(SwissTimingRaceRecordDTO t) {
|
||||
List<String> strings = new ArrayList<>();
|
||||
|
||||
+2
-1
@@ -177,7 +177,8 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane
|
||||
raceTable.addColumn(raceStartTrackingColumn, stringMessages.startTime());
|
||||
raceTable.setWidth("300px");
|
||||
raceList = new ListDataProvider<SwissTimingReplayRaceDTO>();
|
||||
filterablePanelEvents = new LabeledAbstractFilterablePanel<SwissTimingReplayRaceDTO>(lblFilterEvents, availableSwissTimingRaces, raceList) {
|
||||
filterablePanelEvents = new LabeledAbstractFilterablePanel<SwissTimingReplayRaceDTO>(lblFilterEvents,
|
||||
availableSwissTimingRaces, raceList, stringMessages) {
|
||||
@Override
|
||||
public List<String> getSearchableStrings(SwissTimingReplayRaceDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
|
||||
+2
-1
@@ -252,7 +252,8 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
|
||||
AdminConsoleTableResources tableResources = GWT.create(AdminConsoleTableResources.class);
|
||||
racesTable = new FlushableCellTable<TracTracRaceRecordDTO>(10000, tableResources);
|
||||
racesTable.ensureDebugId("TrackableRacesCellTable");
|
||||
this.racesFilterablePanel = new LabeledAbstractFilterablePanel<TracTracRaceRecordDTO>(racesFilterLabel, availableTracTracRaces, raceList) {
|
||||
this.racesFilterablePanel = new LabeledAbstractFilterablePanel<TracTracRaceRecordDTO>(racesFilterLabel,
|
||||
availableTracTracRaces, raceList, stringMessages) {
|
||||
@Override
|
||||
public List<String> getSearchableStrings(TracTracRaceRecordDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
|
||||
+3
-1
@@ -122,7 +122,7 @@ public class SwissTimingArchivedConnectionTableWrapper extends
|
||||
u -> configACL.openDialog(u));
|
||||
filterField = new LabeledAbstractFilterablePanel<SwissTimingArchiveConfigurationWithSecurityDTO>(
|
||||
new Label(stringMessages.filterSwissTimingAchivedConnections()),
|
||||
new ArrayList<SwissTimingArchiveConfigurationWithSecurityDTO>(), dataProvider) {
|
||||
new ArrayList<SwissTimingArchiveConfigurationWithSecurityDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(SwissTimingArchiveConfigurationWithSecurityDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
@@ -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);
|
||||
|
||||
+3
-1
@@ -140,7 +140,7 @@ public class SwissTimingConnectionTableWrapper extends
|
||||
|
||||
filterField = new LabeledAbstractFilterablePanel<SwissTimingConfigurationWithSecurityDTO>(
|
||||
new Label(stringMessagesClient.filterSwissTimingConnections()),
|
||||
new ArrayList<SwissTimingConfigurationWithSecurityDTO>(), dataProvider) {
|
||||
new ArrayList<SwissTimingConfigurationWithSecurityDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(SwissTimingConfigurationWithSecurityDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
@@ -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);
|
||||
|
||||
+3
-1
@@ -134,7 +134,7 @@ public class TracTracConnectionTableWrapper extends
|
||||
u -> configACL.openDialog(u));
|
||||
filterField = new LabeledAbstractFilterablePanel<TracTracConfigurationWithSecurityDTO>(
|
||||
new Label(stringMessages.filterTracTracConnections()),
|
||||
new ArrayList<TracTracConfigurationWithSecurityDTO>(), dataProvider) {
|
||||
new ArrayList<TracTracConfigurationWithSecurityDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(TracTracConfigurationWithSecurityDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
@@ -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());
|
||||
|
||||
+4
-2
@@ -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
|
||||
|
||||
+4
-2
@@ -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
|
||||
|
||||
+4
-2
@@ -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
|
||||
|
||||
+4
-2
@@ -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=競技者の招待
|
||||
|
||||
+4
-2
@@ -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
|
||||
|
||||
+4
-2
@@ -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=Пригласить участников
|
||||
|
||||
+4
-2
@@ -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=邀请参赛队
|
||||
|
||||
+1
-1
@@ -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
|
||||
+1
-1
@@ -9,4 +9,4 @@ WindIsConfident=風測定確度
|
||||
Rank=ランク
|
||||
BackendPolars=バックエンドポーラー
|
||||
TargetBeatAngle=目標ビートアングル
|
||||
TargetRunawayAngle=目標ランナウェイアングル
|
||||
TargetRunawayAngle=目標ランアングル
|
||||
+1
-1
@@ -9,4 +9,4 @@ WindIsConfident=风力测量置信度
|
||||
Rank=名次
|
||||
BackendPolars=后端极坐标图
|
||||
TargetBeatAngle=目标迎风航行角度
|
||||
TargetRunawayAngle=目标失控角度
|
||||
TargetRunawayAngle=目标顺风航行角度
|
||||
+6
-6
@@ -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);
|
||||
|
||||
+18
@@ -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);
|
||||
}
|
||||
}
|
||||
+13
@@ -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();
|
||||
}
|
||||
}
|
||||
+94
@@ -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<RuntimeException> forResponse(final ClientResponse response) {
|
||||
return forResponse(response, null);
|
||||
}
|
||||
|
||||
public static Optional<RuntimeException> 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+15
@@ -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()));
|
||||
}
|
||||
}
|
||||
+28
-1
@@ -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<RegattaRaces> 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));
|
||||
}
|
||||
}
|
||||
+77
@@ -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<DeviceStatus> 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<CompetitorDeviceStatus> 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<BoatDeviceStatus> 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<MarkDeviceStatus> getMarks() {
|
||||
Object jsonObjectOrNull = get("marks");
|
||||
return jsonObjectOrNull instanceof JSONArray ? ((JSONArray) jsonObjectOrNull).stream()
|
||||
.map(o -> new MarkDeviceStatus((JSONObject) o)).collect(Collectors.toList()) : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+22
@@ -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");
|
||||
}
|
||||
}
|
||||
+49
-1
@@ -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<CompetitorDeviceStatus> competitorsTrackingDeviceStatus = trackingDeviceStatus.getCompetitors();
|
||||
assertEquals(2, competitorsTrackingDeviceStatus.size());
|
||||
final Map<UUID, List<DeviceStatus>> 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<DeviceStatus> 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<DeviceStatus> competitor2DeviceStatuses = statusByCompetitorUUID.get(competitor2.getId());
|
||||
assertEquals(1, competitor1DeviceStatuses.size());
|
||||
DeviceStatus competitor2DeviceStatus = competitor2DeviceStatuses.iterator().next();
|
||||
assertEquals(competitor2DeviceID.toString(), competitor2DeviceStatus.getDeviceId());
|
||||
assertEquals(timeMillisCompetitor2, competitor2DeviceStatus.getLastGPSFix().getTime());
|
||||
}
|
||||
}
|
||||
+55
@@ -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());
|
||||
}
|
||||
}
|
||||
+23
-4
@@ -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<WebDriver, DataEntryPO>() {
|
||||
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<WebDriver, DataEntryPO>() {
|
||||
@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) {
|
||||
|
||||
+13
-2
@@ -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> T getService(Class<T> clazz) {
|
||||
BundleContext context = (BundleContext) servletContext
|
||||
.getAttribute(RestServletContainer.OSGI_RFC66_WEBBUNDLE_BUNDLECONTEXT_NAME);
|
||||
BundleContext context = getBundleContext();
|
||||
ServiceTracker<T, T> tracker = new ServiceTracker<T, T>(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<RacingEventService, RacingEventService> tracker = (ServiceTracker<RacingEventService, RacingEventService>) servletContext.getAttribute(RestServletContainer.RACING_EVENT_SERVICE_TRACKER_NAME);
|
||||
|
||||
Executable → Regular
+57
@@ -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<WithID> regattaLogDeviceMappingFinder = new RegattaLogDeviceMappingFinder<WithID>(
|
||||
regatta.getRegattaLog());
|
||||
final Map<WithID, List<DeviceMappingWithRegattaLogEvent<WithID>>> foundMappings = regattaLogDeviceMappingFinder
|
||||
.analyze();
|
||||
|
||||
final JSONObject result = new JSONObject();
|
||||
foundMappings.forEach((item, mappings) -> {
|
||||
final JSONArray deviceStatusesOfTrackedItem = new JSONArray();
|
||||
deviceStatusesOfTrackedItem
|
||||
.addAll(mappings.stream().map(DeviceMappingWithRegattaLogEvent<WithID>::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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -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);
|
||||
|
||||
+48
@@ -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<DeviceIdentifier, Timed> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -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<TrackingDeviceStatus> {
|
||||
private final GPSFixJsonSerializer gpsFixSerializer = new GPSFixJsonSerializer();
|
||||
private final JsonSerializer<DeviceIdentifier> deviceIdSerializer;
|
||||
|
||||
public TrackingDeviceStatusSerializer(JsonSerializer<DeviceIdentifier> 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;
|
||||
}
|
||||
}
|
||||
+57
@@ -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<TrackingDeviceStatus> 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<UUID> deviceUUIDs) {
|
||||
final JsonSerializer<TrackingDeviceStatus> 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());
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,10 @@ name to make sure you get to the event you're interested in.
|
||||
<td>/api/v1/regattas/{regattaname}/competitors/{competitorId}/remove</td>
|
||||
<td>Removes a competitor from a regatta (POST)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="regattasTrackingDevicesGet.html">/api/v1/regattas/{regattaName}/tracking_devices</a></td>
|
||||
<td>Gets the status for all GPS trackers associated to a regatta (GET)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="raceRunQueryGetDoc.html">/api/v1/regattas/{regattaname}/races/{racename}/datamining/{queryidentifier}</a></td>
|
||||
<td>Executes the query for the race and gets the result</td>
|
||||
@@ -356,6 +360,14 @@ name to make sure you get to the event you're interested in.
|
||||
<td><a href="trackedeventsDeleteDoc.html">/api/v1/trackedevents</a></td>
|
||||
<td>Delete a tracked event for the current user.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="trackingDeviceStatusGet.html">/api/v1/tracking_devices/{deviceUUID}</a></td>
|
||||
<td>Gets the status of an app based tracking device (GET)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="trackingDeviceStatusesGet.html">/api/v1/tracking_devices</a></td>
|
||||
<td>Gets the status of multiple app based tracking devices (GET)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/regattas/{regattaName}/tracking_devices</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>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.</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/regattas/Kornati Cup 2019 - SUP Contest/tracking_devices">http://www.sapsailing.com/sailingserver/api/v1/regattas/Kornati Cup 2019 - SUP Contest/tracking_devices</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/tracking_devices/{deviceUUID}</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>The service gets the status of an app based tracker defined by the given UUID.</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/tracking_devices/dc77e3d1-d405-435e-8699-ce7245a6fd4a">http://www.sapsailing.com/sailingserver/api/v1/tracking_devices/dc77e3d1-d405-435e-8699-ce7245a6fd4a</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/tracking_devices/{deviceUUID}</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>The service gets the status of app based trackers defined by their respective UUID.</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>deviceUUIDs: UUIDs of the devices to get the status for (multiple occurrences of the parameter are allowed)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a href="http://www.sapsailing.com/sailingserver/api/v1/tracking_devices?deviceUUIDs=dc77e3d1-d405-435e-8699-ce7245a6fd4a&deviceUUIDs=dc77e3d1-d405-435e-8699-ce7245a6fd4b">http://www.sapsailing.com/sailingserver/api/v1/tracking_devices?deviceUUIDs=dc77e3d1-d405-435e-8699-ce7245a6fd4a&deviceUUIDs=dc77e3d1-d405-435e-8699-ce7245a6fd4b</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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);
|
||||
|
||||
-1
@@ -800,7 +800,6 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
}
|
||||
});
|
||||
this.dataImportLock = new DataImportLockWithProgress();
|
||||
|
||||
remoteSailingServerSet = new RemoteSailingServerSet(scheduler, baseDomainFactory);
|
||||
regattasByName = new ConcurrentHashMap<String, Regatta>();
|
||||
regattasByNameLock = new NamedReentrantReadWriteLock("regattasByName for " + this, /* fair */false);
|
||||
|
||||
@@ -21,7 +21,19 @@
|
||||
<div class="mainContent">
|
||||
<h2 class="releaseHeadline">Release Notes - Administration Console</h2>
|
||||
<div class="innerContent">
|
||||
<h2 class="articleSubheadline">July 2019</h2>
|
||||
<h2 class="articleSubheadline">August 2019</h2>
|
||||
<ul class="bulletList">
|
||||
<li>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.
|
||||
</li>
|
||||
<li>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.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="articleSubheadline">July 2019</h2>
|
||||
<ul class="bulletList">
|
||||
<li>Result and competitor import URLs are now only considered when the requesting user
|
||||
has the permission to read that URL.</li>
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ public class DimensionFilterSelectionProvider extends AbstractDataMiningComponen
|
||||
|
||||
availableData = new HashSet<>();
|
||||
filteredData = new ListDataProvider<Serializable>(this::elementAsString);
|
||||
filterPanel = new AbstractFilterablePanel<Serializable>(null, filteredData) {
|
||||
filterPanel = new AbstractFilterablePanel<Serializable>(null, filteredData, getDataMiningStringMessages()) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(Serializable element) {
|
||||
return Collections.singleton(elementAsString(element));
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractDa
|
||||
filteredFilterDimensions.addDataDisplay(filterDimensionsList);
|
||||
|
||||
filterFilterDimensionsPanel = new AbstractFilterablePanel<DimensionWithContext>(
|
||||
null, filteredFilterDimensions) {
|
||||
null, filteredFilterDimensions, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(DimensionWithContext dimension) {
|
||||
return dimension.getMatchingStrings();
|
||||
|
||||
@@ -100,4 +100,6 @@ public interface StringMessages extends Messages {
|
||||
String whatsNewDialogMessage();
|
||||
String showChangelog();
|
||||
|
||||
String hideElementsWithoutUpdateRights();
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
showChangelog=Änderungen anzeigen
|
||||
hideElementsWithoutUpdateRights=Elemente ohne Aktualisierungsberechtigung ausblenden
|
||||
+75
-3
@@ -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<T> extends HorizontalPanel {
|
||||
protected ListDataProvider<T> all;
|
||||
protected final ListDataProvider<T> filtered;
|
||||
protected final TextBox textBox;
|
||||
protected final CheckBox checkbox;
|
||||
|
||||
private final Set<Filter<T>> filters = new HashSet<>();
|
||||
private final CheckboxEnablableFilter<T> checkboxFilter;
|
||||
|
||||
private boolean added = false;
|
||||
|
||||
static class CheckboxEnablableFilter<T> implements Filter<T> {
|
||||
|
||||
private final CheckBox checkbox;
|
||||
private Filter<T> 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<T> filterToApply) {
|
||||
this.filterToApply = filterToApply;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected final AbstractKeywordFilter<T> filterer = new AbstractKeywordFilter<T>() {
|
||||
@Override
|
||||
@@ -72,13 +110,18 @@ public abstract class AbstractFilterablePanel<T> extends HorizontalPanel {
|
||||
* normal circumstances the text box will be empty in this case, not making filtering any stricter.
|
||||
*/
|
||||
public AbstractFilterablePanel(Iterable<T> all, final ListDataProvider<T> 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<T> extends HorizontalPanel {
|
||||
}
|
||||
}
|
||||
|
||||
public AbstractFilterablePanel(Iterable<T> all, final ListDataProvider<T> filtered) {
|
||||
this(all, filtered, /* show default filter text box */ true);
|
||||
public AbstractFilterablePanel(Iterable<T> all, final ListDataProvider<T> filtered,
|
||||
final StringMessages stringMessages) {
|
||||
this(all, filtered, /* show default filter text box */ true, stringMessages);
|
||||
}
|
||||
|
||||
private void setAll(Iterable<? extends T> all) {
|
||||
@@ -221,6 +265,14 @@ public abstract class AbstractFilterablePanel<T> 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<T> extends HorizontalPanel {
|
||||
return textBox;
|
||||
}
|
||||
|
||||
public CheckBox getCheckBox() {
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
public void setCheckboxEnabledFilter(Function<T, Boolean> filterFunction) {
|
||||
|
||||
this.checkboxFilter.setFilterToApply(new Filter<T>() {
|
||||
@Override
|
||||
public boolean matches(T object) {
|
||||
return filterFunction.apply(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Update Permission Filter";
|
||||
}
|
||||
});
|
||||
addDefaultCheckBoxIfNecessary();
|
||||
}
|
||||
|
||||
public Iterable<T> getAll() {
|
||||
return all.getList();
|
||||
}
|
||||
|
||||
+3
-2
@@ -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<T> 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<T> all, ListDataProvider<T> filtered) {
|
||||
super(all, filtered, /* show default filter text box */ false);
|
||||
public CustomizableFilterablePanel(Iterable<T> all, ListDataProvider<T> filtered, StringMessages stringMessages) {
|
||||
super(all, filtered, /* show default filter text box */ false, stringMessages);
|
||||
}
|
||||
|
||||
public void add(Label label, Widget widget, Filter<T> filter) {
|
||||
|
||||
+4
-2
@@ -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<T> extends AbstractFilterab
|
||||
|
||||
/**
|
||||
*/
|
||||
public LabeledAbstractFilterablePanel(Label label, Iterable<T> all, final ListDataProvider<T> filtered) {
|
||||
super(all, filtered);
|
||||
public LabeledAbstractFilterablePanel(Label label, Iterable<T> all, final ListDataProvider<T> filtered,
|
||||
StringMessages stringMessages) {
|
||||
super(all, filtered, stringMessages);
|
||||
insert(label, 0);
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -80,7 +80,7 @@ public class RoleDefinitionsPanel extends VerticalPanel {
|
||||
this.userManagementService = userService.getUserManagementService();
|
||||
rolesListDataProvider = new ListDataProvider<>();
|
||||
filterablePanelRoleDefinitions = new LabeledAbstractFilterablePanel<RoleDefinitionDTO>(new Label(stringMessages.filterRoles()), new ArrayList<>(),
|
||||
rolesListDataProvider) {
|
||||
rolesListDataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> 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");
|
||||
|
||||
+2
-1
@@ -108,7 +108,7 @@ public class UserGroupTableWrapper extends
|
||||
u -> configACL.openDialog(u));
|
||||
|
||||
filterField = new LabeledAbstractFilterablePanel<UserGroupDTO>(new Label(stringMessages.filterUserGroups()),
|
||||
new ArrayList<UserGroupDTO>(), dataProvider) {
|
||||
new ArrayList<UserGroupDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(UserGroupDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -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);
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ public class RoleDefinitionTableWrapper extends
|
||||
// filter field configuration
|
||||
filterField = new LabeledAbstractFilterablePanel<Pair<StrippedRoleDefinitionDTO, Boolean>>(
|
||||
new Label(stringMessages.filterRoles()), new ArrayList<Pair<StrippedRoleDefinitionDTO, Boolean>>(),
|
||||
dataProvider) {
|
||||
dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(Pair<StrippedRoleDefinitionDTO, Boolean> t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
|
||||
+2
-1
@@ -195,7 +195,7 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> {
|
||||
u -> configACL.openDialog(u));
|
||||
|
||||
filterField = new LabeledAbstractFilterablePanel<UserDTO>(new Label(stringMessages.filterUsers()),
|
||||
new ArrayList<UserDTO>(), dataProvider) {
|
||||
new ArrayList<UserDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(UserDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -213,6 +213,7 @@ extends TableWrapper<UserDTO, S, StringMessages, TR> {
|
||||
}
|
||||
};
|
||||
registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
|
||||
filterField.setCheckboxEnabledFilter(user -> userService.hasPermission(user, DefaultActions.UPDATE));
|
||||
|
||||
mainPanel.insert(filterField, 0);
|
||||
table.addColumnSortHandler(userColumnListHandler);
|
||||
|
||||
+3
-1
@@ -119,7 +119,7 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends
|
||||
// filter field configuration
|
||||
filterField = new LabeledAbstractFilterablePanel<WildcardPermissionWithSecurityDTO>(
|
||||
new Label(stringMessages.filterPermission()), new ArrayList<WildcardPermissionWithSecurityDTO>(),
|
||||
dataProvider) {
|
||||
dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(WildcardPermissionWithSecurityDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -133,6 +133,8 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends
|
||||
}
|
||||
};
|
||||
registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider());
|
||||
filterField
|
||||
.setCheckboxEnabledFilter(permission -> userService.hasPermission(permission, DefaultActions.UPDATE));
|
||||
|
||||
mainPanel.insert(filterField, 0);
|
||||
|
||||
|
||||
+2
-1
@@ -120,7 +120,7 @@ public class RoleWithSecurityDTOTableWrapper extends
|
||||
|
||||
// filter field configuration
|
||||
filterField = new LabeledAbstractFilterablePanel<RoleWithSecurityDTO>(new Label(stringMessages.filterRoles()),
|
||||
new ArrayList<RoleWithSecurityDTO>(), dataProvider) {
|
||||
new ArrayList<RoleWithSecurityDTO>(), dataProvider, stringMessages) {
|
||||
@Override
|
||||
public Iterable<String> getSearchableStrings(RoleWithSecurityDTO t) {
|
||||
List<String> string = new ArrayList<String>();
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user