java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/RaceLogTrackingAdapter.java
... ...
@@ -140,4 +140,6 @@ public interface RaceLogTrackingAdapter {
140 140
*/
141 141
void copyPairingListFromOtherLeaderboard(RegattaLeaderboard sourceLeaderboard, RegattaLeaderboard targetLeaderboard,
142 142
String fromRaceColumnName, String toRaceColumnInclusiveName) throws NotFoundException;
143
+
144
+ void revokeExplicitTrackingTimes(RegattaLeaderboard leaderboard, RacingEventService raceLogResolver);
143 145
}
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/RaceLogTrackingAdapterImpl.java
... ...
@@ -22,8 +22,14 @@ import org.osgi.framework.ServiceReference;
22 22
import com.sap.sailing.domain.abstractlog.impl.LastEventOfTypeFinder;
23 23
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
24 24
import com.sap.sailing.domain.abstractlog.race.RaceLog;
25
+import com.sap.sailing.domain.abstractlog.race.RaceLogEndOfTrackingEvent;
25 26
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
27
+import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
28
+import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder;
26 29
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastPublishedCourseDesignFinder;
30
+import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
31
+import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
32
+import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TrackingTimesEventFinder;
27 33
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogCourseDesignChangedEventImpl;
28 34
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogDenoteForTrackingEvent;
29 35
import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogStartTrackingEvent;
... ...
@@ -463,4 +469,42 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
463 469
copyCompetitors(sourceRaceColumn, sourceFleet, Collections.singleton(new Pair<>(targetRaceColumn, targetFleet)));
464 470
}
465 471
}
472
+
473
+ @Override
474
+ public void revokeExplicitTrackingTimes(RegattaLeaderboard leaderboard, RacingEventService service) {
475
+ for (final RaceColumn raceColumn : leaderboard.getRaceColumns()) {
476
+ for (final Fleet fleet : raceColumn.getFleets()) {
477
+ final RaceLog raceLog = raceColumn.getRaceLog(fleet);
478
+ if (raceLog != null) {
479
+ // handle only races denoted for smartphone tracking:
480
+ if (new RaceLogTrackingStateAnalyzer(raceLog).analyze().isForTracking()) {
481
+ final StartTimeFinder startTimeFinder = new StartTimeFinder(service, raceLog);
482
+ final StartTimeFinderResult startTimeFinderResult = startTimeFinder.analyze();
483
+ final FinishedTimeFinder finishedTimeFinder = new FinishedTimeFinder(raceLog);
484
+ final TimePoint finishedTime = finishedTimeFinder.analyze();
485
+ final TrackingTimesEventFinder trackingTimesEventFinder = new TrackingTimesEventFinder(raceLog);
486
+ if (startTimeFinderResult != null && startTimeFinderResult.getStartTime() != null && finishedTime != null) {
487
+ Pair<RaceLogStartOfTrackingEvent, RaceLogEndOfTrackingEvent> trackingTimes;
488
+ while ((trackingTimes = trackingTimesEventFinder.analyze()) != null) {
489
+ if (trackingTimes.getA() != null) {
490
+ try {
491
+ raceLog.revokeEvent(service.getServerAuthor(), trackingTimes.getA(), "revoke explicit start of tracking time");
492
+ } catch (NotRevokableException e) {
493
+ logger.log(Level.WARNING, "could not revoke explicit start of tracking time by adding RevokeEvent", e);
494
+ }
495
+ }
496
+ if (trackingTimes.getB() != null) {
497
+ try {
498
+ raceLog.revokeEvent(service.getServerAuthor(), trackingTimes.getB(), "revoke explicit end of tracking time");
499
+ } catch (NotRevokableException e) {
500
+ logger.log(Level.WARNING, "could not revoke explicit end of tracking time by adding RevokeEvent", e);
501
+ }
502
+ }
503
+ }
504
+ }
505
+ }
506
+ }
507
+ }
508
+ }
509
+ }
466 510
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleResources.java
... ...
@@ -118,6 +118,9 @@ public interface AdminConsoleResources extends ClientBundle {
118 118
119 119
@Source("com/sap/sailing/gwt/ui/client/images/compose_mail_small.png")
120 120
ImageResource inviteBuoyTenders();
121
+
122
+ @Source("com/sap/sailing/gwt/ui/client/images/eraser-icon.png")
123
+ ImageResource eraser();
121 124
122 125
@Source("com/sap/sailing/gwt/ui/client/images/ajax-loader.gif")
123 126
ImageResource loaderGif();
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementImagesBarCell.java
... ...
@@ -17,6 +17,7 @@ public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell {
17 17
public final static String ACTION_MAP_DEVICES = "ACTION_MAP_DEVICES";
18 18
public final static String ACTION_INVITE_BUOY_TENDERS = "ACTION_INVITE_BUOY_TENDERS";
19 19
public static final String ACTION_SHOW_REGATTA_LOG = "ACTION_SHOW_REGATTA_LOG";
20
+ public static final String ACTION_REVOKE_EXPLICIT_TRACKING_TIMES = "ACTION_REVOKE_EXPLICIT_TRACKING_TIMES";
20 21
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
21 22
private final StringMessages stringMessages;
22 23
... ...
@@ -48,11 +49,12 @@ public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell {
48 49
makeImagePrototype(resources.inviteBuoyTenders())));
49 50
result.add(new ImageSpec(ACTION_SHOW_REGATTA_LOG, stringMessages.regattaLog(),
50 51
makeImagePrototype(resources.flagIcon())));
52
+ result.add(new ImageSpec(ACTION_REVOKE_EXPLICIT_TRACKING_TIMES, stringMessages.revokeExplicitTrackingTimes(),
53
+ makeImagePrototype(resources.eraser())));
51 54
result.add(new ImageSpec(DefaultActions.CHANGE_OWNERSHIP.name(), stringMessages.actionChangeOwnership(),
52 55
IconResources.INSTANCE.changeOwnershipIcon()));
53 56
result.add(new ImageSpec(DefaultActions.CHANGE_ACL.name(), stringMessages.actionChangeACL(),
54 57
IconResources.INSTANCE.changeACLIcon()));
55
-
56 58
return result;
57 59
}
58 60
}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java
... ...
@@ -208,6 +208,8 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
208 208
DefaultActions.UPDATE, t -> openChooseEventDialogAndSendMails(t.getName()));
209 209
leaderboardActionColumn.addAction(RaceLogTrackingEventManagementImagesBarCell.ACTION_SHOW_REGATTA_LOG,
210 210
DefaultActions.UPDATE, t -> showRegattaLog());
211
+ leaderboardActionColumn.addAction(RaceLogTrackingEventManagementImagesBarCell.ACTION_REVOKE_EXPLICIT_TRACKING_TIMES,
212
+ DefaultActions.UPDATE, t -> revokeExplicitTrackingTimes());
211 213
leaderboardActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_OWNERSHIP, DefaultActions.UPDATE,
212 214
configOwnership::openOwnershipDialog);
213 215
leaderboardActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.UPDATE,
... ...
@@ -223,6 +225,23 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
223 225
selectionCheckboxColumn.getSelectionManager());
224 226
}
225 227
228
+ private void revokeExplicitTrackingTimes() {
229
+ if (Window.confirm(stringMessages.confirmRevokeExplicitTrackingTimes(getSelectedLeaderboard().getName()))) {
230
+ sailingServiceWrite.revokeExplicitTrackingTimes(getSelectedLeaderboard().getName(), new AsyncCallback<Void>() {
231
+ @Override
232
+ public void onFailure(Throwable caught) {
233
+ errorReporter.reportError(stringMessages.errorRevokingExplicitTrackingTimes(getSelectedLeaderboard().getName(), caught.getMessage()));
234
+ }
235
+
236
+ @Override
237
+ public void onSuccess(Void result) {
238
+ Notification.notify(stringMessages.successfullyREvokedExplicitTrackingTimes(getSelectedLeaderboard().getName()), NotificationType.SUCCESS);
239
+ loadAndRefreshLeaderboard(getSelectedLeaderboard().getName());
240
+ }
241
+ });
242
+ }
243
+ }
244
+
226 245
private RaceLogTrackingState getTrackingState(
227 246
RaceColumnDTOAndFleetDTOWithNameBasedEquality race) {
228 247
return race.getA().getRaceLogTrackingInfo(race.getB()).raceLogTrackingState;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWrite.java
... ...
@@ -762,4 +762,6 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa
762 762
763 763
void copyPairingListFromOtherLeaderboard(String sourceLeaderboardName, String targetLeaderboardName, String fromRaceColumnName,
764 764
String toRaceColumnInclusiveName) throws UnauthorizedException, NotFoundException;
765
+
766
+ void revokeExplicitTrackingTimes(String leaderboardName) throws NotFoundException;
765 767
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWriteAsync.java
... ...
@@ -13,6 +13,7 @@ import org.apache.shiro.authz.UnauthorizedException;
13 13
import com.google.gwt.user.client.rpc.AsyncCallback;
14 14
import com.sap.sailing.domain.base.Fleet;
15 15
import com.sap.sailing.domain.base.RaceColumn;
16
+import com.sap.sailing.domain.base.Regatta;
16 17
import com.sap.sailing.domain.common.CompetitorDescriptor;
17 18
import com.sap.sailing.domain.common.CompetitorRegistrationType;
18 19
import com.sap.sailing.domain.common.DataImportProgress;
... ...
@@ -41,6 +42,7 @@ import com.sap.sailing.domain.common.orc.ORCCertificate;
41 42
import com.sap.sailing.domain.common.orc.impl.ORCPerformanceCurveLegImpl;
42 43
import com.sap.sailing.domain.common.security.SecuredDomainType;
43 44
import com.sap.sailing.domain.common.security.SecuredDomainType.EventActions;
45
+import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
44 46
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
45 47
import com.sap.sailing.gwt.ui.adminconsole.RaceLogSetTrackingTimesDTO;
46 48
import com.sap.sailing.gwt.ui.adminconsole.RemoteSailingServerEventsSelectionDialog;
... ...
@@ -776,4 +778,18 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic
776 778
777 779
void copyPairingListFromOtherLeaderboard(String sourceLeaderboardName, String targetLeaderboardName, String fromRaceColumnName,
778 780
String toRaceColumnInclusiveName, AsyncCallback<Void> asyncCallback);
781
+
782
+ /**
783
+ * For the leaderboard identified by {@code leaderboardName}, revokes the explicit tracking times for all its
784
+ * {@link RaceColumn}/{@link Fleet} combinations where the race log of that slot has a valid race start time and
785
+ * valid finishing/finished times ("blue flag") in the Race Log. Multiple revocation events may be required because
786
+ * due to priorities and time order, some tracking time events may "shadow" other such events that become valid
787
+ * after revoking the one currently valid. The process continues until no more valid start/end tracking events exist
788
+ * in that race log.
789
+ * <p>
790
+ *
791
+ * Precondition: The leaderboard must be a {@link RegattaLeaderboard}, and the corresponding regatta must be in more
792
+ * {@link Regatta#isControlTrackingFromStartAndFinishTimes()}
793
+ */
794
+ void revokeExplicitTrackingTimes(String leaderboardName, AsyncCallback<Void> asyncCallback);
779 795
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
... ...
@@ -2555,4 +2555,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
2555 2555
String ipsLockedForUserCreationAbuse();
2556 2556
String unableToLoadIpsBlockedForUserCreationAbuse();
2557 2557
String sourceCode();
2558
+ String revokeExplicitTrackingTimes();
2559
+ String confirmRevokeExplicitTrackingTimes(String leaderboardName);
2560
+ String errorRevokingExplicitTrackingTimes(String leaderboardName, String message);
2561
+ String successfullyREvokedExplicitTrackingTimes(String leaderboardName);
2558 2562
}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
... ...
@@ -2591,3 +2591,7 @@ unlock=Unlock
2591 2591
ipsLockedForUserCreationAbuse=IPs Locked for User Creation Abuse
2592 2592
unableToLoadIpsBlockedForUserCreationAbuse=Unable to load IPs Blocked for User Creation Abuse
2593 2593
sourceCode=Source Code
2594
+revokeExplicitTrackingTimes=Revoke explicit tracking times for races with valid start and finishing times
2595
+confirmRevokeExplicitTrackingTimes=Really revoke explicit tracking times for all races of leaderboard {0} that have valid start and finishing times?
2596
+errorRevokingExplicitTrackingTimes=Error revoking explicit tracking times for leaderboard {0}: {1}
2597
+successfullyREvokedExplicitTrackingTimes=Successfully revoked explicit tracking times for leaderboard {0}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
... ...
@@ -2585,3 +2585,7 @@ unlock=Entsperren
2585 2585
ipsLockedForUserCreationAbuse=Wegen Missbrauchs bei der Benutzererstellung gesperrte IPs
2586 2586
unableToLoadIpsBlockedForUserCreationAbuse=Wegen Missbrauchs der Benutzererstellung blockierte IPs können nicht geladen werden
2587 2587
sourceCode=Quellcode
2588
+revokeExplicitTrackingTimes=Explizite Tracking-Zeiten zurücksetzen für Rennen mit gültigen Start- und Endzeitpunkten
2589
+confirmRevokeExplicitTrackingTimes=Wirklich explizite Tracking-Zeiten zurücksetzen für Rennen mit gültigen Start- und Endzeitpunkten der Rangliste {0}?
2590
+errorRevokingExplicitTrackingTimes=Fehler beim Zurücksetzen expliziter Tracking-Zeiten für Rennen mit gültigen Start- und Endzeitpunkten der Rangliste {0}: {1}
2591
+successfullyREvokedExplicitTrackingTimes=Explizite Tracking-Zeiten erfolgreich zurückgesetzt für Rangliste {0}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceWriteImpl.java
... ...
@@ -4195,4 +4195,24 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
4195 4195
getRaceLogTrackingAdapter().copyPairingListFromOtherLeaderboard((RegattaLeaderboard) sourceLeaderboard,
4196 4196
(RegattaLeaderboard) targetLeaderboard, fromRaceColumnName, toRaceColumnInclusiveName);
4197 4197
}
4198
+
4199
+ @Override
4200
+ public void revokeExplicitTrackingTimes(String leaderboardName) throws NotFoundException {
4201
+ final Leaderboard leaderboard = getLeaderboardByName(leaderboardName);
4202
+ getService().getSecurityService().checkCurrentUserUpdatePermission(leaderboard);
4203
+ if (leaderboard instanceof RegattaLeaderboard) {
4204
+ final Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
4205
+ if (regatta.isControlTrackingFromStartAndFinishTimes()) {
4206
+ getRaceLogTrackingAdapter().revokeExplicitTrackingTimes((RegattaLeaderboard) leaderboard, /* raceLogResolver */ getService());
4207
+ } else {
4208
+ throw new IllegalStateException("Leaderboard " + leaderboardName
4209
+ + " is not configured to control tracking from start and finish times, so explicit tracking times should not exist and thus cannot be revoked.");
4210
+ }
4211
+ } else {
4212
+ throw new IllegalArgumentException("Leaderboard " + leaderboardName
4213
+ + " must be a regatta leaderboard, but was: " + leaderboard.getLeaderboardType());
4214
+ }
4215
+ // TODO Auto-generated method stub
4216
+
4217
+ }
4198 4218
}
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/eraser-icon.png
... ...
Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/eraser-icon.png differ