java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/racelog/tracking/TrackingTimesRevocationErrorCode.java
... ...
@@ -0,0 +1,15 @@
1
+package com.sap.sailing.domain.common.racelog.tracking;
2
+
3
+/**
4
+ * Error codes for the revocation of tracking times. These are used to report errors that occur during the revocation
5
+ * process, e.g. when there is no regatta leaderboard or when there are no automated tracking times.
6
+ *
7
+ * @see com.sap.sailing.domain.racelogtracking.TrackingTimesRevocationReport
8
+ *
9
+ * @author Axel Uhl (d043530)
10
+ *
11
+ */
12
+public enum TrackingTimesRevocationErrorCode {
13
+ NO_REGATTA_LEADERBOARD,
14
+ NO_AUTOMATED_TRACKING_TIMES
15
+}
... ...
\ No newline at end of file
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
+ TrackingTimesRevocationReport revokeExplicitTrackingTimes(RegattaLeaderboard leaderboard, RacingEventService raceLogResolver);
143 145
}
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/TrackingTimesRevocationReport.java
... ...
@@ -0,0 +1,31 @@
1
+package com.sap.sailing.domain.racelogtracking;
2
+
3
+import java.util.Map;
4
+import java.util.Set;
5
+
6
+import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
7
+import com.sap.sailing.domain.base.Fleet;
8
+import com.sap.sailing.domain.base.RaceColumn;
9
+import com.sap.sailing.domain.common.racelog.tracking.TrackingTimesRevocationErrorCode;
10
+import com.sap.sse.common.TimePoint;
11
+import com.sap.sse.common.Util.Pair;
12
+
13
+public interface TrackingTimesRevocationReport {
14
+ /**
15
+ * @return {@code null} means no error
16
+ */
17
+ TrackingTimesRevocationErrorCode getErrorCode();
18
+
19
+ void revoked(RaceColumn raceColumn, Fleet fleet, RaceLogEvent event);
20
+
21
+ void notRevokedBecauseOfMissingStartOrFinishTime(RaceColumn raceColumn, Fleet fleet, TimePoint startTime, TimePoint finishedTime);
22
+
23
+ void notRevokedBecauseNotForTracking(RaceColumn raceColumn, Fleet fleet);
24
+
25
+ Map<Pair<RaceColumn, Fleet>, Pair<TimePoint, TimePoint>> getNotRevokedBecauseOfMissingStartOfFinishTime();
26
+
27
+ Map<Pair<RaceColumn, Fleet>, Set<RaceLogEvent>> getRevokedEvents();
28
+
29
+ Iterable<Pair<RaceColumn, Fleet>> getNotRevokedBecauseNotForTracking();
30
+
31
+}
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;
... ...
@@ -67,6 +73,7 @@ import com.sap.sailing.domain.leaderboard.Leaderboard;
67 73
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
68 74
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
69 75
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
76
+import com.sap.sailing.domain.racelogtracking.TrackingTimesRevocationReport;
70 77
import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike;
71 78
import com.sap.sailing.domain.tracking.RaceHandle;
72 79
import com.sap.sailing.domain.tracking.RaceTrackingHandler;
... ...
@@ -463,4 +470,50 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
463 470
copyCompetitors(sourceRaceColumn, sourceFleet, Collections.singleton(new Pair<>(targetRaceColumn, targetFleet)));
464 471
}
465 472
}
473
+
474
+ @Override
475
+ public TrackingTimesRevocationReport revokeExplicitTrackingTimes(RegattaLeaderboard leaderboard, RacingEventService service) {
476
+ final TrackingTimesRevocationReportImpl result = new TrackingTimesRevocationReportImpl(/* error code */ null /* meaning no error */);
477
+ for (final RaceColumn raceColumn : leaderboard.getRaceColumns()) {
478
+ for (final Fleet fleet : raceColumn.getFleets()) {
479
+ final RaceLog raceLog = raceColumn.getRaceLog(fleet);
480
+ if (raceLog != null) {
481
+ // handle only races denoted for smartphone tracking:
482
+ if (new RaceLogTrackingStateAnalyzer(raceLog).analyze().isForTracking()) {
483
+ final StartTimeFinder startTimeFinder = new StartTimeFinder(service, raceLog);
484
+ final StartTimeFinderResult startTimeFinderResult = startTimeFinder.analyze();
485
+ final FinishedTimeFinder finishedTimeFinder = new FinishedTimeFinder(raceLog);
486
+ final TimePoint finishedTime = finishedTimeFinder.analyze();
487
+ final TrackingTimesEventFinder trackingTimesEventFinder = new TrackingTimesEventFinder(raceLog);
488
+ if (startTimeFinderResult != null && startTimeFinderResult.getStartTime() != null && finishedTime != null) {
489
+ Pair<RaceLogStartOfTrackingEvent, RaceLogEndOfTrackingEvent> trackingTimes;
490
+ while ((trackingTimes = trackingTimesEventFinder.analyze()) != null) {
491
+ if (trackingTimes.getA() != null) {
492
+ try {
493
+ raceLog.revokeEvent(service.getServerAuthor(), trackingTimes.getA(), "revoke explicit start of tracking time");
494
+ result.revoked(raceColumn, fleet, trackingTimes.getA());
495
+ } catch (NotRevokableException e) {
496
+ logger.log(Level.WARNING, "could not revoke explicit start of tracking time by adding RevokeEvent", e);
497
+ }
498
+ }
499
+ if (trackingTimes.getB() != null) {
500
+ try {
501
+ raceLog.revokeEvent(service.getServerAuthor(), trackingTimes.getB(), "revoke explicit end of tracking time");
502
+ result.revoked(raceColumn, fleet, trackingTimes.getB());
503
+ } catch (NotRevokableException e) {
504
+ logger.log(Level.WARNING, "could not revoke explicit end of tracking time by adding RevokeEvent", e);
505
+ }
506
+ }
507
+ }
508
+ } else {
509
+ result.notRevokedBecauseOfMissingStartOrFinishTime(raceColumn, fleet, startTimeFinderResult != null ? startTimeFinderResult.getStartTime() : null, finishedTime);
510
+ }
511
+ } else {
512
+ result.notRevokedBecauseNotForTracking(raceColumn, fleet);
513
+ }
514
+ }
515
+ }
516
+ }
517
+ return result;
518
+ }
466 519
}
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/TrackingTimesRevocationReportImpl.java
... ...
@@ -0,0 +1,68 @@
1
+package com.sap.sailing.domain.racelogtracking.impl;
2
+
3
+import java.util.ArrayList;
4
+import java.util.Collections;
5
+import java.util.HashSet;
6
+import java.util.LinkedHashMap;
7
+import java.util.List;
8
+import java.util.Map;
9
+import java.util.Set;
10
+
11
+import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
12
+import com.sap.sailing.domain.base.Fleet;
13
+import com.sap.sailing.domain.base.RaceColumn;
14
+import com.sap.sailing.domain.common.racelog.tracking.TrackingTimesRevocationErrorCode;
15
+import com.sap.sailing.domain.racelogtracking.TrackingTimesRevocationReport;
16
+import com.sap.sse.common.TimePoint;
17
+import com.sap.sse.common.Util.Pair;
18
+
19
+public class TrackingTimesRevocationReportImpl implements TrackingTimesRevocationReport {
20
+ private final Map<Pair<RaceColumn, Fleet>, Set<RaceLogEvent>> revokedEvents;
21
+ private final Map<Pair<RaceColumn, Fleet>, Pair<TimePoint, TimePoint>> notRevokedBecauseOfMissingStartOrFinishTime;
22
+ private final List<Pair<RaceColumn, Fleet>> notRevokedBecauseNotForTracking;
23
+ private final TrackingTimesRevocationErrorCode errorCode;
24
+
25
+ public TrackingTimesRevocationReportImpl(TrackingTimesRevocationErrorCode errorCode) {
26
+ super();
27
+ this.errorCode = errorCode;
28
+ this.revokedEvents = new LinkedHashMap<>();
29
+ this.notRevokedBecauseOfMissingStartOrFinishTime = new LinkedHashMap<>();
30
+ this.notRevokedBecauseNotForTracking = new ArrayList<>();
31
+ }
32
+
33
+ @Override
34
+ public TrackingTimesRevocationErrorCode getErrorCode() {
35
+ return errorCode;
36
+ }
37
+
38
+ @Override
39
+ public void revoked(RaceColumn raceColumn, Fleet fleet, RaceLogEvent event) {
40
+ final Set<RaceLogEvent> set = revokedEvents.computeIfAbsent(new Pair<>(raceColumn, fleet), k->new HashSet<>());
41
+ set.add(event);
42
+ }
43
+
44
+ @Override
45
+ public void notRevokedBecauseOfMissingStartOrFinishTime(RaceColumn raceColumn, Fleet fleet, TimePoint startTime, TimePoint finishedTime) {
46
+ notRevokedBecauseOfMissingStartOrFinishTime.put(new Pair<>(raceColumn, fleet), new Pair<>(startTime, finishedTime));
47
+ }
48
+
49
+ @Override
50
+ public void notRevokedBecauseNotForTracking(RaceColumn raceColumn, Fleet fleet) {
51
+ notRevokedBecauseNotForTracking.add(new Pair<>(raceColumn, fleet));
52
+ }
53
+
54
+ @Override
55
+ public Map<Pair<RaceColumn, Fleet>, Pair<TimePoint, TimePoint>> getNotRevokedBecauseOfMissingStartOfFinishTime() {
56
+ return Collections.unmodifiableMap(notRevokedBecauseOfMissingStartOrFinishTime);
57
+ }
58
+
59
+ @Override
60
+ public Map<Pair<RaceColumn, Fleet>, Set<RaceLogEvent>> getRevokedEvents() {
61
+ return Collections.unmodifiableMap(revokedEvents);
62
+ }
63
+
64
+ @Override
65
+ public Iterable<Pair<RaceColumn, Fleet>> getNotRevokedBecauseNotForTracking() {
66
+ return Collections.unmodifiableList(notRevokedBecauseNotForTracking);
67
+ }
68
+}
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
... ...
@@ -4,6 +4,7 @@ import java.util.ArrayList;
4 4
5 5
import com.google.gwt.core.client.GWT;
6 6
import com.google.gwt.text.shared.SafeHtmlRenderer;
7
+import com.sap.sailing.domain.common.LeaderboardType;
7 8
import com.sap.sailing.gwt.ui.client.StringMessages;
8 9
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
9 10
import com.sap.sse.gwt.client.IconResources;
... ...
@@ -17,6 +18,7 @@ public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell {
17 18
public final static String ACTION_MAP_DEVICES = "ACTION_MAP_DEVICES";
18 19
public final static String ACTION_INVITE_BUOY_TENDERS = "ACTION_INVITE_BUOY_TENDERS";
19 20
public static final String ACTION_SHOW_REGATTA_LOG = "ACTION_SHOW_REGATTA_LOG";
21
+ public static final String ACTION_REVOKE_EXPLICIT_TRACKING_TIMES = "ACTION_REVOKE_EXPLICIT_TRACKING_TIMES";
20 22
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
21 23
private final StringMessages stringMessages;
22 24
... ...
@@ -48,11 +50,14 @@ public class RaceLogTrackingEventManagementImagesBarCell extends ImagesBarCell {
48 50
makeImagePrototype(resources.inviteBuoyTenders())));
49 51
result.add(new ImageSpec(ACTION_SHOW_REGATTA_LOG, stringMessages.regattaLog(),
50 52
makeImagePrototype(resources.flagIcon())));
53
+ if (selectedLeaderboard.type == LeaderboardType.RegattaLeaderboard) {
54
+ result.add(new ImageSpec(ACTION_REVOKE_EXPLICIT_TRACKING_TIMES, stringMessages.revokeExplicitTrackingTimes(),
55
+ makeImagePrototype(resources.eraser())));
56
+ }
51 57
result.add(new ImageSpec(DefaultActions.CHANGE_OWNERSHIP.name(), stringMessages.actionChangeOwnership(),
52 58
IconResources.INSTANCE.changeOwnershipIcon()));
53 59
result.add(new ImageSpec(DefaultActions.CHANGE_ACL.name(), stringMessages.actionChangeACL(),
54 60
IconResources.INSTANCE.changeACLIcon()));
55
-
56 61
return result;
57 62
}
58 63
}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SmartphoneTrackingEventManagementPanel.java
... ...
@@ -59,6 +59,7 @@ import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
59 59
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
60 60
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
61 61
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
62
+import com.sap.sailing.gwt.ui.shared.TrackingTimesRevocationReportDTO;
62 63
import com.sap.sse.common.Distance;
63 64
import com.sap.sse.common.Util;
64 65
import com.sap.sse.common.Util.Pair;
... ...
@@ -208,6 +209,8 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
208 209
DefaultActions.UPDATE, t -> openChooseEventDialogAndSendMails(t.getName()));
209 210
leaderboardActionColumn.addAction(RaceLogTrackingEventManagementImagesBarCell.ACTION_SHOW_REGATTA_LOG,
210 211
DefaultActions.UPDATE, t -> showRegattaLog());
212
+ leaderboardActionColumn.addAction(RaceLogTrackingEventManagementImagesBarCell.ACTION_REVOKE_EXPLICIT_TRACKING_TIMES,
213
+ DefaultActions.UPDATE, t -> revokeExplicitTrackingTimes());
211 214
leaderboardActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_OWNERSHIP, DefaultActions.UPDATE,
212 215
configOwnership::openOwnershipDialog);
213 216
leaderboardActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.UPDATE,
... ...
@@ -223,6 +226,66 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
223 226
selectionCheckboxColumn.getSelectionManager());
224 227
}
225 228
229
+ private void revokeExplicitTrackingTimes() {
230
+ if (Window.confirm(stringMessages.confirmRevokeExplicitTrackingTimes(getSelectedLeaderboard().getName()))) {
231
+ sailingServiceWrite.revokeExplicitTrackingTimes(getSelectedLeaderboard().getName(), new AsyncCallback<TrackingTimesRevocationReportDTO>() {
232
+ @Override
233
+ public void onFailure(Throwable caught) {
234
+ errorReporter.reportError(stringMessages.errorRevokingExplicitTrackingTimes(getSelectedLeaderboard().getName(), caught.getMessage()));
235
+ }
236
+
237
+ @Override
238
+ public void onSuccess(TrackingTimesRevocationReportDTO result) {
239
+ Window.alert(result.getErrorCode() == null ?
240
+ stringMessages.successfullyRevokedExplicitTrackingTimes(getSelectedLeaderboard().getName(), i18n(result)) :
241
+ stringMessages.errorRevokingExplicitTrackingTimes(getSelectedLeaderboard().getName(), i18n(result)));
242
+ loadAndRefreshLeaderboard(getSelectedLeaderboard().getName());
243
+ }
244
+
245
+ private String i18n(TrackingTimesRevocationReportDTO report) {
246
+ final StringBuilder result = new StringBuilder();
247
+ if (report.getErrorCode() != null) {
248
+ switch (report.getErrorCode()) {
249
+ case NO_REGATTA_LEADERBOARD:
250
+ result.append(stringMessages.noRegattaLeaderboard(getSelectedLeaderboard().getName())).append("\n");
251
+ break;
252
+ case NO_AUTOMATED_TRACKING_TIMES:
253
+ result.append(stringMessages.noAutomatedTrackingTimes(getSelectedLeaderboard().getName())).append("\n");
254
+ break;
255
+ default:
256
+ result.append(stringMessages.unknownError(report.getErrorCode().name())).append("\n");
257
+ }
258
+ } else {
259
+ result.append(stringMessages.revokedTrackingTimesForEvents()).append("\n");
260
+ report.getRevokedEvents().forEach((raceColumnAndFleet, events)->{
261
+ final String raceColumnName = raceColumnAndFleet.getA().getName();
262
+ final String fleetName = raceColumnAndFleet.getB().getName();
263
+ result.append(" - ").append(raceColumnName).append(" / ").append(fleetName).append("\n");
264
+ events.forEach(event->{
265
+ result.append(" - ").append(event.getType()).append(" (").append(event.getLogicalTimePoint()).append(")\n");
266
+ });
267
+ });
268
+ result.append(stringMessages.notRevokedTrackingTimesBecauseNotForTracking()).append("\n");
269
+ report.getNotRevokedBecauseNotForTracking().forEach(raceColumnAndFleet->{
270
+ final String raceColumnName = raceColumnAndFleet.getA().getName();
271
+ final String fleetName = raceColumnAndFleet.getB().getName();
272
+ result.append(" - ").append(raceColumnName).append(" / ").append(fleetName).append("\n");
273
+ });
274
+ result.append(stringMessages.notRevokedTrackingTimesBecauseOfMissingStartOrFinishTime()).append("\n");
275
+ report.getNotRevokedBecauseOfMissingStartOrFinishTime().forEach((raceColumnAndFleet, startAndEndTime)->{
276
+ final String raceColumnName = raceColumnAndFleet.getA().getName();
277
+ final String fleetName = raceColumnAndFleet.getB().getName();
278
+ result.append(" - ").append(raceColumnName).append(" / ").append(fleetName)
279
+ .append(" (").append(stringMessages.startTime()).append(": ").append(startAndEndTime.getA())
280
+ .append(", ").append(stringMessages.finishedTime()).append(": ").append(startAndEndTime.getB()).append(")\n");
281
+ });
282
+ }
283
+ return result.toString();
284
+ }
285
+ });
286
+ }
287
+ }
288
+
226 289
private RaceLogTrackingState getTrackingState(
227 290
RaceColumnDTOAndFleetDTOWithNameBasedEquality race) {
228 291
return race.getA().getRaceLogTrackingInfo(race.getB()).raceLogTrackingState;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceWrite.java
... ...
@@ -84,6 +84,7 @@ import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
84 84
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationWithSecurityDTO;
85 85
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
86 86
import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO;
87
+import com.sap.sailing.gwt.ui.shared.TrackingTimesRevocationReportDTO;
87 88
import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO;
88 89
import com.sap.sailing.gwt.ui.shared.UrlDTO;
89 90
import com.sap.sailing.gwt.ui.shared.VenueDTO;
... ...
@@ -762,4 +763,6 @@ public interface SailingServiceWrite extends FileStorageManagementGwtService, Sa
762 763
763 764
void copyPairingListFromOtherLeaderboard(String sourceLeaderboardName, String targetLeaderboardName, String fromRaceColumnName,
764 765
String toRaceColumnInclusiveName) throws UnauthorizedException, NotFoundException;
766
+
767
+ TrackingTimesRevocationReportDTO revokeExplicitTrackingTimes(String leaderboardName) throws NotFoundException;
765 768
}
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;
... ...
@@ -70,6 +72,7 @@ import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
70 72
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationWithSecurityDTO;
71 73
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
72 74
import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO;
75
+import com.sap.sailing.gwt.ui.shared.TrackingTimesRevocationReportDTO;
73 76
import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO;
74 77
import com.sap.sailing.gwt.ui.shared.UrlDTO;
75 78
import com.sap.sailing.gwt.ui.shared.VenueDTO;
... ...
@@ -776,4 +779,18 @@ public interface SailingServiceWriteAsync extends FileStorageManagementGwtServic
776 779
777 780
void copyPairingListFromOtherLeaderboard(String sourceLeaderboardName, String targetLeaderboardName, String fromRaceColumnName,
778 781
String toRaceColumnInclusiveName, AsyncCallback<Void> asyncCallback);
782
+
783
+ /**
784
+ * For the leaderboard identified by {@code leaderboardName}, revokes the explicit tracking times for all its
785
+ * {@link RaceColumn}/{@link Fleet} combinations where the race log of that slot has a valid race start time and
786
+ * valid finishing/finished times ("blue flag") in the Race Log. Multiple revocation events may be required because
787
+ * due to priorities and time order, some tracking time events may "shadow" other such events that become valid
788
+ * after revoking the one currently valid. The process continues until no more valid start/end tracking events exist
789
+ * in that race log.
790
+ * <p>
791
+ *
792
+ * Precondition: The leaderboard must be a {@link RegattaLeaderboard}, and the corresponding regatta must be in more
793
+ * {@link Regatta#isControlTrackingFromStartAndFinishTimes()}
794
+ */
795
+ void revokeExplicitTrackingTimes(String leaderboardName, AsyncCallback<TrackingTimesRevocationReportDTO> asyncCallback);
779 796
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
... ...
@@ -105,6 +105,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
105 105
String event();
106 106
String startTime();
107 107
String endTime();
108
+ String finishedTime();
108 109
String startOfTracking();
109 110
String endOfTracking();
110 111
String regatta();
... ...
@@ -2555,4 +2556,14 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
2555 2556
String ipsLockedForUserCreationAbuse();
2556 2557
String unableToLoadIpsBlockedForUserCreationAbuse();
2557 2558
String sourceCode();
2559
+ String revokeExplicitTrackingTimes();
2560
+ String confirmRevokeExplicitTrackingTimes(String leaderboardName);
2561
+ String errorRevokingExplicitTrackingTimes(String leaderboardName, String message);
2562
+ String successfullyRevokedExplicitTrackingTimes(String leaderboardName, String formattedAndTranslatedReport);
2563
+ String revokedTrackingTimesForEvents();
2564
+ String notRevokedTrackingTimesBecauseNotForTracking();
2565
+ String notRevokedTrackingTimesBecauseOfMissingStartOrFinishTime();
2566
+ String noRegattaLeaderboard(String leaderboardName);
2567
+ String noAutomatedTrackingTimes(String leaderboardName);
2568
+ String unknownError(String name);
2558 2569
}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
... ...
@@ -111,6 +111,7 @@ regattaName=Regatta name
111 111
eventName=Event name
112 112
event=Event
113 113
startTime=Start time
114
+finishedTime=Finished time
114 115
endTime=End time
115 116
startOfTracking=Start of tracking
116 117
endOfTracking=End of tracking
... ...
@@ -2591,3 +2592,13 @@ unlock=Unlock
2591 2592
ipsLockedForUserCreationAbuse=IPs Locked for User Creation Abuse
2592 2593
unableToLoadIpsBlockedForUserCreationAbuse=Unable to load IPs Blocked for User Creation Abuse
2593 2594
sourceCode=Source Code
2595
+revokeExplicitTrackingTimes=Revoke explicit tracking times for races with valid start and finishing times
2596
+confirmRevokeExplicitTrackingTimes=Really revoke explicit tracking times for all races of leaderboard {0} that have valid start and finishing times?
2597
+errorRevokingExplicitTrackingTimes=Error revoking explicit tracking times for leaderboard {0}: {1}
2598
+successfullyRevokedExplicitTrackingTimes=Successfully revoked explicit tracking times for leaderboard {0}: {1}
2599
+revokedTrackingTimesForEvents=Revoked tracking times for events:
2600
+notRevokedTrackingTimesBecauseNotForTracking=Not revoked tracking times for events that are not tracked:
2601
+notRevokedTrackingTimesBecauseOfMissingStartOrFinishTime=Not revoked tracking times for events with missing start or finish time:
2602
+noRegattaLeaderboard=Leaderboard {0} is not a regatta leaderboard.
2603
+noAutomatedTrackingTimes=Leaderboard {0} has no automated tracking times.
2604
+unknownError=Unknown error: {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
... ...
@@ -112,6 +112,7 @@ regattaName=Name der Regatta
112 112
eventName=Event-Name
113 113
event=Veranstaltung
114 114
startTime=Startzeit
115
+finishedTime=Zielenlauf-Endezeit
115 116
endTime=Schlusszeit
116 117
startOfTracking=Start des Trackings
117 118
endOfTracking=Ende des Trackings
... ...
@@ -2585,3 +2586,13 @@ unlock=Entsperren
2585 2586
ipsLockedForUserCreationAbuse=Wegen Missbrauchs bei der Benutzererstellung gesperrte IPs
2586 2587
unableToLoadIpsBlockedForUserCreationAbuse=Wegen Missbrauchs der Benutzererstellung blockierte IPs können nicht geladen werden
2587 2588
sourceCode=Quellcode
2589
+revokeExplicitTrackingTimes=Explizite Tracking-Zeiten zurücksetzen für Rennen mit gültigen Start- und Endzeitpunkten
2590
+confirmRevokeExplicitTrackingTimes=Wirklich explizite Tracking-Zeiten zurücksetzen für Rennen mit gültigen Start- und Endzeitpunkten der Rangliste {0}?
2591
+errorRevokingExplicitTrackingTimes=Fehler beim Zurücksetzen expliziter Tracking-Zeiten für Rennen mit gültigen Start- und Endzeitpunkten der Rangliste {0}: {1}
2592
+successfullyRevokedExplicitTrackingTimes=Explizite Tracking-Zeiten erfolgreich zurückgesetzt für Rangliste {0}: {1}
2593
+revokedTrackingTimesForEvents=Widerrufene explizite Tracking-Zeiten:
2594
+notRevokedTrackingTimesBecauseNotForTracking=Nicht widerrufene explizite Tracking-Zeiten für nicht getrackte Rennen:
2595
+notRevokedTrackingTimesBecauseOfMissingStartOrFinishTime=Nicht widerrufene explizite Tracking-Zeiten für Rennen mit ungültigen Start- oder Endzeitpunkten:
2596
+noRegattaLeaderboard=Rangliste {0} ist keine Regatta-Rangliste.
2597
+noAutomatedTrackingTimes=Rangliste {0} nutzt keine automatisierten Tracking-Zeiten, auf die zurückgesetzt werden könnte.
2598
+unknownError=Unbekannter Fehler: {0}
... ...
\ No newline at end of file
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceWriteImpl.java
... ...
@@ -207,6 +207,7 @@ import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogExcep
207 207
import com.sap.sailing.domain.common.racelog.tracking.MarkAlreadyUsedInRaceException;
208 208
import com.sap.sailing.domain.common.racelog.tracking.NotDenotableForRaceLogTrackingException;
209 209
import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTrackingException;
210
+import com.sap.sailing.domain.common.racelog.tracking.TrackingTimesRevocationErrorCode;
210 211
import com.sap.sailing.domain.common.security.SecuredDomainType;
211 212
import com.sap.sailing.domain.common.security.SecuredDomainType.EventActions;
212 213
import com.sap.sailing.domain.common.tagging.RaceLogNotFoundException;
... ...
@@ -288,6 +289,7 @@ import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
288 289
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationWithSecurityDTO;
289 290
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
290 291
import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO;
292
+import com.sap.sailing.gwt.ui.shared.TrackingTimesRevocationReportDTO;
291 293
import com.sap.sailing.gwt.ui.shared.TypedDeviceMappingDTO;
292 294
import com.sap.sailing.gwt.ui.shared.UrlDTO;
293 295
import com.sap.sailing.gwt.ui.shared.VenueDTO;
... ...
@@ -4195,4 +4197,23 @@ public class SailingServiceWriteImpl extends SailingServiceImpl implements Saili
4195 4197
getRaceLogTrackingAdapter().copyPairingListFromOtherLeaderboard((RegattaLeaderboard) sourceLeaderboard,
4196 4198
(RegattaLeaderboard) targetLeaderboard, fromRaceColumnName, toRaceColumnInclusiveName);
4197 4199
}
4200
+
4201
+ @Override
4202
+ public TrackingTimesRevocationReportDTO revokeExplicitTrackingTimes(String leaderboardName) throws NotFoundException {
4203
+ final Leaderboard leaderboard = getLeaderboardByName(leaderboardName);
4204
+ getService().getSecurityService().checkCurrentUserUpdatePermission(leaderboard);
4205
+ final TrackingTimesRevocationReportDTO result;
4206
+ if (leaderboard instanceof RegattaLeaderboard) {
4207
+ final Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
4208
+ if (regatta.isControlTrackingFromStartAndFinishTimes()) {
4209
+ result = new TrackingTimesRevocationReportDTO(getRaceLogTrackingAdapter().revokeExplicitTrackingTimes(
4210
+ (RegattaLeaderboard) leaderboard, /* raceLogResolver */ getService()));
4211
+ } else {
4212
+ result = new TrackingTimesRevocationReportDTO(TrackingTimesRevocationErrorCode.NO_AUTOMATED_TRACKING_TIMES);
4213
+ }
4214
+ } else {
4215
+ result = new TrackingTimesRevocationReportDTO(TrackingTimesRevocationErrorCode.NO_REGATTA_LEADERBOARD);
4216
+ }
4217
+ return result;
4218
+ }
4198 4219
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/TrackingTimesRevocationReportDTO.java
... ...
@@ -0,0 +1,97 @@
1
+package com.sap.sailing.gwt.ui.shared;
2
+
3
+import java.util.ArrayList;
4
+import java.util.HashMap;
5
+import java.util.HashSet;
6
+import java.util.LinkedHashMap;
7
+import java.util.Map.Entry;
8
+import java.util.Set;
9
+
10
+import com.google.gwt.core.shared.GwtIncompatible;
11
+import com.google.gwt.user.client.rpc.IsSerializable;
12
+import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
13
+import com.sap.sailing.domain.base.Fleet;
14
+import com.sap.sailing.domain.base.RaceColumn;
15
+import com.sap.sailing.domain.common.dto.FleetDTO;
16
+import com.sap.sailing.domain.common.dto.RaceColumnDTO;
17
+import com.sap.sailing.domain.common.racelog.tracking.TrackingTimesRevocationErrorCode;
18
+import com.sap.sailing.domain.racelogtracking.TrackingTimesRevocationReport;
19
+import com.sap.sse.common.TimePoint;
20
+import com.sap.sse.common.Util.Pair;
21
+
22
+public class TrackingTimesRevocationReportDTO implements IsSerializable {
23
+ private LinkedHashMap<Pair<RaceColumnDTO, FleetDTO>, HashSet<RaceLogEventDTO>> revokedEvents;
24
+ private LinkedHashMap<Pair<RaceColumnDTO, FleetDTO>, Pair<TimePoint, TimePoint>> notRevokedBecauseOfMissingStartOrFinishTime;
25
+ private ArrayList<Pair<RaceColumnDTO, FleetDTO>> notRevokedBecauseNotForTracking;
26
+ private TrackingTimesRevocationErrorCode errorCode;
27
+
28
+ @Deprecated
29
+ TrackingTimesRevocationReportDTO() {
30
+ // for GWT serialization
31
+ }
32
+
33
+ @GwtIncompatible
34
+ public TrackingTimesRevocationReportDTO(TrackingTimesRevocationErrorCode errorCode) {
35
+ this.errorCode = errorCode;
36
+ }
37
+
38
+ @GwtIncompatible
39
+ public TrackingTimesRevocationReportDTO(TrackingTimesRevocationReport report) {
40
+ this.errorCode = report.getErrorCode();
41
+ this.revokedEvents = new LinkedHashMap<>();
42
+ for (final Entry<Pair<RaceColumn, Fleet>, Set<RaceLogEvent>> e : report.getRevokedEvents().entrySet()) {
43
+ revokedEvents.put(
44
+ new Pair<>(toDTO(e.getKey().getA()), toDTO(e.getKey().getB())),
45
+ new HashSet<>(e.getValue().stream().map(this::toDTO)
46
+ .collect(java.util.stream.Collectors.toSet())));
47
+ }
48
+ this.notRevokedBecauseOfMissingStartOrFinishTime = new LinkedHashMap<>();
49
+ for (final Entry<Pair<RaceColumn, Fleet>, Pair<TimePoint, TimePoint>> e : report.getNotRevokedBecauseOfMissingStartOfFinishTime().entrySet()) {
50
+ notRevokedBecauseOfMissingStartOrFinishTime.put(
51
+ new Pair<>(toDTO(e.getKey().getA()), toDTO(e.getKey().getB())),
52
+ e.getValue());
53
+ }
54
+ this.notRevokedBecauseNotForTracking = new ArrayList<>();
55
+ for (final Pair<RaceColumn, Fleet> e : report.getNotRevokedBecauseNotForTracking()) {
56
+ notRevokedBecauseNotForTracking.add(new Pair<>(toDTO(e.getA()), toDTO(e.getB())));
57
+ }
58
+ }
59
+
60
+ /**
61
+ * @return {@code null} means no error
62
+ */
63
+ public TrackingTimesRevocationErrorCode getErrorCode() {
64
+ return errorCode;
65
+ }
66
+
67
+ public HashMap<Pair<RaceColumnDTO, FleetDTO>, HashSet<RaceLogEventDTO>> getRevokedEvents() {
68
+ return revokedEvents;
69
+ }
70
+
71
+ public HashMap<Pair<RaceColumnDTO, FleetDTO>, Pair<TimePoint, TimePoint>> getNotRevokedBecauseOfMissingStartOrFinishTime() {
72
+ return notRevokedBecauseOfMissingStartOrFinishTime;
73
+ }
74
+
75
+ public ArrayList<Pair<RaceColumnDTO, FleetDTO>> getNotRevokedBecauseNotForTracking() {
76
+ return notRevokedBecauseNotForTracking;
77
+ }
78
+
79
+ @GwtIncompatible
80
+ private FleetDTO toDTO(Fleet fleet) {
81
+ return new FleetDTO(fleet.getName(), fleet.getOrdering(), fleet.getColor());
82
+ }
83
+
84
+ @GwtIncompatible
85
+ private RaceColumnDTO toDTO(final RaceColumn raceColumn) {
86
+ return new RaceColumnDTO(raceColumn.getName(), raceColumn.isOneAlwaysStaysOne());
87
+ }
88
+
89
+ @GwtIncompatible
90
+ private RaceLogEventDTO toDTO(RaceLogEvent raceLogEvent) {
91
+ return new RaceLogEventDTO(raceLogEvent.getPassId(), raceLogEvent.getAuthor().getName(),
92
+ raceLogEvent.getAuthor().getPriority(),
93
+ raceLogEvent.getCreatedAt() != null ? raceLogEvent.getCreatedAt().asDate() : null,
94
+ raceLogEvent.getLogicalTimePoint() != null ? raceLogEvent.getLogicalTimePoint().asDate() : null,
95
+ raceLogEvent.getClass().getSimpleName(), raceLogEvent.getShortInfo());
96
+ }
97
+}
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