diff --git a/doc/SAPSailingAnalytics_Administrator_Training.pptx b/doc/SAPSailingAnalytics_Administrator_Training.pptx index 73acb57142a..417c356472f 100644 Binary files a/doc/SAPSailingAnalytics_Administrator_Training.pptx and b/doc/SAPSailingAnalytics_Administrator_Training.pptx differ diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/DashboardClientFactoryImpl.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/DashboardClientFactoryImpl.java index 9ca74a9fbdf..57e263299b0 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/DashboardClientFactoryImpl.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/DashboardClientFactoryImpl.java @@ -1,12 +1,9 @@ package com.sap.sailing.dashboards.gwt.client; -import com.google.gwt.core.client.GWT; -import com.google.gwt.user.client.rpc.ServiceDefTarget; import com.sap.sailing.dashboards.gwt.shared.dispatch.DashboardDispatchSystem; import com.sap.sailing.dashboards.gwt.shared.dispatch.impl.DashboardDispatchSystemImpl; -import com.sap.sailing.gwt.ui.client.SailingService; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sse.gwt.client.EntryPointHelper; +import com.sap.sailing.gwt.ui.client.SailingServiceHelper; import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.player.Timer.PlayModes; @@ -24,8 +21,7 @@ public class DashboardClientFactoryImpl implements DashboardClientFactory{ public DashboardClientFactoryImpl() { dispatch = new DashboardDispatchSystemImpl(); - sailingService = GWT.create(SailingService.class); - EntryPointHelper.registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.WEB_CONTEXT_PATH, RemoteServiceMappingConstants.sailingServiceRemotePath); + sailingService = SailingServiceHelper.createSailingServiceInstance(false); dashboardFiveSecondsTimer = new Timer(PlayModes.Live); dashboardFiveSecondsTimer.setRefreshInterval(DASHBOARD_RERFRESH_INTERVAL); diff --git a/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF index 98840fcc156..366d8cd72b9 100644 --- a/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain.common/META-INF/MANIFEST.MF @@ -17,6 +17,7 @@ Export-Package: com.sap.sailing.domain.common, com.sap.sailing.domain.common.quadtree, com.sap.sailing.domain.common.quadtree.impl;x-friends:="com.sap.sailing.domain.test", com.sap.sailing.domain.common.racelog, + com.sap.sailing.domain.common.sharding, com.sap.sailing.domain.common.racelog.tracking, com.sap.sailing.domain.common.racelog.utils, com.sap.sailing.domain.common.scalablevalue.impl, diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java index 79b6493febb..211470e68cf 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java @@ -31,6 +31,7 @@ public enum BoatClassMasterdata { CANOE_TAIFUN ("Canoe Taifun", true, 5.20, 1.32, BoatHullType.MONOHULL, false, "Taifun", "Taifun Kanu", "Kanu Taifun"), CONTENDER ("Contender", true, 4.88, 1.42, BoatHullType.MONOHULL, false), CC_30 ("C&C 30", true, 9.12, 3.25, BoatHullType.MONOHULL, true), + CLUB_SWAN_50 ("Club Swan 50", true, 16.74, 4.20, BoatHullType.MONOHULL, true, "ClubSwan50"), D_ONE ("D-One", true, 4.23, 2.31, BoatHullType.MONOHULL, true, "Devoti D-One", "DOne", "D_One"), DRAGON_INT ("Dragon Int.", true, 8.89, 1.96, BoatHullType.MONOHULL, true, "Drachen", "Dragon"), DELPHIA_24 ("Delphia 24", true, 7.70, 2.50, BoatHullType.MONOHULL, true, "Delphia 24 One Design", "Delphia 24 OD"), @@ -44,6 +45,7 @@ public enum BoatClassMasterdata { FARR_280 ("Farr 280", true, 8.72, 2.87, BoatHullType.MONOHULL, true, "F280", "F-280", "Farr-280"), FINN ("Finn", true, 4.50, 1.51, BoatHullType.MONOHULL, false), FLYING_DUTCHMAN ("Flying Dutchman", true, 6.10, 1.80, BoatHullType.MONOHULL, true, "FD"), + FLYING_JUNIOR ("Flying Junior", true, 4.03, 1.50, BoatHullType.MONOHULL, true, "FJ"), FLYING_PHANTOM ("Flying Phantom", true, 5.52, 3.00, BoatHullType.CATAMARAN, true), FOLKBOAT ("Folkboat", true, 7.68, 2.20, BoatHullType.MONOHULL, false, "Folke", "Folkeboot"), FUN ("FUN", true, 7.20, 2.45, BoatHullType.MONOHULL, true, "FUN O.D.", "FUN OD", "Open FUN"), @@ -115,6 +117,7 @@ public enum BoatClassMasterdata { TOM_28_MAX ("Tom 28 MAX", true, 8.48, 2.48, BoatHullType.MONOHULL, true, "Tom 28"), TRIAS ("Trias", true, 9.20, 2.12, BoatHullType.MONOHULL, true), TP52 ("TP52", true, 15.85, 4.35, BoatHullType.MONOHULL, true, "TP 52", "Transpac 52", "Transpac52"), + VAURIEN ("Vaurien", true, 4.08, 1.47, BoatHullType.MONOHULL, true), VENT_D_OUEST ("Vent d'Ouest", true, 5.85, 1.75, BoatHullType.MONOHULL, true, "VENTDOUEST", "VENTD'OUEST"), VIPER_640 ("Viper 640", true, 6.43, 2.49, BoatHullType.MONOHULL, true), VO60 ("VO60", true, 19.5, 5.25, BoatHullType.MONOHULL, true, "Volvo Ocean 60", "VolvoOcean60", "W60", "Whitbread60", "Whitbread 60"), diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java index 5c727f3a542..b9d0717a096 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java @@ -5,10 +5,9 @@ import static com.sap.sailing.domain.common.SortingOrder.DESCENDING; import static com.sap.sailing.domain.common.SortingOrder.NONE; import java.io.Serializable; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.List; +import java.util.LinkedHashSet; /** * Identifies details that can be requested from the racing service. Optionally, the details can specify a precision as @@ -244,11 +243,11 @@ public enum DetailType implements Serializable { } /** - * Special List of DetailTypes, that allows operators to select for example the RideHeight, that is usually only - * selectable, if it already has data. + * Special collection of {@link DetailType}s, that allows operators to select for example the RideHeight, that is + * usually only selectable, if it already has data. */ - public static List getAutoplayDetailTypesForChart() { - List availableDetailsTypes = new ArrayList<>(); + public static Collection getAutoplayDetailTypesForChart() { + final Collection availableDetailsTypes = new LinkedHashSet<>(); availableDetailsTypes.add(DetailType.CHART_WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD); availableDetailsTypes.add(DetailType.LEG_CURRENT_SPEED_OVER_GROUND_IN_KNOTS); availableDetailsTypes.add(DetailType.LEG_DISTANCE_TRAVELED); @@ -266,8 +265,8 @@ public enum DetailType implements Serializable { return availableDetailsTypes; } - public static List getRaceExpeditionDetailTypes() { - ArrayList allowed = new ArrayList<>(); + public static Collection getRaceExpeditionDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); allowed.add(EXPEDITION_RACE_AWA); allowed.add(EXPEDITION_RACE_AWS); allowed.add(EXPEDITION_RACE_TWA); @@ -310,15 +309,8 @@ public enum DetailType implements Serializable { return allowed; } - public static List getLegExtendedBravoDetailTypes() { - ArrayList allowed = new ArrayList<>(); - allowed.add(DetailType.BRAVOEXTENDED_LEG_CURRENT_DISTANCE_FOILED_IN_METERS); - allowed.add(DetailType.BRAVOEXTENDED_LEG_CURRENT_DURATION_FOILED_IN_SECONDS); - return allowed; - } - - public static List getRaceExtendedBravoDetailTypes() { - ArrayList allowed = new ArrayList<>(); + public static Collection getRaceExtendedBravoDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); allowed.add(DetailType.BRAVOEXTENDED_RACE_CURRENT_PORT_DAGGERBOARD_RAKE); allowed.add(DetailType.BRAVOEXTENDED_RACE_CURRENT_STBD_DAGGERBOARD_RAKE); allowed.add(DetailType.BRAVOEXTENDED_RACE_CURRENT_PORT_RUDDER_RAKE); @@ -340,24 +332,28 @@ public enum DetailType implements Serializable { return allowed; } - public static List getRaceBravoDetailTypes() { - ArrayList allowed = new ArrayList<>(); + public static Collection getRaceBravoDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); allowed.add(DetailType.BRAVO_RACE_HEEL_IN_DEGREES); allowed.add(DetailType.BRAVO_RACE_PITCH_IN_DEGREES); allowed.add(DetailType.BRAVO_RACE_CURRENT_RIDE_HEIGHT_IN_METERS); + allowed.add(DetailType.RACE_CURRENT_DISTANCE_FOILED_IN_METERS); + allowed.add(DetailType.RACE_CURRENT_DURATION_FOILED_IN_SECONDS); return allowed; } - public static List getLegBravoDetailTypes() { - ArrayList allowed = new ArrayList<>(); + public static Collection getLegBravoDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); allowed.add(DetailType.BRAVO_LEG_CURRENT_HEEL_IN_DEGREES); allowed.add(DetailType.BRAVO_LEG_CURRENT_PITCH_IN_DEGREES); allowed.add(DetailType.BRAVO_LEG_CURRENT_RIDE_HEIGHT_IN_METERS); + allowed.add(DetailType.BRAVOEXTENDED_LEG_CURRENT_DISTANCE_FOILED_IN_METERS); + allowed.add(DetailType.BRAVOEXTENDED_LEG_CURRENT_DURATION_FOILED_IN_SECONDS); return allowed; } - public static List getAllRaceDetailTypes() { - ArrayList allowed = new ArrayList<>(); + public static Collection getAllRaceDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); allowed.add(RACE_GAP_TO_LEADER_IN_SECONDS); allowed.add(RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS); allowed.add(RACE_DISTANCE_TRAVELED); @@ -366,7 +362,6 @@ public enum DetailType implements Serializable { allowed.add(RACE_CALCULATED_TIME_TRAVELED); allowed.add(RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD); allowed.add(RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS); - allowed.add(BRAVO_RACE_CURRENT_RIDE_HEIGHT_IN_METERS); allowed.add(RACE_CURRENT_DISTANCE_FOILED_IN_METERS); allowed.add(RACE_CURRENT_DURATION_FOILED_IN_SECONDS); allowed.add(RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS); @@ -388,7 +383,7 @@ public enum DetailType implements Serializable { * @return */ public static Collection getAllNonRestrictedDetailTypes() { - List all = new ArrayList<>(Arrays.asList(values())); + final Collection all = new LinkedHashSet<>(Arrays.asList(values())); all.removeAll(getRaceBravoDetailTypes()); all.removeAll(getLegBravoDetailTypes()); all.removeAll(getRaceExtendedBravoDetailTypes()); @@ -398,83 +393,150 @@ public enum DetailType implements Serializable { return all; } - public static List getAvailableOverallDetailColumnTypes() { - return Arrays.asList(new DetailType[] { DetailType.REGATTA_RANK, DetailType.OVERALL_TOTAL_DISTANCE_TRAVELED, - DetailType.OVERALL_TOTAL_AVERAGE_SPEED_OVER_GROUND, DetailType.OVERALL_TOTAL_TIME_SAILED_IN_SECONDS, - DetailType.OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, DetailType.OVERALL_TIME_ON_TIME_FACTOR, - DetailType.OVERALL_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE, - DetailType.OVERALL_TOTAL_SCORED_RACE_COUNT, TOTAL_TIME_SAILED_UPWIND_IN_SECONDS, - TOTAL_TIME_SAILED_REACHING_IN_SECONDS, TOTAL_TIME_SAILED_DOWNWIND_IN_SECONDS }); + public static Collection getAvailableOverallDetailColumnTypes() { + final Collection allowed = new LinkedHashSet<>(); + allowed.add(REGATTA_RANK); + allowed.add(OVERALL_TOTAL_DISTANCE_TRAVELED); + allowed.add(OVERALL_TOTAL_AVERAGE_SPEED_OVER_GROUND); + allowed.add(OVERALL_TOTAL_TIME_SAILED_IN_SECONDS); + allowed.add(OVERALL_MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS); + allowed.add(OVERALL_TIME_ON_TIME_FACTOR); + allowed.add(OVERALL_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE); + allowed.add(OVERALL_TOTAL_SCORED_RACE_COUNT); + allowed.add(TOTAL_TIME_SAILED_UPWIND_IN_SECONDS); + allowed.add(TOTAL_TIME_SAILED_REACHING_IN_SECONDS); + allowed.add(TOTAL_TIME_SAILED_DOWNWIND_IN_SECONDS); + return allowed; } - public static List getOverallBravoDetailTypes() { - return Arrays.asList(new DetailType[] { DetailType.OVERALL_TOTAL_DISTANCE_FOILED_IN_METERS, - OVERALL_TOTAL_DURATION_FOILED_IN_SECONDS }); + public static Collection getOverallBravoDetailTypes() { + final Collection allowed = new LinkedHashSet<>(); + allowed.add(OVERALL_TOTAL_DISTANCE_FOILED_IN_METERS); + allowed.add(OVERALL_TOTAL_DURATION_FOILED_IN_SECONDS); + return allowed; } - public static List getLegDetailColumnTypes() { - return Arrays.asList(new DetailType[] { DetailType.LEG_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, - DetailType.LEG_DISTANCE_TRAVELED, DetailType.LEG_DISTANCE_TRAVELED_INCLUDING_GATE_START, - DetailType.LEG_GAP_TO_LEADER_IN_SECONDS, DetailType.LEG_GAP_CHANGE_SINCE_LEG_START_IN_SECONDS, - DetailType.LEG_SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED, - DetailType.LEG_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.LEG_WINDWARD_DISTANCE_TO_GO_IN_METERS, - DetailType.NUMBER_OF_MANEUVERS, DetailType.LEG_ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS, - DetailType.LEG_VELOCITY_MADE_GOOD_IN_KNOTS, DetailType.LEG_TIME_TRAVELED, - DetailType.LEG_CORRECTED_TIME_TRAVELED, DetailType.LEG_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, - DetailType.LEG_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, DetailType.LEG_RANK_GAIN }); + public static Collection getLegDetailColumnTypes() { + final Collection allowed = new LinkedHashSet<>(); + allowed.add(LEG_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS); + allowed.add(LEG_DISTANCE_TRAVELED); + allowed.add(LEG_DISTANCE_TRAVELED_INCLUDING_GATE_START); + allowed.add(LEG_GAP_TO_LEADER_IN_SECONDS); + allowed.add(LEG_GAP_CHANGE_SINCE_LEG_START_IN_SECONDS); + allowed.add(LEG_SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED); + allowed.add(LEG_CURRENT_SPEED_OVER_GROUND_IN_KNOTS); + allowed.add(LEG_WINDWARD_DISTANCE_TO_GO_IN_METERS); + allowed.add(NUMBER_OF_MANEUVERS); + allowed.add(LEG_ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS); + allowed.add(LEG_VELOCITY_MADE_GOOD_IN_KNOTS); + allowed.add(LEG_TIME_TRAVELED); + allowed.add(LEG_CORRECTED_TIME_TRAVELED); + allowed.add(LEG_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS); + allowed.add(LEG_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS); + allowed.add(LEG_RANK_GAIN); + return allowed; } - public static List getRaceStartAnalysisColumnTypes() { - return Arrays.asList(new DetailType[] { DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START, - DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START, DetailType.DISTANCE_TO_START_AT_RACE_START, - DetailType.TIME_BETWEEN_RACE_START_AND_COMPETITOR_START, DetailType.SPEED_OVER_GROUND_AT_RACE_START, - DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, - DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, - DetailType.START_TACK }); + public static Collection getRaceStartAnalysisColumnTypes() { + final Collection allowed = new LinkedHashSet<>(); + allowed.add(RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START); + allowed.add(RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START); + allowed.add(DISTANCE_TO_START_AT_RACE_START); + allowed.add(TIME_BETWEEN_RACE_START_AND_COMPETITOR_START); + allowed.add(SPEED_OVER_GROUND_AT_RACE_START); + allowed.add(SPEED_OVER_GROUND_WHEN_PASSING_START); + allowed.add(DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS); + allowed.add(START_TACK); + return allowed; } - public static List getLegExpeditionDetailColumnTypes() { - return Arrays.asList(new DetailType[] { EXPEDITION_LEG_AWA, EXPEDITION_LEG_AWS, EXPEDITION_LEG_TWA, - EXPEDITION_LEG_TWS, EXPEDITION_LEG_TWD, EXPEDITION_LEG_TARG_TWA, EXPEDITION_LEG_BOAT_SPEED, - EXPEDITION_LEG_TARG_BOAT_SPEED, EXPEDITION_LEG_SOG, EXPEDITION_LEG_COG, EXPEDITION_LEG_FORESTAY_LOAD, - EXPEDITION_LEG_RAKE, EXPEDITION_LEG_COURSE, EXPEDITION_LEG_HEADING, EXPEDITION_LEG_VMG, - EXPEDITION_LEG_VMG_TARG_VMG_DELTA, EXPEDITION_LEG_RATE_OF_TURN, EXPEDITION_LEG_RUDDER_ANGLE, - EXPEDITION_LEG_TARGET_HEEL, EXPEDITION_LEG_TIME_TO_PORT_LAYLINE, - EXPEDITION_LEG_TIME_TO_STB_LAYLINE, EXPEDITION_LEG_DIST_TO_PORT_LAYLINE, - EXPEDITION_LEG_DIST_TO_STB_LAYLINE, EXPEDITION_LEG_TIME_TO_GUN, EXPEDITION_LEG_TIME_TO_COMMITTEE_BOAT, - EXPEDITION_LEG_TIME_TO_PIN, EXPEDITION_LEG_TIME_TO_BURN_TO_LINE, - EXPEDITION_LEG_TIME_TO_BURN_TO_COMMITTEE_BOAT, EXPEDITION_LEG_TIME_TO_BURN_TO_PIN, - EXPEDITION_LEG_DISTANCE_TO_COMMITTEE_BOAT, EXPEDITION_LEG_DISTANCE_TO_PIN, - EXPEDITION_LEG_DISTANCE_BELOW_LINE, EXPEDITION_LEG_LINE_SQUARE_FOR_WIND_DIRECTION, EXPEDITION_LEG_BARO, - EXPEDITION_LEG_LOAD_S, EXPEDITION_LEG_LOAD_P, EXPEDITION_LEG_JIB_CAR_PORT, EXPEDITION_LEG_JIB_CAR_STBD, - EXPEDITION_LEG_MAST_BUTT }); + public static Collection getLegExpeditionDetailColumnTypes() { + final Collection allowed = new LinkedHashSet<>(); + allowed.add(EXPEDITION_LEG_AWA); + allowed.add(EXPEDITION_LEG_AWS); + allowed.add(EXPEDITION_LEG_TWA); + allowed.add(EXPEDITION_LEG_TWS); + allowed.add(EXPEDITION_LEG_TWD); + allowed.add(EXPEDITION_LEG_TARG_TWA); + allowed.add(EXPEDITION_LEG_BOAT_SPEED); + allowed.add(EXPEDITION_LEG_TARG_BOAT_SPEED); + allowed.add(EXPEDITION_LEG_SOG); + allowed.add(EXPEDITION_LEG_COG); + allowed.add(EXPEDITION_LEG_FORESTAY_LOAD); + allowed.add(EXPEDITION_LEG_RAKE); + allowed.add(EXPEDITION_LEG_COURSE); + allowed.add(EXPEDITION_LEG_HEADING); + allowed.add(EXPEDITION_LEG_VMG); + allowed.add(EXPEDITION_LEG_VMG_TARG_VMG_DELTA); + allowed.add(EXPEDITION_LEG_RATE_OF_TURN); + allowed.add(EXPEDITION_LEG_RUDDER_ANGLE); + allowed.add(EXPEDITION_LEG_TARGET_HEEL); + allowed.add(EXPEDITION_LEG_TIME_TO_PORT_LAYLINE); + allowed.add(EXPEDITION_LEG_TIME_TO_STB_LAYLINE); + allowed.add(EXPEDITION_LEG_DIST_TO_PORT_LAYLINE); + allowed.add(EXPEDITION_LEG_DIST_TO_STB_LAYLINE); + allowed.add(EXPEDITION_LEG_TIME_TO_GUN); + allowed.add(EXPEDITION_LEG_TIME_TO_COMMITTEE_BOAT); + allowed.add(EXPEDITION_LEG_TIME_TO_PIN); + allowed.add(EXPEDITION_LEG_TIME_TO_BURN_TO_LINE); + allowed.add(EXPEDITION_LEG_TIME_TO_BURN_TO_COMMITTEE_BOAT); + allowed.add(EXPEDITION_LEG_TIME_TO_BURN_TO_PIN); + allowed.add(EXPEDITION_LEG_DISTANCE_TO_COMMITTEE_BOAT); + allowed.add(EXPEDITION_LEG_DISTANCE_TO_PIN); + allowed.add(EXPEDITION_LEG_DISTANCE_BELOW_LINE); + allowed.add(EXPEDITION_LEG_LINE_SQUARE_FOR_WIND_DIRECTION); + allowed.add(EXPEDITION_LEG_BARO); + allowed.add(EXPEDITION_LEG_LOAD_S); + allowed.add(EXPEDITION_LEG_LOAD_P); + allowed.add(EXPEDITION_LEG_JIB_CAR_PORT); + allowed.add(EXPEDITION_LEG_JIB_CAR_STBD); + allowed.add(EXPEDITION_LEG_MAST_BUTT); + return allowed; } /** * Not yet implemented types can be added here, they will be hidden from any user under all circumstances */ - public static List getDisabledDetailColumTypes() { - return Arrays.asList(new DetailType[] { EXPEDITION_LEG_TARG_TWA, EXPEDITION_LEG_VMG, - EXPEDITION_LEG_VMG_TARG_VMG_DELTA, EXPEDITION_LEG_RATE_OF_TURN, EXPEDITION_LEG_RUDDER_ANGLE, - EXPEDITION_LEG_TIME_TO_PORT_LAYLINE, EXPEDITION_LEG_TIME_TO_STB_LAYLINE, - EXPEDITION_LEG_DIST_TO_PORT_LAYLINE, EXPEDITION_LEG_DIST_TO_STB_LAYLINE, - EXPEDITION_LEG_TIME_TO_COMMITTEE_BOAT, EXPEDITION_LEG_TIME_TO_PIN, EXPEDITION_LEG_TIME_TO_BURN_TO_COMMITTEE_BOAT, - EXPEDITION_LEG_TIME_TO_BURN_TO_PIN, EXPEDITION_LEG_DISTANCE_TO_COMMITTEE_BOAT, - EXPEDITION_LEG_DISTANCE_TO_PIN, EXPEDITION_LEG_LINE_SQUARE_FOR_WIND_DIRECTION, EXPEDITION_RACE_TARG_TWA, - EXPEDITION_RACE_VMG, EXPEDITION_RACE_VMG_TARG_VMG_DELTA, - EXPEDITION_RACE_RUDDER_ANGLE, EXPEDITION_RACE_TIME_TO_PORT_LAYLINE, EXPEDITION_RACE_TIME_TO_STB_LAYLINE, - EXPEDITION_RACE_DIST_TO_PORT_LAYLINE, EXPEDITION_RACE_DIST_TO_STB_LAYLINE, - EXPEDITION_RACE_TIME_TO_COMMITTEE_BOAT, EXPEDITION_RACE_TIME_TO_PIN, - EXPEDITION_RACE_TIME_TO_BURN_TO_PIN, EXPEDITION_RACE_TIME_TO_BURN_TO_COMMITTEE_BOAT, - EXPEDITION_RACE_DISTANCE_TO_COMMITTEE_BOAT, EXPEDITION_RACE_DISTANCE_TO_PIN, - EXPEDITION_RACE_LINE_SQUARE_FOR_WIND_DIRECTION }); + public static Collection getDisabledDetailColumTypes() { + final Collection disabled = new LinkedHashSet<>(); + disabled.add(EXPEDITION_LEG_TARG_TWA); + disabled.add(EXPEDITION_LEG_VMG); + disabled.add(EXPEDITION_LEG_VMG_TARG_VMG_DELTA); + disabled.add(EXPEDITION_LEG_RATE_OF_TURN); + disabled.add(EXPEDITION_LEG_RUDDER_ANGLE); + disabled.add(EXPEDITION_LEG_TIME_TO_PORT_LAYLINE); + disabled.add(EXPEDITION_LEG_TIME_TO_STB_LAYLINE); + disabled.add(EXPEDITION_LEG_DIST_TO_PORT_LAYLINE); + disabled.add(EXPEDITION_LEG_DIST_TO_STB_LAYLINE); + disabled.add(EXPEDITION_LEG_TIME_TO_COMMITTEE_BOAT); + disabled.add(EXPEDITION_LEG_TIME_TO_PIN); + disabled.add(EXPEDITION_LEG_TIME_TO_BURN_TO_COMMITTEE_BOAT); + disabled.add(EXPEDITION_LEG_TIME_TO_BURN_TO_PIN); + disabled.add(EXPEDITION_LEG_DISTANCE_TO_COMMITTEE_BOAT); + disabled.add(EXPEDITION_LEG_DISTANCE_TO_PIN); + disabled.add(EXPEDITION_LEG_LINE_SQUARE_FOR_WIND_DIRECTION); + disabled.add(EXPEDITION_RACE_TARG_TWA); + disabled.add(EXPEDITION_RACE_VMG); + disabled.add(EXPEDITION_RACE_VMG_TARG_VMG_DELTA); + disabled.add(EXPEDITION_RACE_RUDDER_ANGLE); + disabled.add(EXPEDITION_RACE_TIME_TO_PORT_LAYLINE); + disabled.add(EXPEDITION_RACE_TIME_TO_STB_LAYLINE); + disabled.add(EXPEDITION_RACE_DIST_TO_PORT_LAYLINE); + disabled.add(EXPEDITION_RACE_DIST_TO_STB_LAYLINE); + disabled.add(EXPEDITION_RACE_TIME_TO_COMMITTEE_BOAT); + disabled.add(EXPEDITION_RACE_TIME_TO_PIN); + disabled.add(EXPEDITION_RACE_TIME_TO_BURN_TO_PIN); + disabled.add(EXPEDITION_RACE_TIME_TO_BURN_TO_COMMITTEE_BOAT); + disabled.add(EXPEDITION_RACE_DISTANCE_TO_COMMITTEE_BOAT); + disabled.add(EXPEDITION_RACE_DISTANCE_TO_PIN); + disabled.add(EXPEDITION_RACE_LINE_SQUARE_FOR_WIND_DIRECTION); + return disabled; } - public static List getAllLegDetailColumnTypes() { - ArrayList all = new ArrayList<>(); + public static Collection getAllLegDetailColumnTypes() { + final Collection all = new LinkedHashSet<>(); all.addAll(getLegDetailColumnTypes()); all.addAll(getLegBravoDetailTypes()); - all.addAll(getLegExtendedBravoDetailTypes()); all.addAll(getLegExpeditionDetailColumnTypes()); return all; } @@ -498,11 +560,10 @@ public enum DetailType implements Serializable { return t; } } - } } } throw new IllegalArgumentException("Could not restore " + value + " to an DetailType enum"); } -} \ No newline at end of file +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sharding/ShardingType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sharding/ShardingType.java new file mode 100644 index 00000000000..1ed04cadfaa --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/sharding/ShardingType.java @@ -0,0 +1,61 @@ +package com.sap.sailing.domain.common.sharding; + +/** + * Enum that defines all sharding types known by the system. + * + * For a sharding to work, we need both client and backend properly identifying a shard. + * + * While the client has to properly deliver sharding information on each request, the server side + * infrastructure must be able to identify the information and properly route the requests. + * + * The current implementation uses a SUFFIX appended the GWT RPC URLs (allowing path based routing), so that each call to the server + * propagates the sharding information through the url. + * + * A load balancer can be used to properly route each request to the corresponding server node. The first + * sharding implemented is the sharding by leaderboard name. + * + * The suffix appended to the request URL consists of two parts: a prefix that is used to identify the type of shard + * and a payload that will be used to identify the shard. + * + * + */ +public enum ShardingType { + LEADERBOARDNAME("/leaderboard/"); + + private String prefix; + + private ShardingType(String prefix) { + this.prefix = prefix; + } + + public String encodeIfNeeded(String shardingInfo) { + if (shardingInfo.startsWith(prefix)) { + return shardingInfo; + } + return new StringBuilder().append(prefix) + .append(normalize(shardingInfo)) + .toString(); + } + + private String normalize(String replace) { + char[] chars = replace.toCharArray(); + StringBuilder answer = new StringBuilder(); + for(int i = 0;i 47 && c < 58) { // digits + answer.append(c); + } else if (c > 64 && c < 91) { // uppercase letters + answer.append(c); + } else if (c > 69 && c < 123) { // lowercase letters + answer.append(c); + } else { + answer.append("_"); + } + } + return answer.toString(); + } + + public String getPrefix() { + return prefix; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/RegattaConfiguration.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/RegattaConfiguration.java index 1828d8c0ef1..2819e8ee4e3 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/RegattaConfiguration.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/RegattaConfiguration.java @@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.configuration.procedures.RRS26Configuration; import com.sap.sailing.domain.base.configuration.procedures.SWCStartConfiguration; import com.sap.sailing.domain.common.CourseDesignerMode; import com.sap.sailing.domain.common.racelog.RacingProcedureType; +import com.sap.sse.common.Duration; /** * Interface holding configuration options for a Regatta, including configurations for @@ -26,6 +27,12 @@ public interface RegattaConfiguration extends Serializable, Cloneable { * Gets the default {@link RacingProcedureType} for races configured. */ RacingProcedureType getDefaultRacingProcedureType(); + + /** + * Gets the default protest time duration. May be {@code null} in case no default is set for the + * regatta. + */ + Duration getDefaultProtestTimeDuration(); /** * Gets the configuration for RRS26 races. diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/EmptyRegattaConfiguration.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/EmptyRegattaConfiguration.java index 4b39cbeed7f..0363327b55b 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/EmptyRegattaConfiguration.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/EmptyRegattaConfiguration.java @@ -4,6 +4,7 @@ import com.sap.sailing.domain.base.configuration.ConfigurationLoader; import com.sap.sailing.domain.base.configuration.RegattaConfiguration; import com.sap.sailing.domain.common.CourseDesignerMode; import com.sap.sailing.domain.common.racelog.RacingProcedureType; +import com.sap.sse.common.Duration; /** * Is empty, does nothing on store and load. @@ -17,6 +18,7 @@ public class EmptyRegattaConfiguration extends RegattaConfigurationImpl implemen public RegattaConfiguration load() { setDefaultRacingProcedureType(RacingProcedureType.UNKNOWN); setDefaultCourseDesignerMode(CourseDesignerMode.UNKNOWN); + setDefaultProtestTimeDuration(Duration.ONE_MINUTE.times(90)); setRRS26Configuration(new RRS26ConfigurationImpl()); setSWCStartConfiguration(new SWCStartConfigurationImpl()); setGateStartConfiguration(new GateStartConfigurationImpl()); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/RegattaConfigurationImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/RegattaConfigurationImpl.java index 5810aa96e41..55c1f89b320 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/RegattaConfigurationImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/configuration/impl/RegattaConfigurationImpl.java @@ -9,6 +9,7 @@ import com.sap.sailing.domain.base.configuration.procedures.RRS26Configuration; import com.sap.sailing.domain.base.configuration.procedures.SWCStartConfiguration; import com.sap.sailing.domain.common.CourseDesignerMode; import com.sap.sailing.domain.common.racelog.RacingProcedureType; +import com.sap.sse.common.Duration; public class RegattaConfigurationImpl implements RegattaConfiguration { @@ -16,6 +17,7 @@ public class RegattaConfigurationImpl implements RegattaConfiguration { private RacingProcedureType defaultRacingProcedureType; private CourseDesignerMode defaultCourseDesignerMode; + private Duration defaultProtestTimeDuration; private RRS26Configuration rrs26Configuration; private SWCStartConfiguration swcStartConfiguration; @@ -42,6 +44,15 @@ public class RegattaConfigurationImpl implements RegattaConfiguration { defaultCourseDesignerMode = mode; } + @Override + public Duration getDefaultProtestTimeDuration() { + return defaultProtestTimeDuration; + } + + public void setDefaultProtestTimeDuration(Duration defaultProtestTimeDuration) { + this.defaultProtestTimeDuration = defaultProtestTimeDuration; + } + @Override public RRS26Configuration getRRS26Configuration() { return rrs26Configuration; @@ -101,6 +112,7 @@ public class RegattaConfigurationImpl implements RegattaConfiguration { RegattaConfigurationImpl copy = new RegattaConfigurationImpl(); copy.setDefaultRacingProcedureType(defaultRacingProcedureType); copy.setDefaultCourseDesignerMode(defaultCourseDesignerMode); + copy.setDefaultProtestTimeDuration(defaultProtestTimeDuration); copy.setRRS26Configuration(rrs26Configuration); copy.setSWCStartConfiguration(swcStartConfiguration); copy.setGateStartConfiguration(gateStartConfiguration); @@ -120,6 +132,9 @@ public class RegattaConfigurationImpl implements RegattaConfiguration { if (update.getDefaultRacingProcedureType() != null) { target.setDefaultRacingProcedureType(update.getDefaultRacingProcedureType()); } + if (update.getDefaultProtestTimeDuration() != null) { + target.setDefaultProtestTimeDuration(update.getDefaultProtestTimeDuration()); + } if (update.getRRS26Configuration() != null) { target.setRRS26Configuration( (RRS26Configuration) target.getRRS26Configuration().merge(update.getRRS26Configuration())); diff --git a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF index 5567e3bc181..6f6d9a68bbe 100755 --- a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF @@ -31,6 +31,7 @@ Export-Package: com.sap.sailing.domain.base, com.sap.sailing.domain.trackimport, com.sap.sailing.domain.tracking, com.sap.sailing.domain.tracking.impl, + com.sap.sailing.domain.sharding, com.sap.sailing.domain.windfinder, com.sap.sailing.util, com.sap.sailing.util.impl;x-friends:="com.sap.sailing.server.replication,com.sap.sailing.domain.test" diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java index ed3880b105b..ec56da0c10f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/caching/LiveLeaderboardUpdater.java @@ -12,7 +12,9 @@ import java.util.logging.Logger; import com.sap.sailing.domain.base.DomainFactory; import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.dto.LeaderboardDTO; +import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.sharding.ShardingContext; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -274,6 +276,7 @@ public class LiveLeaderboardUpdater implements Runnable { public void run() { assert running; try { + ShardingContext.setShardingConstraint(ShardingType.LEADERBOARDNAME, leaderboard.getName()); logger.info("Starting " + LiveLeaderboardUpdater.class.getSimpleName() + " thread for leaderboard " + leaderboard.getName()); // interrupt the current thread if not producing a single result within the overall timeout @@ -326,6 +329,8 @@ public class LiveLeaderboardUpdater implements Runnable { notifyAll(); } logger.log(Level.SEVERE, "exception updating live leaderboard "+leaderboard.getName(), e); + } finally { + ShardingContext.clearShardingConstraint(ShardingType.LEADERBOARDNAME); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java index e3bd3246dff..bf8bbf41983 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java @@ -55,6 +55,7 @@ import com.sap.sailing.domain.common.dto.LegEntryDTO; import com.sap.sailing.domain.common.dto.MetaLeaderboardRaceColumnDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; import com.sap.sailing.domain.common.dto.RaceDTO; +import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.common.tracking.BravoExtendedFix; import com.sap.sailing.domain.common.tracking.BravoFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; @@ -68,6 +69,7 @@ import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.ranking.RankingMetric.CompetitorRankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; +import com.sap.sailing.domain.sharding.ShardingContext; import com.sap.sailing.domain.tracking.BravoFixTrack; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; @@ -355,6 +357,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { final boolean waitForLatestAnalyses, TrackedRegattaRegistry trackedRegattaRegistry, final DomainFactory baseDomainFactory, final boolean fillTotalPointsUncorrected) throws NoWindException { + ShardingContext.checkConstraint(ShardingType.LEADERBOARDNAME, getName()); long startOfRequestHandling = System.currentTimeMillis(); final LeaderboardDTOCalculationReuseCache cache = new LeaderboardDTOCalculationReuseCache(timePoint); final BoatClass boatClass = getBoatClass(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 808d4bca7b5..d9577814e88 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -52,16 +52,17 @@ import com.sap.sse.concurrent.NamedReentrantReadWriteLock; /** * Base implementation for various types of leaderboards. The {@link RaceColumnListener} implementation forwards events - * received to all {@link RaceColumnListener} subscribed with this leaderboard. To which objects this leaderboard subscribes - * as {@link RaceColumnListener} is left to the concrete subclasses to implement, but the race columns seem like useful - * candidates. + * received to all {@link RaceColumnListener} subscribed with this leaderboard. To which objects this leaderboard + * subscribes as {@link RaceColumnListener} is left to the concrete subclasses to implement, but the race columns seem + * like useful candidates. * * @author Axel Uhl (d043530) * */ -public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardWithCache implements Leaderboard, RaceColumnListener { +public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardWithCache + implements Leaderboard, RaceColumnListener { private static final long serialVersionUID = 330156778603279333L; - + static final Double DOUBLE_0 = new Double(0); private final SettableScoreCorrection scoreCorrection; @@ -75,15 +76,15 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW private final Map displayNames; /** - * Backs the {@link #getCarriedPoints(Competitor)} API with data. Can be used to prime this leaderboard - * with aggregated results of races not tracked / displayed by this leaderboard in detail. The points - * provided by this map are considered by {@link #getNetPoints(Competitor, TimePoint)}. + * Backs the {@link #getCarriedPoints(Competitor)} API with data. Can be used to prime this leaderboard with + * aggregated results of races not tracked / displayed by this leaderboard in detail. The points provided by this + * map are considered by {@link #getNetPoints(Competitor, TimePoint)}. */ private final Map carriedPoints; /** - * A set that manages the difference between {@link #getCompetitors()} and {@link #getAllCompetitors()}. Access - * is controlled by the {@link #suppressedCompetitorsLock} lock. + * A set that manages the difference between {@link #getCompetitors()} and {@link #getAllCompetitors()}. Access is + * controlled by the {@link #suppressedCompetitorsLock} lock. */ private final Set suppressedCompetitors; private final NamedReentrantReadWriteLock suppressedCompetitorsLock; @@ -107,8 +108,8 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW private final Fleet fleet; private EntryImpl(Callable trackedRankProvider, Double totalPoints, - Callable totalPointsUncorrectedProvider, boolean isTotalPointsCorrected, - Double netPoints, MaxPointsReason maxPointsReason, boolean discarded, Fleet fleet) { + Callable totalPointsUncorrectedProvider, boolean isTotalPointsCorrected, Double netPoints, + MaxPointsReason maxPointsReason, boolean discarded, Fleet fleet) { super(); this.trackedRankProvider = trackedRankProvider; this.totalPoints = totalPoints; @@ -119,6 +120,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW this.discarded = discarded; this.fleet = fleet; } + @Override public int getTrackedRank() { try { @@ -127,30 +129,37 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW throw new RuntimeException(e); } } + @Override public Double getTotalPoints() { return totalPoints; } + @Override public boolean isTotalPointsCorrected() { return isTotalPointsCorrected; } + @Override public Double getNetPoints() { return netPoints; } + @Override public MaxPointsReason getMaxPointsReason() { return maxPointsReason; } + @Override public boolean isDiscarded() { return discarded; } + @Override public Fleet getFleet() { return fleet; } + @Override public Double getTotalPointsUncorrected() { try { @@ -160,7 +169,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } } } - + /** * Computing the competitors can be a bit expensive, particularly if the fleet is large and there may be suppressed * competitors, and the leaderboard may be a meta-leaderboard that refers to other leaderboards which each have @@ -171,10 +180,10 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW * * As it turns out, one of the most frequent uses of the {@link AbstractSimpleLeaderboardImpl#getCompetitors} * competitors list is to determine their number which in turn is only required for high-point scoring systems and - * for computing the default score for penalties. Again, the most frequently used low-point family of scoring schemes - * does not require this number. Yet, the scoring scheme requires an argument for polymorphic use by those that - * need it. Instead of computing it for each call, this interface lets us defer the actual calculation until the - * point when it's really needed. Once asked, this object will cache the result. Therefore, a new one should be + * for computing the default score for penalties. Again, the most frequently used low-point family of scoring + * schemes does not require this number. Yet, the scoring scheme requires an argument for polymorphic use by those + * that need it. Instead of computing it for each call, this interface lets us defer the actual calculation until + * the point when it's really needed. Once asked, this object will cache the result. Therefore, a new one should be * constructed each time the number shall be computed. * * @author Axel Uhl (D043530) @@ -183,7 +192,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW public class NumberOfCompetitorsFetcherImpl implements NumberOfCompetitorsInLeaderboardFetcher { private int numberOfCompetitors = -1; private int numberOfCompetitorsWithoutMaxPointReason = -1; - + @Override public int getNumberOfCompetitorsInLeaderboard() { if (numberOfCompetitors == -1) { @@ -191,7 +200,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } return numberOfCompetitors; } - + @Override public int getNumberOfCompetitorsWithoutMaxPointReason(RaceColumn column, TimePoint timePoint) { if (numberOfCompetitorsWithoutMaxPointReason == -1) { @@ -215,17 +224,17 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW this.suppressedCompetitors = new HashSet(); this.suppressedCompetitorsLock = new NamedReentrantReadWriteLock("suppressedCompetitorsLock", /* fair */ false); } - + protected RaceColumnListeners getRaceColumnListeners() { return raceColumnListeners; } - + @Override public void trackedRaceUnlinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) { getRaceColumnListeners().notifyListenersAboutTrackedRaceUnlinked(raceColumn, fleet, trackedRace); super.trackedRaceUnlinked(raceColumn, fleet, trackedRace); } - + @Override public void trackedRaceLinked(RaceColumn raceColumn, Fleet fleet, TrackedRace trackedRace) { getRaceColumnListeners().notifyListenersAboutTrackedRaceLinked(raceColumn, fleet, trackedRace); @@ -235,10 +244,11 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW public void isMedalRaceChanged(RaceColumn raceColumn, boolean newIsMedalRace) { getRaceColumnListeners().notifyListenersAboutIsMedalRaceChanged(raceColumn, newIsMedalRace); } - + @Override public void isFleetsCanRunInParallelChanged(RaceColumn raceColumn, boolean newIsFleetsCanRunInParallel) { - getRaceColumnListeners().notifyListenersAboutIsFleetsCanRunInParallelChanged(raceColumn, newIsFleetsCanRunInParallel); + getRaceColumnListeners().notifyListenersAboutIsFleetsCanRunInParallelChanged(raceColumn, + newIsFleetsCanRunInParallel); } @Override @@ -247,13 +257,16 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } @Override - public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, boolean firstColumnIsNonDiscardableCarryForward) { - getRaceColumnListeners().notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, firstColumnIsNonDiscardableCarryForward); + public void isFirstColumnIsNonDiscardableCarryForwardChanged(RaceColumn raceColumn, + boolean firstColumnIsNonDiscardableCarryForward) { + getRaceColumnListeners().notifyListenersAboutIsFirstColumnIsNonDiscardableCarryForwardChanged(raceColumn, + firstColumnIsNonDiscardableCarryForward); } @Override public void hasSplitFleetContiguousScoringChanged(RaceColumn raceColumn, boolean hasSplitFleetContiguousScoring) { - getRaceColumnListeners().notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, hasSplitFleetContiguousScoring); + getRaceColumnListeners().notifyListenersAboutHasSplitFleetContiguousScoringChanged(raceColumn, + hasSplitFleetContiguousScoring); } @Override @@ -283,11 +296,13 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW @Override public void competitorDisplayNameChanged(Competitor competitor, String oldDisplayName, String displayName) { - getRaceColumnListeners().notifyListenersAboutCompetitorDisplayNameChanged(competitor, oldDisplayName, displayName); + getRaceColumnListeners().notifyListenersAboutCompetitorDisplayNameChanged(competitor, oldDisplayName, + displayName); } @Override - public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, ResultDiscardingRule newDiscardingRule) { + public void resultDiscardingRuleChanged(ResultDiscardingRule oldDiscardingRule, + ResultDiscardingRule newDiscardingRule) { getRaceColumnListeners().notifyListenersAboutResultDiscardingRuleChanged(oldDiscardingRule, newDiscardingRule); } @@ -304,7 +319,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW protected SettableScoreCorrection createScoreCorrection() { return new ScoreCorrectionImpl(this); } - + @Override public SettableScoreCorrection getScoreCorrection() { return scoreCorrection; @@ -372,7 +387,8 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW String oldDisplayName = displayNames.get(competitor); displayNames.put(competitor, displayName); if (!Util.equalsWithNull(oldDisplayName, displayName)) { - getRaceColumnListeners().notifyListenersAboutCompetitorDisplayNameChanged(competitor, oldDisplayName, displayName); + getRaceColumnListeners().notifyListenersAboutCompetitorDisplayNameChanged(competitor, oldDisplayName, + displayName); } } @@ -385,9 +401,9 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW @Override public Double getTotalPoints(final Competitor competitor, final RaceColumn raceColumn, final TimePoint timePoint) { - return getScoreCorrection().getCorrectedScore( - ()->getTrackedRank(competitor, raceColumn, timePoint), competitor, - raceColumn, this, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()).getCorrectedScore(); + return getScoreCorrection().getCorrectedScore(() -> getTrackedRank(competitor, raceColumn, timePoint), + competitor, raceColumn, this, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()) + .getCorrectedScore(); } @Override @@ -399,19 +415,19 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW public boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { return isDiscarded(competitor, raceColumn, getRaceColumns(), timePoint); } - + private boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, Iterable raceColumnsToConsider, TimePoint timePoint) { - final Set discardedRaceColumns = getResultDiscardingRule() - .getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint); + final Set discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, + raceColumnsToConsider, timePoint); return isDiscarded(competitor, raceColumn, timePoint, discardedRaceColumns); } /** * Same as {@link #isDiscarded(Competitor, RaceColumn, TimePoint)}, only that the set of discarded race columns can - * be specified which is useful when net points are to be computed for more than one column for the same - * competitor because then the calculation of discards (which requires looking at all columns) only needs to be done - * once and not again for each column (which would lead to quadratic effort). + * be specified which is useful when net points are to be computed for more than one column for the same competitor + * because then the calculation of discards (which requires looking at all columns) only needs to be done once and + * not again for each column (which would lead to quadratic effort). * * @param discardedRaceColumns * expected to be the result of what we would get if we called {@link #getResultDiscardingRule()}. @@ -420,8 +436,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW */ private boolean isDiscarded(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, final Set discardedRaceColumns) { - return !raceColumn.isMedalRace() - && getMaxPointsReason(competitor, raceColumn, timePoint).isDiscardable() + return !raceColumn.isMedalRace() && getMaxPointsReason(competitor, raceColumn, timePoint).isDiscardable() && discardedRaceColumns.contains(raceColumn); } @@ -431,16 +446,16 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } @Override - public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, - Iterable raceColumnsToConsider, TimePoint timePoint) { - final Set discardedRaceColumns = getResultDiscardingRule() - .getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint); + public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, Iterable raceColumnsToConsider, + TimePoint timePoint) { + final Set discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, + raceColumnsToConsider, timePoint); return getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns); } /** - * Same as {@link #getNetPoints(Competitor, RaceColumn, Iterable, TimePoint)}, only that the set of discarded race columns can - * be specified which is useful when net points are to be computed for more than one column for the same + * Same as {@link #getNetPoints(Competitor, RaceColumn, Iterable, TimePoint)}, only that the set of discarded race + * columns can be specified which is useful when net points are to be computed for more than one column for the same * competitor because then the calculation of discards (which requires looking at all columns) only needs to be done * once and not again for each column (which would lead to quadratic effort). * @@ -450,7 +465,8 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW * getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint)}. */ @Override - public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, Set discardedRaceColumns) { + public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint, + Set discardedRaceColumns) { Double result; if (isDiscarded(competitor, raceColumn, timePoint, discardedRaceColumns)) { result = 0.0; @@ -473,11 +489,12 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW @Override public Double getNetPoints(Competitor competitor, final Iterable raceColumnsToConsider, TimePoint timePoint) { - // when a column with isStartsWithZeroScore() is found, only reset score if the competitor scored in any race from there on + // when a column with isStartsWithZeroScore() is found, only reset score if the competitor scored in any race + // from there on boolean needToResetScoreUponNextNonEmptyEntry = false; double result = getCarriedPoints(competitor); - final Set discardedRaceColumns = getResultDiscardingRule() - .getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint); + final Set discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, + raceColumnsToConsider, timePoint); for (RaceColumn raceColumn : raceColumnsToConsider) { if (raceColumn.isStartsWithZeroScore()) { needToResetScoreUponNextNonEmptyEntry = true; @@ -497,16 +514,19 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } /** - * All competitors with non-null total points are added to the result which is then sorted by total points in ascending - * order. The fleet, if ordered, is the primary ordering criterion, followed by the total points. + * All competitors with non-null total points are added to the result which is then sorted by total + * points in ascending order. The fleet, if ordered, is the primary ordering criterion, followed by the total + * points. */ @Override - public List getCompetitorsFromBestToWorst(final RaceColumn raceColumn, TimePoint timePoint) throws NoWindException { + public List getCompetitorsFromBestToWorst(final RaceColumn raceColumn, TimePoint timePoint) + throws NoWindException { final Map> totalPointsAndFleet = new HashMap>(); for (Competitor competitor : getCompetitors()) { Double totalPoints = getTotalPoints(competitor, raceColumn, timePoint); if (totalPoints != null) { - totalPointsAndFleet.put(competitor, new com.sap.sse.common.Util.Pair(totalPoints, raceColumn.getFleetOfCompetitor(competitor))); + totalPointsAndFleet.put(competitor, new com.sap.sse.common.Util.Pair(totalPoints, + raceColumn.getFleetOfCompetitor(competitor))); } } List result = new ArrayList(totalPointsAndFleet.keySet()); @@ -518,7 +538,8 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW comparisonResult = 0; } else { if (raceColumn.hasSplitFleets() && !raceColumn.hasSplitFleetContiguousScoring()) { - // only check fleets if there are more than one and the column is not to be contiguously scored even in case + // only check fleets if there are more than one and the column is not to be contiguously scored + // even in case // of split fleets final Fleet o1Fleet = totalPointsAndFleet.get(o1).getB(); final Fleet o2Fleet = totalPointsAndFleet.get(o2).getB(); @@ -537,13 +558,14 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } } } else { - // either there are no split fleets or the split isn't relevant for scoring as for ordered fleets + // either there are no split fleets or the split isn't relevant for scoring as for ordered + // fleets // the scoring runs contiguously from top to bottom comparisonResult = 0; } if (comparisonResult == 0) { - comparisonResult = getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false).compare( - totalPointsAndFleet.get(o1).getA(), totalPointsAndFleet.get(o2).getA()); + comparisonResult = getScoringScheme().getScoreComparator(/* nullScoresAreBetter */ false) + .compare(totalPointsAndFleet.get(o1).getA(), totalPointsAndFleet.get(o2).getA()); } } return comparisonResult; @@ -559,11 +581,12 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW public List getCompetitorsFromBestToWorst(TimePoint timePoint) { return getCompetitorsFromBestToWorst(getRaceColumns(), timePoint); } - + /** * suppressed competitors are removed from the result */ - private List getCompetitorsFromBestToWorst(Iterable raceColumnsToConsider, TimePoint timePoint) { + private List getCompetitorsFromBestToWorst(Iterable raceColumnsToConsider, + TimePoint timePoint) { List result = new ArrayList(); for (Competitor competitor : getCompetitors()) { result.add(competitor); @@ -571,9 +594,11 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW Collections.sort(result, getTotalRankComparator(raceColumnsToConsider, timePoint)); return result; } - - protected Comparator getTotalRankComparator(Iterable raceColumnsToConsider, TimePoint timePoint) { - return new LeaderboardTotalRankComparator(this, timePoint, getScoringScheme(), /* nullScoresAreBetter */ false, raceColumnsToConsider); + + protected Comparator getTotalRankComparator(Iterable raceColumnsToConsider, + TimePoint timePoint) { + return new LeaderboardTotalRankComparator(this, timePoint, getScoringScheme(), /* nullScoresAreBetter */ false, + raceColumnsToConsider); } @Override @@ -599,15 +624,16 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } @Override - public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) { + public boolean countRaceForComparisonWithDiscardingThresholds(Competitor competitor, RaceColumn raceColumn, + TimePoint timePoint) { TrackedRace trackedRaceForCompetitorInColumn; - return getScoringScheme().isValidInNetScore(this, raceColumn, competitor, timePoint) && - (getScoreCorrection().isScoreCorrected(competitor, raceColumn, timePoint) || - ((trackedRaceForCompetitorInColumn=raceColumn.getTrackedRace(competitor)) != null && - trackedRaceForCompetitorInColumn.hasStarted(timePoint) && - trackedRaceForCompetitorInColumn.getRank(competitor, timePoint) != 0)); + return getScoringScheme().isValidInNetScore(this, raceColumn, competitor, timePoint) + && (getScoreCorrection().isScoreCorrected(competitor, raceColumn, timePoint) + || ((trackedRaceForCompetitorInColumn = raceColumn.getTrackedRace(competitor)) != null + && trackedRaceForCompetitorInColumn.hasStarted(timePoint) + && trackedRaceForCompetitorInColumn.getRank(competitor, timePoint) != 0)); } - + @Override public void addRaceColumnListener(RaceColumnListener listener) { getRaceColumnListeners().addRaceColumnListener(listener); @@ -619,27 +645,31 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } @Override - public Entry getEntry(final Competitor competitor, final RaceColumn race, final TimePoint timePoint) throws NoWindException { - final Set discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, getRaceColumns(), timePoint); + public Entry getEntry(final Competitor competitor, final RaceColumn race, final TimePoint timePoint) + throws NoWindException { + final Set discardedRaceColumns = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, + getRaceColumns(), timePoint); return getEntry(competitor, race, timePoint, discardedRaceColumns); } - + @Override public Entry getEntry(final Competitor competitor, final RaceColumn race, final TimePoint timePoint, Set discardedRaceColumns) throws NoWindException { - Callable trackedRankProvider = ()->getTrackedRank(competitor, race, timePoint); + Callable trackedRankProvider = () -> getTrackedRank(competitor, race, timePoint); final Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, race, this, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()); boolean discarded = isDiscarded(competitor, race, timePoint, discardedRaceColumns); final Double correctedScore = correctedResults.getCorrectedScore(); - return new EntryImpl(trackedRankProvider, correctedScore, ()->correctedResults.getUncorrectedScore(), - correctedResults.isCorrected(), discarded ? DOUBLE_0 : correctedScore == null ? null - : Double.valueOf(correctedScore * race.getFactor()), correctedResults.getMaxPointsReason(), discarded, - race.getFleetOfCompetitor(competitor)); + return new EntryImpl(trackedRankProvider, correctedScore, () -> correctedResults.getUncorrectedScore(), + correctedResults.isCorrected(), + discarded ? DOUBLE_0 + : correctedScore == null ? null : Double.valueOf(correctedScore * race.getFactor()), + correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor)); } @Override - public Map> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint) throws NoWindException { + public Map> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(TimePoint timePoint) + throws NoWindException { Map> result = new LinkedHashMap<>(); List raceColumnsToConsider = new ArrayList<>(); for (RaceColumn raceColumn : getRaceColumns()) { @@ -663,7 +693,8 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW public Map call() { Map netPointsSumPerCompetitorInColumn = new HashMap<>(); for (Competitor competitor : getCompetitors()) { - netPointsSumPerCompetitorInColumn.put(competitor, getNetPoints(competitor, finalRaceColumnsToConsider, timePoint)); + netPointsSumPerCompetitorInColumn.put(competitor, + getNetPoints(competitor, finalRaceColumnsToConsider, timePoint)); } synchronized (result) { return netPointsSumPerCompetitorInColumn; @@ -686,27 +717,29 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } @Override - public Map, Entry> getContent(final TimePoint timePoint) throws NoWindException { + public Map, Entry> getContent(final TimePoint timePoint) + throws NoWindException { Map, Entry> result = new HashMap, Entry>(); Map> discardedRaces = new HashMap>(); for (final RaceColumn raceColumn : getRaceColumns()) { for (final Competitor competitor : getCompetitors()) { - Callable trackedRankProvider = ()->getTrackedRank(competitor, raceColumn, timePoint); - final Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, raceColumn, - this, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()); + Callable trackedRankProvider = () -> getTrackedRank(competitor, raceColumn, timePoint); + final Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, + raceColumn, this, timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme()); Set discardedRacesForCompetitor = discardedRaces.get(competitor); if (discardedRacesForCompetitor == null) { - discardedRacesForCompetitor = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, getRaceColumns(), timePoint); + discardedRacesForCompetitor = getResultDiscardingRule().getDiscardedRaceColumns(competitor, this, + getRaceColumns(), timePoint); discardedRaces.put(competitor, discardedRacesForCompetitor); } boolean discarded = discardedRacesForCompetitor.contains(raceColumn); final Double correctedScore = correctedResults.getCorrectedScore(); Entry entry = new EntryImpl(trackedRankProvider, correctedScore, - ()->correctedResults.getUncorrectedScore(), - correctedResults.isCorrected(), - discarded ? DOUBLE_0 : (correctedScore==null?null: - Double.valueOf((correctedScore * raceColumn.getFactor()))), correctedResults.getMaxPointsReason(), - discarded, raceColumn.getFleetOfCompetitor(competitor)); + () -> correctedResults.getUncorrectedScore(), correctedResults.isCorrected(), + discarded ? DOUBLE_0 + : (correctedScore == null ? null + : Double.valueOf((correctedScore * raceColumn.getFactor()))), + correctedResults.getMaxPointsReason(), discarded, raceColumn.getFleetOfCompetitor(competitor)); result.put(new com.sap.sse.common.Util.Pair(competitor, raceColumn), entry); } } @@ -714,7 +747,8 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } /** - * A leaderboard will only accept the addition of a race column if the column's name is unique across the leaderboard. + * A leaderboard will only accept the addition of a race column if the column's name is unique across the + * leaderboard. */ @Override public boolean canAddRaceColumnToContainer(RaceColumn newRaceColumn) { @@ -731,17 +765,18 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW /** * Finds out the time point when any of the {@link Leaderboard#getTrackedRaces() tracked races currently attached to * the leaderboard} and the {@link Leaderboard#getScoreCorrection() score corrections} have last been - * modified. If no tracked race is attached and no time-stamped score corrections have been applied to the leaderboard, - * null is returned. The time point computed this way is a good choice for normalizing queries for later time - * points in an attempt to achieve more cache hits.

+ * modified. If no tracked race is attached and no time-stamped score corrections have been applied to the + * leaderboard, null is returned. The time point computed this way is a good choice for normalizing + * queries for later time points in an attempt to achieve more cache hits. + *

* - * Note, however, that the result does not tell about structural changes to the leaderboard and therefore cannot be used - * to determine the need for cache invalidation. For example, if a column is added to a leaderboard after the time point - * returned by this method but that column's attached tracked race has finished before the time point returned by this method, - * the result of this method won't change. Still, the contents of the leaderboard will change by a change in column structure. - * A different means to determine the possibility of changes that happened to this leaderboard must be used for cache - * management. Such a facility has to listen for score correction changes, tracked races being attached or detached and - * the column structure changing. + * Note, however, that the result does not tell about structural changes to the leaderboard and therefore cannot be + * used to determine the need for cache invalidation. For example, if a column is added to a leaderboard after the + * time point returned by this method but that column's attached tracked race has finished before the time point + * returned by this method, the result of this method won't change. Still, the contents of the leaderboard will + * change by a change in column structure. A different means to determine the possibility of changes that happened + * to this leaderboard must be used for cache management. Such a facility has to listen for score correction + * changes, tracked races being attached or detached and the column structure changing. * * @see TrackedRace#getTimePointOfNewestEvent() * @see SettableScoreCorrection#getTimePointOfLastCorrectionsValidity() @@ -750,19 +785,22 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW public TimePoint getTimePointOfLatestModification() { TimePoint result = null; for (TrackedRace trackedRace : getTrackedRaces()) { - if (result == null || (trackedRace.getTimePointOfNewestEvent() != null && trackedRace.getTimePointOfNewestEvent().after(result))) { + if (result == null || (trackedRace.getTimePointOfNewestEvent() != null + && trackedRace.getTimePointOfNewestEvent().after(result))) { result = trackedRace.getTimePointOfNewestEvent(); } } TimePoint timePointOfLastScoreCorrection = getScoreCorrection().getTimePointOfLastCorrectionsValidity(); - if (timePointOfLastScoreCorrection != null && (result == null || timePointOfLastScoreCorrection.after(result))) { + if (timePointOfLastScoreCorrection != null + && (result == null || timePointOfLastScoreCorrection.after(result))) { result = timePointOfLastScoreCorrection; } return result; } @Override - public com.sap.sse.common.Util.Pair getMaximumSpeedOverGround(Competitor competitor, TimePoint timePoint) { + public com.sap.sse.common.Util.Pair getMaximumSpeedOverGround(Competitor competitor, + TimePoint timePoint) { com.sap.sse.common.Util.Pair result = null; // TODO should we ensure that competitor participated in all race columns? for (TrackedRace trackedRace : getTrackedRaces()) { @@ -771,16 +809,17 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW if (!markPassings.isEmpty()) { TimePoint from = markPassings.first().getTimePoint(); TimePoint to; - if (timePoint.after(markPassings.last().getTimePoint()) && - markPassings.last().getWaypoint() == trackedRace.getRace().getCourse().getLastWaypoint()) { + if (timePoint.after(markPassings.last().getTimePoint()) && markPassings.last() + .getWaypoint() == trackedRace.getRace().getCourse().getLastWaypoint()) { // stop counting when competitor finished the race to = markPassings.last().getTimePoint(); } else { to = timePoint; } - com.sap.sse.common.Util.Pair maxSpeed = trackedRace.getTrack(competitor).getMaximumSpeedOverGround(from, to); - if (result == null || result.getB() == null || - (maxSpeed != null && maxSpeed.getB() != null && maxSpeed.getB().compareTo(result.getB()) > 0)) { + com.sap.sse.common.Util.Pair maxSpeed = trackedRace.getTrack(competitor) + .getMaximumSpeedOverGround(from, to); + if (result == null || result.getB() == null || (maxSpeed != null && maxSpeed.getB() != null + && maxSpeed.getB().compareTo(result.getB()) > 0)) { result = maxSpeed; } } @@ -799,7 +838,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } return result; } - + @Override public Iterable getCompetitors() { final Iterable result; @@ -815,10 +854,11 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } return result; } - + @Override public Iterable getCompetitors(RaceColumn raceColumn, Fleet fleet) { - return getCompetitorIterableSkippingSuppressed(getAllCompetitors(raceColumn, fleet), getSuppressedCompetitors()); + return getCompetitorIterableSkippingSuppressed(getAllCompetitors(raceColumn, fleet), + getSuppressedCompetitors()); } /** @@ -838,7 +878,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW LockUtil.unlockAfterRead(suppressedCompetitorsLock); } } - + @Override public boolean isSuppressed(Competitor competitor) { LockUtil.lockForRead(suppressedCompetitorsLock); @@ -848,12 +888,12 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW LockUtil.unlockAfterRead(suppressedCompetitorsLock); } } - + @Override public void setSuppressed(Competitor competitor, boolean suppressed) { - if (competitor == null) { - throw new IllegalArgumentException("Cannot change suppression for a null competitor"); - } + if (competitor == null) { + throw new IllegalArgumentException("Cannot change suppression for a null competitor"); + } LockUtil.lockForWrite(suppressedCompetitorsLock); try { if (suppressed) { @@ -874,7 +914,7 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW TimePoint timePoint = delayToLiveInMillis == null ? now : now.minus(delayToLiveInMillis); return timePoint; } - + @Override public void raceLogEventAdded(RaceColumn raceColumn, RaceLogIdentifier raceLogIdentifier, RaceLogEvent event) { getRaceColumnListeners().notifyListenersAboutRaceLogEventAdded(raceColumn, raceLogIdentifier, event); @@ -887,14 +927,16 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW } public String toString() { - return getName() + " " + (getDefaultCourseArea() != null ? getDefaultCourseArea().getName() : "") + " " + (getScoringScheme() != null ? getScoringScheme().getType().name() : ""); + return getName() + " " + + (getDefaultCourseArea() != null ? getDefaultCourseArea().getName() : "") + " " + + (getScoringScheme() != null ? getScoringScheme().getType().name() : ""); } @Override public NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher() { return new NumberOfCompetitorsFetcherImpl(); } - + @Override public Pair getRaceColumnAndFleet(TrackedRace trackedRace) { for (final RaceColumn raceColumn : getRaceColumns()) { @@ -914,8 +956,8 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW Map competitorsAndTheirBoats = raceColumn.getAllCompetitorsAndTheirBoats(); allBoats.addAll(competitorsAndTheirBoats.values()); } - return Util.getDominantObject(StreamSupport.stream(allBoats.spliterator(), /* parallel */ false). - map(b->b.getBoatClass()).collect(Collectors.toList())); + return Util.getDominantObject(StreamSupport.stream(allBoats.spliterator(), /* parallel */ false) + .map(b -> b.getBoatClass()).collect(Collectors.toList())); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/impl/GPSFixStoreImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/impl/GPSFixStoreImpl.java deleted file mode 100755 index c16c669fe70..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/impl/GPSFixStoreImpl.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.sap.sailing.domain.racelog.impl; - -import java.util.List; -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; - -import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; -import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder; -import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMarkMappingFinder; -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.Mark; -import com.sap.sailing.domain.common.DeviceIdentifier; -import com.sap.sailing.domain.common.racelog.tracking.TransformationException; -import com.sap.sailing.domain.common.tracking.GPSFix; -import com.sap.sailing.domain.common.tracking.GPSFixMoving; -import com.sap.sailing.domain.racelog.tracking.GPSFixStore; -import com.sap.sailing.domain.racelog.tracking.SensorFixStore; -import com.sap.sailing.domain.racelogtracking.DeviceMapping; -import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; -import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; -import com.sap.sse.common.NoCorrespondingServiceRegisteredException; -import com.sap.sse.common.TimePoint; -import com.sap.sse.common.Util; - -/** - * At the moment, the timerange covered by the fixes for a device, and the number of fixes for a device are stored in a - * metadata collection. Should be changed, see bug 1982. - * - * @author Fredrik Teschke - */ -public class GPSFixStoreImpl implements GPSFixStore { - private final SensorFixStore sensorFixStore; - - public GPSFixStoreImpl(SensorFixStore sensorFixStore) { - this.sensorFixStore = sensorFixStore; - } - - private void loadTrack(DynamicGPSFixTrack track, DeviceIdentifier device, - TimePoint from, TimePoint to, boolean toIsInclusive) - throws NoCorrespondingServiceRegisteredException, TransformationException { - loadTrack(track, device, from, to, toIsInclusive, () -> { - return false; - }, (c) -> { - }); - } - - private void loadTrack(DynamicGPSFixTrack track, DeviceIdentifier device, - TimePoint from, TimePoint to, boolean toIsInclusive, BooleanSupplier isPreemptiveStopped, - Consumer progress) - throws NoCorrespondingServiceRegisteredException, TransformationException { - sensorFixStore. loadFixes(fix -> track.add(fix, /* replace */ true), device, from, to, toIsInclusive, - isPreemptiveStopped, progress); - } - - @Override - public void loadCompetitorTrack(DynamicGPSFixTrack track, RegattaLog log, - Competitor competitor) throws NoCorrespondingServiceRegisteredException, TransformationException { - List> mappings = new RegattaLogDeviceCompetitorMappingFinder(log) - .analyze().get(competitor); - if (mappings != null) { - for (DeviceMapping mapping : mappings) { - loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), - false /* toIsInclusive */); - } - } - } - - @Override - public void loadMarkTrack(DynamicGPSFixTrack track, RegattaLog log, Mark mark) - throws NoCorrespondingServiceRegisteredException, TransformationException { - List> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze() - .get(mark); - if (mappings != null) { - for (DeviceMapping mapping : mappings) { - loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), - false /* toIsInclusive */); - } - } - } - - @Override - public void storeFix(DeviceIdentifier device, GPSFix fix) { - sensorFixStore.storeFix(device, fix); - } - - @Override - public void loadCompetitorTrack(DynamicGPSFixTrack track, - DeviceMapping mapping, TimePoint start, TimePoint end, BooleanSupplier isPreemptiveStopped, - Consumer progressReporter) - throws TransformationException, NoCorrespondingServiceRegisteredException { - - // loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true - // /*inclusive*/); - final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from()); - final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to()); - loadTrack(track, mapping.getDevice(), from, to, true /* inclusive */, isPreemptiveStopped, progressReporter); - } - - @Override - public void loadMarkTrack(DynamicGPSFixTrack track, DeviceMapping mapping, TimePoint start, - TimePoint end, BooleanSupplier isPreemptiveStopped, Consumer progressReport) - throws TransformationException, NoCorrespondingServiceRegisteredException { - final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from()); - final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to()); - loadTrack(track, mapping.getDevice(), from, to, true /* inclusive */, isPreemptiveStopped, progressReport); - } - - @Override - public void loadMarkTrack(DynamicGPSFixTrack track, RegattaLog log, Mark mark, TimePoint start, - TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException { - List> mappings = new RegattaLogDeviceMarkMappingFinder(log).analyze() - .get(mark); - if (mappings != null) { - for (DeviceMapping mapping : mappings) { - final TimePoint from = Util.getLatestOfTimePoints(start, mapping.getTimeRange().from()); - final TimePoint to = Util.getEarliestOfTimePoints(end, mapping.getTimeRange().to()); - loadTrack(track, mapping.getDevice(), from, to, false /* toIsInclusive */); - } - } - } -} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/EmptyGPSFixStore.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/EmptyGPSFixStore.java deleted file mode 100644 index 58a500bbcb1..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/EmptyGPSFixStore.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sap.sailing.domain.racelog.tracking; - -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; - -import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.Mark; -import com.sap.sailing.domain.common.DeviceIdentifier; -import com.sap.sailing.domain.common.racelog.tracking.TransformationException; -import com.sap.sailing.domain.common.tracking.GPSFix; -import com.sap.sailing.domain.common.tracking.GPSFixMoving; -import com.sap.sailing.domain.racelogtracking.DeviceMapping; -import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; -import com.sap.sse.common.NoCorrespondingServiceRegisteredException; -import com.sap.sse.common.TimePoint; - -public enum EmptyGPSFixStore implements GPSFixStore { - INSTANCE; - - @Override - public void storeFix(DeviceIdentifier device, GPSFix fix) { - } - - @Override - public void loadCompetitorTrack(DynamicGPSFixTrack track, - RegattaLog log, Competitor competitor) { - } - - @Override - public void loadMarkTrack(DynamicGPSFixTrack track, - RegattaLog log, Mark mark) { - } - - @Override - public void loadMarkTrack(DynamicGPSFixTrack track, RegattaLog log, Mark mark, - TimePoint start, TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException { - } - - @Override - public void loadCompetitorTrack(DynamicGPSFixTrack track, - DeviceMapping mapping, TimePoint start, TimePoint end, BooleanSupplier isPreemptiveStopped, - Consumer progressReporter) - throws TransformationException, NoCorrespondingServiceRegisteredException { - - } - - @Override - public void loadMarkTrack(DynamicGPSFixTrack track, DeviceMapping mapping, TimePoint start, - TimePoint end, BooleanSupplier isPreemptiveStopped, Consumer progressReporter) - throws TransformationException, NoCorrespondingServiceRegisteredException { - - } -} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/GPSFixStore.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/GPSFixStore.java deleted file mode 100644 index b83eabef87f..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/racelog/tracking/GPSFixStore.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.sap.sailing.domain.racelog.tracking; - -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; - -import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; -import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent; -import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent; -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.Mark; -import com.sap.sailing.domain.common.DeviceIdentifier; -import com.sap.sailing.domain.common.racelog.tracking.TransformationException; -import com.sap.sailing.domain.common.tracking.GPSFix; -import com.sap.sailing.domain.common.tracking.GPSFixMoving; -import com.sap.sailing.domain.racelogtracking.DeviceMapping; -import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; -import com.sap.sse.common.NoCorrespondingServiceRegisteredException; -import com.sap.sse.common.TimePoint; - - -public interface GPSFixStore { - /** - * Load all fixes that correspond to the {@link RegattaLogDeviceCompetitorMappingEvent}s found in the {@code raceLog}. - */ - void loadCompetitorTrack(DynamicGPSFixTrack track, RegattaLog log, - Competitor competitor) throws TransformationException; - - /** - * Load all fixes that correspond to the {@link RegattaLogDeviceMarkMappingEvent}s found in the {@code raceLog}. - */ - void loadMarkTrack(DynamicGPSFixTrack track, RegattaLog log, Mark mark) throws TransformationException, - NoCorrespondingServiceRegisteredException; - - /** - * Load all fixes within the start and end time point (inclusive) that correspond to the - * {@link RegattaLogDeviceMarkMappingEvent}s found in the {@code raceLog}. - * - * @param start - * if null, the start of the time range for which to load fixes is only constrained by the - * device mapping intervals - * @param end - * if null, the end of the time range for which to load fixes is only constrained by the - * device mapping intervals - */ - void loadMarkTrack(DynamicGPSFixTrack track, RegattaLog log, Mark mark, TimePoint start, - TimePoint end) throws TransformationException, NoCorrespondingServiceRegisteredException; - - void loadCompetitorTrack(DynamicGPSFixTrack track, DeviceMapping mapping, - TimePoint start, TimePoint end, BooleanSupplier isPreemptiveStopped, Consumer progressReporter) - throws TransformationException, NoCorrespondingServiceRegisteredException; - - /** - * Load all fixes that correspond to the {@code mapping}. - */ - void loadMarkTrack(DynamicGPSFixTrack track, DeviceMapping mapping, TimePoint start, - TimePoint end, BooleanSupplier isPreemptiveStopped, Consumer progressReport) - throws TransformationException, - NoCorrespondingServiceRegisteredException; - - void storeFix(DeviceIdentifier device, GPSFix fix) throws TransformationException, NoCorrespondingServiceRegisteredException; -} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java new file mode 100644 index 00000000000..221d4ec164c --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharding/ShardingContext.java @@ -0,0 +1,121 @@ +package com.sap.sailing.domain.sharding; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.sap.sailing.domain.common.sharding.ShardingType; +import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater; + +/** + * The sharding context is used to store and verify contraints for a thread. + * + * This is currently used to verify if a given thread, that is explicitly expected to only + * trigger the calculation of a given leaderboard, does trigger the calculation of further/ other + * leaderboards. + * + * It is also used to log warnings if a leaderboard is calculated without predefining the thread constraint. + * + * The enum {@link ShardingType} lists the known sharding types. Each sharding type defines a prefix that will + * be used to identify the sharding type from the request, as implemented in {@link #identifyAndSetShardingConstraint(String)}. + * + * By calling {@link #setShardingConstraint(ShardingType, String)} it is possible to directly set a thread constraint. + * This is useful in background computing code, as in {@link LiveLeaderboardUpdater} + * + */ +public class ShardingContext { + private static final Logger logger = Logger.getLogger(ShardingContext.class.getName()); + + private static final ConcurrentMap> shardingMap = new ConcurrentHashMap<>(); + + /** + * Sets the sharding context. The type of sharding will be identified by the prefix. + * + * @param shardingInfo + */ + public static ShardingType identifyAndSetShardingConstraint(String shardingInfo) { + if (shardingInfo == null || shardingInfo.isEmpty()) { + logger.warning("Empty sharding constraint"); + return null; + } + ThreadLocal identifiedShardingHolder = null; + for (ShardingType shardingType : ShardingType.values()) { + if (shardingInfo.startsWith(shardingType.getPrefix())) { + identifiedShardingHolder = shardingMap.computeIfAbsent(shardingType, t -> new ThreadLocal<>()); + checkAndSetShardingInfo(shardingType, shardingInfo, identifiedShardingHolder); + return shardingType; + } + } + if (identifiedShardingHolder == null) { + logger.warning("Could not identify sharding type for: " + shardingInfo); + } + return null; + } + + /** + * Provide sharding constraint for given sharding type. + * + * @param shardingType + * @param shardingInfo + */ + public static void setShardingConstraint(ShardingType shardingType, String shardingInfo) { + final String encodedShardingInfo = shardingType.encodeIfNeeded(shardingInfo); + ThreadLocal shardingHolder = shardingMap.computeIfAbsent(shardingType, t -> new ThreadLocal<>()); + checkAndSetShardingInfo(shardingType, encodedShardingInfo, shardingHolder); + } + + /** + * Check if provided sharding information name equals shard constraint. + * + * If the information is null or empty, we log a warning. + * If shard does not have a constraint, we log a warning. If + * the information provided does not match information previously stored in shard, + * we make a severe log entry. + * + * @param shardingInfo + */ + public static void checkConstraint(final ShardingType type, final String shardingInfo) { + if (shardingInfo == null || shardingInfo.isEmpty()) { + logger.warning("Empty sharding constraint"); + return; + } + final ThreadLocal shardingHolder = shardingMap.get(type); + if (shardingHolder == null) { + logger.log(Level.WARNING, "No current sharding context set for " + type.name(), new RuntimeException()); + return; + } + String currentShardingInfo = shardingHolder.get(); + if (currentShardingInfo == null || currentShardingInfo.isEmpty()) { + logger.warning("No current sharding constraint for " + type.name()); + return; + } + final String encodedShardingInfo = type.encodeIfNeeded(shardingInfo); + if (!encodedShardingInfo.equals(currentShardingInfo)) { + logger.log(Level.SEVERE, "Current sharding constraint vialation for " + type.name() + ". Got " + + shardingInfo + ", shard requires " + currentShardingInfo, new RuntimeException()); + return; + } + } + + public static void clearShardingConstraint(ShardingType type) { + ThreadLocal shardingHolder = shardingMap.get(type); + if (shardingHolder != null) { + shardingHolder.remove(); + } + } + + private static void checkAndSetShardingInfo(ShardingType shardingType, String encodedShardingInfo, + ThreadLocal shardingHolder) { + if (encodedShardingInfo == null || encodedShardingInfo.isEmpty()) { + logger.log(Level.SEVERE, "Cannot set empty sharding information for " + shardingType.name(), + new RuntimeException()); + } + String currentShardingInfo = shardingHolder.get(); + if (currentShardingInfo == null) { + shardingHolder.set(encodedShardingInfo); + } else if (!encodedShardingInfo.equals(currentShardingInfo)) { + logger.log(Level.SEVERE, "Switching shard constraint for " + shardingType.name()+". Got <<" + encodedShardingInfo +">>, expeted <<"+ currentShardingInfo+">>", new RuntimeException()); + } + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index 6120df5646a..c598d23b01d 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -49,7 +49,7 @@ import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuse import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; -import com.sap.sailing.domain.racelog.tracking.GPSFixStore; +import com.sap.sailing.domain.racelog.tracking.SensorFixStore; import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache; @@ -773,7 +773,7 @@ public interface TrackedRace extends Serializable, IsManagedByCache + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF index ed917794a7e..06fcd8d9c08 100644 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -77,6 +77,7 @@ TrackLifecycle: true Export-Package: com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.confidence.impl, com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.impl, com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.tracking.impl, + com.sap.sailing.gwt.common.communication.routing, com.sap.sailing.gwt.home.communication, com.sap.sailing.gwt.home.server, com.sap.sailing.gwt.server, diff --git a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml index 8538c7b0804..7f9c9641021 100755 --- a/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml +++ b/java/com.sap.sailing.gwt.ui/WEB-INF/web.xml @@ -70,6 +70,7 @@ SailingService /service/sailing + /service/sailing/* @@ -90,6 +91,7 @@ sailingDispatch /service/dispatch + /service/dispatch/* diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/app/AutoPlayClientFactoryBase.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/app/AutoPlayClientFactoryBase.java index 0c865358e6e..242ea7d5c6a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/app/AutoPlayClientFactoryBase.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/app/AutoPlayClientFactoryBase.java @@ -1,16 +1,20 @@ package com.sap.sailing.gwt.autoplay.client.app; +import java.util.HashMap; +import java.util.Map; + import com.google.gwt.core.client.GWT; import com.google.gwt.place.shared.Place; import com.google.gwt.place.shared.PlaceController; import com.google.gwt.user.client.rpc.ServiceDefTarget; import com.google.web.bindery.event.shared.EventBus; import com.sap.sailing.gwt.autoplay.client.places.autoplaystart.AutoPlayStartPlace; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sailing.gwt.ui.client.MediaService; import com.sap.sailing.gwt.ui.client.MediaServiceAsync; import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants; -import com.sap.sailing.gwt.ui.client.SailingService; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.SailingServiceHelper; import com.sap.sse.gwt.client.EntryPointHelper; import com.sap.sse.security.ui.client.SecureClientFactoryImpl; @@ -20,11 +24,13 @@ public abstract class AutoPlayClientFactoryBase private final MediaServiceAsync mediaService; private final AutoPlayPlaceNavigator navigator; + private final Map services = new HashMap<>(); + public AutoPlayClientFactoryBase(ApplicationTopLevelView root, EventBus eventBus, PlaceController placeController, AutoPlayPlaceNavigator navigator) { super(root, eventBus, placeController); this.navigator = navigator; - sailingService = GWT.create(SailingService.class); + sailingService = SailingServiceHelper.createSailingServiceInstance(); mediaService = GWT.create(MediaService.class); EntryPointHelper.registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.sailingServiceRemotePath); @@ -39,8 +45,25 @@ public abstract class AutoPlayClientFactoryBase @Override public SailingServiceAsync getSailingService() { + if (isConfigured()) { + return getSailingService(() -> getAutoPlayCtxSignalError().getContextDefinition().getLeaderboardName()); + } return sailingService; } + + @Override + public SailingServiceAsync getSailingService(ProvidesLeaderboardRouting routingProvider) { + if (routingProvider == null) { + return sailingService; + } else { + SailingServiceAsync sailingServiceAsync = services.get(routingProvider.routingSuffixPath()); + if (sailingServiceAsync == null) { + sailingServiceAsync = SailingServiceHelper.createSailingServiceInstance(routingProvider); + services.put(routingProvider.routingSuffixPath(), sailingServiceAsync); + } + return sailingServiceAsync; + } + } @Override public MediaServiceAsync getMediaService() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/AutoPlayConfiguration.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/AutoPlayConfiguration.java index e3c631638db..349d9ba9ac7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/AutoPlayConfiguration.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/AutoPlayConfiguration.java @@ -1,10 +1,13 @@ package com.sap.sailing.gwt.autoplay.client.configs; +import com.google.gwt.http.client.UrlBuilder; import com.google.gwt.user.client.Window; import com.sap.sailing.gwt.autoplay.client.app.AutoPlayClientFactory; import com.sap.sailing.gwt.ui.shared.EventDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings; +import com.sap.sse.gwt.settings.SettingsToUrlSerializer; +import com.sap.sse.gwt.settings.UrlBuilderUtil; import com.sap.sse.security.ui.client.UserService; public abstract class AutoPlayConfiguration { @@ -19,20 +22,28 @@ public abstract class AutoPlayConfiguration { OnSettingsCallback holder, PerspectiveCompositeSettings settings, AutoPlayContextDefinition apcd, UserService userService) { Window.alert("This configuration does not have settings"); - } /** * This method should be pure/standalone, it is not allowed to make any guesses on fields written by startRootNode, * as it can be called before that */ - public void loadSettingsDefault(EventDTO selectedEvent, StrippedLeaderboardDTO leaderboard, UserService userService, - OnSettingsCallback holder) { - holder.newSettings(null); + public void loadSettingsDefault(EventDTO selectedEvent, AutoPlayContextDefinition apcd, + StrippedLeaderboardDTO leaderboard, UserService userService, OnSettingsCallback holder) { + holder.newSettings(null, getUrlWithSettings(apcd, null)); + } + + private String getUrlWithSettings(AutoPlayContextDefinition apcd, PerspectiveCompositeSettings settings) { + UrlBuilder urlBuilder = UrlBuilderUtil.createUrlBuilderFromCurrentLocationWithCleanParameters(); + SettingsToUrlSerializer urlSerializer = new SettingsToUrlSerializer(); + if (settings != null) { + urlSerializer.serializeSettingsMapToUrlBuilder(settings, urlBuilder); + } + urlSerializer.serializeToUrlBuilder(apcd, urlBuilder); + return urlBuilder.buildString(); } public interface OnSettingsCallback { - void newSettings(PerspectiveCompositeSettings newSettings); + void newSettings(PerspectiveCompositeSettings newSettings, String urlWithSettings); } - } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/impl/AutoPlayClassicConfiguration.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/impl/AutoPlayClassicConfiguration.java index bc1cd20e127..ec9dade106a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/impl/AutoPlayClassicConfiguration.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/configs/impl/AutoPlayClassicConfiguration.java @@ -26,58 +26,81 @@ import com.sap.sse.security.ui.client.UserService; public class AutoPlayClassicConfiguration extends AutoPlayConfiguration { private static final Logger logger = Logger.getLogger(AutoPlayClassicConfiguration.class.getName()); - + @Override public void startRootNode(AutoPlayClientFactory cf, AutoPlayContextDefinition context, PerspectiveCompositeSettings settings, EventDTO initialEventData) { - cf.getSailingService().getAvailableDetailTypesForLeaderboard(context.getLeaderboardName(), new AsyncCallback>() { - @Override - public void onFailure(Throwable caught) { - logger.log(Level.WARNING, "Could not load detailtypes for leaderboard", caught); - } + cf.getSailingService().getAvailableDetailTypesForLeaderboard(context.getLeaderboardName(), + null, new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + logger.log(Level.WARNING, "Could not load detailtypes for leaderboard", caught); + } - @SuppressWarnings("unchecked") - @Override - public void onSuccess(Iterable result) { - StrippedLeaderboardDTO leaderBoardDTO = AutoplayHelper.getSelectedLeaderboard(initialEventData, - context.getLeaderboardName()); - AutoplayPerspectiveLifecycle autoplayLifecycle = new AutoplayPerspectiveLifecycle(leaderBoardDTO, cf.getUserService(), result); - cf.setAutoPlayContext(new AutoPlayContextImpl(autoplayLifecycle, - (PerspectiveCompositeSettings) settings, - AutoPlayClassicConfiguration.this, context, initialEventData)); - // start sixty inch slide loop nodes... - RootNodeClassic root = new RootNodeClassic(cf); - root.start(cf.getEventBus()); - } - }); + @SuppressWarnings("unchecked") + @Override + public void onSuccess(Iterable result) { + StrippedLeaderboardDTO leaderBoardDTO = AutoplayHelper.getSelectedLeaderboard(initialEventData, + context.getLeaderboardName()); + AutoplayPerspectiveLifecycle autoplayLifecycle = new AutoplayPerspectiveLifecycle( + leaderBoardDTO, cf.getUserService(), result); + cf.setAutoPlayContext(new AutoPlayContextImpl(autoplayLifecycle, + (PerspectiveCompositeSettings) settings, + AutoPlayClassicConfiguration.this, context, initialEventData)); + // start sixty inch slide loop nodes... + RootNodeClassic root = new RootNodeClassic(cf); + root.start(cf.getEventBus()); + } + }); } @Override public void openSettingsDialog(EventDTO selectedEvent, StrippedLeaderboardDTO leaderboard, - OnSettingsCallback settingsCallback, PerspectiveCompositeSettings settings, AutoPlayContextDefinition apcd, UserService userService) { + OnSettingsCallback settingsCallback, PerspectiveCompositeSettings settings, + AutoPlayContextDefinition apcd, UserService userService) { DialogCallback> callback = new DialogCallback>() { @Override public void ok(PerspectiveCompositeSettings editedObject) { - settingsCallback.newSettings(editedObject); + settingsCallback.newSettings(editedObject, + getUrlWithSettings(apcd, editedObject, leaderboard, userService)); } @Override public void cancel() { } }; - AutoplayPerspectiveLifecycle autoplayLifecycle = new AutoplayPerspectiveLifecycle(leaderboard, userService, Arrays.asList(DetailType.values())); - PerspectiveCompositeSettings autoplayPerspectiveSettings = autoplayLifecycle - .createDefaultSettings(); - LinkWithSettingsGenerator> settingsGenerator = new LinkWithSettingsGenerator<>(Window.Location.getPath(), autoplayLifecycle::createDefaultSettings, apcd); - new SettingsDialogForLinkSharing>(settingsGenerator, - autoplayLifecycle, autoplayPerspectiveSettings, StringMessages.INSTANCE, true, callback).show(); + AutoplayPerspectiveLifecycle autoplayLifecycle = new AutoplayPerspectiveLifecycle(leaderboard, userService, + Arrays.asList(DetailType.values())); + @SuppressWarnings("unchecked") + PerspectiveCompositeSettings autoplayPerspectiveSettings = settings != null + ? (PerspectiveCompositeSettings) settings + : autoplayLifecycle.createDefaultSettings(); + LinkWithSettingsGenerator> settingsGenerator = new LinkWithSettingsGenerator<>( + Window.Location.getPath(), autoplayLifecycle::createDefaultSettings, apcd); + new SettingsDialogForLinkSharing>( + settingsGenerator, autoplayLifecycle, autoplayPerspectiveSettings, StringMessages.INSTANCE, true, + callback).show(); } @Override - public void loadSettingsDefault(EventDTO selectedEvent, StrippedLeaderboardDTO leaderboard, - UserService userService, OnSettingsCallback settingsCallback) { - AutoplayPerspectiveLifecycle autoplayLifecycle = new AutoplayPerspectiveLifecycle(leaderboard, userService, Arrays.asList(DetailType.values())); - settingsCallback.newSettings(autoplayLifecycle.createDefaultSettings()); + public void loadSettingsDefault(EventDTO selectedEvent, AutoPlayContextDefinition apcd, + StrippedLeaderboardDTO leaderboard, UserService userService, OnSettingsCallback settingsCallback) { + AutoplayPerspectiveLifecycle autoplayLifecycle = new AutoplayPerspectiveLifecycle(leaderboard, userService, + Arrays.asList(DetailType.values())); + PerspectiveCompositeSettings defaultSettings = autoplayLifecycle + .createDefaultSettings(); + settingsCallback.newSettings(defaultSettings, + getUrlWithSettings(apcd, defaultSettings, leaderboard, userService)); } + private String getUrlWithSettings(AutoPlayContextDefinition apcd, + PerspectiveCompositeSettings settings, StrippedLeaderboardDTO leaderboard, + UserService userService) { + AutoplayPerspectiveLifecycle autoplayLifecycle = new AutoplayPerspectiveLifecycle(leaderboard, userService, + Arrays.asList(DetailType.values())); + LinkWithSettingsGenerator> settingsGenerator = new LinkWithSettingsGenerator<>( + Window.Location.getPath(), autoplayLifecycle::createDefaultSettings, apcd); + + return settingsGenerator.createUrl(settings); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleRaceLeaderboard.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleRaceLeaderboard.java index 0ea917d7fd1..f73c2f82b74 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleRaceLeaderboard.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleRaceLeaderboard.java @@ -17,6 +17,7 @@ import com.sap.sailing.gwt.autoplay.client.utils.AutoplayHelper; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint; +import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.player.Timer.PlayModes; import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings; @@ -35,6 +36,7 @@ public class IdleRaceLeaderboard extends FiresPlaceNode { } public void onStart() { + leaderboardTimer.setTime(MillisecondsTimePoint.now().asMillis()); leaderboardTimer.play(); PerspectiveCompositeSettings settings = cf.getAutoPlayCtxSignalError() .getAutoplaySettings(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleSixtyInchLeaderboardNode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleSixtyInchLeaderboardNode.java index 2b2231c106a..ee1361b1c53 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleSixtyInchLeaderboardNode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/IdleSixtyInchLeaderboardNode.java @@ -93,11 +93,11 @@ public class IdleSixtyInchLeaderboardNode extends FiresPlaceNode { private void startWithLeaderbaord(final MultiRaceLeaderboardSettings leaderboardSettings, CompetitorSelectionProvider provider, String leaderboardName) { - MultiRaceLeaderboardPanel leaderboardPanel = new MultiRaceLeaderboardPanel(null, null, cf.getSailingService(), - new AsyncActionsExecutor(), leaderboardSettings, false, provider, timer, null, leaderboardName, - cf.getErrorReporter(), StringMessages.INSTANCE, false, null, false, null, false, true, false, false, - false, new SixtyInchLeaderboardStyle(true), FlagImageResolverImpl.get(), - Arrays.asList(DetailType.values())); + MultiRaceLeaderboardPanel leaderboardPanel = new MultiRaceLeaderboardPanel(null, null, + cf.getSailingService(() -> leaderboardName), new AsyncActionsExecutor(), leaderboardSettings, false, + provider, timer, null, leaderboardName, cf.getErrorReporter(), StringMessages.INSTANCE, false, null, + false, null, false, true, false, false, false, new SixtyInchLeaderboardStyle(true), + FlagImageResolverImpl.get(), Arrays.asList(DetailType.values())); IdleSixtyInchLeaderboardPlace place = new IdleSixtyInchLeaderboardPlace(leaderboardPanel, provider, durationConsumer); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/LiveRaceBoardNode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/LiveRaceBoardNode.java index c7e5493dc6b..b5bd0f557bd 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/LiveRaceBoardNode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/nodes/LiveRaceBoardNode.java @@ -24,6 +24,7 @@ import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.raceboard.RaceBoardPanel; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO; +import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.player.Timer.PlayModes; import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings; @@ -54,6 +55,8 @@ public class LiveRaceBoardNode extends FiresPlaceNode implements RaceTimesInfoPr } public void onStart() { + raceboardTimer.setTime(MillisecondsTimePoint.now().asMillis()); + raceboardTimer.play(); raceTimesInfoProvider.addRaceIdentifier(cf.getAutoPlayCtxSignalError().getLifeOrPreLiveRace(), true); PerspectiveCompositeSettings settings = cf.getAutoPlayCtxSignalError().getAutoplaySettings(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartPresenterImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartPresenterImpl.java index 69d1a6a2e36..879c748387c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartPresenterImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartPresenterImpl.java @@ -55,10 +55,10 @@ public class AutoPlayStartPresenterImpl extends AbstractActivity implements Auto StrippedLeaderboardDTO leaderBoard = AutoplayHelper.getSelectedLeaderboard(event, apcd.getLeaderboardName()); - apcd.getType().getConfig().loadSettingsDefault(event, leaderBoard, clientFactory.getUserService(), new OnSettingsCallback() { + apcd.getType().getConfig().loadSettingsDefault(event, apcd, leaderBoard, clientFactory.getUserService(), new OnSettingsCallback() { @Override - public void newSettings(PerspectiveCompositeSettings newSettings) { + public void newSettings(PerspectiveCompositeSettings newSettings, String urlWithSettings) { if (newSettings != null) { serializer.deserializeSettingsMapFromCurrentLocation(newSettings); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartViewImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartViewImpl.java index ce8f0b88f03..2e21e7d8da2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartViewImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/places/autoplaystart/AutoPlayStartViewImpl.java @@ -8,7 +8,6 @@ import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.http.client.UrlBuilder; import com.google.gwt.i18n.client.LocaleInfo; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; @@ -30,8 +29,6 @@ import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.GWTLocaleUtil; import com.sap.sse.gwt.client.shared.perspective.PerspectiveCompositeSettings; -import com.sap.sse.gwt.settings.SettingsToUrlSerializer; -import com.sap.sse.gwt.settings.UrlBuilderUtil; public class AutoPlayStartViewImpl extends Composite implements AutoPlayStartView { private static StartPageViewUiBinder uiBinder = GWT.create(StartPageViewUiBinder.class); @@ -151,9 +148,9 @@ public class AutoPlayStartViewImpl extends Composite implements AutoPlayStartVie selectedAutoPlayType.getConfig().openSettingsDialog(selectedEvent, selectedLeaderboard, new OnSettingsCallback() { @Override - public void newSettings(PerspectiveCompositeSettings newSettings) { + public void newSettings(PerspectiveCompositeSettings newSettings, String urlWithSettings) { settings = newSettings; - updateURL(); + updateURL(urlWithSettings); } }, settings, apcd, currentPresenter.getUserService()); } @@ -197,12 +194,12 @@ public class AutoPlayStartViewImpl extends Composite implements AutoPlayStartVie settingsButton.removeStyleName(SharedResources.INSTANCE.mainCss().buttoninactive()); startAutoPlayButton.removeStyleName(SharedResources.INSTANCE.mainCss().buttoninactive()); apcd = new AutoPlayContextDefinitionImpl(selectedAutoPlayType, selectedEvent.id, selectedLeaderboardName); - apcd.getType().getConfig().loadSettingsDefault(selectedEvent, selectedLeaderboard, currentPresenter.getUserService(), + apcd.getType().getConfig().loadSettingsDefault(selectedEvent, apcd, selectedLeaderboard, currentPresenter.getUserService(), new OnSettingsCallback() { @Override - public void newSettings(PerspectiveCompositeSettings newSettings) { + public void newSettings(PerspectiveCompositeSettings newSettings, String urlWithSettings) { settings = newSettings; - updateURL(); + updateURL(urlWithSettings); } }); } @@ -210,16 +207,10 @@ public class AutoPlayStartViewImpl extends Composite implements AutoPlayStartVie return readyToGo; } - private void updateURL() { - UrlBuilder urlBuilder = UrlBuilderUtil.createUrlBuilderFromCurrentLocationWithCleanParameters(); - SettingsToUrlSerializer urlSerializer = new SettingsToUrlSerializer(); - if (settings != null) { - urlSerializer.serializeSettingsMapToUrlBuilder(settings, urlBuilder); - } - urlSerializer.serializeToUrlBuilder(apcd, urlBuilder); - configuratedUrl = urlBuilder.buildString(); - configStarter.setText(configuratedUrl); - configStarter.setHref(configuratedUrl); + private void updateURL(String urlWithSettings) { + configuratedUrl = urlWithSettings; + configStarter.setText(urlWithSettings); + configStarter.setHref(urlWithSettings); } @UiHandler("localeSelectionBox") diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SourceCommon.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SourceCommon.gwt.xml index a20e68ee3dd..f1e0f650ecf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SourceCommon.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SourceCommon.gwt.xml @@ -6,6 +6,8 @@ + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java index c4f83ff28d1..b7ca2ca21a3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResolver.java @@ -45,6 +45,7 @@ public class BoatClassImageResolver { boatClassIconsMap.put(BoatClassMasterdata.CANOE_TAIFUN.getDisplayName(), imageResources.CanoeTaifunIcon()); boatClassIconsMap.put(BoatClassMasterdata.CONTENDER.getDisplayName(), imageResources.ContenderIcon()); boatClassIconsMap.put(BoatClassMasterdata.CC_30.getDisplayName(), imageResources.CC30Icon()); + boatClassIconsMap.put(BoatClassMasterdata.CLUB_SWAN_50.getDisplayName(), imageResources.CLUBSWAN50Icon()); boatClassIconsMap.put(BoatClassMasterdata.DELPHIA_24.getDisplayName(), imageResources.Delphia24Icon()); boatClassIconsMap.put(BoatClassMasterdata.DRAGON_INT.getDisplayName(), imageResources.DragonIcon()); boatClassIconsMap.put(BoatClassMasterdata.DYAS.getDisplayName(), imageResources.DyasIcon()); @@ -58,6 +59,7 @@ public class BoatClassImageResolver { boatClassIconsMap.put(BoatClassMasterdata.FARR_280.getDisplayName(), imageResources.Farr280Icon()); boatClassIconsMap.put(BoatClassMasterdata.FINN.getDisplayName(), imageResources.FinnIcon()); boatClassIconsMap.put(BoatClassMasterdata.FLYING_DUTCHMAN.getDisplayName(), imageResources.FlyingDutchmanIcon()); + boatClassIconsMap.put(BoatClassMasterdata.FLYING_JUNIOR.getDisplayName(), imageResources.FlyingJuniorIcon()); boatClassIconsMap.put(BoatClassMasterdata.FOLKBOAT.getDisplayName(), imageResources.FolkBoatIcon()); boatClassIconsMap.put(BoatClassMasterdata.FUN.getDisplayName(), imageResources.FUNIcon()); boatClassIconsMap.put(BoatClassMasterdata.GC_32.getDisplayName(), imageResources.GC32Icon()); @@ -126,6 +128,7 @@ public class BoatClassImageResolver { boatClassIconsMap.put(BoatClassMasterdata.TOM_28_MAX.getDisplayName(), imageResources.Tom28MaxIcon()); boatClassIconsMap.put(BoatClassMasterdata.TP52.getDisplayName(), imageResources.TP52Icon()); boatClassIconsMap.put(BoatClassMasterdata.TRIAS.getDisplayName(), imageResources.TriasIcon()); + boatClassIconsMap.put(BoatClassMasterdata.VAURIEN.getDisplayName(), imageResources.VaurienIcon()); boatClassIconsMap.put(BoatClassMasterdata.VENT_D_OUEST.getDisplayName(), imageResources.VentdOuestIcon()); boatClassIconsMap.put(BoatClassMasterdata.VIPER_640.getDisplayName(), imageResources.Viper640Icon()); boatClassIconsMap.put(BoatClassMasterdata.VX_ONE.getDisplayName(), imageResources.VXOneIcon()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java index 2855dbbf8db..0270a334a1f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java @@ -132,6 +132,10 @@ public interface BoatClassImageResources extends ClientBundle { @ImageOptions(preventInlining = true) ImageResource FinnIcon(); + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FLYING_JUNIOR.png") + @ImageOptions(preventInlining = true) + ImageResource FlyingJuniorIcon(); + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FLYING_DUTCHMAN.png") @ImageOptions(preventInlining = true) ImageResource FlyingDutchmanIcon(); @@ -396,6 +400,10 @@ public interface BoatClassImageResources extends ClientBundle { @ImageOptions(preventInlining = true) ImageResource CC30Icon(); + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CLUBSWAN50.png") + @ImageOptions(preventInlining = true) + ImageResource CLUBSWAN50Icon(); + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/GP_26.png") @ImageOptions(preventInlining = true) ImageResource GP26Icon(); @@ -431,4 +439,8 @@ public interface BoatClassImageResources extends ClientBundle { @Source("com/sap/sailing/gwt/ui/client/images/boatclass/RUNNING.png") @ImageOptions(preventInlining = true) ImageResource RunningIcon(); + + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/VAURIEN.png") + @ImageOptions(preventInlining = true) + ImageResource VaurienIcon(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/controls/tabbar/TabPanel.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/controls/tabbar/TabPanel.ui.xml index 15379e2d27b..89523c3b333 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/controls/tabbar/TabPanel.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/controls/tabbar/TabPanel.ui.xml @@ -25,6 +25,7 @@ .tabExtension { float: right; + margin-top: .5em; } .tabExtension > * { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/communication/routing/ProvidesLeaderboardRouting.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/communication/routing/ProvidesLeaderboardRouting.java new file mode 100644 index 00000000000..3c224cd0998 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/communication/routing/ProvidesLeaderboardRouting.java @@ -0,0 +1,16 @@ +package com.sap.sailing.gwt.common.communication.routing; + +import com.sap.sailing.domain.common.sharding.ShardingType; +import com.sap.sse.gwt.client.ServiceRoutingProvider; + +/** + * Interface used to provide leaderboard specific routing, see {@link ServiceRoutingProvider} + */ +public interface ProvidesLeaderboardRouting extends ServiceRoutingProvider { + + String getLeaderboardName(); + + default String routingSuffixPath() { + return ShardingType.LEADERBOARDNAME.encodeIfNeeded(getLeaderboardName()); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetCompetitionFormatRacesAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetCompetitionFormatRacesAction.java index 7479b838571..479278ed257 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetCompetitionFormatRacesAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetCompetitionFormatRacesAction.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.home.communication.event; import java.util.UUID; import com.google.gwt.core.shared.GwtIncompatible; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sailing.gwt.home.communication.SailingAction; import com.sap.sailing.gwt.home.communication.SailingDispatchContext; import com.sap.sailing.gwt.home.server.EventActionUtil; @@ -26,7 +27,7 @@ import com.sap.sse.gwt.dispatch.shared.exceptions.DispatchException; *

*/ public class GetCompetitionFormatRacesAction implements - SailingAction>>, IsClientCacheable { + SailingAction>>, IsClientCacheable, ProvidesLeaderboardRouting { private UUID eventId; private String regattaId; @@ -58,4 +59,9 @@ public class GetCompetitionFormatRacesAction implements public void cacheInstanceKey(StringBuilder key) { key.append(eventId).append("_").append(regattaId); } + + @Override + public String getLeaderboardName() { + return regattaId; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetFinishedRacesAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetFinishedRacesAction.java index f634c0ec9b4..36103a132d4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetFinishedRacesAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/communication/event/GetFinishedRacesAction.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.home.communication.event; import java.util.UUID; import com.google.gwt.core.shared.GwtIncompatible; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sailing.gwt.home.communication.SailingAction; import com.sap.sailing.gwt.home.communication.SailingDispatchContext; import com.sap.sailing.gwt.home.server.EventActionUtil; @@ -25,7 +26,7 @@ import com.sap.sse.gwt.dispatch.shared.commands.ResultWithTTL; *

*/ public class GetFinishedRacesAction implements SailingAction>>, - IsClientCacheable { + IsClientCacheable, ProvidesLeaderboardRouting { private UUID eventId; private String regattaId; @@ -59,4 +60,9 @@ public class GetFinishedRacesAction implements SailingAction */ public class GetLiveRacesForRegattaAction implements SailingAction>>, - IsClientCacheable { + IsClientCacheable, ProvidesLeaderboardRouting { private UUID eventId; private String regattaName; @@ -66,4 +67,10 @@ public class GetLiveRacesForRegattaAction implements SailingAction5 minutes. *

*/ -public class GetRegattaStatisticsAction implements SailingAction>, IsClientCacheable { +public class GetRegattaStatisticsAction implements SailingAction>, IsClientCacheable, ProvidesLeaderboardRouting { private UUID eventId; private String regattaId; @@ -53,4 +54,9 @@ public class GetRegattaStatisticsAction implements SailingAction5 minutes for currently running events. *

*/ -public class GetRegattaWithProgressAction implements SailingAction>, IsClientCacheable { +public class GetRegattaWithProgressAction implements SailingAction>, IsClientCacheable, ProvidesLeaderboardRouting { private UUID eventId; private String regattaId; @@ -55,4 +56,9 @@ public class GetRegattaWithProgressAction implements SailingAction */ -public class GetMiniLeaderbordAction implements SailingAction>, IsClientCacheable { +public class GetMiniLeaderbordAction implements SailingAction>, IsClientCacheable, ProvidesLeaderboardRouting { private UUID eventId; private String leaderboardName; private int limit = 0; @@ -74,4 +75,9 @@ public class GetMiniLeaderbordAction implements SailingAction */ -public class GetSixtyInchStatisticAction implements SailingAction { +public class GetSixtyInchStatisticAction implements SailingAction, ProvidesLeaderboardRouting { private static final Logger LOGGER = Logger.getLogger(GetSixtyInchStatisticAction.class.getName()); // transfer as string, to avoid transfering incompatible types private String racename; @@ -95,4 +96,9 @@ public class GetSixtyInchStatisticAction implements SailingAction seriesNavigation, EventListEventSeriesDTO eventSeries) { eventImage.appendChild(new EventTeaserSeriesInfoCorner(seriesNavigation, eventSeries).getElement()); + eventName.setInnerSafeHtml(LongNamesUtil.breakLongName(eventSeries.getDisplayName())); + venue.setInnerText(StringMessages.INSTANCE.lastEvent(event.getLocationOrVenue())); + eventImage.setTitle(StringMessages.INSTANCE.teaserOverallLinkToolTip()); } public void hideImage(boolean hide) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/sailorinfo/SailorInfo.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/sailorinfo/SailorInfo.java index e8f8256c259..734bbc937e8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/sailorinfo/SailorInfo.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/sailorinfo/SailorInfo.java @@ -20,12 +20,12 @@ public class SailorInfo extends Composite { FlowPanel panel = new FlowPanel(); Style panelStyle = panel.getElement().getStyle(); panelStyle.setDisplay(Display.INLINE_BLOCK); + panelStyle.setVerticalAlign(VerticalAlign.BOTTOM); Label sailorInfoLabel = new Label(); sailorInfoLabel.getElement().setInnerHTML(i18n.sailorInfoLongText().replace("\n", "
")); Style labelStyle = sailorInfoLabel.getElement().getStyle(); labelStyle.setDisplay(Display.INLINE_BLOCK); - sailorInfoLabel.getElement().getStyle().setMarginTop(0.5, Unit.EM); sailorInfoLabel.getElement().getStyle().setMarginRight(0.5, Unit.EM); panel.add(sailorInfoLabel); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java index aa8ca601d0f..eaeb6c02a20 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/AbstractEventActivity.java @@ -37,7 +37,6 @@ import com.sap.sailing.gwt.settings.client.raceboard.RaceBoardPerspectiveOwnSett import com.sap.sailing.gwt.settings.client.raceboard.RaceboardContextDefinition; import com.sap.sailing.gwt.settings.client.regattaoverview.RegattaOverviewContextDefinition; import com.sap.sailing.gwt.settings.client.regattaoverview.RegattaRaceStatesSettings; -import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.refresh.ErrorAndBusyClientFactory; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapLifecycle; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings; @@ -83,10 +82,6 @@ public abstract class AbstractEventActivity ex this.timerForClientServerOffset = new Timer(PlayModes.Replay); this.clientFactory = clientFactory; } - - public SailingServiceAsync getSailingService() { - return clientFactory.getSailingService(); - } public SailingDispatchSystem getDispatch() { return clientFactory.getDispatch(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/EventView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/EventView.java index 67df7061b24..130a9419a8a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/EventView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/EventView.java @@ -21,7 +21,6 @@ import com.sap.sailing.gwt.home.shared.places.event.EventDefaultPlace; import com.sap.sailing.gwt.home.shared.places.events.EventsPlace; import com.sap.sailing.gwt.home.shared.places.fakeseries.SeriesDefaultPlace; import com.sap.sailing.gwt.home.shared.places.start.StartPlace; -import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.refresh.ErrorAndBusyClientFactory; import com.sap.sse.gwt.client.player.Timer; @@ -37,7 +36,6 @@ public interface EventView> asyncCallback) { + clientFactory.getSailingService(()-> leaderboardName).getAvailableDetailTypesForLeaderboard(leaderboardName, raceOrNull, asyncCallback); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/EventRegattaView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/EventRegattaView.java index 3ba25f59503..4b6ba6e5061 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/EventRegattaView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/EventRegattaView.java @@ -1,5 +1,8 @@ package com.sap.sailing.gwt.home.desktop.places.event.regatta; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.domain.common.DetailType; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.gwt.home.desktop.places.event.EventView; import com.sap.sailing.gwt.home.desktop.places.event.regatta.overviewtab.RegattaOverviewPlace; import com.sap.sailing.gwt.home.shared.app.PlaceNavigation; @@ -15,5 +18,8 @@ public interface EventRegattaView extends EventView> asyncCallback); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java index cd2674c4483..c68b0705aba 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/RegattaAnalyticsDataManager.java @@ -6,6 +6,7 @@ import com.sap.sailing.gwt.settings.client.leaderboard.MultiRaceLeaderboardSetti import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.DebugIdHelper; import com.sap.sailing.gwt.ui.client.FlagImageResolver; +import com.sap.sailing.gwt.ui.client.SailingClientFactory; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorLeaderboardChart; @@ -31,16 +32,16 @@ public class RegattaAnalyticsDataManager { private final CompetitorSelectionModel competitorSelectionProvider; private final AsyncActionsExecutor asyncActionsExecutor; private final ErrorReporter errorReporter; - private final SailingServiceAsync sailingService; + private final SailingClientFactory sailingCF; private final Timer timer; private final FlagImageResolver flagImageResolver; - public RegattaAnalyticsDataManager(final SailingServiceAsync sailingService, + public RegattaAnalyticsDataManager(final SailingClientFactory sailingCF, AsyncActionsExecutor asyncActionsExecutor, Timer timer, ErrorReporter errorReporter, FlagImageResolver flagImageResolver) { this.flagImageResolver = flagImageResolver; this.competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */true); - this.sailingService = sailingService; + this.sailingCF = sailingCF; this.asyncActionsExecutor = asyncActionsExecutor; this.timer = timer; this.errorReporter = errorReporter; @@ -53,6 +54,7 @@ public class RegattaAnalyticsDataManager { final String leaderboardGroupName, String leaderboardName, boolean showRaceDetails, boolean autoExpandLastRaceColumn, Iterable availableDetailTypes) { if (leaderboardPanel == null) { + SailingServiceAsync sailingService = sailingCF.getSailingService(()-> leaderboardName); leaderboardPanel = new MultiRaceLeaderboardPanel(parent, context, sailingService, asyncActionsExecutor, leaderboardSettings, @@ -68,6 +70,7 @@ public class RegattaAnalyticsDataManager { public MultiCompetitorLeaderboardChart createMultiCompetitorChart(String leaderboardName, DetailType chartDetailType) { if(multiCompetitorChart == null) { + SailingServiceAsync sailingService = sailingCF.getSailingService(()-> leaderboardName); multiCompetitorChart = new MultiCompetitorLeaderboardChart(null, null, sailingService, asyncActionsExecutor, leaderboardName, chartDetailType, competitorSelectionProvider, timer, StringMessages.INSTANCE, false, errorReporter); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/TabletAndDesktopRegattaEventView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/TabletAndDesktopRegattaEventView.java index 2e738b85c8d..3a01b27d22c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/TabletAndDesktopRegattaEventView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/TabletAndDesktopRegattaEventView.java @@ -2,6 +2,7 @@ package com.sap.sailing.gwt.home.desktop.places.event.regatta; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.TextTransform; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -65,7 +66,12 @@ public class TabletAndDesktopRegattaEventView extends Composite implements Event racesTabUi = new RegattaRacesTabView(flagImageResolver); regattaOverviewTabUi = new RegattaOverviewTabView(flagImageResolver); initWidget(uiBinder.createAndBindUi(this)); - + + String sailorsInfoURL = currentPresenter.getEventDTO().getSailorsInfoWebsiteURL(); + if (sailorsInfoURL != null && !sailorsInfoURL.isEmpty()) { + tabPanelUi.addTabExtension(new SailorInfo(sailorsInfoURL)); + } + if(currentPresenter.getEventDTO().getType() == EventType.SERIES) { final PlaceNavigation currentEventSeriesNavigation = currentPresenter.getCurrentEventSeriesNavigation(); Anchor seriesAnchor = new Anchor(i18n.overallLeaderboardSelection()); @@ -82,14 +88,10 @@ public class TabletAndDesktopRegattaEventView extends Composite implements Event seriesAnchor.setStyleName(SharedResources.INSTANCE.mainCss().button()); seriesAnchor.addStyleName(SharedResources.INSTANCE.mainCss().buttonprimary()); Style style = seriesAnchor.getElement().getStyle(); - style.setFontSize(16, Unit.PX); - style.setPadding(0.75, Unit.EM); + style.setPaddingLeft(0.75, Unit.EM); + style.setPaddingRight(0.75, Unit.EM); + style.setTextTransform(TextTransform.UPPERCASE); tabPanelUi.addTabExtension(seriesAnchor); - } else { - String sailorsInfoURL = currentPresenter.getEventDTO().getSailorsInfoWebsiteURL(); - if(sailorsInfoURL != null && ! sailorsInfoURL.isEmpty()) { - tabPanelUi.addTabExtension(new SailorInfo(sailorsInfoURL)); - } } final Iterable spots = currentPresenter.getEventDTO().getAllWindFinderSpotIdsUsedByEvent(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java index 327906f426a..54b514a9f7c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/analyticstab/RegattaCompetitorAnalyticsTabView.java @@ -99,8 +99,8 @@ public class RegattaCompetitorAnalyticsTabView extends SharedLeaderboardRegattaT } }; if (regattaAnalyticsManager.getLeaderboardPanel() == null) { - currentPresenter.getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, - new AsyncCallback>() { + currentPresenter.getAvailableDetailTypesForLeaderboard(leaderboardName, + null, new AsyncCallback>() { @Override public void onSuccess(Iterable result) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java index 2b3d471ba91..588316451ce 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/event/regatta/leaderboardtab/RegattaLeaderboardTabView.java @@ -147,8 +147,8 @@ public class RegattaLeaderboardTabView extends SharedLeaderboardRegattaTabView> callback) { - currentPresenter.getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, - new AsyncCallback>() { + currentPresenter.getAvailableDetailTypesForLeaderboard(leaderboardName, + null, new AsyncCallback>() { @Override public void onSuccess(Iterable result) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java index d2356ebb303..0ffae739c78 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/EventSeriesAnalyticsDataManager.java @@ -3,8 +3,11 @@ package com.sap.sailing.gwt.home.desktop.places.fakeseries; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.function.Function; +import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.domain.common.DetailType; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.gwt.settings.client.leaderboard.MultiCompetitorLeaderboardChartSettings; import com.sap.sailing.gwt.settings.client.leaderboard.MultiRaceLeaderboardSettings; @@ -12,6 +15,7 @@ import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; import com.sap.sailing.gwt.ui.client.DebugIdHelper; import com.sap.sailing.gwt.ui.client.FlagImageResolver; +import com.sap.sailing.gwt.ui.client.SailingClientFactory; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorLeaderboardChart; @@ -42,33 +46,32 @@ public class EventSeriesAnalyticsDataManager { private final CompetitorSelectionModel competitorSelectionProvider; private final AsyncActionsExecutor asyncActionsExecutor; private final ErrorReporter errorReporter; - private final SailingServiceAsync sailingService; + private final SailingClientFactory sailingCF; private final Timer timer; private final int MAX_COMPETITORS_IN_CHART = 30; private final FlagImageResolver flagImageResolver; - public EventSeriesAnalyticsDataManager(final SailingServiceAsync sailingService, + public EventSeriesAnalyticsDataManager(final SailingClientFactory sailingCF, AsyncActionsExecutor asyncActionsExecutor, Timer timer, ErrorReporter errorReporter, FlagImageResolver flagImageResolver) { this.flagImageResolver = flagImageResolver; this.competitorSelectionProvider = new CompetitorSelectionModel(/* hasMultiSelection */true); - this.sailingService = sailingService; + this.sailingCF = sailingCF; this.asyncActionsExecutor = asyncActionsExecutor; this.timer = timer; this.errorReporter = errorReporter; this.overallLeaderboardPanel = null; this.multiCompetitorChart = null; } - - public SailingServiceAsync getSailingService() { - return sailingService; - } public MultiRaceLeaderboardPanel createMultiRaceOverallLeaderboardPanel(Component parent, ComponentContext context, final MultiRaceLeaderboardSettings leaderboardSettings, final String leaderboardGroupName, String leaderboardName, boolean showRaceDetails, boolean autoExpandLastRaceColumn, Iterable availableDetailTypes) { + + if(overallLeaderboardPanel == null) { + SailingServiceAsync sailingService = sailingCF.getSailingService(()-> leaderboardName); overallLeaderboardPanel = new MultiRaceLeaderboardPanel(parent, context, sailingService, asyncActionsExecutor, leaderboardSettings, true, competitorSelectionProvider, timer, leaderboardGroupName, leaderboardName, errorReporter, @@ -84,7 +87,10 @@ public class EventSeriesAnalyticsDataManager { public MultiCompetitorLeaderboardChart createMultiCompetitorChart(Component parent, ComponentContext context, String leaderboardName, DetailType chartDetailType) { + if(multiCompetitorChart == null) { + SailingServiceAsync sailingService = sailingCF.getSailingService(()-> leaderboardName); + multiCompetitorChart = new MultiCompetitorLeaderboardChart(parent, context, sailingService, asyncActionsExecutor, leaderboardName, chartDetailType, @@ -98,8 +104,10 @@ public class EventSeriesAnalyticsDataManager { MultiRaceLeaderboardSettings leaderboardSettings, String preselectedLeaderboardName, String leaderboardGroupName, String metaLeaderboardName, boolean showRaceDetails, boolean autoExpandLastRaceColumn, Iterable availableDetailTypes) { - if(multiLeaderboardPanel == null) { - multiLeaderboardPanel = new MultiLeaderboardProxyPanel(parent, context, sailingService, metaLeaderboardName, + if (multiLeaderboardPanel == null) { + final Function sailingServiceFactory = leaderBoardName -> sailingCF + .getSailingService(() -> leaderBoardName); + multiLeaderboardPanel = new MultiLeaderboardProxyPanel(parent, context, sailingServiceFactory, metaLeaderboardName, asyncActionsExecutor, timer, true /* isEmbedded */, preselectedLeaderboardName, errorReporter, StringMessages.INSTANCE, showRaceDetails, autoExpandLastRaceColumn, leaderboardSettings, flagImageResolver, availableDetailTypes); @@ -179,4 +187,10 @@ public class EventSeriesAnalyticsDataManager { public Timer getTimer() { return timer; } + + public void getAvailableDetailTypesForLeaderboard(String leaderboardName, RegattaAndRaceIdentifier raceOrNull, + AsyncCallback> asyncCallback) { + SailingServiceAsync sailingService = sailingCF.getSailingService(()-> leaderboardName); + sailingService.getAvailableDetailTypesForLeaderboard(leaderboardName, raceOrNull, asyncCallback); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SeriesActivity.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SeriesActivity.java index 3e615dad84c..260c47542b9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SeriesActivity.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/SeriesActivity.java @@ -59,7 +59,7 @@ public class SeriesActivity extends AbstractActivity implements SeriesView.Prese if (this.ctx.getAnalyticsManager() == null) { ctx.withAnalyticsManager(new EventSeriesAnalyticsDataManager( // - clientFactory.getSailingService(), // + clientFactory, // asyncActionsExecutor, // new Timer(PlayModes.Live, PlayStates.Paused, delayBetweenAutoAdvancesInMilliseconds), // clientFactory.getErrorReporter(), flagImageResolver)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java index a2c71c335ec..e838a90f357 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/analyticstab/EventSeriesCompetitorAnalyticsTabView.java @@ -78,8 +78,8 @@ public class EventSeriesCompetitorAnalyticsTabView extends SharedLeaderboardEven } }; if (eventSeriesAnalyticsManager.getLeaderboardPanel() == null) { - eventSeriesAnalyticsManager.getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, - new AsyncCallback>() { + eventSeriesAnalyticsManager.getAvailableDetailTypesForLeaderboard(leaderboardName, + null, new AsyncCallback>() { @Override public void onSuccess(Iterable result) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/leaderboardstab/EventSeriesLeaderboardsTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/leaderboardstab/EventSeriesLeaderboardsTabView.java index 3a6df7f843b..0ad163958f8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/leaderboardstab/EventSeriesLeaderboardsTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/leaderboardstab/EventSeriesLeaderboardsTabView.java @@ -77,8 +77,8 @@ public class EventSeriesLeaderboardsTabView extends Composite implements SeriesT final EventSeriesAnalyticsDataManager regattaAnalyticsManager = currentPresenter.getCtx() .getAnalyticsManager(); - regattaAnalyticsManager.getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, - new AsyncCallback>() { + regattaAnalyticsManager.getAvailableDetailTypesForLeaderboard(leaderboardName, + null, new AsyncCallback>() { @Override public void onSuccess(Iterable result) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java index 5f924bdb3c3..b34f29b1291 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/fakeseries/overallleaderboardtab/EventSeriesOverallLeaderboardTabView.java @@ -64,7 +64,7 @@ public class EventSeriesOverallLeaderboardTabView extends SharedLeaderboardEvent contentArea.setWidget(currentPresenter.getErrorAndBusyClientFactory().createBusyView()); String leaderboardName = currentPresenter.getSeriesDTO().getLeaderboardId(); if (leaderboardName != null && !leaderboardName.isEmpty()) { - currentPresenter.getCtx().getAnalyticsManager().getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, new AsyncCallback>() { + currentPresenter.getCtx().getAnalyticsManager().getAvailableDetailTypesForLeaderboard(leaderboardName, null, new AsyncCallback>() { @Override public void onSuccess(Iterable result) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html index bc21c9b9ca2..f1d5075a2e4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/RaceCommitteeAppNotes.html @@ -4,6 +4,14 @@

What's New - SAP Sailing Race Manager

+
February 2018
+
    +
  • The protest time is now configurable in the backend. An empty value means, the RCApp + will take 90 minutes as default. If no time is set in the backend, the RCApp will save a + custom duration as new default. A server side duration (device or regatta specific) will always + be the default.
  • +
+
August 2017
  • The result list can now be edited without a finish time.
  • @@ -22,6 +30,11 @@ for the competitor is cleared, the rank is set to 0 and the penalty is set to "NONE" then the competitor will remain in the list, and this "empty" result will be sent to the server, clearing any previous score + corrections that were sent to the server. This is the only way to + "reset" an erroneous score correction entirely from the app, because + swiping and thus removing the competitor from the list would leave the + previous score correction unchanged on the server upon the next result + publication.
  • When entering and publishing partial results, updates will only be applied for those competitors for which a result has been entered. In particular, @@ -30,12 +43,6 @@ partial results entered on one device will not unnecessarily remove changes applied on another device.
  • - corrections that were sent to the server. This is the only way to - "reset" an erroneous score correction entirely from the app, because - swiping and thus removing the competitor from the list would leave the - previous score correction unchanged on the server upon the next result - publication. -
April 2017
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html index 415f7fea5dd..954412dc7fa 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html @@ -19,13 +19,10 @@
  • Selecting a filter dimension adds a table containing the available values of the selected dimension. The order of the value tables is equal to the order of the dimensions list.
  • -
  • Note that the selection of the filter dimensions follows the table with checkbox column pattern. So simply clicking on a - dimension will result in deselecting all previously selected dimensions, removing possible filter values. Clicking on the - checkbox column or using ctrl+click is advised. -
  • Each filter value table now has its own scroll-bar.
  • +
  • The grouping dimensions are now ordered alphabetically.
  • April 2018
    diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.java index 9f0d24cda4a..acec846051f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.java @@ -6,6 +6,7 @@ import java.util.Set; import java.util.UUID; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; @@ -39,9 +40,11 @@ public abstract class AbstractEventView

    exten static class AbstractEventViewLayout { @UiField(provided = true) EventHeader eventHeaderUi; @UiField Quickfinder quickFinderUi; - @UiField SimpleInfoBlock simpleInfoUi; + @UiField SimpleInfoBlock sailorInfoUi; + @UiField SimpleInfoBlock seriesNavigationoUi; + @UiField DivElement windfinderWrapperUi; @UiField(provided = true) WindfinderControl windfinderUi; - @UiField SimplePanel viewContentUi;; + @UiField SimplePanel viewContentUi; private AbstractEventViewLayout(EventViewDTO event, String regattaName, PlaceNavigation logoNavigation) { this.eventHeaderUi = new EventHeader(event, regattaName, logoNavigation); @@ -125,18 +128,19 @@ public abstract class AbstractEventView

    exten @Override public void setSailorInfos(String description, String buttonLabel, String url) { - layout.simpleInfoUi.setDescription(SafeHtmlUtils.fromString(description.replace("\n", " "))); - layout.simpleInfoUi.setAction(buttonLabel, url); + layout.sailorInfoUi.setDescription(SafeHtmlUtils.fromString(description.replace("\n", " "))); + layout.sailorInfoUi.setAction(buttonLabel, url); } @Override public void setWindfinderNavigations(Iterable spotData) { + layout.windfinderWrapperUi.getStyle().clearDisplay(); layout.windfinderUi.setSpotData(spotData); } @Override public void setSeriesNavigation(String buttonLabel, PlaceNavigation placeNavigation) { - layout.simpleInfoUi.setAction(buttonLabel, placeNavigation); + layout.seriesNavigationoUi.setAction(buttonLabel, placeNavigation); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.ui.xml index 2258c94f492..bb367cc4c7d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/AbstractEventView.ui.xml @@ -7,9 +7,12 @@ .headerWrapper { background-color: #f2f2f2; } + .windfinderWrapper { + padding-bottom: 10px; + margin: 0 10px; + } .windfinder { - display: block; - margin: 0 10px 10px; + width: 100%; border-radius: .384615384615385em; text-align: center; } @@ -18,8 +21,11 @@

    - - + + +
    diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/overview/regatta/RegattaActivity.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/overview/regatta/RegattaActivity.java index 5b08d12688d..35ca51d380f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/overview/regatta/RegattaActivity.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/overview/regatta/RegattaActivity.java @@ -30,11 +30,11 @@ public class RegattaActivity extends AbstractEventActivity @Override protected EventViewBase initView() { final AbstractEventOverview view = new RegattaOverviewImpl(this, flagImageResolver); + initSailorInfo(view); initSeriesNavigation(view); initQuickfinder(view, true); initWindfinderNavigations(view); if (!isMultiRegattaEvent()) { - // initSailorInfo(view); initMedia(view); } return view; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java index 323139e954c..14a5070e01e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java @@ -1,8 +1,17 @@ package com.sap.sailing.gwt.home.server.servlets; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; +import com.sap.sailing.domain.common.sharding.ShardingType; +import com.sap.sailing.domain.sharding.ShardingContext; import com.sap.sailing.domain.windfinder.WindFinderTrackerFactory; import com.sap.sailing.gwt.home.communication.SailingDispatchContext; import com.sap.sailing.gwt.home.server.SailingDispatchContextImpl; @@ -47,4 +56,19 @@ public class SailingDispatchServlet extends AbstractDispatchServlet

    -

    - - -

    diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/error/ErrorMessage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/error/ErrorMessage.java index 46afd7d5c0f..029138da8be 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/error/ErrorMessage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/error/ErrorMessage.java @@ -12,6 +12,8 @@ import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.gwt.client.Notification; +import com.sap.sse.gwt.client.Notification.NotificationType; public class ErrorMessage extends Composite { @@ -42,7 +44,7 @@ public class ErrorMessage extends Composite { } this.errorMessageDetail.setInnerText(errorMessageDetail); - Window.setStatus(errorMessageDetail); + Notification.notify(errorMessageDetail, NotificationType.WARNING); } public void addReloadPageButtonStyleNames(String styleNames) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/windfinder/Windfinder.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/windfinder/Windfinder.gss index 842e9380598..faf7d6a365e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/windfinder/Windfinder.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/partials/windfinder/Windfinder.gss @@ -7,7 +7,7 @@ text-decoration: none; vertical-align: bottom; padding-left: 1em; - padding-right: 0.5em + padding-right: 0.5em; } .windfindercontrol_text, .windfindercontrol_icon { display: inline-block; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java index 99a59b307cc..de81d943467 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java @@ -64,6 +64,7 @@ public class RegattaOverviewEntryPoint extends AbstractSailingEntryPoint { return; } + createAndAddDetailPanel(); createAndAddRegattaPanel(regattaOverviewContextDefinition); toggleDetailPanel(false); @@ -88,7 +89,7 @@ public class RegattaOverviewEntryPoint extends AbstractSailingEntryPoint { } private void createAndAddRegattaPanel(RegattaOverviewContextDefinition regattaOverviewContextDefinition) { - regattaPanel = new RegattaOverviewPanel(sailingService, getUserService(), this, getStringMessages(), + regattaPanel = new RegattaOverviewPanel(getSailingService(), getUserService(), this, getStringMessages(), regattaOverviewContextDefinition); regattaPanel.addHandler(new EventDTOLoadedEvent.Handler() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java index 2e1378601f7..39977934462 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java @@ -83,14 +83,14 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements private Widget createAdminConsolePanel() { AdminConsolePanel panel = new AdminConsolePanel(getUserService(), SailingPermissionsForRoleProvider.INSTANCE, - sailingService, getStringMessages().releaseNotes(), "/release_notes_admin.html", /* error reporter */ this, SecurityStylesheetResources.INSTANCE.css(), getStringMessages()); + getSailingService(), getStringMessages().releaseNotes(), "/release_notes_admin.html", /* error reporter */ this, SecurityStylesheetResources.INSTANCE.css(), getStringMessages()); panel.addStyleName("adminConsolePanel"); regattasDisplayers = new HashSet<>(); leaderboardsDisplayers = new HashSet<>(); leaderboardGroupsDisplayers = new HashSet<>(); - final EventManagementPanel eventManagementPanel = new EventManagementPanel(sailingService, this, this, getStringMessages(), panel); + final EventManagementPanel eventManagementPanel = new EventManagementPanel(getSailingService(), this, this, getStringMessages(), panel); eventManagementPanel.ensureDebugId("EventManagement"); panel.addToVerticalTabPanel(new DefaultRefreshableAdminConsolePanel(eventManagementPanel) { @Override @@ -104,7 +104,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements /* REGATTAS */ RegattaManagementPanel regattaManagementPanel = new RegattaManagementPanel( - sailingService, this, getStringMessages(), this, eventManagementPanel); + getSailingService(), this, getStringMessages(), this, eventManagementPanel); regattaManagementPanel.ensureDebugId("RegattaStructureManagement"); panel.addToVerticalTabPanel(new DefaultRefreshableAdminConsolePanel(regattaManagementPanel) { @Override @@ -117,7 +117,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements /* LEADERBOARDS */ final HorizontalTabLayoutPanel leaderboardTabPanel = panel.addVerticalTab(getStringMessages().leaderboards(), "LeaderboardPanel"); - final LeaderboardConfigPanel leaderboardConfigPanel = new LeaderboardConfigPanel(sailingService, this, this, + final LeaderboardConfigPanel leaderboardConfigPanel = new LeaderboardConfigPanel(getSailingService(), this, this, getStringMessages(), /* showRaceDetails */true, this); leaderboardConfigPanel.ensureDebugId("LeaderboardConfiguration"); panel.addToTabPanel(leaderboardTabPanel, new DefaultRefreshableAdminConsolePanel(leaderboardConfigPanel) { @@ -129,7 +129,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements regattasDisplayers.add(leaderboardConfigPanel); leaderboardsDisplayers.add(leaderboardConfigPanel); - final LeaderboardGroupConfigPanel leaderboardGroupConfigPanel = new LeaderboardGroupConfigPanel(sailingService, + final LeaderboardGroupConfigPanel leaderboardGroupConfigPanel = new LeaderboardGroupConfigPanel(getSailingService(), this, this, this, this, getStringMessages()); leaderboardGroupConfigPanel.ensureDebugId("LeaderboardGroupConfiguration"); panel.addToTabPanel(leaderboardTabPanel, new DefaultRefreshableAdminConsolePanel(leaderboardGroupConfigPanel) { @@ -154,7 +154,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements final HorizontalTabLayoutPanel racesTabPanel = panel.addVerticalTab(getStringMessages().trackedRaces(), "RacesPanel"); racesTabPanel.ensureDebugId("RacesPanel"); - final TrackedRacesManagementPanel trackedRacesManagementPanel = new TrackedRacesManagementPanel(sailingService, this, + final TrackedRacesManagementPanel trackedRacesManagementPanel = new TrackedRacesManagementPanel(getSailingService(), this, this, getStringMessages()); trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement"); panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(trackedRacesManagementPanel) { @@ -165,7 +165,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements }, getStringMessages().trackedRaces(), Permission.SHOW_TRACKED_RACES); regattasDisplayers.add(trackedRacesManagementPanel); - final CompetitorPanel competitorPanel = new CompetitorPanel(sailingService, getStringMessages(), this); + final CompetitorPanel competitorPanel = new CompetitorPanel(getSailingService(), getStringMessages(), this); competitorPanel.ensureDebugId("CompetitorPanel"); panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(competitorPanel) { @Override @@ -174,7 +174,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements } }, getStringMessages().competitors(), Permission.MANAGE_ALL_COMPETITORS); - final BoatPanel boatPanel = new BoatPanel(sailingService, getStringMessages(), this); + final BoatPanel boatPanel = new BoatPanel(getSailingService(), getStringMessages(), this); boatPanel.ensureDebugId("BoatPanel"); panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(boatPanel) { @Override @@ -183,18 +183,18 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements } }, getStringMessages().boats(), Permission.MANAGE_ALL_BOATS); - RaceCourseManagementPanel raceCourseManagementPanel = new RaceCourseManagementPanel(sailingService, this, this, getStringMessages()); + RaceCourseManagementPanel raceCourseManagementPanel = new RaceCourseManagementPanel(getSailingService(), this, this, getStringMessages()); panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(raceCourseManagementPanel), getStringMessages().courseLayout(), Permission.MANAGE_COURSE_LAYOUT); regattasDisplayers.add(raceCourseManagementPanel); final AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor(); - WindPanel windPanel = new WindPanel(sailingService, asyncActionsExecutor, this, this, getStringMessages()); + WindPanel windPanel = new WindPanel(getSailingService(), asyncActionsExecutor, this, this, getStringMessages()); panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(windPanel), getStringMessages().wind(), Permission.MANAGE_WIND); regattasDisplayers.add(windPanel); - final MediaPanel mediaPanel = new MediaPanel(regattasDisplayers, sailingService, this, mediaService, this, getStringMessages()); + final MediaPanel mediaPanel = new MediaPanel(regattasDisplayers, getSailingService(), this, mediaService, this, getStringMessages()); panel.addToTabPanel(racesTabPanel, new DefaultRefreshableAdminConsolePanel(mediaPanel) { @Override public void refreshAfterBecomingVisible() { @@ -205,7 +205,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements /* RACE COMMITTEE APP */ final HorizontalTabLayoutPanel raceCommitteeTabPanel = panel.addVerticalTab(getStringMessages().raceCommitteeApp(), "RaceCommiteeAppPanel"); - final DeviceConfigurationUserPanel deviceConfigurationUserPanel = new DeviceConfigurationUserPanel(sailingService, + final DeviceConfigurationUserPanel deviceConfigurationUserPanel = new DeviceConfigurationUserPanel(getSailingService(), getUserService(), getStringMessages(), this); panel.addToTabPanel(raceCommitteeTabPanel, new DefaultRefreshableAdminConsolePanel(deviceConfigurationUserPanel), getStringMessages().deviceConfiguration(), Permission.MANAGE_DEVICE_CONFIGURATION); @@ -213,7 +213,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements /* CONNECTORS */ final HorizontalTabLayoutPanel connectorsTabPanel = panel.addVerticalTab(getStringMessages().connectors(), "TrackingProviderPanel"); - TracTracEventManagementPanel tractracEventManagementPanel = new TracTracEventManagementPanel(sailingService, + TracTracEventManagementPanel tractracEventManagementPanel = new TracTracEventManagementPanel(getSailingService(), this, this, getStringMessages()); tractracEventManagementPanel.ensureDebugId("TracTracEventManagement"); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(tractracEventManagementPanel), @@ -221,19 +221,19 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements regattasDisplayers.add(tractracEventManagementPanel); SwissTimingReplayConnectorPanel swissTimingReplayConnectorPanel = new SwissTimingReplayConnectorPanel( - sailingService, this, this, getStringMessages()); + getSailingService(), this, this, getStringMessages()); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(swissTimingReplayConnectorPanel), getStringMessages().swissTimingArchiveConnector(), Permission.MANAGE_TRACKED_RACES); regattasDisplayers.add(swissTimingReplayConnectorPanel); SwissTimingEventManagementPanel swisstimingEventManagementPanel = new SwissTimingEventManagementPanel( - sailingService, this, this, getStringMessages()); + getSailingService(), this, this, getStringMessages()); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(swisstimingEventManagementPanel), getStringMessages().swissTimingEvents(), Permission.MANAGE_TRACKED_RACES); regattasDisplayers.add(swisstimingEventManagementPanel); final SmartphoneTrackingEventManagementPanel raceLogTrackingEventManagementPanel = new SmartphoneTrackingEventManagementPanel( - sailingService, this, this, this, getStringMessages()); + getSailingService(), this, this, this, getStringMessages()); raceLogTrackingEventManagementPanel.ensureDebugId("SmartphoneTrackingPanel"); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(raceLogTrackingEventManagementPanel) { @Override @@ -244,12 +244,12 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements regattasDisplayers.add(raceLogTrackingEventManagementPanel); leaderboardsDisplayers.add(raceLogTrackingEventManagementPanel); - IgtimiAccountsPanel igtimiAccountsPanel = new IgtimiAccountsPanel(sailingService, this, getStringMessages()); + IgtimiAccountsPanel igtimiAccountsPanel = new IgtimiAccountsPanel(getSailingService(), this, getStringMessages()); igtimiAccountsPanel.ensureDebugId("IgtimiAccounts"); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(igtimiAccountsPanel), getStringMessages().igtimiAccounts(), Permission.MANAGE_IGTIMI_ACCOUNTS); - ExpeditionDeviceConfigurationsPanel expeditionDeviceConfigurationsPanel = new ExpeditionDeviceConfigurationsPanel(sailingService, this, getStringMessages()); + ExpeditionDeviceConfigurationsPanel expeditionDeviceConfigurationsPanel = new ExpeditionDeviceConfigurationsPanel(getSailingService(), this, getStringMessages()); expeditionDeviceConfigurationsPanel.ensureDebugId("ExpeditionDeviceConfigurations"); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(expeditionDeviceConfigurationsPanel) { @Override @@ -258,11 +258,11 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements } }, getStringMessages().expeditionDeviceConfigurations(), Permission.MANAGE_EXPEDITION_DEVICE_CONFIGURATIONS); - ResultImportUrlsManagementPanel resultImportUrlsManagementPanel = new ResultImportUrlsManagementPanel(sailingService, this, getStringMessages()); + ResultImportUrlsManagementPanel resultImportUrlsManagementPanel = new ResultImportUrlsManagementPanel(getSailingService(), this, getStringMessages()); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(resultImportUrlsManagementPanel), getStringMessages().resultImportUrls(), Permission.MANAGE_RESULT_IMPORT_URLS); - StructureImportManagementPanel structureImportUrlsManagementPanel = new StructureImportManagementPanel(sailingService, this, getStringMessages(), this, eventManagementPanel); + StructureImportManagementPanel structureImportUrlsManagementPanel = new StructureImportManagementPanel(getSailingService(), this, getStringMessages(), this, eventManagementPanel); panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel(structureImportUrlsManagementPanel), getStringMessages().manage2Sail() + " " + getStringMessages().regattaStructureImport(), Permission.MANAGE_STRUCTURE_IMPORT_URLS); @@ -270,7 +270,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements final HorizontalTabLayoutPanel advancedTabPanel = panel.addVerticalTab(getStringMessages().advanced(), "AdvancedPanel"); advancedTabPanel.ensureDebugId("AdvancedTab"); - final ReplicationPanel replicationPanel = new ReplicationPanel(sailingService, this, getStringMessages()); + final ReplicationPanel replicationPanel = new ReplicationPanel(getSailingService(), this, getStringMessages()); panel.addToTabPanel(advancedTabPanel, new DefaultRefreshableAdminConsolePanel(replicationPanel) { @Override public void refreshAfterBecomingVisible() { @@ -278,17 +278,17 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements } }, getStringMessages().replication(), Permission.MANAGE_REPLICATION); - final MasterDataImportPanel masterDataImportPanel = new MasterDataImportPanel(getStringMessages(), sailingService, + final MasterDataImportPanel masterDataImportPanel = new MasterDataImportPanel(getStringMessages(), getSailingService(), this, eventManagementPanel, this, this, mediaPanel); masterDataImportPanel.ensureDebugId("MasterDataImport"); panel.addToTabPanel(advancedTabPanel, new DefaultRefreshableAdminConsolePanel(masterDataImportPanel), getStringMessages().masterDataImportPanel(), Permission.MANAGE_MASTERDATA_IMPORT); - RemoteServerInstancesManagementPanel remoteServerInstancesManagementPanel = new RemoteServerInstancesManagementPanel(sailingService, this, getStringMessages()); + RemoteServerInstancesManagementPanel remoteServerInstancesManagementPanel = new RemoteServerInstancesManagementPanel(getSailingService(), this, getStringMessages()); panel.addToTabPanel(advancedTabPanel, new DefaultRefreshableAdminConsolePanel(remoteServerInstancesManagementPanel), getStringMessages().remoteServerInstances(), Permission.MANAGE_SAILING_SERVER_INSTANCES); - LocalServerManagementPanel localServerInstancesManagementPanel = new LocalServerManagementPanel(sailingService, this, getStringMessages()); + LocalServerManagementPanel localServerInstancesManagementPanel = new LocalServerManagementPanel(getSailingService(), this, getStringMessages()); panel.addToTabPanel(advancedTabPanel, new DefaultRefreshableAdminConsolePanel(localServerInstancesManagementPanel), getStringMessages().localServer(), Permission.MANAGE_LOCAL_SERVER_INSTANCE); @@ -297,7 +297,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements panel.addToTabPanel(advancedTabPanel, new DefaultRefreshableAdminConsolePanel(userManagementPanel), getStringMessages().userManagement(), Permission.MANAGE_USERS); - final FileStoragePanel fileStoragePanel = new FileStoragePanel(sailingService, this); + final FileStoragePanel fileStoragePanel = new FileStoragePanel(getSailingService(), this); panel.addToTabPanel(advancedTabPanel, new DefaultRefreshableAdminConsolePanel(fileStoragePanel), getStringMessages().fileStorage(), Permission.MANAGE_FILE_STORAGE); @@ -311,7 +311,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements @Override public void fillLeaderboards() { - sailingService.getLeaderboards(new MarkedAsyncCallback>( + getSailingService().getLeaderboards(new MarkedAsyncCallback>( new AsyncCallback>() { @Override public void onSuccess(List leaderboards) { @@ -338,7 +338,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements @Override public void fillLeaderboardGroups() { - sailingService.getLeaderboardGroups(false /*withGeoLocationData*/, + getSailingService().getLeaderboardGroups(false /*withGeoLocationData*/, new MarkedAsyncCallback>( new AsyncCallback>() { @Override @@ -366,7 +366,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements @Override public void fillRegattas() { - sailingService.getRegattas(new MarkedAsyncCallback>( + getSailingService().getRegattas(new MarkedAsyncCallback>( new AsyncCallback>() { @Override public void onSuccess(List result) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java index f7d023319c1..f215666b1b3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java @@ -1,6 +1,6 @@ package com.sap.sailing.gwt.ui.adminconsole; -import java.util.ArrayList; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -334,7 +334,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { }); editCompetitorsDialog.show(); } else if (LeaderboardConfigImagesBarCell.ACTION_CONFIGURE_URL.equals(value)) { - sailingService.getAvailableDetailTypesForLeaderboard(leaderboardDTO.name, new AsyncCallback>() { + sailingService.getAvailableDetailTypesForLeaderboard(leaderboardDTO.name, null, new AsyncCallback>() { @Override public void onFailure(Throwable caught) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java index 82ef7f13ac5..ce032b46a75 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceCourseManagementPanel.java @@ -4,7 +4,6 @@ import java.util.Date; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlowPanel; @@ -14,6 +13,8 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.RaceCourseDTO; import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.Notification; +import com.sap.sse.gwt.client.Notification.NotificationType; /** * A panel that has a race selection (inherited from {@link AbstractRaceManagementPanel}) and which adds a table @@ -41,7 +42,7 @@ public class RaceCourseManagementPanel extends AbstractRaceManagementPanel { @Override public void onSuccess(Void result) { - Window.setStatus(stringMessages.successfullyUpdatedCourse()); + Notification.notify(stringMessages.successfullyUpdatedCourse(), NotificationType.INFO); refreshSelectedRaceData(); } }); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaConfigurationDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaConfigurationDialog.java index 30e77991424..4c832da3c12 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaConfigurationDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaConfigurationDialog.java @@ -23,12 +23,13 @@ import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO; import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO.RegattaConfigurationDTO; import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO.RegattaConfigurationDTO.RacingProcedureConfigurationDTO; +import com.sap.sse.common.Duration; +import com.sap.sse.gwt.client.controls.IntegerBox; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class RegattaConfigurationDialog extends DataEntryDialog { private final AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); - private final StringMessages stringMessages; private final DeviceConfigurationDTO.RegattaConfigurationDTO originalConfiguration; @@ -36,6 +37,7 @@ public class RegattaConfigurationDialog extends DataEntryDialog filterablePanelEvents; private final ListDataProvider raceList; - private final CellTable raceTable; + private final FlushableCellTable raceTable; private final Map previousConfigurations; private final ListBox previousConfigurationsComboBox; + private final TextBox eventIdBox; private final TextBox jsonUrlBox; private final TextBox hostnameTextbox; private final IntegerBox portIntegerbox; private final List availableSwissTimingRaces = new ArrayList(); + private final String manage2sailBaseAPIUrl = "http://manage2sail.com/api/public/links/event/"; + private final String manage2sailAPIaccessToken = "?accesstoken=bDAv8CwsTM94ujZ"; + private final String manage2sailUrlAppendix = "&mediaType=json&includeRaces=true"; + private final String eventIdPattern = "[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}"; public SwissTimingEventManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter, @@ -83,7 +88,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane captionPanelConnections.setContentWidget(verticalPanel); captionPanelConnections.setStyleName("bold"); - Grid connectionsGrid = new Grid(5, 2); + Grid connectionsGrid = new Grid(6, 2); verticalPanel.add(connectionsGrid); previousConfigurations = new HashMap(); @@ -102,89 +107,50 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane } }); fillConfigurations(); - + jsonUrlBox = new TextBox(); jsonUrlBox.getElement().getStyle().setWidth(50, Unit.EM); connectionsGrid.setWidget(0, 0, new Label(stringMessages.swissTimingEvents() + ":")); connectionsGrid.setWidget(0, 1, previousConfigurationsComboBox); - connectionsGrid.setWidget(1, 0, new Label("Manage2Sail Event-URL (json):")); - connectionsGrid.setWidget(1, 1, jsonUrlBox); + + eventIdBox = new TextBox(); + eventIdBox.getElement().getStyle().setWidth(30, Unit.EM); + eventIdBox.setTitle(stringMessages.manage2SailEventIdBoxTooltip()); + connectionsGrid.setWidget(1, 0, new Label(stringMessages.manage2SailEventIdBox() + ":")); + connectionsGrid.setWidget(1, 1, eventIdBox); + eventIdBox.addChangeHandler(event -> { + if (eventIdBox.getValue() != "") { + updateUrlFromEventId(eventIdBox.getValue()); + } + }); + + connectionsGrid.setWidget(2, 0, new Label(stringMessages.manage2SailEventURLBox() + ":")); + connectionsGrid.setWidget(2, 1, jsonUrlBox); + jsonUrlBox.addChangeHandler(event -> { + if (jsonUrlBox.getValue() != "") { + updateEventIdFromUrl(jsonUrlBox.getValue()); + } + }); hostnameTextbox = new TextBox(); portIntegerbox = new IntegerBox(); - connectionsGrid.setWidget(2, 0, new Label(stringConstants.hostname() + ":")); - connectionsGrid.setWidget(2, 1, hostnameTextbox); + connectionsGrid.setWidget(3, 0, new Label(stringConstants.hostname() + ":")); + connectionsGrid.setWidget(3, 1, hostnameTextbox); - connectionsGrid.setWidget(3, 0, new Label(stringConstants.port() + ":")); - connectionsGrid.setWidget(3, 1, portIntegerbox); + connectionsGrid.setWidget(4, 0, new Label(stringMessages.manage2SailPort() + ":")); + connectionsGrid.setWidget(4, 1, portIntegerbox); Button btnListRaces = new Button(stringConstants.listRaces()); - connectionsGrid.setWidget(4, 1, btnListRaces); + connectionsGrid.setWidget(5, 1, btnListRaces); btnListRaces.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { fillRaces(sailingService); } }); - - TextColumn raceNameColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.getName(); - } - }; - - TextColumn regattaNameColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.regattaName; - } - }; - - TextColumn seriesNameColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.seriesName; - } - }; - - TextColumn raceIdColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.raceId; - } - }; - TextColumn boatClassColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.boatClass != null ? object.boatClass : ""; - } - }; - - TextColumn genderColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.gender != null ? object.gender : ""; - } - }; - - TextColumn raceStatusColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.raceStatus != null ? object.raceStatus : ""; - } - }; - - TextColumn raceStartTimeColumn = new TextColumn() { - @Override - public String getValue(SwissTimingRaceRecordDTO object) { - return object.raceStartTime==null?"":dateFormatter.render(object.raceStartTime) + " " + timeFormatter.render(object.raceStartTime); - } - }; - HorizontalPanel racesSplitPanel = new HorizontalPanel(); mainPanel.add(racesSplitPanel); @@ -236,7 +202,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane useInternalMarkPassingAlgorithmCheckbox.setWordWrap(false); useInternalMarkPassingAlgorithmCheckbox.setValue(Boolean.FALSE); trackableRacesPanel.add(useInternalMarkPassingAlgorithmCheckbox); - + // text box for filtering the cell table HorizontalPanel filterPanel = new HorizontalPanel(); filterPanel.setSpacing(5); @@ -246,23 +212,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane filterPanel.add(lblFilterEvents); filterPanel.setCellVerticalAlignment(lblFilterEvents, HasVerticalAlignment.ALIGN_MIDDLE); - raceNameColumn.setSortable(true); - raceStartTimeColumn.setSortable(true); - boatClassColumn.setSortable(true); - raceIdColumn.setSortable(true); - genderColumn.setSortable(true); - raceStatusColumn.setSortable(true); - - AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); - raceTable = new BaseCelltable(/* pageSize */10000, tableRes); - raceTable.addColumn(regattaNameColumn, stringConstants.regatta()); - raceTable.addColumn(seriesNameColumn, stringConstants.series()); - raceTable.addColumn(raceNameColumn, stringConstants.name()); - //raceTable.addColumn(raceIdColumn, stringConstants.id()); - raceTable.addColumn(raceStatusColumn, stringConstants.status()); - raceTable.addColumn(boatClassColumn, stringConstants.boatClass()); - raceTable.addColumn(genderColumn, "Gender"); - raceTable.addColumn(raceStartTimeColumn, stringConstants.startTime()); + raceTable = new FlushableCellTable(/* pageSize */10000, tableRes); raceTable.setWidth("300px"); raceList = new ListDataProvider(); filterablePanelEvents = new LabeledAbstractFilterablePanel(lblFilterEvents, @@ -282,19 +232,99 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane return strings; } }; - raceTable.setSelectionModel(new RefreshableMultiSelectionModel( - new EntityIdentityComparator() { - @Override - public boolean representSameEntity(SwissTimingRaceRecordDTO dto1, SwissTimingRaceRecordDTO dto2) { - return dto1.raceId.equals(dto2.raceId); - } - @Override - public int hashCode(SwissTimingRaceRecordDTO t) { - return t.raceId.hashCode(); - } - }, filterablePanelEvents.getAllListDataProvider()) { - }); + + final EntityIdentityComparator entityIdentityComparator = new EntityIdentityComparator() { + @Override + public boolean representSameEntity(SwissTimingRaceRecordDTO dto1, SwissTimingRaceRecordDTO dto2) { + return dto1.raceId.equals(dto2.raceId); + } + @Override + public int hashCode(SwissTimingRaceRecordDTO t) { + return t.raceId.hashCode(); + } + }; + TextColumn raceNameColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.getName(); + } + }; + + TextColumn regattaNameColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.regattaName; + } + }; + + SelectionCheckboxColumn selectionColumn = new SelectionCheckboxColumn( + tableRes.cellTableStyle().cellTableCheckboxSelected(), + tableRes.cellTableStyle().cellTableCheckboxDeselected(), + tableRes.cellTableStyle().cellTableCheckboxColumnCell(), entityIdentityComparator, raceList, raceTable); + + TextColumn seriesNameColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.seriesName; + } + }; + + TextColumn raceIdColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.raceId; + } + }; + + TextColumn boatClassColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.boatClass != null ? object.boatClass : ""; + } + }; + + TextColumn genderColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.gender != null ? object.gender : ""; + } + }; + + TextColumn raceStatusColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.raceStatus != null ? object.raceStatus : ""; + } + }; + + TextColumn raceStartTimeColumn = new TextColumn() { + @Override + public String getValue(SwissTimingRaceRecordDTO object) { + return object.raceStartTime==null?"":dateFormatter.render(object.raceStartTime) + " " + timeFormatter.render(object.raceStartTime); + } + }; + + raceNameColumn.setSortable(true); + raceStartTimeColumn.setSortable(true); + boatClassColumn.setSortable(true); + raceIdColumn.setSortable(true); + genderColumn.setSortable(true); + raceStatusColumn.setSortable(true); + + + raceTable.addColumn(selectionColumn, selectionColumn.getHeader()); + raceTable.addColumn(regattaNameColumn, stringConstants.regatta()); + raceTable.addColumn(seriesNameColumn, stringConstants.series()); + raceTable.addColumn(raceNameColumn, stringConstants.name()); + //raceTable.addColumn(raceIdColumn, stringConstants.id()); + raceTable.addColumn(raceStatusColumn, stringConstants.status()); + raceTable.addColumn(boatClassColumn, stringConstants.boatClass()); + raceTable.addColumn(genderColumn, stringConstants.gender()); + raceTable.addColumn(raceStartTimeColumn, stringConstants.startTime()); + + raceTable.setSelectionModel(selectionColumn.getSelectionModel(), selectionColumn.getSelectionManager()); + trackableRacesPanel.add(raceTable); raceList.addDataDisplay(raceTable); Handler columnSortHandler = getRaceTableColumnSortHandler(raceList.getList(), regattaNameColumn, seriesNameColumn, @@ -318,6 +348,31 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane }); } + /** + * This function tries to infer a valid JsonUrl for any input given that matches the pattern of an event Id from + * M2S. If there is an event id detected the Json Url gets updated and the event Id textbox is filled with the + * detected event Id. The ID pattern is defined in {@link eventIdPattern}. + */ + private void updateUrlFromEventId(String eventIdTextbox) { + if (eventIdTextbox.matches(".*" + eventIdPattern + ".*")) { + final String inferredEventId = eventIdTextbox.replaceFirst(".*(" + eventIdPattern + ").*", "$1"); + jsonUrlBox.setValue( + manage2sailBaseAPIUrl + inferredEventId + manage2sailAPIaccessToken + manage2sailUrlAppendix); + eventIdBox.setValue(inferredEventId); + } + } + + /** + * Similar to {@link #updateUrlFromEventId} this function tries to extract a M2S event Id by looking at the given + * url in the Json Url Textbox. The value of {@link eventIdBox} is then set to the event ID inferred from the Json Url. + */ + private void updateEventIdFromUrl(String jsonUrlTextBox) { + if (jsonUrlTextBox.matches("http://manage2sail.com/.*" + eventIdPattern + ".*")) { + final String inferredEventId = jsonUrlTextBox.replaceFirst(".*(" + eventIdPattern + ").*", "$1"); + eventIdBox.setValue(inferredEventId); + } + } + private ListHandler getRaceTableColumnSortHandler(List raceRecords, Column regattaNameColumn, Column seriesNameColumn, Column nameColumn, Column trackingStartColumn, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractSailingEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractSailingEntryPoint.java index 119b2dcaa60..e9103ef00f4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractSailingEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractSailingEntryPoint.java @@ -1,17 +1,32 @@ package com.sap.sailing.gwt.ui.client; -import com.google.gwt.core.client.GWT; -import com.google.gwt.user.client.rpc.ServiceDefTarget; -import com.sap.sse.gwt.client.EntryPointHelper; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; +import com.sap.sse.gwt.client.ServiceRoutingProvider; import com.sap.sse.security.ui.client.AbstractSecureEntryPoint; public abstract class AbstractSailingEntryPoint extends AbstractSecureEntryPoint { - protected final SailingServiceAsync sailingService = GWT.create(SailingService.class); + private SailingServiceAsync sailingService; @Override protected void doOnModuleLoad() { super.doOnModuleLoad(); - EntryPointHelper.registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.sailingServiceRemotePath); + } + + /** + * Lazily initialize sailing service. Concrete entry point subclasses may influence sailing service creation by + * implementing a given routing information providing interface {@link ProvidesLeaderboardRouting}. The routing + * information provided via {@link ProvidesLeaderboardRouting#getLeaderboardName()} needs to be available when this + * method is called for the first time. Repetitive calls will not cause a new leaderboard name to take effect. + */ + protected SailingServiceAsync getSailingService() { + if (sailingService == null) { + if (this instanceof ServiceRoutingProvider) { + sailingService = SailingServiceHelper.createSailingServiceInstance((ServiceRoutingProvider)this); + } else { + sailingService = SailingServiceHelper.createSailingServiceInstance(); + } + } + return sailingService; } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingClientFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingClientFactory.java index 770d1fb5141..40418b7a3e9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingClientFactory.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingClientFactory.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.ui.client; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sse.gwt.client.mvp.ClientFactory; import com.sap.sse.gwt.client.mvp.ErrorView; import com.sap.sse.security.ui.client.WithSecurity; @@ -11,9 +12,12 @@ import com.sap.sse.security.ui.client.WithSecurity; * */ public interface SailingClientFactory extends ClientFactory, WithSecurity { + SailingServiceAsync getSailingService(); + SailingServiceAsync getSailingService(ProvidesLeaderboardRouting routingProvider); + MediaServiceAsync getMediaService(); - + ErrorView createErrorView(String errorMessage, Throwable errorReason); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index c9712c2fd05..b45952f0b1e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -54,7 +54,7 @@ import com.sap.sailing.domain.common.racelog.tracking.TransformationException; import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl.PreciseCompactPosition; import com.sap.sailing.domain.common.windfinder.SpotDTO; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; -import com.sap.sailing.domain.racelog.tracking.GPSFixStore; +import com.sap.sailing.domain.racelog.tracking.SensorFixStore; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration; @@ -539,7 +539,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS RaceCourseDTO getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, String fleetName) throws NotFoundException; /** - * Adds a fix to the {@link GPSFixStore}, and creates a mapping with a virtual device for exactly the current + * Adds a fix to the {@link SensorFixStore}, and creates a mapping with a virtual device for exactly the current * timepoint. * * @param timePoint @@ -756,7 +756,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS List getRaceDisplayNamesFromLeaderboard(String leaderboardName, List raceColumnNames) throws NotFoundException; - Iterable getAvailableDetailTypesForLeaderboard(String leaderboardName); + Iterable getAvailableDetailTypesForLeaderboard(String leaderboardName, RegattaAndRaceIdentifier raceOrNull); SpotDTO getWindFinderSpot(String spotId) throws Exception; @@ -778,5 +778,8 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS */ SliceRacePreperationDTO prepareForSlicingOfRace(RegattaAndRaceIdentifier raceIdentifier); - Boolean checkIfRaceIsTracked(RegattaAndRaceIdentifier race); + /** + * Checks if the given race is currently in state tracking or loading. + */ + Boolean checkIfRaceIsTracking(RegattaAndRaceIdentifier race); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index 6e291a86a4d..2c5324fc800 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -965,7 +965,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void getRaceDisplayNamesFromLeaderboard(final String leaderboardName, List raceColumnNames, AsyncCallback> callback); - void getAvailableDetailTypesForLeaderboard(String leaderboardName, + void getAvailableDetailTypesForLeaderboard(String leaderboardName, RegattaAndRaceIdentifier raceOrNull, AsyncCallback> asyncCallback); void canSliceRace(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback callback); @@ -985,5 +985,8 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan */ void getWindFinderSpot(String spotId, AsyncCallback callback); - void checkIfRaceIsTracked(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback asyncCallback); + /** + * @see SailingService#checkIfRaceIsTracking(RegattaAndRaceIdentifier) + */ + void checkIfRaceIsTracking(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback asyncCallback); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceHelper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceHelper.java new file mode 100644 index 00000000000..261db38393a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceHelper.java @@ -0,0 +1,60 @@ +package com.sap.sailing.gwt.ui.client; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.ServiceDefTarget; +import com.sap.sse.gwt.client.ServiceRoutingProvider; + +/** + * Set of convenience factory methods that create a {@link SailingServiceAsync} instance. + */ +public abstract class SailingServiceHelper { + private final static String moduleBaseURL = GWT.getModuleBaseURL(); + + private SailingServiceHelper() { + } + + /** + * Creates a new {@link SailingServiceAsync} instance, should be used when code resides in the same bundle as the sailing service code. + */ + public static SailingServiceAsync createSailingServiceInstance() { + return createSailingServiceInstance(true, null); + } + /** + * Creates a new {@link SailingServiceAsync} instance. + * + * @param sameBundle {@code false} if using {@link SailingService} from a different OSGi bundle (e.g. dashboards), {@code true} otherwise + */ + public static SailingServiceAsync createSailingServiceInstance(boolean sameBundle) { + return createSailingServiceInstance(sameBundle, null); + } + + /** + * Creates a new {@link SailingServiceAsync} instance that uses a routing provider, for code in same bundle. + */ + public static SailingServiceAsync createSailingServiceInstance(ServiceRoutingProvider routingProvider) { + return createSailingServiceInstance(true, routingProvider); + } + + /** + * Crates a new {@link SailingServiceAsync} instance. + * + * @param sameBundle {@code false} if using {@link SailingService} from a different OSGi bundle (e.g. dashboards), {@code true} otherwise + */ + public static SailingServiceAsync createSailingServiceInstance(boolean sameBundle, ServiceRoutingProvider routingProvider) { + SailingServiceAsync service = GWT.create(SailingService.class); + ServiceDefTarget serviceToRegister = (ServiceDefTarget) service; + StringBuilder baseURL = new StringBuilder(); + baseURL.append(moduleBaseURL.substring(0, moduleBaseURL.lastIndexOf('/', moduleBaseURL.length() - 2) + 1)); + if (!sameBundle) { + baseURL.append(RemoteServiceMappingConstants.WEB_CONTEXT_PATH).append("/"); + } + + baseURL.append(RemoteServiceMappingConstants.sailingServiceRemotePath); + + if (routingProvider != null) { + baseURL.append(routingProvider.routingSuffixPath()); + } + serviceToRegister.setServiceEntryPoint(baseURL.toString()); + return service; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 1ae7582ed35..e7b43637398 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -153,7 +153,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String legDetailsToShow(); String columnMoveUp(); String columnMoveDown(); - String port(); String raceStartTimeColumn(); String showOnlySelectedCompetitors(); String showSelectedCompetitorsInfo(); @@ -2076,4 +2075,11 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String multiUrlChangeNewURL(); String multiUrlNoPrefixWarning(); String multiUrlChangeExplain(); + String lastEvent(String locationOrVenue); + String teaserOverallLinkToolTip(); + String gender(); + String manage2SailEventIdBox(); + String manage2SailEventURLBox(); + String manage2SailEventIdBoxTooltip(); + String manage2SailPort(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 276103ac8f5..8954cd37957 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -153,7 +153,6 @@ overallDetailsToShow=Overall Details legDetailsToShow=Leg Details columnMoveUp=Up columnMoveDown=Down -port=Port raceStartTimeColumn=Race Started showOnlySelectedCompetitors=Show only selected competitors showSelectedCompetitorsInfo=Show info box for selected competitors @@ -1870,7 +1869,7 @@ anniversaryMajorCountdownTeaser[one]=Countdown! Only {0,number,#,###} race left anniversaryMajorCountdownDescription=We are celebrating our {0,number,#,###}th race on www.sapsailing.com! Which race will break the mark? The organizer of this anniversary race will receive a total sum of 10,000 Euro for charity purposes. The winner will be announced on this website. Stay tuned and count with us. anniversaryRepdigitCountdownTeaser=Countdown! Only {0,number,#,###} races left until the {1,number,#,###}th race. anniversaryRepdigitCountdownTeaser[one]=Countdown! Only {0,number,#,###} race left until the {1,number,#,###}th race. -anniversaryRepdigitCountdownDescription=We are celebrating our lucky number races! Who will make the {0,number, #,###}th race on www.sapsailing.com? The participants of this race will get a free top-class summer festival from SAP. The winners will be announced on this website. Stay tuned and count with us. +anniversaryRepdigitCountdownDescription=We are celebrating our lucky number races! Who will make the {0,number, #,###}th race on www.sapsailing.com? The organizers will get a free barbecue with drinks. The winners will be announced on this website. Stay tuned and count with us. anniversaryAnnouncementTeaser=Mission accomplished! {0,number,#,###} races with SAP Sailing Analytics! anniversaryAnnouncementDescription= 3,2,1... We congratulate the participants of the race {0}. You did it! We thank you for your trust in SAP Sailing Analytics and hopefully continue sailing some more 10,000 races with you! anniversaryRaceLinkText=Show anniversary race @@ -2069,4 +2068,11 @@ multiUrlChangeCannotSave=An error occured while saving multiUrlChangeSave=Save url changes multiUrlChangeNewURL=new url multiUrlNoPrefixWarning=No common prefix was found, this usually means that not all selected videos are hosted at the same location currently. Proceed at own risk! -multiUrlChangeExplain=This dialog will bulk replace common parts at the start of mediatrack urls. Make sure all urls start with the same prefix! Also please take a look at the new url column, and test the resulting urls before pressing save! \ No newline at end of file +multiUrlChangeExplain=This dialog will bulk replace common parts at the start of mediatrack urls. Make sure all urls start with the same prefix! Also please take a look at the new url column, and test the resulting urls before pressing save! +lastEvent=Last Event: {0} +teaserOverallLinkToolTip=To see the overall series, click the yellow corner +gender=Gender +manage2SailEventIdBox=Manage2Sail Event ID +manage2SailEventURLBox=Manage2Sail EventURL (json) +manage2SailEventIdBoxTooltip=Event ID or a URL that contains the event ID from Manage2Sail +manage2SailPort=Port \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index cebfe425beb..c3bbce8f1b5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -155,7 +155,6 @@ overallDetailsToShow=Regatta-Details legDetailsToShow=Schenkeldetails columnMoveUp=Hoch columnMoveDown=Runter -port=Backbord raceStartTimeColumn=Rennstart showOnlySelectedCompetitors=Nur ausgewählte Teilnehmer anzeigen showSelectedCompetitorsInfo=Infobox für ausgewählte Teilnehmer anzeigen @@ -1876,7 +1875,7 @@ anniversaryMajorCountdownTeaser[one]=Countdown! Noch {0,number,#,###} Rennen bis anniversaryMajorCountdownDescription=Wir zelebrieren unser {0,number,#,###}. Rennen auf www.sapsailing.com! Welches Rennen schafft es die Marke zu durchbrechen? Der Veranstalter des Jubiläumsrennens erhält insgesamt 10.000 Euro für Wohltätigkeitszwecke. Der Gewinner wird auf der Website bekannt gegeben. Stay tuned. anniversaryRepdigitCountdownTeaser=Countdown! Noch {0,number,#,###} Rennen bis zum {1,number,#,###}. Rennen. anniversaryRepdigitCountdownTeaser[one]=Countdown! Noch {0,number,#,###} Rennen bis zum {1,number,#,###}. Rennen. -anniversaryRepdigitCountdownDescription=Wir zelebrieren unsere Schnapszahlenrennen! Wer schafft es das {0,number,#,###}. Rennen zu machen? Dem Gewinner schenkt SAP ein Sommerfest der Extraklasse! Der Gewinner wird auf der Website bekannt gegeben. Stay tuned. +anniversaryRepdigitCountdownDescription=Wir zelebrieren unsere Schnapszahlenrennen! Wer schafft es, das {0,number,#,###}. Rennen zu machen? Den Veranstaltern schenkt SAP ein kostenloses Barbecue mit Getränken. Der Gewinner wird auf der Website bekannt gegeben. Stay tuned. anniversaryAnnouncementTeaser=Mission erfüllt! {0,number,#,###} Rennen mit SAP Sailing Analytics! anniversaryAnnouncementDescription=3,2,1... Wir gratulieren den Teilnehmern des Rennens {0}. Ihr habt es geschafft die Marke zu durchbrechen. Wir danken euch für euer Vertrauen in SAP Sailing Analytics und hoffen, dass wir noch viele weitere Rennen mit euch machen werden! anniversaryRaceLinkText=Jubiläumsrennen anzeigen @@ -2065,4 +2064,11 @@ multiUrlChangeCannotSave=Es ist ein Fehler beim Speichern aufgetreten multiUrlChangeSave=Änderungen der URLs speichern multiUrlChangeNewURL=Neue Url multiUrlNoPrefixWarning=Es wurde kein gemeinsamer Präfix gefunden, dies deutet darauf hin, dass nicht alle ausgewählten Videos vom gleichen Host kommen. Auf eigene Gefahr fortsetzen! -multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichzeitig auszutauschen. Hierzu müssen diese den gleichen Präfix besitzen. Bitte unbedingt die Spalte mit den neuen URLs beachten und diese testen bevor gespeichert wird! \ No newline at end of file +multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichzeitig auszutauschen. Hierzu müssen diese den gleichen Präfix besitzen. Bitte unbedingt die Spalte mit den neuen URLs beachten und diese testen bevor gespeichert wird! +lastEvent=Letztes Event: {0} +teaserOverallLinkToolTip=Um die Serien-Übersicht zu sehen, in die gelbe Ecke klicken +gender=Geschlecht +manage2SailEventId=Manage2Sail Event ID +manage2SailEventURL=Manage2Sail Event URL (json) +manage2SailEventIdBoxTooltip=Event ID von Manage 2 Sail oder URL die die Event ID beinhaltet +manage2SailPort=Port \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties index 4812828ca75..eb527a6c840 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Detalles generales legDetailsToShow=Detalles de tramo columnMoveUp=Arriba columnMoveDown=Abajo -port=Babor raceStartTimeColumn=Carrera iniciada showOnlySelectedCompetitors=Mostrar solo competidores seleccionados showSelectedCompetitorsInfo=Mostrar cuadro de información para competidores seleccionados diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties index 6381633c88b..fa770dc2b88 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Détails généraux legDetailsToShow=Détails de la portion de parcours columnMoveUp=Dans le lit columnMoveDown=Arrière -port=À bâbord raceStartTimeColumn=Course lancée showOnlySelectedCompetitors=Afficher uniquement concurrents sélectionnés showSelectedCompetitorsInfo=Afficher carré d''infos pour concurrents sélectionnés diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties index b553cdf0a6d..fc84b874550 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties @@ -158,7 +158,6 @@ overallDetailsToShow=全体詳細 legDetailsToShow=レグ詳細 columnMoveUp=上 columnMoveDown=下 -port=ポート raceStartTimeColumn=レーススタート済 showOnlySelectedCompetitors=選択した競技者のみ表示 showSelectedCompetitorsInfo=選択した競技者の情報ボックスを表示 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties index cc63e58185d..e4a167d5b5f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Detalhes gerais legDetailsToShow=Detalhes da perna columnMoveUp=Para cima columnMoveDown=Para baixo -port=Bombordo raceStartTimeColumn=Corrida iniciada showOnlySelectedCompetitors=Visualizar somente os competidores selecionados showSelectedCompetitorsInfo=Visualizar caixa de informações para competidores selecionados diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 152f6763935..ab370e8dc0d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -158,7 +158,6 @@ overallDetailsToShow=Общие подробности legDetailsToShow=Подробности columnMoveUp=Вверх columnMoveDown=Вниз -port=Порт raceStartTimeColumn=Гонка начата showOnlySelectedCompetitors=Показать только выбранных участников showSelectedCompetitorsInfo=Показать окно сведений для выбранных участников diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 0115aaad20f..936062c31c8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -158,7 +158,6 @@ overallDetailsToShow=总体详情 legDetailsToShow=航段详情 columnMoveUp=向上 columnMoveDown=向下 -port=端口 raceStartTimeColumn=比赛轮次开始时间 showOnlySelectedCompetitors=仅显示选择的参赛队 showSelectedCompetitorsInfo=显示所选参赛队的信息框 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index 2db2a59776e..f635328e165 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -13,6 +13,8 @@ import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.media.client.Audio; import com.google.gwt.media.client.MediaBase; import com.google.gwt.typedarrays.shared.Int8Array; @@ -40,6 +42,8 @@ import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.media.MimeType; import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator; +import com.sap.sse.gwt.client.controls.datetime.DateAndTimeInput; +import com.sap.sse.gwt.client.controls.datetime.DateTimeInput.Accuracy; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class NewMediaDialog extends DataEntryDialog { @@ -63,7 +67,7 @@ public class NewMediaDialog extends DataEntryDialog { private TextBox titleBox; - protected TextBox startTimeBox; + protected DateAndTimeInput startTimeBox; private SimplePanel infoLabel; // showing either mime type or youtube id @@ -83,6 +87,7 @@ public class NewMediaDialog extends DataEntryDialog { private Button defaultTimeButton; private SimpleBusyIndicator busyIndicator; + boolean manuallyEditedStartTime = false; public NewMediaDialog(MediaServiceAsync mediaService, TimePoint defaultStartTime, StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, DialogCallback dialogCallback) { @@ -98,39 +103,16 @@ public class NewMediaDialog extends DataEntryDialog { @Override protected MediaTrack getResult() { mediaTrack.title = titleBox.getValue(); - updateStartTimeFromUi(); - updateDurationFromUi(); connectMediaWithRace(); return mediaTrack; } - private void updateDurationFromUi() { - String duration = durationBox.getValue(); - if (duration != null && !duration.trim().isEmpty()) { - mediaTrack.duration = TimeFormatUtil.hrsMinSecToMilliSeconds(duration); - } else { - mediaTrack.duration = null; - } - } - protected void connectMediaWithRace() { Set assignedRaces = new HashSet(); assignedRaces.add(this.raceIdentifier); mediaTrack.assignedRaces = assignedRaces; } - protected void updateStartTimeFromUi() { - String startTimeText = startTimeBox.getValue(); - if (startTimeText != null && !startTimeText.equals("")) { - try { - Date startTime = TimeFormatUtil.DATETIME_FORMAT.parse(startTimeText); - mediaTrack.startTime = new MillisecondsTimePoint(startTime); - } catch (IllegalArgumentException ex) { - // TODO: highlight format error in UI - } - } - } - private void loadMediaDuration() { MediaBase mediaBase = Audio.createIfSupported(); if (mediaBase != null) { @@ -150,7 +132,9 @@ public class NewMediaDialog extends DataEntryDialog { }-*/; public void loadedmetadata(MediaElement mediaElement) { - mediaTrack.startTime = this.defaultStartTime; + if (!manuallyEditedStartTime) { + mediaTrack.startTime = this.defaultStartTime; + } double duration = mediaElement.getDuration(); if (duration > 0) { mediaTrack.duration = new MillisecondsDurationImpl((long) Math.round(duration * 1000)); @@ -166,7 +150,19 @@ public class NewMediaDialog extends DataEntryDialog { Grid formGrid = new Grid(6, 2); formGrid.setCellSpacing(3); formGrid.setWidget(0, 0, new Label(stringMessages.url() + ":")); - urlBox = createUrlBox(); + urlBox = createTextBox(null); + urlBox.addChangeHandler(new ChangeHandler() { + @Override + public void onChange(ChangeEvent event) { + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + manuallyEditedStartTime = false; + updateFromUrl(); + } + }); + } + }); formGrid.setWidget(0, 1, urlBox); formGrid.setWidget(1, 0, new Label(stringMessages.name() + ":")); titleBox = createTextBox(null); @@ -177,13 +173,23 @@ public class NewMediaDialog extends DataEntryDialog { infoLabel.setWidget(new Label("")); formGrid.setWidget(2, 1, infoLabel); formGrid.setWidget(3, 0, new Label(stringMessages.startTime() + ":")); - startTimeBox = createTextBox(null); + + startTimeBox = new DateAndTimeInput(Accuracy.MILLISECONDS); FlowPanel startTimePanel = new FlowPanel(); startTimePanel.add(startTimeBox); + startTimeBox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + manuallyEditedStartTime = true; + mediaTrack.startTime = new MillisecondsTimePoint(event.getValue()); + } + }); + defaultTimeButton = new Button(StringMessages.INSTANCE.resetStartTimeToDefault()); defaultTimeButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { + manuallyEditedStartTime = true; mediaTrack.startTime = defaultStartTime; refreshUI(); } @@ -192,6 +198,17 @@ public class NewMediaDialog extends DataEntryDialog { formGrid.setWidget(3, 1, startTimePanel); formGrid.setWidget(4, 0, new Label(stringMessages.duration() + ":")); durationBox = createTextBox(null); + durationBox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + String duration = durationBox.getValue(); + if (duration != null && !duration.trim().isEmpty()) { + mediaTrack.duration = TimeFormatUtil.hrsMinSecToMilliSeconds(duration); + } else { + mediaTrack.duration = null; + } + } + }); formGrid.setWidget(4, 1, durationBox); busyIndicator = new SimpleBusyIndicator(); formGrid.setWidget(5, 0, busyIndicator); @@ -199,22 +216,6 @@ public class NewMediaDialog extends DataEntryDialog { return mainPanel; } - private TextBox createUrlBox() { - TextBox result = createTextBox(null); - result.addChangeHandler(new ChangeHandler() { - @Override - public void onChange(ChangeEvent event) { - Scheduler.get().scheduleDeferred(new ScheduledCommand() { - @Override - public void execute() { - updateFromUrl(); - } - }); - } - }); - return result; - } - protected void updateFromUrl() { String url = urlBox.getValue(); String youtubeId = YoutubeApi.getIdByUrl(url); @@ -315,7 +316,7 @@ public class NewMediaDialog extends DataEntryDialog { refreshUI(); } - private void refreshUI() { + protected void refreshUI() { titleBox.setValue(mediaTrack.title, DONT_FIRE_EVENTS); if (mediaTrack.isYoutube()) { infoLabelLabel.setText(stringMessages.youtubeId() + ":"); @@ -335,10 +336,7 @@ public class NewMediaDialog extends DataEntryDialog { } } TimePoint startTime = mediaTrack.startTime != null ? mediaTrack.startTime : defaultStartTime; - String startTimeText = TimeFormatUtil.DATETIME_FORMAT.format(startTime.asDate()); - - startTimeBox.setText(startTimeText); - updateStartTimeFromUi(); + startTimeBox.setValue(startTime == null ? null : startTime.asDate()); if (mediaTrack.duration != null) { durationBox.setText(TimeFormatUtil.durationToHrsMinSec(mediaTrack.duration)); } else { @@ -439,7 +437,7 @@ public class NewMediaDialog extends DataEntryDialog { } else { mediaTrack.duration = result.getDuration(); mediaTrack.mimeType = result.isSpherical() ? MimeType.mp4panorama : MimeType.mp4; - if (result.getRecordStartedTime() != null) { + if (result.getRecordStartedTime() != null && !manuallyEditedStartTime) { mediaTrack.startTime = new MillisecondsTimePoint(result.getRecordStartedTime()); } refreshUI(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaWithRaceSelectionDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaWithRaceSelectionDialog.java index 9b13ed8f305..4ed9ecb38c5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaWithRaceSelectionDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaWithRaceSelectionDialog.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.ui.client.media; +import java.util.Date; import java.util.Set; import com.google.gwt.user.client.ui.VerticalPanel; @@ -13,7 +14,6 @@ import com.sap.sailing.gwt.ui.client.RegattasDisplayer; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sse.common.TimePoint; -import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.gwt.client.ErrorReporter; public class NewMediaWithRaceSelectionDialog extends NewMediaDialog { @@ -41,19 +41,7 @@ public class NewMediaWithRaceSelectionDialog extends NewMediaDialog { this.regattasDisplayers = regattasDisplayers; } - protected void updateStartTimeFromUi() { - try { - String startTime = startTimeBox.getValue(); - if (startTime != null && !startTime.equals("")) { - mediaTrack.startTime = new MillisecondsTimePoint(TimeFormatUtil.DATETIME_FORMAT.parse(startTime)); - regattaRefresher.fillRegattas(); - listOfRacesForMedia.setVisible(true); - } - } catch (Exception e) { - listOfRacesForMedia.setVisible(false); - } - } - + @Override protected void connectMediaWithRace() { mediaTrack.assignedRaces = racesForMediaDialog.getAssignedRaces(); } @@ -81,6 +69,7 @@ public class NewMediaWithRaceSelectionDialog extends NewMediaDialog { return listOfRacesForMedia = racesForMediaDialog.getAdditionalWidget(); } + @Override protected Widget getAdditionalWidget() { VerticalPanel mainPanel = (VerticalPanel) super.getAdditionalWidget(); mainPanel.add(racesForMedia()); @@ -88,4 +77,17 @@ public class NewMediaWithRaceSelectionDialog extends NewMediaDialog { return mainPanel; } + @Override + protected void refreshUI() { + super.refreshUI(); + try { + Date value = startTimeBox.getValue(); + if (value != null) { + regattaRefresher.fillRegattas(); + listOfRacesForMedia.setVisible(true); + } + } catch (Exception e) { + listOfRacesForMedia.setVisible(false); + } + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java index c3a85cc3274..5fb4d1b13c6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/EditMarkPositionPanel.java @@ -114,7 +114,6 @@ public class EditMarkPositionPanel extends AbstractRaceChart i private LinePlotOptions markSeriesPlotOptions; private final Label noMarkSelectedLabel; private MapWidget map; - private boolean visible; private List courseMarkClickHandlers; private Map> marksFromToTimes; @@ -131,6 +130,7 @@ public class EditMarkPositionPanel extends AbstractRaceChart i private final MarkPositionService markPositionService; private final RaceIdentifierToLeaderboardRaceColumnAndFleetMapper raceIdentifierToLeaderboardRaceColumnAndFleetMapper; + protected boolean nonTrackingWarningWasDisplayed; public EditMarkPositionPanel(Component parent, ComponentContext context, final RaceMap raceMap, final SingleRaceLeaderboardPanel leaderboardPanel, @@ -373,20 +373,25 @@ public class EditMarkPositionPanel extends AbstractRaceChart i overlay.setVisible(false); if (currentFixPositionChooser == null) { OverlayClickHandler.this.unregisterAll(); - currentFixPositionChooser = new FixPositionChooser(EditMarkPositionPanel.this, stringMessages, map, getIndexOfFixInPolyline(mark, fix), polyline.getPath(), overlay, new Callback() { - @Override - public void onFailure(Exception reason) { - overlay.setVisible(true); - OverlayClickHandler.this.registerAll(); - resetCurrentFixPositionChooser(); - } - @Override - public void onSuccess(Position result) { - editMarkFix(mark, fix, result); - overlay.setVisible(true); - OverlayClickHandler.this.registerAll(); - resetCurrentFixPositionChooser(); - } + checkIfTracking(() -> { + currentFixPositionChooser = new FixPositionChooser(EditMarkPositionPanel.this, + stringMessages, map, getIndexOfFixInPolyline(mark, fix), polyline.getPath(), + overlay, new Callback() { + @Override + public void onFailure(Exception reason) { + overlay.setVisible(true); + OverlayClickHandler.this.registerAll(); + resetCurrentFixPositionChooser(); + } + + @Override + public void onSuccess(Position result) { + editMarkFix(mark, fix, result); + overlay.setVisible(true); + OverlayClickHandler.this.registerAll(); + resetCurrentFixPositionChooser(); + } + }); }); } popup.hide(); @@ -407,26 +412,28 @@ public class EditMarkPositionPanel extends AbstractRaceChart i } private void addDeleteItemAndShowPopup(boolean enableDelete) { - final MenuItem delete; + Runnable r = () -> { + final MenuItem delete = new MenuItem(stringMessages.deleteFix(), new ScheduledCommand() { + @Override + public void execute() { + if (enableDelete) { + removeMarkFix(mark, fix); + popup.hide(); + } + } + }); + if (!enableDelete) { + delete.setEnabled(false); + delete.setTitle(stringMessages.theDeletionOfThisFix()); + } + menu.addItem(delete); + popup.setWidget(menu); + }; if (enableDelete) { - delete = new MenuItem(stringMessages.deleteFix(), new ScheduledCommand() { - @Override - public void execute() { - removeMarkFix(mark, fix); - popup.hide(); - } - }); + checkIfTracking(r); } else { - delete = new MenuItem(stringMessages.deleteFix(), new ScheduledCommand() { - @Override - public void execute() { - } - }); - delete.setEnabled(false); - delete.setTitle(stringMessages.theDeletionOfThisFix()); + r.run(); } - menu.addItem(delete); - popup.setWidget(menu); } }); } @@ -704,20 +711,8 @@ public class EditMarkPositionPanel extends AbstractRaceChart i } public void setVisible(boolean visible) { - this.visible = visible; - if(visible && !isVisible()){ - sailingService.checkIfRaceIsTracked(selectedRaceIdentifier, new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - Window.alert(stringMessages.serverError()); - } - @Override - public void onSuccess(Boolean result) { - if (Boolean.FALSE.equals(result)) { - Window.alert(stringMessages.positionEditOnNonTrackingRace()); - } - } - }); + if (visible && !isVisible()) { + checkIfTracking(() -> {}); } if (map == null) { map = raceMap.getMap(); @@ -725,7 +720,7 @@ public class EditMarkPositionPanel extends AbstractRaceChart i setMap(map); } } - if (this.visible) { + if (visible) { if (sideBySideComponentViewer != null) { sideBySideComponentViewer.setLeftComponent(marksPanel); sideBySideComponentViewer.setLeftComponentToggleButtonVisible(false); @@ -756,7 +751,30 @@ public class EditMarkPositionPanel extends AbstractRaceChart i raceMap.unregisterAllCourseMarkInfoWindowClickHandlers(); raceMap.registerAllCourseMarkInfoWindowClickHandlers(); } - super.setVisible(this.visible); + super.setVisible(visible); + } + + private void checkIfTracking(Runnable continuation) { + if (nonTrackingWarningWasDisplayed) { + continuation.run(); + } else { + sailingService.checkIfRaceIsTracking(selectedRaceIdentifier, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + Window.alert(stringMessages.serverError()); + continuation.run(); + } + + @Override + public void onSuccess(Boolean result) { + if (Boolean.FALSE.equals(result)) { + Window.alert(stringMessages.positionEditOnNonTrackingRace()); + nonTrackingWarningWasDisplayed = true; + } + continuation.run(); + } + }); + } } @Override @@ -943,17 +961,21 @@ public class EditMarkPositionPanel extends AbstractRaceChart i * Is thrown when another fix position chooser is still open */ public void createFixPositionChooserToAddFixToMark(MarkDTO mark, Callback callback) { - if (currentFixPositionChooser != null) { + if (currentFixPositionChooser != null) { return; } - int index = 0; - for (GPSFixDTO fix : marks.get(mark).keySet()) { - if (fix.timepoint.after(timer.getTime())) - break; - index++; - } - Polyline polyline = polylines.get(mark); - currentFixPositionChooser = new FixPositionChooser(this, stringMessages, map, index, polyline != null ? polyline.getPath() : null, map.getCenter(), raceMap.getCoordinateSystem(), callback); + checkIfTracking(() -> { + int index = 0; + for (GPSFixDTO fix : marks.get(mark).keySet()) { + if (fix.timepoint.after(timer.getTime())) + break; + index++; + } + Polyline polyline = polylines.get(mark); + currentFixPositionChooser = new FixPositionChooser(EditMarkPositionPanel.this, stringMessages, map, index, + polyline != null ? polyline.getPath() : null, map.getCenter(), raceMap.getCoordinateSystem(), + callback); + }); } public void resetCurrentFixPositionChooser() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionService.java index c56dd513909..f11866ee636 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionService.java @@ -50,12 +50,6 @@ public interface MarkPositionService { void getMarksInTrackedRace(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, AsyncCallback> callback); - // Jonas Dann: I had no great idea for handling of massive amount of fixes. An exception is no real option. I - // think the fixes have to be thinned out somehow and maybe shown in more detail when zoomed in. The aggregation of - // fixes is in my eyes no option, because then you have to define behaviour when moving them. - // I had no remaining time to incorporate the thinned out variable in my code. - void getMarkTracks(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, AsyncCallback callback); - void getMarkTrack(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, String markIdAsString, AsyncCallback callback); /** diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionServiceForSailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionServiceForSailingService.java index 410c67aaa92..05fe6a96b81 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionServiceForSailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MarkPositionServiceForSailingService.java @@ -20,11 +20,6 @@ public class MarkPositionServiceForSailingService implements MarkPositionService sailingService.getMarksInTrackedRace(raceIdentifier.getLeaderboardName(), raceIdentifier.getRaceColumnName(), raceIdentifier.getFleetName(), callback); } - @Override - public void getMarkTracks(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, AsyncCallback callback) { - sailingService.getMarkTracks(raceIdentifier.getLeaderboardName(), raceIdentifier.getRaceColumnName(), raceIdentifier.getFleetName(), callback); - } - @Override public void getMarkTrack(LeaderboardNameRaceColumnNameAndFleetName raceIdentifier, String markIdAsString, AsyncCallback callback) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java index cb5558f9bc6..c9cbcf86dd9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import org.moxieapps.gwt.highcharts.client.Axis; @@ -226,65 +227,46 @@ public class WindChart extends AbstractRaceChart implements R } private void updateVisibleSeries() { - List currentSeries = Arrays.asList(chart.getSeries()); - Set visibleSeries = new HashSet(currentSeries); + final Set visibleSeries = new HashSet(Arrays.asList(chart.getSeries())); if (preselectFilter != null) { forceSeriesSelection(visibleSeries, windSourceDirectionSeries); forceSeriesSelection(visibleSeries, windSourceSpeedSeries); } else { - if (settings.isShowWindDirectionsSeries()) { - for (Map.Entry e : windSourceDirectionSeries.entrySet()) { - Series series = e.getValue(); - if (settings.getWindDirectionSourcesToDisplay().contains(e.getKey().getType())) { - if (!visibleSeries.contains(series)) { - chart.addSeries(series, false, false); - } - series.select(true); // ensures that the checkbox will be ticked - series.setVisible(true); //and that it is actually being rendered - } else { - if (visibleSeries.contains(series)) { - chart.removeSeries(series, false); - } - } - } - } else { - for (Map.Entry e : windSourceDirectionSeries.entrySet()) { - Series series = e.getValue(); - if (visibleSeries.contains(series)) { - chart.removeSeries(series, false); - } - } - } - - if (settings.isShowWindSpeedSeries()) { - for (Map.Entry e : windSourceSpeedSeries.entrySet()) { - Series series = e.getValue(); - if (settings.getWindSpeedSourcesToDisplay().contains(e.getKey().getType())) { - if (!visibleSeries.contains(series)) { - chart.addSeries(series, false, false); - } - series.select(true); // ensures that the checkbox will be ticked - series.setVisible(true); //and that it is actually being rendered - } else { - if (visibleSeries.contains(series)) { - chart.removeSeries(series, false); - } - } - } - } else { - for (Map.Entry e : windSourceSpeedSeries.entrySet()) { - Series series = e.getValue(); - if (visibleSeries.contains(series)) { - chart.removeSeries(series, false); - } - } - } + final boolean showDirectionSeries = settings.isShowWindDirectionsSeries(); + final Set directionSourceTypesToDisplay = settings.getWindDirectionSourcesToDisplay(); + updateSeries(visibleSeries, windSourceDirectionSeries, showDirectionSeries, directionSourceTypesToDisplay); + final boolean showSpeedSeries = settings.isShowWindSpeedSeries(); + final Set speedSourceTypesToDisplay = settings.getWindSpeedSourcesToDisplay(); + updateSeries(visibleSeries, windSourceSpeedSeries, showSpeedSeries, speedSourceTypesToDisplay); } onResize(); } + private void updateSeries(final Set visibleSeries, final Map windSourceToSeriesMap, + final boolean showSeries, final Set windSourceTypesToDisplay) { + if (showSeries) { + for (Entry entry : windSourceToSeriesMap.entrySet()) { + final Series series = entry.getValue(); + if (windSourceTypesToDisplay.contains(entry.getKey().getType())) { + if (!visibleSeries.contains(series)) { + chart.addSeries(series, false, false); + } + } else if (visibleSeries.contains(series)) { + chart.removeSeries(series, false); + } + } + } else { + for (Entry entry : windSourceToSeriesMap.entrySet()) { + final Series series = entry.getValue(); + if (visibleSeries.contains(series)) { + chart.removeSeries(series, false); + } + } + } + } + private boolean forceSeriesSelection(Set visibleSeries, Map toProcess) { boolean wasInResult = false; for (Map.Entry e : toProcess.entrySet()) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java index ea87432c188..7a6e7de725c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java @@ -44,7 +44,8 @@ public class BoatClassVectorGraphicsResolver { BoatClassMasterdata.A_CAT, BoatClassMasterdata.TORNADO, BoatClassMasterdata.FLYING_PHANTOM); BoatClassVectorGraphics keelBoatWithGennaker = new KeelBoatWithGennakerVectorGraphics(BoatClassMasterdata.J70, BoatClassMasterdata.B_ONE, BoatClassMasterdata.J80, BoatClassMasterdata.LASER_SB3, BoatClassMasterdata.LONGTZE, - BoatClassMasterdata.RS_FEVA, BoatClassMasterdata.RS_TERA, BoatClassMasterdata.RS100, BoatClassMasterdata.TP52); + BoatClassMasterdata.RS_FEVA, BoatClassMasterdata.RS_TERA, BoatClassMasterdata.RS100, BoatClassMasterdata.TP52, + BoatClassMasterdata.CLUB_SWAN_50); BoatClassVectorGraphics dinghyWithSpinnaker = new DinghyWithSpinnakerVectorGraphics(BoatClassMasterdata._420, BoatClassMasterdata._470, BoatClassMasterdata._5O5, BoatClassMasterdata.CADET, BoatClassMasterdata.FLYING_DUTCHMAN, BoatClassMasterdata.FOLKBOAT, BoatClassMasterdata.DYAS, BoatClassMasterdata.DRAGON_INT, @@ -53,7 +54,7 @@ public class BoatClassVectorGraphicsResolver { BoatClassMasterdata.TOM_28_MAX, BoatClassMasterdata.DELPHIA_24, BoatClassMasterdata.RS200, BoatClassMasterdata.RS400, BoatClassMasterdata.RS500, BoatClassMasterdata.RS800, BoatClassMasterdata.STREAMLINE, BoatClassMasterdata.SWAN_45, BoatClassMasterdata.TEENY, BoatClassMasterdata.X_99, - BoatClassMasterdata.TRIAS, BoatClassMasterdata.VENT_D_OUEST); + BoatClassMasterdata.TRIAS, BoatClassMasterdata.VENT_D_OUEST, BoatClassMasterdata.FLYING_JUNIOR, BoatClassMasterdata.VAURIEN); BoatClassVectorGraphics circle = new CircleVectorGraphics(BoatClassMasterdata.RUNNING); defaultBoatVectorGraphics = dinghyWithSpinnaker; // TODO see bug 2571; this should be a slup-rigged icon working for 470, 505, J/70 etc. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableLifecycle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableLifecycle.java index 29e75a75493..bb443da6d15 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableLifecycle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableLifecycle.java @@ -24,7 +24,7 @@ public class ManeuverTableLifecycle implements ComponentLifecycle selectedManeuverTypes; - private Iterable defaultTypes = Arrays.asList(ManeuverType.values()); - @Override protected void addChildSettings() { - selectedManeuverTypes = new EnumSetSetting<>("selectedManeuverTypes", this, defaultTypes, + selectedManeuverTypes = new EnumSetSetting<>("selectedManeuverTypes", this, + Arrays.asList(ManeuverType.TACK, ManeuverType.JIBE, ManeuverType.PENALTY_CIRCLE), ManeuverType::valueOf); } @@ -25,9 +24,6 @@ public class ManeuverTableSettings extends AbstractGenericSerializableSettings { * The default settings */ public ManeuverTableSettings() { - selectedManeuverTypes.addValue(ManeuverType.TACK); - selectedManeuverTypes.addValue(ManeuverType.JIBE); - selectedManeuverTypes.addValue(ManeuverType.PENALTY_CIRCLE); } public ManeuverTableSettings(Set selectedManeuverTypes) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java index 425cc616288..acb5b07e0b8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java @@ -78,7 +78,6 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { queryDefinitionProviderWithControls = new QueryDefinitionProviderWithControls(null, null, session, dataMiningService, DataMiningEntryPoint.this, settingsControl, settingsManager, resultsPresenter); - queryDefinitionProviderWithControls.getEntryWidget().addStyleName("dataMiningPanel"); selectionDockPanel.add(queryDefinitionProviderWithControls.getEntryWidget()); queryRunner = new SimpleQueryRunner(null, null, session, dataMiningService, DataMiningEntryPoint.this, queryDefinitionProviderWithControls, resultsPresenter); @@ -89,7 +88,7 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { * automatic execution of queries. Re-enable this, when this functionality is desired again. */ // settingsControl.addSettingsComponent(queryRunner); - SplitLayoutPanel splitPanel = new SplitLayoutPanel(15); + SplitLayoutPanel splitPanel = new SplitLayoutPanel(10); splitPanel.addSouth(resultsPresenter.getEntryWidget(), 350); splitPanel.add(selectionDockPanel); return splitPanel; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/NumberPairResultsPresenter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/NumberPairResultsPresenter.java index 0ead4ea8645..ccfe0118e7e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/NumberPairResultsPresenter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/NumberPairResultsPresenter.java @@ -86,20 +86,16 @@ public class NumberPairResultsPresenter extends AbstractSailingResultsPresenter< protected void internalShowResults(QueryResultDTO res) { result = res; createAndAddSeriesToChart(); - for (Entry resultEntry : result.getResults().entrySet()) { @SuppressWarnings("unchecked") PairWithStats value = (PairWithStats) resultEntry.getValue(); - - if(value.getIndividualPairs() != null) { - for(Pair pair : value.getIndividualPairs()) { + if (value.getIndividualPairs() != null) { + for (Pair pair : value.getIndividualPairs()) { createAndAddPoint(resultEntry.getKey(), pair.getA(), pair.getB()); } - } - else { + } else { createAndAddPoint(resultEntry.getKey(), value.getAverage().getA(), value.getAverage().getB()); } - } chart.getXAxis().setAxisTitleText(result.getResultSignifier()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/TabbedSailingResultsPresenter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/TabbedSailingResultsPresenter.java index 69efcd24436..88b9400075d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/TabbedSailingResultsPresenter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/presentation/TabbedSailingResultsPresenter.java @@ -7,28 +7,28 @@ import com.sap.sailing.gwt.ui.polarmining.PolarResultsPresenter; import com.sap.sailing.polars.datamining.shared.PolarAggregation; import com.sap.sailing.polars.datamining.shared.PolarBackendData; import com.sap.sse.datamining.shared.data.PairWithStats; -import com.sap.sse.datamining.ui.client.presentation.AbstractTabbedResultsPresenter; +import com.sap.sse.datamining.ui.client.presentation.TabbedResultsPresenter; import com.sap.sse.datamining.ui.client.presentation.ResultsChart.DrillDownCallback; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public class TabbedSailingResultsPresenter extends AbstractTabbedResultsPresenter { +public class TabbedSailingResultsPresenter extends TabbedResultsPresenter { public TabbedSailingResultsPresenter(Component parent, ComponentContext context, DrillDownCallback drillDownCallback, StringMessages stringMessages) { super(parent, context, drillDownCallback); - registerResultPresenter(PolarAggregation.class, + registerResultsPresenter(PolarAggregation.class, new PolarResultsPresenter(TabbedSailingResultsPresenter.this, getComponentContext(), stringMessages)); - registerResultPresenter(PolarBackendData.class, new PolarBackendResultsPresenter( + registerResultsPresenter(PolarBackendData.class, new PolarBackendResultsPresenter( TabbedSailingResultsPresenter.this, getComponentContext(), stringMessages)); - registerResultPresenter(ManeuverSpeedDetailsAggregation.class, new ManeuverSpeedDetailsResultsPresenter( + registerResultsPresenter(ManeuverSpeedDetailsAggregation.class, new ManeuverSpeedDetailsResultsPresenter( TabbedSailingResultsPresenter.this, getComponentContext(), stringMessages)); - registerResultPresenter(PairWithStats.class, new NumberPairResultsPresenter(TabbedSailingResultsPresenter.this, + registerResultsPresenter(PairWithStats.class, new NumberPairResultsPresenter(TabbedSailingResultsPresenter.this, getComponentContext(), stringMessages)); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java index 6ed205f16b9..9133d11f089 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.leaderboard; import java.util.UUID; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; @@ -17,6 +18,7 @@ import com.sap.sailing.domain.common.DetailType; import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO; import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication; import com.sap.sailing.gwt.common.authentication.SAPSailingHeaderWithAuthentication; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardContextDefinition; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardPerspectiveLifecycle; import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardPerspectiveOwnSettings; @@ -25,6 +27,8 @@ import com.sap.sailing.gwt.settings.client.leaderboard.MultiCompetitorLeaderboar import com.sap.sailing.gwt.settings.client.leaderboard.MultiCompetitorLeaderboardChartSettings; import com.sap.sailing.gwt.settings.client.utils.StoredSettingsLocationFactory; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.SailingServiceHelper; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; @@ -40,12 +44,13 @@ import com.sap.sse.gwt.settings.SettingsToUrlSerializer; import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage; import com.sap.sse.security.ui.settings.StoredSettingsLocation; -public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { +public class LeaderboardEntryPoint extends AbstractSailingEntryPoint implements ProvidesLeaderboardRouting { public static final long DEFAULT_REFRESH_INTERVAL_MILLIS = 3000l; private static final Logger logger = Logger.getLogger(LeaderboardEntryPoint.class.getName()); private StringMessages stringmessages = StringMessages.INSTANCE; + private UUID eventId; private String leaderboardName; private String leaderboardGroupName; private AbstractLeaderboardDTO leaderboardDTO; @@ -58,7 +63,7 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { leaderboardContextDefinition = new SettingsToUrlSerializer() .deserializeFromCurrentLocation(new LeaderboardContextDefinition()); - final UUID eventId = leaderboardContextDefinition.getEventId(); + eventId = leaderboardContextDefinition.getEventId(); leaderboardName = leaderboardContextDefinition.getLeaderboardName(); leaderboardGroupName = leaderboardContextDefinition.getLeaderboardGroupName(); @@ -67,9 +72,8 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { if (eventId == null) { checkLeaderboardNameAndCreateUI(); // use null-initialized event field } else { - // TODO it seems we do not really need the EventDTO. What's the intention of loading it? Should we - // visualize some information in the header? - sailingService.getEventById(eventId, /* withStatisticalData */false, + // TODO it seems we do not really need the EventDTO. What's the intention of loading it? Should we visualize some information in the header? + getSailingService().getEventById(eventId, /* withStatisticalData */false, new MarkedAsyncCallback(new AsyncCallback() { @Override public void onFailure(Throwable caught) { @@ -78,7 +82,7 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { @Override public void onSuccess(EventDTO result) { - if (result != null) { + if(result != null) { leaderboardDTO = result.getLeaderboardByName(leaderboardName); } checkLeaderboardNameAndCreateUI(); @@ -89,27 +93,27 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { RootPanel.get().add(new Label(getStringMessages().noSuchLeaderboard())); } } - + private void checkLeaderboardNameAndCreateUI() { - if (leaderboardDTO == null) { - sailingService.getLeaderboard(leaderboardName, - new MarkedAsyncCallback(new AsyncCallback() { - @Override - public void onSuccess(StrippedLeaderboardDTO leaderboardDTO) { - if (leaderboardDTO != null) { - LeaderboardEntryPoint.this.leaderboardDTO = leaderboardDTO; - Window.setTitle(leaderboardName); - loadSettingsAndCreateUI(); - } else { - RootPanel.get().add(new Label(getStringMessages().noSuchLeaderboard())); + if(leaderboardDTO == null) { + getSailingService().getLeaderboard(leaderboardName, + new MarkedAsyncCallback(new AsyncCallback() { + @Override + public void onSuccess(StrippedLeaderboardDTO leaderboardDTO) { + if (leaderboardDTO != null) { + LeaderboardEntryPoint.this.leaderboardDTO = leaderboardDTO; + Window.setTitle(leaderboardName); + loadSettingsAndCreateUI(); + } else { + RootPanel.get().add(new Label(getStringMessages().noSuchLeaderboard())); + } } - } - @Override - public void onFailure(Throwable t) { - reportError("Error trying to obtain list of leaderboard names: " + t.getMessage()); - } - })); + @Override + public void onFailure(Throwable t) { + reportError("Error trying to obtain list of leaderboard names: " + t.getMessage()); + } + })); } else { loadSettingsAndCreateUI(); } @@ -118,13 +122,13 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { private void loadSettingsAndCreateUI() { long delayBetweenAutoAdvancesInMilliseconds = DEFAULT_REFRESH_INTERVAL_MILLIS; final Timer timer = new Timer(PlayModes.Live, PlayStates.Paused, delayBetweenAutoAdvancesInMilliseconds); - + // make a single live request as the default but don't continue to play by default final StoredSettingsLocation storageDefinition = StoredSettingsLocationFactory .createStoredSettingsLocatorForLeaderboard(leaderboardContextDefinition); - sailingService.getAvailableDetailTypesForLeaderboard(leaderboardName, - new AsyncCallback>() { + getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, + null, new AsyncCallback>() { @Override public void onFailure(Throwable caught) { logger.log(Level.SEVERE, "Could not load detailtypes", caught); @@ -132,6 +136,13 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { @Override public void onSuccess(Iterable result) { + final Function sailingServiceFactory = leaderboardName -> SailingServiceHelper + .createSailingServiceInstance(new ProvidesLeaderboardRouting() { + @Override + public String getLeaderboardName() { + return leaderboardName; + } + }); if (leaderboardDTO.type.isMetaLeaderboard()) { // overall MetaLeaderboardPerspectiveLifecycle rootComponentLifeCycle = new MetaLeaderboardPerspectiveLifecycle( @@ -144,10 +155,9 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { public void onSuccess( PerspectiveCompositeSettings defaultSettings) { configureWithSettings(defaultSettings, timer); - final MetaLeaderboardViewer leaderboardViewer = new MetaLeaderboardViewer( null, context, rootComponentLifeCycle, defaultSettings, - sailingService, new AsyncActionsExecutor(), timer, null, + sailingServiceFactory, new AsyncActionsExecutor(), timer, null, leaderboardGroupName, leaderboardName, LeaderboardEntryPoint.this, getStringMessages(), getActualChartDetailType(defaultSettings), result); @@ -174,8 +184,8 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { @Override public void onSuccess( PerspectiveCompositeSettings defaultSettings) { - sailingService.getAvailableDetailTypesForLeaderboard(leaderboardName, - new AsyncCallback>() { + getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, + null, new AsyncCallback>() { @Override public void onFailure(Throwable caught) { @@ -189,7 +199,7 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { configureWithSettings(defaultSettings, timer); final MultiRaceLeaderboardViewer leaderboardViewer = new MultiRaceLeaderboardViewer( null, context, rootComponentLifeCycle, - defaultSettings, sailingService, + defaultSettings, sailingServiceFactory, new AsyncActionsExecutor(), timer, leaderboardGroupName, leaderboardName, LeaderboardEntryPoint.this, getStringMessages(), @@ -212,25 +222,26 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { } } }); + } - + private DetailType getActualChartDetailType( PerspectiveCompositeSettings settings) { MultiCompetitorLeaderboardChartSettings chartSettings = settings .findSettingsByComponentId(MultiCompetitorLeaderboardChartLifecycle.ID); DetailType chartDetailType = chartSettings == null ? null : chartSettings.getDetailType(); - + if (chartDetailType == DetailType.REGATTA_NET_POINTS_SUM) { return chartDetailType; } return MultiCompetitorLeaderboardChartSettings.getDefaultDetailType(leaderboardDTO.type.isMetaLeaderboard()); } - + private void createUi(Widget leaderboardViewer, PerspectiveCompositeSettings settings, Timer timer, LeaderboardContextDefinition leaderboardContextSettings) { LeaderboardPerspectiveOwnSettings ownSettings = settings.getPerspectiveOwnSettings(); - + DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX); RootLayoutPanel.get().add(mainPanel); if (!ownSettings.isEmbedded()) { @@ -258,11 +269,15 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint { + ");-o-transform-origin: 0 0;-webkit-transform: scale(" + zoomTo + ");-webkit-transform-origin: 0 0;"); } - + if (perspectiveOwnSettings.isLifePlay()) { timer.setPlayMode(PlayModes.Live); // the leaderboard, viewed via the entry point, goes "live" and "playing" // if an auto-refresh } } - + + @Override + public String getLeaderboardName() { + return leaderboardName; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index 3c55078e36c..bf8166a6b7c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -777,7 +777,7 @@ public abstract class LeaderboardPanel extends A protected abstract void setDefaultRaceColumnSelection(LS settings); - private void setValuesWithReferenceOrder(Iterable valuesToSet, List referenceOrder, + private void setValuesWithReferenceOrder(Iterable valuesToSet, Collection referenceOrder, List collectionToSetValuesTo) { collectionToSetValuesTo.clear(); collectionToSetValuesTo.addAll(referenceOrder); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java index 7a14b373bec..0d43b6594fa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/MetaLeaderboardViewer.java @@ -1,5 +1,7 @@ package com.sap.sailing.gwt.ui.leaderboard; +import java.util.function.Function; + import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; @@ -37,12 +39,12 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer> componentContext, MetaLeaderboardPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, + Function sailingServiceFactory, AsyncActionsExecutor asyncActionsExecutor, Timer timer, String preselectedLeaderboardName, String leaderboardGroupName, String metaLeaderboardName, ErrorReporter errorReporter, StringMessages stringMessages, DetailType chartDetailType, Iterable availableDetailTypes) { this(parent, componentContext, lifecycle, settings, new CompetitorSelectionModel(/* hasMultiSelection */true), - sailingService, asyncActionsExecutor, timer, + sailingServiceFactory, asyncActionsExecutor, timer, preselectedLeaderboardName, leaderboardGroupName, metaLeaderboardName, errorReporter, stringMessages, chartDetailType, availableDetailTypes); } @@ -51,7 +53,7 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer> componentContext, MetaLeaderboardPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, - CompetitorSelectionModel competitorSelectionModel, SailingServiceAsync sailingService, + CompetitorSelectionModel competitorSelectionModel, Function sailingServiceFactory, AsyncActionsExecutor asyncActionsExecutor, Timer timer, String preselectedLeaderboardName, String leaderboardGroupName, String metaLeaderboardName, ErrorReporter errorReporter, StringMessages stringMessages, @@ -59,7 +61,8 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer availableDetailTypes; + private Function sailingServiceFactory; public MultiLeaderboardProxyPanel(Component parent, ComponentContext context, - SailingServiceAsync sailingService, String metaLeaderboardName, + Function sailingServiceFactory, String metaLeaderboardName, AsyncActionsExecutor asyncActionsExecutor, Timer timer, boolean isEmbedded, String preselectedLeaderboardName, ErrorReporter errorReporter, StringMessages stringMessages, boolean showRaceDetails, boolean autoExpandLastRaceColumn, MultiRaceLeaderboardSettings settings, FlagImageResolver flagImageResolver, Iterable availableDetailTypes) { super(parent, context); + this.sailingServiceFactory = sailingServiceFactory; loadedSettings = settings; this.availableDetailTypes = availableDetailTypes; this.stringMessages = stringMessages; this.errorReporter = errorReporter; - this.sailingService = sailingService; + this.sailingService = sailingServiceFactory.apply(metaLeaderboardName); this.metaLeaderboardName = metaLeaderboardName; this.asyncActionsExecutor = asyncActionsExecutor; this.showRaceDetails = showRaceDetails; @@ -237,8 +240,9 @@ public class MultiLeaderboardProxyPanel extends AbstractLazyComponent> componentContext, LeaderboardPerspectiveLifecycle lifecycle, PerspectiveCompositeSettings settings, - final SailingServiceAsync sailingService, final AsyncActionsExecutor asyncActionsExecutor, + final Function sailingServiceFactory, final AsyncActionsExecutor asyncActionsExecutor, final Timer timer, final String leaderboardGroupName, String leaderboardName, final ErrorReporter errorReporter, final StringMessages stringMessages, DetailType chartDetailType, Iterable availableDetailTypes) { this(parent, componentContext, lifecycle, settings, new CompetitorSelectionModel(/* hasMultiSelection */true), - sailingService, asyncActionsExecutor, timer, + sailingServiceFactory, asyncActionsExecutor, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, chartDetailType, availableDetailTypes); } @@ -51,13 +52,16 @@ public class MultiRaceLeaderboardViewer extends AbstractLeaderboardViewer settings, CompetitorSelectionModel competitorSelectionModel, - final SailingServiceAsync sailingService, final AsyncActionsExecutor asyncActionsExecutor, + final Function sailingServiceFactory, final AsyncActionsExecutor asyncActionsExecutor, final Timer timer, final String leaderboardGroupName, String leaderboardName, final ErrorReporter errorReporter, final StringMessages stringMessages, DetailType chartDetailType, Iterable availableDetailTypes) { super(parent, componentContext, lifecycle, settings, competitorSelectionModel, asyncActionsExecutor, timer, stringMessages); - init(new MultiRaceLeaderboardPanel(this, getComponentContext(), sailingService, asyncActionsExecutor, + + final SailingServiceAsync sailingServiceForMainLeaderboard = sailingServiceFactory.apply(leaderboardName); + + init(new MultiRaceLeaderboardPanel(this, getComponentContext(), sailingServiceForMainLeaderboard, asyncActionsExecutor, settings.findSettingsByComponentId(LeaderboardPanelLifecycle.ID), false, competitorSelectionModel, timer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages, settings.getPerspectiveOwnSettings().isShowRaceDetails(), @@ -71,9 +75,8 @@ public class MultiRaceLeaderboardViewer extends AbstractLeaderboardViewer>( + if (perspectiveSettings.isShowOverallLeaderboard()) { + sailingServiceForMainLeaderboard.getOverallLeaderboardNamesContaining(leaderboardName, new MarkedAsyncCallback>( new AsyncCallback>() { @Override public void onSuccess(List result) { - if(result.size() == 1) { + if (result.size() == 1) { String overallLeaderboardName = result.get(0); + final SailingServiceAsync sailingServiceForOverallLeaderboard = sailingServiceFactory.apply(overallLeaderboardName); overallLeaderboardPanel = new OverallLeaderboardPanel(MultiRaceLeaderboardViewer.this, - getComponentContext(), sailingService, + getComponentContext(), sailingServiceForOverallLeaderboard, asyncActionsExecutor, settings.findSettingsByComponentId(OverallLeaderboardPanelLifecycle.ID), false, competitorSelectionProvider, timer, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java index 9641a8e5e8f..9dda3f72fde 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java @@ -67,6 +67,8 @@ import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSortableColumnWithMinMax; import com.sap.sailing.gwt.ui.leaderboard.MultiRaceLeaderboardPanel; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.Notification; +import com.sap.sse.gwt.client.Notification.NotificationType; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.celltable.BaseCelltable; import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; @@ -708,7 +710,7 @@ public class EditableLeaderboardPanel extends MultiRaceLeaderboardPanel { } @Override public void onSuccess(Void result) { - Window.setStatus("Successfully unsuppressed competitor "+object.getName()); + Notification.notify("Successfully unsuppressed competitor "+object.getName(), NotificationType.INFO); // force a reload of the entire editable leaderboard to hide the now suppressed competitor loadCompleteLeaderboard(/* showProgress */ true); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java index fa59e7e22fa..bebfbd46cd8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java @@ -17,6 +17,7 @@ import com.sap.sailing.domain.common.security.Permission; import com.sap.sailing.domain.common.security.SailingPermissionsForRoleProvider; import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication; import com.sap.sailing.gwt.common.authentication.SAPSailingHeaderWithAuthentication; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sailing.gwt.settings.client.leaderboardedit.LeaderboardEditContextDefinition; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint; @@ -29,22 +30,23 @@ import com.sap.sse.security.ui.authentication.generic.GenericAuthentication; import com.sap.sse.security.ui.authentication.generic.GenericAuthorizedContentDecorator; import com.sap.sse.security.ui.authentication.generic.sapheader.SAPHeaderWithAuthentication; -public class LeaderboardEditPage extends AbstractSailingEntryPoint { +public class LeaderboardEditPage extends AbstractSailingEntryPoint implements ProvidesLeaderboardRouting { private static final Logger logger = Logger.getLogger(LeaderboardEntryPoint.class.getName()); + private String leaderboardName; @Override protected void doOnModuleLoad() { super.doOnModuleLoad(); - sailingService.getLeaderboardNames(new MarkedAsyncCallback>( + final LeaderboardEditContextDefinition settings = new SettingsToUrlSerializer() + .deserializeFromCurrentLocation(new LeaderboardEditContextDefinition()); + leaderboardName = settings.getLeaderboardName(); + getSailingService().getLeaderboardNames(new MarkedAsyncCallback>( new AsyncCallback>() { @Override public void onSuccess(List leaderboardNames) { - final LeaderboardEditContextDefinition settings = new SettingsToUrlSerializer() - .deserializeFromCurrentLocation(new LeaderboardEditContextDefinition()); - final String leaderboardName = settings.getLeaderboardName(); if (leaderboardNames.contains(leaderboardName)) { - sailingService.getAvailableDetailTypesForLeaderboard(leaderboardName, new AsyncCallback>() { + getSailingService().getAvailableDetailTypesForLeaderboard(leaderboardName, null, new AsyncCallback>() { @Override public void onFailure(Throwable caught) { @@ -60,7 +62,7 @@ public class LeaderboardEditPage extends AbstractSailingEntryPoint { authorizedContentDecorator.setContentWidgetFactory(new WidgetFactory() { @Override public Widget get() { - EditableLeaderboardPanel leaderboardPanel = new EditableLeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardName, null, + EditableLeaderboardPanel leaderboardPanel = new EditableLeaderboardPanel(getSailingService(), new AsyncActionsExecutor(), leaderboardName, null, LeaderboardEditPage.this, getStringMessages(), userAgent, result); leaderboardPanel.ensureDebugId("EditableLeaderboardPanel"); return leaderboardPanel; @@ -91,4 +93,9 @@ public class LeaderboardEditPage extends AbstractSailingEntryPoint { header.getElement().getStyle().setWidth(100, Unit.PCT); return header; } + + @Override + public String getLeaderboardName() { + return leaderboardName; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/MatchAndApplyScoreCorrectionsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/MatchAndApplyScoreCorrectionsDialog.java index 9dbfea79ba3..6a8e1771168 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/MatchAndApplyScoreCorrectionsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/MatchAndApplyScoreCorrectionsDialog.java @@ -16,7 +16,6 @@ import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.regexp.shared.MatchResult; import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.Grid; @@ -39,6 +38,8 @@ import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO; import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO.ScoreCorrectionEntryDTO; import com.sap.sse.common.Util; import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.Notification; +import com.sap.sse.gwt.client.Notification.NotificationType; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class MatchAndApplyScoreCorrectionsDialog extends DataEntryDialog { @@ -433,7 +434,7 @@ public class MatchAndApplyScoreCorrectionsDialog extends DataEntryDialog() { @Override public void onFailure(Throwable caught) { @@ -68,12 +71,12 @@ public class PairingListEntryPoint extends AbstractSailingEntryPoint { contentPanel.getElement().getStyle().setProperty("marginTop", "15px"); contentPanel.getElement().getStyle().setProperty("marginBottom", "15px"); scrollPanel.add(contentPanel); - sailingService.getPairingListFromRaceLogs(pairingListContextDefinition.getLeaderboardName(), + getSailingService().getPairingListFromRaceLogs(pairingListContextDefinition.getLeaderboardName(), new AsyncCallback() { @Override public void onSuccess(PairingListDTO result) { if (strippedLeaderboardDTO != null) { - sailingService.getRaceDisplayNamesFromLeaderboard(strippedLeaderboardDTO.getName(), + getSailingService().getRaceDisplayNamesFromLeaderboard(strippedLeaderboardDTO.getName(), result.getRaceColumnNames(), new AsyncCallback>() { @Override public void onFailure(Throwable caught) { @@ -126,7 +129,7 @@ public class PairingListEntryPoint extends AbstractSailingEntryPoint { pairingListPanel.getElement().getStyle().setProperty("marginTop", "15px"); return pairingListPanel; } - + private native void printPairingListGrid(String pageHTMLContent) /*-{ var frameID = '__gwt_historyFrame'; var frame = $doc.getElementById(frameID); @@ -167,4 +170,8 @@ public class PairingListEntryPoint extends AbstractSailingEntryPoint { }, 100); }-*/; + @Override + public String getLeaderboardName() { + return leaderboardName; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java index ed160691daf..1731e7251f3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java @@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication; import com.sap.sailing.gwt.common.authentication.SAPSailingHeaderWithAuthentication; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sailing.gwt.settings.client.raceboard.RaceBoardPerspectiveOwnSettings; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; import com.sap.sailing.gwt.ui.client.CompetitorColorProvider; @@ -62,7 +63,7 @@ import com.sap.sse.gwt.client.player.Timer.PlayModes; import com.sap.sse.gwt.shared.GwtHttpRequestUtils; import com.sap.sse.security.ui.authentication.generic.sapheader.SAPHeaderWithAuthentication; -public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint { +public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint implements ProvidesLeaderboardRouting { private static final String PARAM_REGATTA_LIKE_NAME = "regattaLikeName"; private static final String PARAM_RACE_COLUMN_NAME = "raceColumnName"; private static final String PARAM_FLEET_NAME = "fleetName"; @@ -122,13 +123,13 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint defaultRaceMapSettings.isShowDouglasPeuckerPoints(), true, defaultRaceMapSettings.getStartCountDownFontSizeScaling()); - sailingService.getRaceIdentifier(regattaLikeName, raceColumnName, fleetName, new AsyncCallback() { + getSailingService().getRaceIdentifier(regattaLikeName, raceColumnName, fleetName, new AsyncCallback() { @Override public void onSuccess(final RegattaAndRaceIdentifier selectedRaceIdentifier) { if (selectedRaceIdentifier == null) { createErrorPage(getStringMessages().couldNotObtainRace(regattaLikeName, raceColumnName, fleetName, /* technicalErrorMessage */ "")); } else { - sailingService.getCompetitorBoats(selectedRaceIdentifier, new AsyncCallback>() { + getSailingService().getCompetitorBoats(selectedRaceIdentifier, new AsyncCallback>() { @Override public void onSuccess(Map result) { createEmbeddedMap(selectedRaceIdentifier, result, raceboardPerspectiveSettings, raceMapSettings, @@ -187,7 +188,7 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint return true; } }; - RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, /* errorReporter */ this, + RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), asyncActionsExecutor, /* errorReporter */ this, Collections.singleton(selectedRaceIdentifier), 30000l /* requestInterval*/); raceTimesInfoProvider.addRaceTimesInfoProviderListener(new RaceTimesInfoProviderListener() { @Override @@ -208,7 +209,7 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint competitorSelection = createEmptyFilterCompetitorModel(colorProvider, competitorsAndBoats); // show no competitors } final RaceMap raceMap = new RaceMap(null, null, new RaceMapLifecycle(getStringMessages()), raceMapSettings, - sailingService, asyncActionsExecutor, /* errorReporter */ EmbeddedMapAndWindChartEntryPoint.this, timer, + getSailingService(), asyncActionsExecutor, /* errorReporter */ EmbeddedMapAndWindChartEntryPoint.this, timer, competitorSelection, new RaceCompetitorSet(competitorSelection), getStringMessages(), selectedRaceIdentifier, raceMapResources, /* showHeaderPanel */ false, new DefaultQuickRanksDTOProvider()) { @Override @@ -219,7 +220,7 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint }; final WindChart windChart; if (raceboardPerspectiveSettings.isShowWindChart()) { - windChart = new WindChart(null, null, new WindChartLifecycle(getStringMessages()), sailingService, + windChart = new WindChart(null, null, new WindChartLifecycle(getStringMessages()), getSailingService(), selectedRaceIdentifier, timer, timeRangeWithZoomProvider, new WindChartSettings(), getStringMessages(), asyncActionsExecutor, /* errorReporter */ @@ -254,4 +255,9 @@ public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint p.insert(raceMap.getEntryWidget(), raceMap, Direction.CENTER, 400); p.addStyleName("dockLayoutPanel"); } + + @Override + public String getLeaderboardName() { + return regattaLikeName; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index d5ab94621a9..4b6cfbe7256 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -14,6 +14,7 @@ import com.google.gwt.user.client.ui.RootLayoutPanel; import com.sap.sailing.domain.common.DetailType; import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication; import com.sap.sailing.gwt.common.authentication.SAPSailingHeaderWithAuthentication; +import com.sap.sailing.gwt.common.communication.routing.ProvidesLeaderboardRouting; import com.sap.sailing.gwt.settings.client.raceboard.RaceBoardPerspectiveOwnSettings; import com.sap.sailing.gwt.settings.client.raceboard.RaceboardContextDefinition; import com.sap.sailing.gwt.settings.client.utils.StoredSettingsLocationFactory; @@ -38,7 +39,7 @@ import com.sap.sse.security.ui.authentication.generic.sapheader.SAPHeaderWithAut import com.sap.sse.security.ui.settings.ComponentContextWithSettingsStorage; import com.sap.sse.security.ui.settings.StoredSettingsLocation; -public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { +public class RaceBoardEntryPoint extends AbstractSailingEntryPoint implements ProvidesLeaderboardRouting { private RaceWithCompetitorsAndBoatsDTO selectedRace; /** @@ -48,10 +49,13 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { private RaceboardContextDefinition raceboardContextDefinition; + private String leaderBoardName; + @Override protected void doOnModuleLoad() { super.doOnModuleLoad(); - EntryPointHelper.registerASyncService((ServiceDefTarget) mediaService, RemoteServiceMappingConstants.mediaServiceRemotePath); + EntryPointHelper.registerASyncService((ServiceDefTarget) mediaService, + RemoteServiceMappingConstants.mediaServiceRemotePath); raceboardContextDefinition = new SettingsToUrlSerializer() .deserializeFromCurrentLocation(new RaceboardContextDefinition()); final RaceBoardModes finalMode; @@ -61,12 +65,16 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { finalMode = null; } if (raceboardContextDefinition.getRegattaName() == null || raceboardContextDefinition.getRegattaName().isEmpty() - || raceboardContextDefinition.getRaceName() == null || raceboardContextDefinition.getRaceName().isEmpty() + || raceboardContextDefinition.getRaceName() == null + || raceboardContextDefinition.getRaceName().isEmpty() || raceboardContextDefinition.getLeaderboardName() == null || raceboardContextDefinition.getLeaderboardName().isEmpty()) { createErrorPage(getStringMessages().requiresRegattaRaceAndLeaderboard()); return; } + + leaderBoardName = raceboardContextDefinition.getLeaderboardName(); + AsyncCallback asyncCallback = new AsyncCallback() { @Override public void onSuccess(RaceboardDataDTO raceboardData) { @@ -93,17 +101,18 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { } } if (raceboardData.getRace() == null) { - createErrorPage(getStringMessages().couldNotFindRaceInRegatta(raceboardContextDefinition.getRaceName(), raceboardContextDefinition.getRegattaName())); + createErrorPage(getStringMessages().couldNotFindRaceInRegatta( + raceboardContextDefinition.getRaceName(), raceboardContextDefinition.getRegattaName())); return; } - + final boolean showChartMarkEditMediaButtonsAndVideo = !DeviceDetector.isMobile(); final StoredSettingsLocation storageDefinition = StoredSettingsLocationFactory .createStoredSettingsLocatorForRaceBoard(raceboardContextDefinition, finalMode != null ? finalMode.name() : null); // TODO bug2822: also determine regatta's boat change parameter and parameterize Leaderboard settings // --> show boat info column yes/no? - sailingService.determineDetailTypesForCompetitorChart( + getSailingService().determineDetailTypesForCompetitorChart( raceboardContextDefinition.getLeaderboardGroupName(), raceboardData.getRace().getRaceIdentifier(), new AsyncCallback>() { @@ -114,9 +123,9 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { @Override public void onSuccess(Iterable chartAllowedTypes) { - sailingService.getAvailableDetailTypesForLeaderboard( + getSailingService().getAvailableDetailTypesForLeaderboard( raceboardContextDefinition.getLeaderboardName(), - new AsyncCallback>() { + raceboardData.getRace().getRaceIdentifier(), new AsyncCallback>() { @Override public void onFailure(Throwable caught) { reportError( @@ -152,13 +161,13 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { } }); } - + @Override public void onFailure(Throwable caught) { reportError("Error trying to create the raceboard: " + caught.getMessage()); } }; - sailingService.getRaceboardData(raceboardContextDefinition.getRegattaName(), + getSailingService().getRaceboardData(raceboardContextDefinition.getRegattaName(), raceboardContextDefinition.getRaceName(), raceboardContextDefinition.getLeaderboardName(), raceboardContextDefinition.getLeaderboardGroupName(), raceboardContextDefinition.getEventId(), asyncCallback); @@ -185,10 +194,10 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { selectedRace = raceboardData.getRace(); Window.setTitle(selectedRace.getName()); AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor(); - RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, this, + RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), asyncActionsExecutor, this, Collections.singletonList(selectedRace.getRaceIdentifier()), 5000l /* requestInterval*/); RaceBoardPanel raceBoardPerspective = new RaceBoardPanel(parent, context, raceLifeCycle, settings, - sailingService, mediaService, getUserService(), asyncActionsExecutor, + getSailingService(), mediaService, getUserService(), asyncActionsExecutor, raceboardData.getCompetitorAndTheirBoats(), timer, selectedRace.getRaceIdentifier(), raceboardContextDefinition.getLeaderboardName(), raceboardContextDefinition.getLeaderboardGroupName(), raceboardContextDefinition.getEventId(), RaceBoardEntryPoint.this, getStringMessages(), userAgent, @@ -196,4 +205,9 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { RootLayoutPanel.get().add(raceBoardPerspective.getEntryWidget()); return raceBoardPerspective; } -} + + @Override + public String getLeaderboardName() { + return leaderBoardName; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index aa049deb2fe..19180b1b143 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -53,6 +53,10 @@ import java.util.stream.StreamSupport; import java.util.zip.GZIPInputStream; import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import org.apache.http.client.ClientProtocolException; import org.apache.shiro.SecurityUtils; @@ -278,6 +282,7 @@ import com.sap.sailing.domain.common.racelog.tracking.RaceLogTrackingState; import com.sap.sailing.domain.common.racelog.tracking.TransformationException; import com.sap.sailing.domain.common.security.Permission; import com.sap.sailing.domain.common.security.Permission.Mode; +import com.sap.sailing.domain.common.sharding.ShardingType; import com.sap.sailing.domain.common.tracking.BravoFix; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; @@ -306,10 +311,10 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.RaceStateOfSameDayHelper; -import com.sap.sailing.domain.racelog.impl.GPSFixStoreImpl; -import com.sap.sailing.domain.racelog.tracking.GPSFixStore; +import com.sap.sailing.domain.racelog.tracking.SensorFixStore; import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler; import com.sap.sailing.domain.racelogtracking.DeviceMapping; +import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl; @@ -318,6 +323,7 @@ import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.IsRegattaLike; import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.regattalog.RegattaLogStore; +import com.sap.sailing.domain.sharding.ShardingContext; import com.sap.sailing.domain.swisstimingadapter.StartList; import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory; @@ -522,8 +528,8 @@ import com.sap.sse.common.CountryCode; import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; import com.sap.sse.common.NoCorrespondingServiceRegisteredException; -import com.sap.sse.common.Speed; import com.sap.sse.common.PairingListCreationException; +import com.sap.sse.common.Speed; import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimeRange; import com.sap.sse.common.Timed; @@ -5418,6 +5424,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S dto.defaultRacingProcedureType = configuration.getDefaultRacingProcedureType(); dto.defaultCourseDesignerMode = configuration.getDefaultCourseDesignerMode(); + dto.defaultProtestTimeDuration = configuration.getDefaultProtestTimeDuration(); if (configuration.getRRS26Configuration() != null) { dto.rrs26Configuration = new DeviceConfigurationDTO.RegattaConfigurationDTO.RRS26ConfigurationDTO(); @@ -5475,6 +5482,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S RegattaConfigurationImpl configuration = new RegattaConfigurationImpl(); configuration.setDefaultRacingProcedureType(dto.defaultRacingProcedureType); configuration.setDefaultCourseDesignerMode(dto.defaultCourseDesignerMode); + configuration.setDefaultProtestTimeDuration(dto.defaultProtestTimeDuration); if (dto.rrs26Configuration != null) { RRS26ConfigurationImpl config = new RRS26ConfigurationImpl(); applyGeneralRacingProcedureConfigProperties(dto.rrs26Configuration, config); @@ -5904,10 +5912,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Util.addAll(competitorToBoatMappingsRegistered.keySet(), competitorsToUnregister); for (final Entry e : competitorToBoatMappingsRegistered.entrySet()) { CompetitorType competitor = e.getKey(); - if (competitorToBoatMappingsToRegister.get(competitor) == e.getValue()) { - // User wants to map competitor to boat, and that mapping already exists; neither add nor remove this registration but leave as is: - competitorToBoatMappingsToRegister.remove(competitor); - competitorsToUnregister.remove(competitor); + if (competitorToBoatMappingsToRegister.containsKey(competitor)) { // is competitor to be registered? + final Boat boatOfCompetitorToRegister = competitorToBoatMappingsToRegister.get(competitor); + final Boat boatOfRegisteredCompetitor = e.getValue(); + if (boatOfCompetitorToRegister == boatOfRegisteredCompetitor) { + // User wants to map competitor to boat, and that mapping already exists; neither add nor remove this registration but leave as is: + competitorToBoatMappingsToRegister.remove(competitor); + competitorsToUnregister.remove(competitor); + } } } return competitorsToUnregister; @@ -6989,7 +7001,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return new MarkTracksDTO(markTracks); } - private MarkTrackDTO getMarkTrack(Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, String markIdAsString) { + private MarkTrackDTO getMarkTrack(Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, + String markIdAsString) { MarkDTO markDTO = null; Mark mark = null; for (final Mark currMark : raceColumn.getAvailableMarks(fleet)) { @@ -6999,25 +7012,39 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S break; } } - + if (markDTO != null) { final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); final GPSFixTrack markTrack; if (trackedRace != null) { markTrack = trackedRace.getOrCreateTrack(mark); } else { - DynamicGPSFixTrackImpl writeableMarkTrack = new DynamicGPSFixTrackImpl(mark, BoatClass.APPROXIMATE_AVERAGE_MANEUVER_DURATION.asMillis()); + DynamicGPSFixTrackImpl writeableMarkTrack = new DynamicGPSFixTrackImpl(mark, + BoatClass.APPROXIMATE_AVERAGE_MANEUVER_DURATION.asMillis()); markTrack = writeableMarkTrack; final RaceLog raceLog = raceColumn.getRaceLog(fleet); final RegattaLog regattaLog = raceColumn.getRegattaLog(); final TrackingTimesFinder trackingTimesFinder = new TrackingTimesFinder(raceLog); - final Pair trackingTimes = trackingTimesFinder.analyze(); + final Pair trackingTimes = trackingTimesFinder + .analyze(); try { - GPSFixStore gfs = new GPSFixStoreImpl(getService().getSensorFixStore()); - gfs.loadMarkTrack(writeableMarkTrack, regattaLog, mark, - trackingTimes.getA().getTimePoint(), trackingTimes.getB().getTimePoint()); + SensorFixStore sensorFixStore = getService().getSensorFixStore(); + List> mappings = new RegattaLogDeviceMarkMappingFinder( + regattaLog).analyze().get(mark); + if (mappings != null) { + for (DeviceMapping mapping : mappings) { + final TimePoint from = Util.getLatestOfTimePoints(trackingTimes.getA().getTimePoint(), + mapping.getTimeRange().from()); + final TimePoint to = Util.getEarliestOfTimePoints(trackingTimes.getB().getTimePoint(), + mapping.getTimeRange().to()); + sensorFixStore. loadFixes(loadedFix -> writeableMarkTrack.add(loadedFix, true), + mapping.getDevice(), from, to, false, () -> false, progressIgnoringConsumer -> { + }); + } + } } catch (TransformationException | NoCorrespondingServiceRegisteredException e) { - logger.info("Error trying to load mark track for mark "+mark+" from "+trackingTimes.getA()+" to "+trackingTimes.getB()); + logger.info("Error trying to load mark track for mark " + mark + " from " + trackingTimes.getA() + + " to " + trackingTimes.getB()); } } Iterable gpsFixDTOTrack = convertToGPSFixDTOTrack(markTrack); @@ -7429,7 +7456,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } @Override - public Iterable getAvailableDetailTypesForLeaderboard(String leaderboardName) { + public Iterable getAvailableDetailTypesForLeaderboard(String leaderboardName, RegattaAndRaceIdentifier raceIdentifierOrNull) { final Set allowed = new HashSet<>(); allowed.addAll(DetailType.getAllNonRestrictedDetailTypes()); final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); @@ -7438,7 +7465,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S boolean hasExtendedBravoFixes = false; abort: for (RaceColumn race : leaderboard.getRaceColumns()) { for (Fleet fleet : race.getFleets()) { - TrackedRace trace = race.getTrackedRace(fleet); + if (raceIdentifierOrNull != null && !raceIdentifierOrNull.equals(race.getRaceIdentifier(fleet))) { + continue; + } + + final TrackedRace trace = race.getTrackedRace(fleet); if (trace != null) { final DynamicTrackedRace trackedRace = getService().getTrackedRace(trace.getRaceIdentifier()); if (trackedRace != null) { @@ -7815,7 +7846,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } @Override - public Boolean checkIfRaceIsTracked(RegattaAndRaceIdentifier race) { + public Boolean checkIfRaceIsTracking(RegattaAndRaceIdentifier race) { boolean result = false; DynamicTrackedRace trace = getService().getTrackedRace(race); if (trace != null) { @@ -7826,4 +7857,19 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } return result; } + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + ShardingType identifiedShardingType = null; + try { + if (req instanceof HttpServletRequest) { + identifiedShardingType = ShardingContext.identifyAndSetShardingConstraint(((HttpServletRequest) req).getPathInfo()); + } + super.service(req, res); + } finally { + if (identifiedShardingType != null) { + ShardingContext.clearShardingConstraint(identifiedShardingType); + } + } + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/DeviceConfigurationDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/DeviceConfigurationDTO.java index 4a8244a7f72..cb56068911b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/DeviceConfigurationDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/DeviceConfigurationDTO.java @@ -6,6 +6,7 @@ import com.google.gwt.user.client.rpc.IsSerializable; import com.sap.sailing.domain.common.CourseDesignerMode; import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.RacingProcedureType; +import com.sap.sse.common.Duration; public class DeviceConfigurationDTO implements IsSerializable { @@ -40,6 +41,7 @@ public class DeviceConfigurationDTO implements IsSerializable { } public RacingProcedureType defaultRacingProcedureType; public CourseDesignerMode defaultCourseDesignerMode; + public Duration defaultProtestTimeDuration; public RRS26ConfigurationDTO rrs26Configuration; public SWCStartConfigurationDTO swcStartConfiguration; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java index 43e8900e55e..b6e133dce2b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java @@ -15,7 +15,6 @@ import com.google.gwt.i18n.client.TimeZone; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.base.Point; import com.google.gwt.maps.client.controls.ControlPosition; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.domain.common.LegIdentifier; import com.sap.sailing.domain.common.LegIdentifierImpl; @@ -31,6 +30,8 @@ import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; import com.sap.sailing.gwt.ui.simulator.util.ColorPalette; import com.sap.sailing.gwt.ui.simulator.util.ColorPaletteGenerator; +import com.sap.sse.gwt.client.Notification; +import com.sap.sse.gwt.client.Notification.NotificationType; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.async.MarkedAsyncCallback; @@ -338,8 +339,8 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { @Override public void onFailure(Throwable caught) { // TODO: add corresponding message to string-messages - // Window.setStatus(stringMessages.errorFetchingWindStreamletData(caught.getMessage())); - Window.setStatus(GET_SIMULATION_CATEGORY); + // Notification.error(stringMessages.errorFetchingWindStreamletData(caught.getMessage())); + Notification.notify(GET_SIMULATION_CATEGORY, NotificationType.WARNING); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/WindStreamletsRaceboardOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/WindStreamletsRaceboardOverlay.java index dd4c6dce336..454418dda8f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/WindStreamletsRaceboardOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/WindStreamletsRaceboardOverlay.java @@ -13,7 +13,6 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.RepeatingCommand; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.controls.ControlPosition; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.WindSource; @@ -31,6 +30,8 @@ import com.sap.sailing.gwt.ui.simulator.streamlets.Swarm; import com.sap.sailing.gwt.ui.simulator.streamlets.VectorField; import com.sap.sailing.gwt.ui.simulator.streamlets.WindInfoForRaceVectorField; import com.sap.sse.common.Util; +import com.sap.sse.gwt.client.Notification; +import com.sap.sse.gwt.client.Notification.NotificationType; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.async.MarkedAsyncCallback; import com.sap.sse.gwt.client.player.Timer; @@ -217,7 +218,8 @@ public class WindStreamletsRaceboardOverlay extends MovingCanvasOverlay { sailingService.getWindSourcesInfo(raceIdentifier, new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable caught) { - Window.setStatus(stringMessages.errorFetchingWindStreamletData(caught.getMessage())); + Notification.notify(stringMessages.errorFetchingWindStreamletData(caught.getMessage()), + NotificationType.WARNING); } @Override @@ -256,7 +258,8 @@ public class WindStreamletsRaceboardOverlay extends MovingCanvasOverlay { new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable caught) { - Window.setStatus(stringMessages.errorFetchingWindStreamletData(caught.getMessage())); + Notification.notify(stringMessages.errorFetchingWindStreamletData(caught.getMessage()), + NotificationType.WARNING); } @Override @@ -266,18 +269,20 @@ public class WindStreamletsRaceboardOverlay extends MovingCanvasOverlay { // confidences for (Entry e : result.windTrackInfoByWindSource.entrySet()) { WindTrackInfoDTO windTrackForSource = windInfoForRace.windTrackInfoByWindSource.get(e.getKey()); - final WindTrackInfoDTO resultWindTrackInfoDTO = result.windTrackInfoByWindSource.get(e.getKey()); - windTrackForSource.resolutionOutsideOfWhichNoFixWillBeReturned = resultWindTrackInfoDTO.resolutionOutsideOfWhichNoFixWillBeReturned; - if (windTrackForSource.windFixes == null) { - windTrackForSource.windFixes = resultWindTrackInfoDTO.windFixes; - } else { - windTrackForSource.windFixes.addAll(resultWindTrackInfoDTO.windFixes); - } - if (resultWindTrackInfoDTO.maxWindConfidence > windTrackForSource.maxWindConfidence) { - windTrackForSource.maxWindConfidence = resultWindTrackInfoDTO.maxWindConfidence; - } - if (resultWindTrackInfoDTO.minWindConfidence < windTrackForSource.minWindConfidence) { - windTrackForSource.minWindConfidence = resultWindTrackInfoDTO.minWindConfidence; + if (windTrackForSource != null) { + final WindTrackInfoDTO resultWindTrackInfoDTO = result.windTrackInfoByWindSource.get(e.getKey()); + windTrackForSource.resolutionOutsideOfWhichNoFixWillBeReturned = resultWindTrackInfoDTO.resolutionOutsideOfWhichNoFixWillBeReturned; + if (windTrackForSource.windFixes == null) { + windTrackForSource.windFixes = resultWindTrackInfoDTO.windFixes; + } else { + windTrackForSource.windFixes.addAll(resultWindTrackInfoDTO.windFixes); + } + if (resultWindTrackInfoDTO.maxWindConfidence > windTrackForSource.maxWindConfidence) { + windTrackForSource.maxWindConfidence = resultWindTrackInfoDTO.maxWindConfidence; + } + if (resultWindTrackInfoDTO.minWindConfidence < windTrackForSource.minWindConfidence) { + windTrackForSource.minWindConfidence = resultWindTrackInfoDTO.minWindConfidence; + } } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java index 4c42318a50a..b72ddfead17 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/SpectatorEntryPoint.java @@ -38,7 +38,7 @@ public class SpectatorEntryPoint extends AbstractSailingEntryPoint implements Re } else { groupName = groupParamValue; Window.setTitle(groupName); - sailingService.getLeaderboardGroupByName(groupName, false /*withGeoLocationData*/, new AsyncCallback() { + getSailingService().getLeaderboardGroupByName(groupName, false /*withGeoLocationData*/, new AsyncCallback() { @Override public void onFailure(Throwable t) { reportError(getStringMessages().noLeaderboardGroupWithNameFound(groupName)); @@ -58,11 +58,11 @@ public class SpectatorEntryPoint extends AbstractSailingEntryPoint implements Re // DON'T DELETE -> the EventOverviewPanel will replace the LeaderboardGroupOverviewPanel later on // EventOverviewPanel eventOverviewPanel = new EventOverviewPanel(sailingService, this, stringMessages, showRaceDetails); // groupOverviewPanel.add( eventOverviewPanel); - LeaderboardGroupOverviewPanel leaderboardGroupOverviewPanel = new LeaderboardGroupOverviewPanel(sailingService, this, getStringMessages(), settings.isShowRaceDetails()); + LeaderboardGroupOverviewPanel leaderboardGroupOverviewPanel = new LeaderboardGroupOverviewPanel(getSailingService(), this, getStringMessages(), settings.isShowRaceDetails()); groupOverviewPanel.add(leaderboardGroupOverviewPanel); rootPanel.add(groupOverviewPanel); } else { - LeaderboardGroupPanel groupPanel = new LeaderboardGroupPanel(sailingService, getStringMessages(), this, + LeaderboardGroupPanel groupPanel = new LeaderboardGroupPanel(getSailingService(), getStringMessages(), this, groupName, settings.getViewMode(), embedded, settings.isShowRaceDetails(), settings.isCanReplayDuringLiveRaces(), settings.isShowMapControls()); groupAndFeedbackPanel.add(groupPanel); diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/CLUBSWAN50.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/CLUBSWAN50.png new file mode 100755 index 00000000000..e6b41978640 Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/CLUBSWAN50.png differ diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/FLYING_JUNIOR.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/FLYING_JUNIOR.png new file mode 100755 index 00000000000..ba7f9d8dc00 Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/FLYING_JUNIOR.png differ diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/VAURIEN.png b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/VAURIEN.png new file mode 100644 index 00000000000..9e06221b7de Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/VAURIEN.png differ diff --git a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreGPSFixTrackTest.java b/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreGPSFixTrackTest.java deleted file mode 100644 index d7c236fe87f..00000000000 --- a/java/com.sap.sailing.mongodb.test/src/com/sap/sailing/mongodb/test/StoreGPSFixTrackTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.sap.sailing.mongodb.test; - -import static junit.framework.Assert.assertTrue; -import static org.junit.Assert.assertEquals; - -import java.net.UnknownHostException; - -import org.junit.After; -import org.junit.Test; - -import com.mongodb.DB; -import com.mongodb.MongoException; -import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; -import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; -import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; -import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl; -import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl; -import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl; -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.DomainFactory; -import com.sap.sailing.domain.base.Mark; -import com.sap.sailing.domain.common.DeviceIdentifier; -import com.sap.sailing.domain.common.impl.DegreePosition; -import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; -import com.sap.sailing.domain.common.racelog.tracking.TransformationException; -import com.sap.sailing.domain.common.tracking.GPSFix; -import com.sap.sailing.domain.common.tracking.GPSFixMoving; -import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl; -import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; -import com.sap.sailing.domain.persistence.DomainObjectFactory; -import com.sap.sailing.domain.persistence.MongoObjectFactory; -import com.sap.sailing.domain.persistence.PersistenceFactory; -import com.sap.sailing.domain.persistence.impl.CollectionNames; -import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl; -import com.sap.sailing.domain.racelog.impl.GPSFixStoreImpl; -import com.sap.sailing.domain.racelog.tracking.GPSFixStore; -import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory; -import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier; -import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; -import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; -import com.sap.sse.common.NoCorrespondingServiceRegisteredException; -import com.sap.sse.common.TimePoint; -import com.sap.sse.common.TypeBasedServiceFinderFactory; -import com.sap.sse.common.Util; -import com.sap.sse.common.impl.DegreeBearingImpl; -import com.sap.sse.common.impl.MillisecondsTimePoint; - -public class StoreGPSFixTrackTest extends AbstractMongoDBTest { - public StoreGPSFixTrackTest() throws UnknownHostException, MongoException { - super(); - } - - @After - public void dropCollection() { - DB db = getMongoService().getDB(); - db.getCollection(CollectionNames.GPS_FIXES.name()).drop(); - } - - @Test - public void testStoreAndLoadFixes() throws TransformationException, NoCorrespondingServiceRegisteredException { - TypeBasedServiceFinderFactory factory = new MockSmartphoneImeiServiceFinderFactory(); - AbstractLogEventAuthor author = new LogEventAuthorImpl("author", 0); - Competitor comp = DomainFactory.INSTANCE.getOrCreateCompetitor("comp", "comp", "c", null, null, null, null, - /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null, null); - Mark mark = DomainFactory.INSTANCE.getOrCreateMark("mark"); - MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService(), factory); - DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE, factory); - GPSFixStore store = new GPSFixStoreImpl(new MongoSensorFixStoreImpl(mongoObjectFactory, domainObjectFactory, factory)); - - TimePoint time0 = new MillisecondsTimePoint(0); - TimePoint time1 = new MillisecondsTimePoint(1); - TimePoint time2 = new MillisecondsTimePoint(2); - - GPSFixMoving fix1 = new GPSFixMovingImpl(new DegreePosition(30, 40), time0, - new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(0))); - GPSFixMoving fix2 = new GPSFixMovingImpl(new DegreePosition(30, 40), time1, - new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(0))); - //lies outside of mapping range - GPSFixMoving fix3 = new GPSFixMovingImpl(new DegreePosition(30, 40), time2, - new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(0))); - DeviceIdentifier device1 = new SmartphoneImeiIdentifier("a"); - store.storeFix(device1, fix1); - store.storeFix(device1, fix2); - store.storeFix(device1, fix3); - - GPSFix fix4 = new GPSFixImpl(new DegreePosition(40, 50), time0); - DeviceIdentifier device2 = new SmartphoneImeiIdentifier("b"); - store.storeFix(device2, fix4); - - RegattaLog regattaLog = new RegattaLogImpl("racelog"); - regattaLog.add(new RegattaLogDeviceCompetitorMappingEventImpl(time0, author, comp, device1, time0, time1)); - regattaLog.add(new RegattaLogDeviceMarkMappingEventImpl(time0, author, mark, device2, time0, time1)); - - DynamicGPSFixMovingTrackImpl track1 = new DynamicGPSFixMovingTrackImpl<>(comp, 0); - store.loadCompetitorTrack(track1, regattaLog, comp); - DynamicGPSFixTrackImpl track2 = new DynamicGPSFixTrackImpl<>(mark, 0); - store.loadMarkTrack(track2, regattaLog, mark); - track1.lockForRead(); - assertEquals(2, Util.size(track1.getRawFixes())); - assertTrue(track1.getFirstRawFix() instanceof GPSFixMoving); - track1.unlockAfterRead(); - track2.lockForRead(); - assertEquals(1, Util.size(track2.getRawFixes())); - track2.unlockAfterRead(); - } -} diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/RegattaConfigurationJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/RegattaConfigurationJsonDeserializer.java index 441db44ff05..c8522a4c2d7 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/RegattaConfigurationJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/deserialization/impl/RegattaConfigurationJsonDeserializer.java @@ -15,9 +15,10 @@ import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; import com.sap.sailing.server.gateway.serialization.impl.RegattaConfigurationJsonSerializer; +import com.sap.sse.common.impl.MillisecondsDurationImpl; public class RegattaConfigurationJsonDeserializer implements JsonDeserializer { - + public static RegattaConfigurationJsonDeserializer create() { return new RegattaConfigurationJsonDeserializer(RRS26ConfigurationJsonDeserializer.create(), SWCStartConfigurationJsonDeserializer.create(), GateStartConfigurationJsonDeserializer.create(), ESSConfigurationJsonDeserializer.create(), @@ -30,8 +31,8 @@ public class RegattaConfigurationJsonDeserializer implements JsonDeserializer rrs26Serializer; private final JsonSerializer swcStartSerializer; private final JsonSerializer gateStartSerializer; @@ -47,9 +48,15 @@ public class RegattaConfigurationJsonSerializer implements JsonSerializer raceColumnNames, - @QueryParam("raceDetails") final List raceDetails, + @QueryParam("raceDetails") final List raceDetails, @QueryParam("time") String time, @QueryParam("timeasmillis") Long timeasmillis, @QueryParam("maxCompetitorsCount") Integer maxCompetitorsCount) { - Response response; - Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); - if (leaderboard == null) { - response = Response.status(Status.NOT_FOUND) - .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") - .type(MediaType.TEXT_PLAIN).build(); - } else { - try { - TimePoint timePoint; + ShardingContext.setShardingConstraint(ShardingType.LEADERBOARDNAME, leaderboardName); + + try { + Response response; + Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); + if (leaderboard == null) { + response = Response.status(Status.NOT_FOUND) + .entity("Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.") + .type(MediaType.TEXT_PLAIN).build(); + } else { try { - timePoint = parseTimePoint(time, timeasmillis, calculateTimePointForResultState(leaderboard, resultState)); - } catch (InvalidDateException e1) { - return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the time.") + TimePoint timePoint; + try { + timePoint = parseTimePoint(time, timeasmillis, calculateTimePointForResultState(leaderboard, resultState)); + } catch (InvalidDateException e1) { + return Response.status(Status.INTERNAL_SERVER_ERROR).entity("Could not parse the time.") + .type(MediaType.TEXT_PLAIN).build(); + } + JSONObject jsonLeaderboard; + if (timePoint != null || resultState == ResultStates.Live) { + jsonLeaderboard = getLeaderboardJson(leaderboard, timePoint, resultState, maxCompetitorsCount, raceColumnNames, raceDetails); + } else { + jsonLeaderboard = createEmptyLeaderboardJson(leaderboard, resultState, maxCompetitorsCount); + } + StringWriter sw = new StringWriter(); + jsonLeaderboard.writeJSONString(sw); + String json = sw.getBuffer().toString(); + response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); + } catch (NoWindException | InterruptedException | ExecutionException | IOException e) { + response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) .type(MediaType.TEXT_PLAIN).build(); } - JSONObject jsonLeaderboard; - if (timePoint != null || resultState == ResultStates.Live) { - jsonLeaderboard = getLeaderboardJson(leaderboard, timePoint, resultState, maxCompetitorsCount, raceColumnNames, raceDetails); - } else { - jsonLeaderboard = createEmptyLeaderboardJson(leaderboard, resultState, maxCompetitorsCount); - } - StringWriter sw = new StringWriter(); - jsonLeaderboard.writeJSONString(sw); - String json = sw.getBuffer().toString(); - response = Response.ok(json).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build(); - } catch (NoWindException | InterruptedException | ExecutionException | IOException e) { - response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()) - .type(MediaType.TEXT_PLAIN).build(); } + return response; + } finally { + ShardingContext.clearShardingConstraint(ShardingType.LEADERBOARDNAME); } - return response; } @Override @@ -122,6 +130,7 @@ public class LeaderboardsResourceV2 extends AbstractLeaderboardsResource { } JSONArray jsonCompetitorEntries = new JSONArray(); jsonLeaderboard.put("competitors", jsonCompetitorEntries); + jsonLeaderboard.put("ShardingLeaderboardName", ShardingType.LEADERBOARDNAME.encodeIfNeeded(leaderboard.getName())); int competitorCounter = 1; // Remark: leaderboardDTO.competitors are ordered by total rank for (CompetitorDTO competitor : leaderboardDTO.competitors) { diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithGPSFixStoreContentsReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithSensorFixStoreContentsReplicationTest.java similarity index 97% rename from java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithGPSFixStoreContentsReplicationTest.java rename to java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithSensorFixStoreContentsReplicationTest.java index a20a4938f18..1c119401b33 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithGPSFixStoreContentsReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackedRaceWithSensorFixStoreContentsReplicationTest.java @@ -37,7 +37,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.persistence.MongoWindStoreFactory; import com.sap.sailing.domain.persistence.PersistenceFactory; -import com.sap.sailing.domain.racelog.tracking.GPSFixStore; +import com.sap.sailing.domain.racelog.tracking.SensorFixStore; import com.sap.sailing.domain.test.PositionAssert; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -58,13 +58,13 @@ import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.testsupport.AbstractServerReplicationTestSetUp.ReplicationServiceTestImpl; /** - * Runs the same tests as {@link TrackedRaceContentsReplicationTest}, but with a non-empty {@link GPSFixStore} that + * Runs the same tests as {@link TrackedRaceContentsReplicationTest}, but with a non-empty {@link SensorFixStore} that * has special serialization requirements. * * @author Axel Uhl (d043530) * */ -public class TrackedRaceWithGPSFixStoreContentsReplicationTest extends AbstractServerReplicationTest { +public class TrackedRaceWithSensorFixStoreContentsReplicationTest extends AbstractServerReplicationTest { private Competitor competitor; private DynamicTrackedRace trackedRace; private RegattaNameAndRaceName raceIdentifier; diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/util/ShardingLeaderBoardEncodingTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/util/ShardingLeaderBoardEncodingTest.java new file mode 100644 index 00000000000..60432760c09 --- /dev/null +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/util/ShardingLeaderBoardEncodingTest.java @@ -0,0 +1,19 @@ +package com.sap.sailing.server.util; + +import org.junit.Test; + +import com.sap.sailing.domain.common.sharding.ShardingType; + +import junit.framework.Assert; + +public class ShardingLeaderBoardEncodingTest { + @Test + public void testEncoding() { + Assert.assertEquals("/leaderboard/pureascistring", ShardingType.LEADERBOARDNAME.encodeIfNeeded("pureascistring")); + Assert.assertEquals("/leaderboard/unpure_asci_string", ShardingType.LEADERBOARDNAME.encodeIfNeeded("unpure asci string")); + Assert.assertEquals("/leaderboard/c_dille", ShardingType.LEADERBOARDNAME.encodeIfNeeded("cédille")); + Assert.assertEquals("/leaderboard/Hello_World", ShardingType.LEADERBOARDNAME.encodeIfNeeded("Hello+World")); + Assert.assertEquals("/leaderboard/Hello_World_", ShardingType.LEADERBOARDNAME.encodeIfNeeded("Hello(World)")); + + } +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java index 0c501a4abcd..a8c51048d72 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RegattaByKeywordSearchService.java @@ -126,12 +126,12 @@ public class RegattaByKeywordSearchService { * * @param leaderboard the leaderboard to get the matching events for * @param leaderboardGroup the LeaderboardGroup hosting the leaderboard - * @param events all events hosting the LeaderboardGroup + * @param events all events hosting the LeaderboardGroup; may be {@code null}; if {@code null}, result will be {@code null} * @return the best matching events for the given Leaderboard/LeaderboardGroup */ private Set filterEventsForLeaderboard(Leaderboard leaderboard, LeaderboardGroup leaderboardGroup, Set events) { final Set result; - if (leaderboardGroup.hasOverallLeaderboard()) { + if (events != null && leaderboardGroup.hasOverallLeaderboard()) { CourseArea defaultCourseArea = leaderboard.getDefaultCourseArea(); Set preResult = null; if (defaultCourseArea != null) { diff --git a/java/com.sap.sailing.www/release_notes_admin.html b/java/com.sap.sailing.www/release_notes_admin.html index eb33f2b4755..28259db3f04 100755 --- a/java/com.sap.sailing.www/release_notes_admin.html +++ b/java/com.sap.sailing.www/release_notes_admin.html @@ -29,6 +29,17 @@ This can be done from the adminconsole->tracked races->audio & videos tab

  • In raceboard when you edit a video you do not need to explicitly press "Preview" before saving anymore. Saving now implicitly applies the changes.
  • +
  • The URLs of our GWT services are extended to include a leaderboard name if a call is context sensitive to a specific leaderboard. + This does not affect the functionality of any service but allows us to setup load balancing with leaderboard specific routing. + The leaderboard names put into the URL are cleaned: Any character that isn't a latin upper/lowercase letter or digit is replaced with an underscore character. + Currently, the following leaderboard specific URLs are used: +
      +
    • /gwt/service/sailing/leaderboard/[CLEANED-LEADERBOARD-NAME]
    • +
    • /gwt/service/dispatch/leaderboard/[CLEANED-LEADERBOARD-NAME]
    • +
    + Be aware that request without leaderboard suffix may be sent from pages that do not show leaderboard specific contents. + Due to this it is required to define a fallback rule for any unqualified request. +
  • May 2018

    diff --git a/java/com.sap.sse.datamining.ui.test/.classpath b/java/com.sap.sse.datamining.ui.test/.classpath new file mode 100644 index 00000000000..341ee4d6e0b --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/java/com.sap.sse.datamining.ui.test/.project b/java/com.sap.sse.datamining.ui.test/.project new file mode 100644 index 00000000000..ad59090e252 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/.project @@ -0,0 +1,39 @@ + + + com.sap.sse.datamining.ui.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + com.gwtplugins.gdt.eclipse.core.webAppProjectValidator + + + + + com.gwtplugins.gwt.eclipse.core.gwtProjectValidator + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + com.gwtplugins.gwt.eclipse.core.gwtNature + + diff --git a/java/com.sap.sse.datamining.ui.test/.settings/com.gwtplugins.gdt.eclipse.core.prefs b/java/com.sap.sse.datamining.ui.test/.settings/com.gwtplugins.gdt.eclipse.core.prefs new file mode 100644 index 00000000000..8f0b83a4967 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/.settings/com.gwtplugins.gdt.eclipse.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +jarsExcludedFromWebInfLib= +warSrcDir= +warSrcDirIsOutput=true diff --git a/java/com.sap.sse.datamining.ui.test/.settings/com.gwtplugins.gwt.eclipse.core.prefs b/java/com.sap.sse.datamining.ui.test/.settings/com.gwtplugins.gwt.eclipse.core.prefs new file mode 100644 index 00000000000..f23a7529805 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/.settings/com.gwtplugins.gwt.eclipse.core.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +entryPointModules= diff --git a/java/com.sap.sse.datamining.ui.test/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sse.datamining.ui.test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..0c68a61dca8 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/java/com.sap.sse.datamining.ui.test/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sse.datamining.ui.test/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 00000000000..f29e940a005 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/java/com.sap.sse.datamining.ui.test/META-INF/MANIFEST.MF b/java/com.sap.sse.datamining.ui.test/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..fa71dc3af04 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/META-INF/MANIFEST.MF @@ -0,0 +1,15 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Test +Bundle-SymbolicName: com.sap.sse.datamining.ui.test +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: SAP +Automatic-Module-Name: com.sap.sse.datamining.ui.test +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Require-Bundle: org.junit, + com.google.gwt.user;bundle-version="2.8.2", + com.sap.sse.datamining.ui, + com.sap.sse.gwt;bundle-version="1.0.0", + slf4j.api;bundle-version="1.6.4", + com.sap.sse.common, + com.sap.sse.datamining.shared diff --git a/java/com.sap.sse.datamining.ui.test/build.properties b/java/com.sap.sse.datamining.ui.test/build.properties new file mode 100644 index 00000000000..237d94f640a --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/build.properties @@ -0,0 +1,5 @@ +source.. = src/,\ + test/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/TestSSEDataMiningUi.gwt.xml b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/TestSSEDataMiningUi.gwt.xml new file mode 100644 index 00000000000..50bbcf4d464 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/TestSSEDataMiningUi.gwt.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyQueryResultDTO.java b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyQueryResultDTO.java new file mode 100644 index 00000000000..1cb0945ee63 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyQueryResultDTO.java @@ -0,0 +1,16 @@ +package com.sap.sse.datamining.ui.test.client.presentation; + +import java.io.Serializable; +import java.util.HashMap; + +import com.sap.sse.datamining.shared.data.QueryResultState; +import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; + +public class DummyQueryResultDTO extends QueryResultDTO { + + private static final long serialVersionUID = 4163808455941696613L; + + public DummyQueryResultDTO(Class resultType) { + super(QueryResultState.NORMAL, resultType, new HashMap<>()); + } +} diff --git a/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyResultsPresenter.java b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyResultsPresenter.java new file mode 100644 index 00000000000..eeb53fde4c6 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyResultsPresenter.java @@ -0,0 +1,85 @@ +package com.sap.sse.datamining.ui.test.client.presentation; + +import java.util.List; + +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sse.common.settings.Settings; +import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; +import com.sap.sse.datamining.ui.client.presentation.AbstractResultsPresenter; +import com.sap.sse.datamining.ui.client.presentation.TabbedResultsPresenter; +import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; + +/** + * Dummy class for testing the + * {@link TabbedResultsPresenter#registerResultsPresenter(Class, com.sap.sse.datamining.ui.client.ResultsPresenter)} + * method. Therefore its {@link #showResult(QueryResultDTO)} method is overridden and simply stores a reference of this + * presenter instance. + * + * @author D064866 + * + */ +public class DummyResultsPresenter extends AbstractResultsPresenter { + + private final DockLayoutPanel layoutPanel; + private final List executedResultsPresenter; + + public DummyResultsPresenter(List executeResultsPresenter) { + super(null, null); + this.executedResultsPresenter = executeResultsPresenter; + layoutPanel = new DockLayoutPanel(Unit.PCT); + } + + /** + * Stores this ResultsPresenter for test purposes. + */ + @Override + public void showResult(QueryResultDTO result) { + executedResultsPresenter.add(this); + } + + @Override + public String getId() { + return null; + } + + @Override + public String getLocalizedShortName() { + return null; + } + + @Override + public boolean hasSettings() { + return false; + } + + @Override + public SettingsDialogComponent getSettingsDialogComponent(Settings useTheseSettings) { + return null; + } + + @Override + public Settings getSettings() { + return null; + } + + @Override + public void updateSettings(Settings newSettings) { + } + + @Override + public String getDependentCssClassName() { + return null; + } + + @Override + protected Widget getPresentationWidget() { + return layoutPanel; + } + + @Override + protected void internalShowResults(QueryResultDTO result) { + } + +} diff --git a/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/TestSSEDataMiningUiJUnit.gwt.xml b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/TestSSEDataMiningUiJUnit.gwt.xml new file mode 100644 index 00000000000..6667613bc7b --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/TestSSEDataMiningUiJUnit.gwt.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/TestSSEDataMiningUiSuite.java b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/TestSSEDataMiningUiSuite.java new file mode 100644 index 00000000000..7fdf2040b1e --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/TestSSEDataMiningUiSuite.java @@ -0,0 +1,19 @@ +package com.sap.sse.datamining.ui.test; + +import com.sap.sse.datamining.ui.test.client.TestSSEDataMiningUi; +import com.google.gwt.junit.tools.GWTTestSuite; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * + * @author D064866 + * + */ +public class TestSSEDataMiningUiSuite extends GWTTestSuite { + public static Test suite() { + TestSuite suite = new TestSuite("Tests for TestSSEDataMiningUi"); + suite.addTestSuite(TestSSEDataMiningUi.class); + return suite; + } +} diff --git a/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/client/TestSSEDataMiningUi.java b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/client/TestSSEDataMiningUi.java new file mode 100644 index 00000000000..59e4dc4ef87 --- /dev/null +++ b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/client/TestSSEDataMiningUi.java @@ -0,0 +1,70 @@ +package com.sap.sse.datamining.ui.test.client; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gwt.junit.client.GWTTestCase; +import com.sap.sse.datamining.ui.client.presentation.MultiResultsPresenter; +import com.sap.sse.datamining.ui.client.presentation.PlainResultsPresenter; +import com.sap.sse.datamining.ui.client.presentation.ResultsChart; +import com.sap.sse.datamining.ui.client.presentation.TabbedResultsPresenter; +import com.sap.sse.datamining.ui.test.client.presentation.DummyQueryResultDTO; +import com.sap.sse.datamining.ui.test.client.presentation.DummyResultsPresenter; + +/** + * GWT JUnit tests must extend GWTTestCase. + * + * @author D064866 + */ +public class TestSSEDataMiningUi extends GWTTestCase { + + /** + * Must refer to a valid module that sources this class. + */ + public String getModuleName() { + return "com.sap.sse.datamining.ui.test.TestSSEDataMiningUiJUnit"; + } + + /** + * Tests the mechanism {@link TabbedResultsPresenter#registerResultsPresenter(Class, ResultsPresenter)}. + */ + public void testAvoidDuplicateResultTypeRegistration() { + TabbedResultsPresenter presenter = new TabbedResultsPresenter(null, null, null); + + presenter.registerResultsPresenter(String.class, new PlainResultsPresenter(null, null)); + presenter.registerResultsPresenter(Double.class, new MultiResultsPresenter(null, null, null)); + try { + presenter.registerResultsPresenter(String.class, new ResultsChart(null, null, true, null)); + fail(); + } catch (IllegalStateException e) { + assertTrue(true); + } + } + + /** + * Tests the mechanism {@link TabbedResultsPresenter#showResult(QueryResultDTO)}. + */ + public void testShowResultWithCorrectResultsPresenter() { + TabbedResultsPresenter presenter = new TabbedResultsPresenter(null, null, null); + List executedPresenter = new ArrayList<>(); + + DummyResultsPresenter stringResultsPresenter = new DummyResultsPresenter(executedPresenter); + DummyResultsPresenter doubleResultsPresenter = new DummyResultsPresenter(executedPresenter); + + presenter.registerResultsPresenter(String.class, stringResultsPresenter); + presenter.registerResultsPresenter(Double.class, doubleResultsPresenter); + + presenter.showResult(new DummyQueryResultDTO<>(String.class)); + assertTrue(executedPresenter.size() == 1); + assertEquals(executedPresenter.get(0), stringResultsPresenter); + + presenter.showResult(new DummyQueryResultDTO<>(Double.class)); + assertTrue(executedPresenter.size() == 2); + assertEquals(executedPresenter.get(1), doubleResultsPresenter); + + presenter.showResult(new DummyQueryResultDTO<>(Number.class)); + assertTrue(executedPresenter.size() == 2); + assertEquals(executedPresenter.get(0), stringResultsPresenter); + assertEquals(executedPresenter.get(1), doubleResultsPresenter); + } +} diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java index d35ccd795fc..9a5d2bb0314 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java @@ -62,5 +62,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { String csvExport(); String search(); String csvCopiedToClipboard(); + String filter(); + String filterShownDimensions(); + String selectDimensionsToFilterBy(); } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties index 6874c85ee5a..522283d4f2b 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties @@ -46,3 +46,6 @@ copiedToClipboard=The text has been copied to clipboard. csvExport=Export as CSV search=Search csvCopiedToClipboard=CSV content has been copied to clipboard. +filter=Filter +filterShownDimensions=Filter shown dimensions +selectDimensionsToFilterBy=Select dimensions to filter by \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties index 84fa8ae473f..7fd0a304851 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties @@ -46,3 +46,6 @@ copiedToClipboard=Der Text wurde in die Zwischenablage kopiert. csvExport=Export als CSV search=Suche csvCopiedToClipboard=Der Inhalt wurde als CSV in die Zwischenablage kopiert. +filter=Filter +filterShownDimensions=Angezeigte Dimensionen filtern +selectDimensionsToFilterBy=Dimensionen der Filterung auswählen \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java index a814a6ce7ff..88d13fc2d8a 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/ResultsChart.java @@ -1,5 +1,6 @@ package com.sap.sse.datamining.ui.client.presentation; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -25,11 +26,15 @@ import org.moxieapps.gwt.highcharts.client.events.SeriesClickEvent; import org.moxieapps.gwt.highcharts.client.events.SeriesClickEventHandler; import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsData; import org.moxieapps.gwt.highcharts.client.labels.AxisLabelsFormatter; +import org.moxieapps.gwt.highcharts.client.labels.DataLabels; +import org.moxieapps.gwt.highcharts.client.labels.DataLabelsData; +import org.moxieapps.gwt.highcharts.client.labels.DataLabelsFormatter; import org.moxieapps.gwt.highcharts.client.labels.YAxisLabels; import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions; import com.google.gwt.text.shared.AbstractRenderer; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.SimpleLayoutPanel; @@ -136,6 +141,7 @@ public class ResultsChart extends AbstractNumericResultsPresenter { private final HorizontalPanel sortByPanel; private final ValueListBox> keyComparatorListBox; private final ValueListBox decimalsListBox; + private final CheckBox showDataLabelsCheckBox; private final SimpleLayoutPanel chartPanel; private final Chart chart; @@ -207,6 +213,19 @@ public class ResultsChart extends AbstractNumericResultsPresenter { showResultData(); }); addControl(decimalsPanel); + + HorizontalPanel showDataLabelsPanel = new HorizontalPanel(); + showDataLabelsPanel.setSpacing(5); + showDataLabelsPanel.add(new Label(getDataMiningStringMessages().showDataLabels() + ":")); + showDataLabelsCheckBox = new CheckBox(); + showDataLabelsPanel.add(showDataLabelsCheckBox); + showDataLabelsCheckBox.setValue(true); + showDataLabelsCheckBox.addValueChangeHandler(e -> { + resetChartSeries(); + showResultData(); + }); + addControl(showDataLabelsPanel); + StringMessages stringMessages = getDataMiningStringMessages(); ChartToCsvExporter csvExporter = new ChartToCsvExporter(stringMessages.csvCopiedToClipboard()); @@ -430,7 +449,15 @@ public class ResultsChart extends AbstractNumericResultsPresenter { } } })); - chart.setSeriesPlotOptions(new SeriesPlotOptions().setSeriesClickEventHandler(new SeriesClickHandler())); + chart.setSeriesPlotOptions(new SeriesPlotOptions() + .setDataLabels(new DataLabels().setEnabled(true).setFormatter(new DataLabelsFormatter() { + @Override + public String format(DataLabelsData dataLabelsData) { + String dataLabel = String.valueOf(BigDecimal.valueOf(dataLabelsData.getYAsDouble()) + .setScale(decimalsListBox.getValue(), BigDecimal.ROUND_HALF_UP).doubleValue()); + return showDataLabelsCheckBox.getValue() ? dataLabel : null; + } + })).setSeriesClickEventHandler(new SeriesClickHandler())); return chart; } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/AbstractTabbedResultsPresenter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/TabbedResultsPresenter.java similarity index 90% rename from java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/AbstractTabbedResultsPresenter.java rename to java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/TabbedResultsPresenter.java index 00dc4046747..bc6bbc13d2c 100755 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/AbstractTabbedResultsPresenter.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/TabbedResultsPresenter.java @@ -9,8 +9,7 @@ import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.BeforeSelectionEvent; import com.google.gwt.event.logical.shared.BeforeSelectionHandler; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; @@ -25,7 +24,7 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.controls.ScrolledTabLayoutPanel; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public abstract class AbstractTabbedResultsPresenter extends AbstractDataMiningComponent +public class TabbedResultsPresenter extends AbstractDataMiningComponent implements ResultsPresenter { protected static final DataMiningResources resources = GWT.create(DataMiningResources.class); @@ -34,13 +33,12 @@ public abstract class AbstractTabbedResultsPresenter extends AbstractDataMiningC protected final DrillDownCallback drillDownCallback; protected final Map> registeredResultPresenterMap; - public AbstractTabbedResultsPresenter(Component parent, ComponentContext context, + public TabbedResultsPresenter(Component parent, ComponentContext context, DrillDownCallback drillDownCallback) { super(parent, context); this.drillDownCallback = drillDownCallback; tabPanel = new ScrolledTabLayoutPanel(30, Unit.PX, resources.arrowLeftIcon(), resources.arrowRightIcon()); tabPanel.setAnimationDuration(0); - tabPanel.getElement().getStyle().setMarginTop(10, Unit.PX); presentersMappedByHeader = new HashMap<>(); registeredResultPresenterMap = new HashMap<>(); @@ -50,7 +48,9 @@ public abstract class AbstractTabbedResultsPresenter extends AbstractDataMiningC private void addNewTabTab() { Label widget = new Label("This should never be shown"); - Image header = new Image(resources.plusIcon()); + FlowPanel header = new FlowPanel(); + header.addStyleName("resultsPresenterTabHeader"); + header.add(new Image(resources.plusIcon())); tabPanel.add(widget, header); // This is necessary to stop the selection of this pseudo tab tabPanel.addBeforeSelectionHandler(new BeforeSelectionHandler() { @@ -58,7 +58,7 @@ public abstract class AbstractTabbedResultsPresenter extends AbstractDataMiningC public void onBeforeSelection(BeforeSelectionEvent event) { if (event.getItem() == tabPanel.getWidgetCount() - 1) { event.cancel(); - addTabAndFocus(new MultiResultsPresenter(AbstractTabbedResultsPresenter.this, getComponentContext(), + addTabAndFocus(new MultiResultsPresenter(TabbedResultsPresenter.this, getComponentContext(), drillDownCallback)); } } @@ -156,13 +156,14 @@ public abstract class AbstractTabbedResultsPresenter extends AbstractDataMiningC return "tabbedResultsPresenters"; } - public class CloseableTabHeader extends HorizontalPanel { + public class CloseableTabHeader extends FlowPanel { - private final HTML label; + private final Label label; public CloseableTabHeader() { - label = new HTML(getDataMiningStringMessages().empty()); - label.getElement().getStyle().setMarginRight(5, Unit.PX); + this.addStyleName("resultsPresenterTabHeader"); + + label = new Label(getDataMiningStringMessages().empty()); this.add(label); Image closeImage = new Image(resources.closeIcon()); closeImage.addClickHandler(new ClickHandler() { @@ -202,7 +203,7 @@ public abstract class AbstractTabbedResultsPresenter extends AbstractDataMiningC * @throws IllegalStateException * if the {@link resultType} is already registered. */ - protected void registerResultPresenter(Class resultType, ResultsPresenter resultPresenter) + public void registerResultsPresenter(Class resultType, ResultsPresenter resultPresenter) throws IllegalStateException { String className = resultType.getName(); if (!registeredResultPresenterMap.containsKey(className)) { diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css index fb967a05a36..9f3ff36a969 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css @@ -24,4 +24,8 @@ .dataGridSpacedSubHeader { padding-top: 1em; +} + +.dataGridClearFocus *:focus { + outline: none; } \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGridResources.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGridResources.java index 24bf97ec73f..ed435942b91 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGridResources.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGridResources.java @@ -14,6 +14,8 @@ public interface DataMiningDataGridResources extends DataGrid.Resources { String dataGridSpacedSubHeader(); String dataGridSubHeaderLabel(); + + String dataGridClearFocus(); } } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java index d048145470f..68bb72469a8 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java @@ -3,6 +3,7 @@ package com.sap.sse.datamining.ui.client.selection; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -19,6 +20,7 @@ import com.google.gwt.user.client.ui.ValueListBox; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.Util; import com.sap.sse.common.settings.SerializableSettings; +import com.sap.sse.common.util.NaturalComparator; import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; @@ -37,6 +39,16 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen implements GroupingProvider { private static final String GROUPING_PROVIDER_ELEMENT_STYLE = "groupingProviderElement"; + private static final NaturalComparator NaturalComparator = new NaturalComparator(); + private static final Comparator DimensionComparator = (d1, d2) -> { + // Null values (to deselect the dimension) on top + if (d1 == null || d2 == null) { + if (d1 == d2) return 0; + if (d1 == null) return -1; + if (d2 == null) return 1; + } + return NaturalComparator.compare(d1.getDisplayName(), d2.getDisplayName()); + }; private final DataMiningServiceAsync dataMiningService; private final ErrorReporter errorReporter; @@ -62,8 +74,11 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen dimensionToGroupByBoxes = new ArrayList>(); mainPanel = new FlowPanel(); + mainPanel.addStyleName("groupingProvider"); Label groupByLabel = new Label(this.getDataMiningStringMessages().groupBy()); groupByLabel.addStyleName(GROUPING_PROVIDER_ELEMENT_STYLE); + groupByLabel.addStyleName("queryProviderElementLabel"); + groupByLabel.addStyleName("emphasizedLabel"); mainPanel.add(groupByLabel); ValueListBox firstDimensionToGroupByBox = createDimensionToGroupByBox(); @@ -110,7 +125,7 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen ValueListBox firstDimensionToGroupByBox = createDimensionToGroupByBox(); addDimensionToGroupByBoxAndUpdateAcceptableValues(firstDimensionToGroupByBox); if (!availableDimensions.isEmpty()) { - Collections.sort(availableDimensions); + Collections.sort(availableDimensions, DimensionComparator); firstDimensionToGroupByBox.setValue(availableDimensions.iterator().next(), true); } else { notifyListeners(); @@ -202,8 +217,8 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen if (dimensionToGroupByBox.getValue() != null) { acceptableValues.add(dimensionToGroupByBox.getValue()); } - Collections.sort(acceptableValues); acceptableValues.add(null); + Collections.sort(acceptableValues, DimensionComparator); dimensionToGroupByBox.setAcceptableValues(acceptableValues); } } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java index e1af16b53ce..57e88513dc1 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java @@ -56,8 +56,9 @@ import com.sap.sse.gwt.client.shared.settings.ComponentContext; public class QueryDefinitionProviderWithControls extends AbstractQueryDefinitionProvider implements WithControls { - private static final double headerPanelHeight = 45; - private static final double footerPanelHeight = 50; + private static final double HeaderPanelHeight = 45; + private static final double FooterPanelHeight = 50; + private static final int SplitterSize = 10; private final DockLayoutPanel mainPanel; private final FlowPanel controlsPanel; @@ -100,10 +101,11 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition } }); queryDefinitionViewer = new QueryDefinitionViewer(parent, context, getDataMiningStringMessages()); + queryDefinitionViewer.getEntryWidget().addStyleName("dataMiningMarginRight"); queryDefinitionViewer.setActive(false); addQueryDefinitionChangedListener(queryDefinitionViewer); predefinedQueryRunner = new PredefinedQueryRunner(parent, context, session, getDataMiningStringMessages(), - dataMiningService, errorReporter, resultsPresenter); + dataMiningService, errorReporter, resultsPresenter); Button clearSelectionButton = new Button(getDataMiningStringMessages().clearSelection()); clearSelectionButton.addClickHandler(new ClickHandler() { @@ -130,30 +132,32 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition statisticProvider = new SuggestBoxStatisticProvider(parent, context, dataMiningService, errorReporter, settingsControl, settingsManager); + statisticProvider.getEntryWidget().addStyleName("statisticProvider"); statisticProvider.addStatisticChangedListener(providerListener); groupingProvider = new MultiDimensionalGroupingProvider(parent, context, dataMiningService, errorReporter, statisticProvider); groupingProvider.addGroupingChangedListener(providerListener); + groupingProvider.getEntryWidget().addStyleName("dataMiningMarginBase"); -// filterSelectionProvider = new ListRetrieverChainFilterSelectionProvider(parent, context, session, -// stringMessages, dataMiningService, errorReporter, statisticProvider); filterSelectionProvider = new HierarchicalDimensionListFilterSelectionProvider( parent, context, session, getDataMiningStringMessages(), dataMiningService, errorReporter, statisticProvider); filterSelectionProvider.addSelectionChangedListener(providerListener); + filterSelectionProvider.getEntryWidget().addStyleName("dataMiningBorderTop"); - filterSplitPanel = new SplitLayoutPanel(15); - filterSplitPanel.addSouth(groupingProvider.getEntryWidget(), footerPanelHeight); + filterSplitPanel = new SplitLayoutPanel(SplitterSize); + filterSplitPanel.addSouth(groupingProvider.getEntryWidget(), FooterPanelHeight); filterSplitPanel.addEast(queryDefinitionViewer.getEntryWidget(), 600); filterSplitPanel.setWidgetHidden(queryDefinitionViewer.getEntryWidget(), true); filterSplitPanel.add(filterSelectionProvider.getEntryWidget()); - SplitLayoutPanel headerPanel = new SplitLayoutPanel(15); + SplitLayoutPanel headerPanel = new SplitLayoutPanel(SplitterSize); + headerPanel.addStyleName("dataMiningMarginBase"); headerPanel.addWest(statisticProvider.getEntryWidget(), 800); headerPanel.add(controlsPanel); mainPanel = new DockLayoutPanel(Unit.PX); - mainPanel.addNorth(headerPanel, headerPanelHeight); + mainPanel.addNorth(headerPanel, HeaderPanelHeight); mainPanel.add(filterSplitPanel); // Storing the different component providers in a list @@ -185,11 +189,15 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition final Collection dimensionsToGroupBy = groupingProvider.getDimensionsToGroupBy(); if (!dimensionsToGroupBy.isEmpty()) { final FunctionDTO firstDimension = dimensionsToGroupBy.iterator().next(); - filterSelectionProvider.setHighestRetrieverLevelWithFilterDimension(firstDimension, - (Serializable) groupKeyForSingleDimension.getValue()); if (dimensionsToGroupBy.size() == 1) { - letUserSelectADifferentFirstDimension(onSuccessCallback); + letUserSelectADifferentFirstDimension(() -> { + filterSelectionProvider.setHighestRetrieverLevelWithFilterDimension(firstDimension, + (Serializable) groupKeyForSingleDimension.getValue()); + onSuccessCallback.run(); + }); } else { + filterSelectionProvider.setHighestRetrieverLevelWithFilterDimension(firstDimension, + (Serializable) groupKeyForSingleDimension.getValue()); groupingProvider.removeDimensionToGroupBy(firstDimension); onSuccessCallback.run(); } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SuggestBoxStatisticProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SuggestBoxStatisticProvider.java index a95fe85101a..c06d793afd0 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SuggestBoxStatisticProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SuggestBoxStatisticProvider.java @@ -11,6 +11,7 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.Set; +import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.i18n.client.LocaleInfo; @@ -99,6 +100,8 @@ public class SuggestBoxStatisticProvider extends AbstractDataMiningComponent(); @@ -114,6 +117,13 @@ public class SuggestBoxStatisticProvider extends AbstractDataMiningComponent { + int keyCode = e.getNativeEvent().getKeyCode(); + if (keyCode == KeyCodes.KEY_ESCAPE) { + extractionFunctionSuggestBox.hideSuggestionList(); + extractionFunctionSuggestBox.setFocus(false); + } + }); extractionFunctionSuggestBox.setLimit(Integer.MAX_VALUE); extractionFunctionSuggestBox.addStyleName(STATISTIC_PROVIDER_ELEMENT_STYLE); extractionFunctionSuggestBox.setWidth("540px"); @@ -222,7 +232,7 @@ public class SuggestBoxStatisticProvider extends AbstractDataMiningComponent { private final AbstractListSuggestOracle suggestOracle; + private final ScrollableSuggestionDisplay display; + private ExtractionFunctionWithContext extractionFunction; @SuppressWarnings("unchecked") public ExtractionFunctionSuggestBox() { super(new AbstractListSuggestOracle() { + @Override + protected Iterable getKeywordStrings(Iterable queryTokens) { + String filterText = Util.first(queryTokens); + if (filterText == null) { + return queryTokens; + } + return Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(filterText); + } @Override protected Iterable getMatchingStrings(ExtractionFunctionWithContext value) { @@ -586,8 +606,14 @@ public class SuggestBoxStatisticProvider extends AbstractDataMiningComponent) getSuggestOracle(); + display = (ScrollableSuggestionDisplay) getSuggestionDisplay(); addSuggestionSelectionHandler(this::setExtractionFunction); } + + @Override + public void hideSuggestionList() { + display.hideSuggestions(); + } public void setSelectableValues(Collection selectableValues) { suggestOracle.setSelectableValues(selectableValues); @@ -596,6 +622,7 @@ public class SuggestBoxStatisticProvider extends AbstractDataMiningComponent(Integer.MAX_VALUE, dataGridResources); dataGrid.setAutoHeaderRefreshDisabled(true); dataGrid.setAutoFooterRefreshDisabled(true); - dataGrid.addStyleName("dimensionFilterSelectionTable"); + dataGrid.addStyleName("dataMiningBorderTop"); filteredData = new ListDataProvider(this::elementAsString); filterPanel = new AbstractFilterablePanel(null, dataGrid, filteredData) { @@ -177,6 +178,12 @@ public class DimensionFilterSelectionProvider extends AbstractComponent { boolean enabled = toggleFilterButton.isDown(); mainPanel.setWidgetHidden(filterPanel, !enabled); + if (enabled) { + Scheduler.get().scheduleDeferred(() -> { + filterPanel.getTextBox().setFocus(true); + filterPanel.getTextBox().selectAll(); + }); + } ListDataProvider oldProvider = enabled ? filterPanel.getAllListDataProvider(): filteredData; ListDataProvider newProvider = enabled ? filteredData : filterPanel.getAllListDataProvider(); diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/filter/HierarchicalDimensionListFilterSelectionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/filter/HierarchicalDimensionListFilterSelectionProvider.java index 4b8cd613828..0581878a35f 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/filter/HierarchicalDimensionListFilterSelectionProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/filter/HierarchicalDimensionListFilterSelectionProvider.java @@ -18,7 +18,6 @@ import com.google.gwt.cell.client.CheckboxCell; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.builder.shared.TableCellBuilder; import com.google.gwt.dom.builder.shared.TableRowBuilder; -import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.NodeList; @@ -62,7 +61,6 @@ import com.sap.sse.datamining.ui.client.resources.DataMiningDataGridResources.Da import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.celltable.BaseCellTableBuilder; import com.sap.sse.gwt.client.panels.AbstractFilterablePanel; -import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel; import com.sap.sse.gwt.client.shared.components.AbstractComponent; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; @@ -121,6 +119,11 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo allFilterDimensions = new ArrayList<>(); filteredFilterDimensions = new ListDataProvider<>(); + Label filterDimensionsSelectionTitleLabel = new Label(stringMessages.selectDimensionsToFilterBy()); + filterDimensionsSelectionTitleLabel.addStyleName("emphasizedLabel"); + filterDimensionsSelectionTitleLabel.addStyleName("dataMiningMarginLeft"); + filterDimensionsSelectionTitleLabel.addStyleName("filterDimensionsTitleLabel"); + DataMiningDataGridResources resources = GWT.create(DataMiningDataGridResources.class); filterDimensionsList = new DataGrid<>(Integer.MAX_VALUE, resources); filterDimensionsList.setAutoHeaderRefreshDisabled(true); @@ -128,8 +131,8 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo filterDimensionsList.setTableBuilder(new FilterDimensionsListBuilder(filterDimensionsList, resources.dataGridStyle())); filteredFilterDimensions.addDataDisplay(filterDimensionsList); - filterFilterDimensionsPanel = new LabeledAbstractFilterablePanel( - new Label(stringMessages.search()), null, filterDimensionsList, filteredFilterDimensions) + filterFilterDimensionsPanel = new AbstractFilterablePanel( + null, filterDimensionsList, filteredFilterDimensions) { @Override public Iterable getSearchableStrings(DimensionWithContext dimension) { @@ -141,6 +144,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo filterFilterDimensionsPanel.setWidth("100%"); filterFilterDimensionsPanel.setHeight("100%"); filterFilterDimensionsPanel.getTextBox().setWidth("100%"); + filterFilterDimensionsPanel.getTextBox().getElement().setPropertyString("placeholder", stringMessages.filterShownDimensions()); filterDimensionSelectionModel = new MultiSelectionModel<>(); filterDimensionSelectionModel.addSelectionChangeHandler(this::selectedFilterDimensionsChanged); @@ -162,15 +166,19 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo filterDimensionsList.addColumn(dimensionColumn); DockLayoutPanel filterDimensionsSelectionPanel = new DockLayoutPanel(LayoutUnit); + filterDimensionsSelectionPanel.addNorth(filterDimensionsSelectionTitleLabel, 20); filterDimensionsSelectionPanel.addNorth(filterFilterDimensionsPanel, DimensionSelectionHeaderHeight); filterDimensionsSelectionPanel.add(filterDimensionsList); dimensionFilterSelectionProviders = new HashMap<>(); dimensionFilterSelectionProvidersPanel = new DockLayoutPanel(LayoutUnit); dimensionFilterSelectionProvidersPanel.addStyleName("dimensionFilterSelectionTablesContainer"); + dimensionFilterSelectionProvidersPanel.addStyleName("dataMiningBorderLeft"); filterSelectionPresenter = new PlainFilterSelectionPresenter(this, context, stringMessages, retrieverChainProvider, this); + filterSelectionPresenter.getEntryWidget().addStyleName("dataMiningMarginLeft"); filterSelectionPresenterContainer = new ScrollPanel(filterSelectionPresenter.getEntryWidget()); + filterSelectionPresenterContainer.addStyleName("dataMiningBorderTop"); mainPanel = new DockLayoutPanel(LayoutUnit); mainPanel.addSouth(filterSelectionPresenterContainer, SelectionPresenterHeight); @@ -522,6 +530,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo private final String firstColumnStyle; private final String hoveredRowStyle; private final String hoveredRowCellStyle; + private final String clearFocus; public FilterDimensionsListBuilder(AbstractCellTable cellTable, DataMiningDataGridStyle style) { super(cellTable); @@ -532,6 +541,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo hoveredRowStyle = style.dataGridHoveredRow(); hoveredRowCellStyle = style.dataGridHoveredRowCell(); subHeaderLabelStyle = style.dataGridSubHeaderLabel(); + clearFocus = style.dataGridClearFocus(); cellTable.addRowHoverHandler(new RowHoverEvent.Handler() { @Override @@ -558,14 +568,15 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo if (!Objects.equals(previousLevel, valueLevel)) { StringBuilder styleBuilder = new StringBuilder(); - styleBuilder.append(subHeaderStyle).append(" ").append(headerStyle); + styleBuilder.append(subHeaderStyle).append(" ").append(headerStyle) + .append(" ").append(clearFocus); if (absRowIndex != 0) { styleBuilder.append(" ").append(spacedSubHeaderStyle); } String style = styleBuilder.toString(); String subHeaderText = valueLevel.getRetrievedDataType().getDisplayName(); - TableRowBuilder subHeaderBuilder = startRow().attribute(DimensionListSubheaderAttribute, subHeaderText); + TableRowBuilder subHeaderBuilder = startRow().className(clearFocus).attribute(DimensionListSubheaderAttribute, subHeaderText); // Additional cell to fix the checkbox column size subHeaderBuilder.startTD().className(style + " " + firstColumnStyle).endTD(); // Actual header cell @@ -584,35 +595,22 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo @Override public boolean clearCurrentSelection(CellPreviewEvent event) { - return !isCheckboxColumn(event.getColumn()); + return false; } @Override public SelectAction translateSelectionEvent(CellPreviewEvent event) { + SelectAction action = SelectAction.TOGGLE; + NativeEvent nativeEvent = event.getNativeEvent(); - if (BrowserEvents.CLICK.equals(nativeEvent.getType())) { - Element targetRow = Element.as(nativeEvent.getEventTarget()); - while (!TableRowElement.is(targetRow) && targetRow != null) { - targetRow = targetRow.getParentElement(); - } - if (targetRow != null && targetRow.hasAttribute(DimensionListSubheaderAttribute)) { - return SelectAction.IGNORE; - } - - if (nativeEvent.getCtrlKey()) { - DimensionWithContext value = event.getValue(); - filterDimensionSelectionModel.setSelected(value, !filterDimensionSelectionModel.isSelected(value)); - return SelectAction.IGNORE; - } - if (!filterDimensionSelectionModel.getSelectedSet().isEmpty() && !isCheckboxColumn(event.getColumn())) { - return SelectAction.DEFAULT; - } + Element targetRow = Element.as(nativeEvent.getEventTarget()); + while (!TableRowElement.is(targetRow) && targetRow != null) { + targetRow = targetRow.getParentElement(); } - return SelectAction.TOGGLE; - } - - private boolean isCheckboxColumn(int columnIndex) { - return columnIndex == filterDimensionsList.getColumnIndex(checkboxColumn); + if (targetRow != null && targetRow.hasAttribute(DimensionListSubheaderAttribute)) { + action = SelectAction.IGNORE; + } + return action; } } diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairCollectingProcessor.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairCollectingProcessor.java index 8a7aa40855f..7ec39013c85 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairCollectingProcessor.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairCollectingProcessor.java @@ -54,14 +54,12 @@ public class ParallelGroupedNumberPairCollectingProcessor final Double doubleValueA = a == null ? null : a.doubleValue(); final Double doubleValueB = b == null ? null : b.doubleValue(); - if (doubleValueA != null && doubleValueB != null) { - if(aggregate == null) { + if (aggregate == null) { aggregate = new HashSet<>(); aggregate.add(new Pair<>(doubleValueA, doubleValueB)); individualPairs.put(element.getKey(), aggregate); - } - else { + } else { individualPairs.get(element.getKey()).add(new Pair<>(doubleValueA, doubleValueB)); } } diff --git a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF index 6e8d7576d06..38cf03ddfe8 100644 --- a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF @@ -57,6 +57,7 @@ Export-Package: com.google.gwt.user.client.rpc.core.java.util, com.sap.sse.gwt.dispatch.client.system, com.sap.sse.gwt.dispatch.client.system.batching, com.sap.sse.gwt.dispatch.client.system.caching, + com.sap.sse.gwt.dispatch.client.system.routing, com.sap.sse.gwt.dispatch.client.transport.gwtrpc, com.sap.sse.gwt.dispatch.servlets, com.sap.sse.gwt.dispatch.shared.caching, diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java index dab48bda285..fbc08f813ff 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DefaultErrorReporter.java @@ -3,13 +3,13 @@ package com.sap.sse.gwt.client; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.safehtml.shared.SafeHtmlUtils; -import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; +import com.sap.sse.gwt.client.Notification.NotificationType; public class DefaultErrorReporter implements ErrorReporter { private DialogBox errorDialogBox; @@ -49,7 +49,7 @@ public class DefaultErrorReporter implements ErrorRepo @Override public void reportError(String message, boolean silentMode) { if (silentMode) { - Window.setStatus(message); + Notification.notify(message, NotificationType.WARNING); } else { reportError(message); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java new file mode 100644 index 00000000000..d9d48801fff --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/Notification.java @@ -0,0 +1,136 @@ +package com.sap.sse.gwt.client; + +import java.util.LinkedList; + +import com.google.gwt.animation.client.Animation; +import com.google.gwt.core.shared.GWT; +import com.google.gwt.dom.client.Style.Cursor; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.dom.client.Style.Visibility; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.CssResource; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.EventListener; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.RootPanel; +import com.sap.sse.common.Util.Pair; + +/** + * Utility class to show non obstructive warning / info messages using a small notification at the bottom of the page. Clicking the notification will hide it. It will autohide after some time. + */ +public class Notification { + private static final int NOTIFICATION_TIME = 20000; + private static final double FADE_OUT_PERCENT = 0.98; + private static final double FADE_IN_PERCENT = 0.005; + + interface NotificationResources extends ClientBundle { + @Source("notification.gss") + NotificationCSS css(); + } + + interface NotificationCSS extends CssResource { + String snackbar(); + } + + private final static FlowPanel snackBar = new FlowPanel(); + private final static NotificationResources ress = GWT.create(NotificationResources.class); + private static Animation notificationAnimation; + + static { + ress.css().ensureInjected(); + snackBar.addStyleName(ress.css().snackbar()); + snackBar.getElement().getStyle().setCursor(Cursor.POINTER); + + notificationAnimation = new Animation() { + @Override + protected void onStart() { + super.onStart(); + snackBar.getElement().getStyle().setOpacity(0); + snackBar.getElement().getStyle().setVisibility(Visibility.VISIBLE); + } + + @Override + protected void onUpdate(double progress) { + if (progress < FADE_IN_PERCENT) { + double relPr = progress / FADE_IN_PERCENT; + double bottom = 60 * relPr - 30; + snackBar.getElement().getStyle().setOpacity(relPr); + snackBar.getElement().getStyle().setBottom(bottom, Unit.PX); + } else if (progress > FADE_OUT_PERCENT) { + double relPr = (progress - FADE_OUT_PERCENT) / (1 - FADE_OUT_PERCENT); + snackBar.getElement().getStyle().setOpacity(1 - relPr); + double bottom = 60 * (1 - relPr) - 30; + snackBar.getElement().getStyle().setBottom(bottom, Unit.PX); + } else { + snackBar.getElement().getStyle().setOpacity(1); + snackBar.getElement().getStyle().setBottom(30, Unit.PX); + } + } + + @Override + protected void onComplete() { + snackBar.getElement().getStyle().setVisibility(Visibility.HIDDEN); + checkQueue(true); + } + + @Override + protected void onCancel() { + snackBar.getElement().getStyle().setVisibility(Visibility.HIDDEN); + checkQueue(true); + } + }; + } + + private final static LinkedList> QUEUE = new LinkedList<>(); + + private Notification() { + } + + public enum NotificationType { + ERROR("\u2716", "RED", "WHITE"), + WARNING("\u26A0", "ORANGE", "WHITE"), + INFO("\u2139", "#008fcc", "WHITE"); + + private String decorator; + private String bgColor; + private String color; + + NotificationType(String decorator, String bgColor, String color) { + this.decorator = decorator; + this.bgColor = bgColor; + this.color = color; + } + } + + public static void notify(String message, NotificationType type) { + QUEUE.add(new Pair<>(message, type)); + checkQueue(false); + } + + /** + * Displays next message if non is displayed currently, else does nothing and will by called by currently displaying + * message TTL timer + */ + private static void checkQueue(boolean onAnimationFinish) { + if (!QUEUE.isEmpty()) { + if (!notificationAnimation.isRunning() || onAnimationFinish) { + RootPanel.get().add(snackBar); + Event.sinkEvents(snackBar.getElement(), Event.ONCLICK); + Event.setEventListener(snackBar.getElement(), new EventListener() { + @Override + public void onBrowserEvent(Event event) { + GWT.log("test"); + notificationAnimation.cancel(); + } + }); + + Pair notification = QUEUE.pop(); + snackBar.getElement().getStyle().setColor(notification.getB().color); + snackBar.getElement().getStyle().setBackgroundColor(notification.getB().bgColor); + snackBar.getElement().setInnerText(notification.getB().decorator + " " + notification.getA() + " " + + notification.getB().decorator); + notificationAnimation.run(NOTIFICATION_TIME); + } + } + } +} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/ServiceRoutingProvider.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/ServiceRoutingProvider.java new file mode 100644 index 00000000000..7c23758b38b --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/ServiceRoutingProvider.java @@ -0,0 +1,12 @@ +package com.sap.sse.gwt.client; + +import com.sap.sse.gwt.dispatch.client.system.DispatchSystemDefaultImpl; + +/** + * Interface used to append a routing suffix to target url + * in GWT RPC service instances and in {@link DispatchSystemDefaultImpl} + */ +public interface ServiceRoutingProvider { + + String routingSuffixPath(); +} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java index 38d274df3ac..c4daf0ca04f 100755 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.java @@ -74,6 +74,7 @@ public interface StringMessages extends Messages { String groupMedianDescending(); String choosePresentation(); String shownDecimals(); + String showDataLabels(); String elements(long count); String resultsChart(); String cantDisplayDataOfType(String resultType); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties index 348b7a83769..4f973609c3a 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages.properties @@ -56,6 +56,7 @@ groupMedianAscending=Group Median (Ascending) groupMedianDescending=Group Median (Descending) choosePresentation=Choose Presentation shownDecimals=Shown Decimals +showDataLabels=Show labels elements={0} elements resultsChart=Results Chart cantDisplayDataOfType=Can''t display data of type {0} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties index 1635e689dcf..94d4788b09f 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/StringMessages_de.properties @@ -55,6 +55,7 @@ groupMedianAscending=Gruppe Median (Aufsteigend) groupMedianDescending=Gruppe Median (Absteigend) choosePresentation=Wähle Präsentation shownDecimals=Angezeigte Nachkommastellen +showDataLabels=Zahlen anzeigen elements={0} Elemente resultsChart=Ergebnis-Diagramm cantDisplayDataOfType=Daten des Typs {0} können nicht dargestellt werden diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/notification.gss b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/notification.gss new file mode 100644 index 00000000000..0e1f3b5a5e0 --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/notification.gss @@ -0,0 +1,10 @@ +.snackbar { + visibility: hidden; + min-width: 250px; + margin-left: -125px; + text-align: center; + border-radius: 2px; + padding: 16px; + position: fixed; + left: 50%; +} \ No newline at end of file diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java index 67aa7170cab..23864691929 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java @@ -18,10 +18,14 @@ public abstract class AbstractListSuggestOracle extends AbstractSuggestOracle @Override protected final void getSuggestions(Request request, Callback callback, Iterable queryTokens) { - Iterable filteredList = suggestionMatchingFilter.applyFilter(queryTokens, suggestionObjectList); + Iterable filteredList = suggestionMatchingFilter.applyFilter(getKeywordStrings(queryTokens), suggestionObjectList); this.setSuggestions(request, callback, filteredList, queryTokens); } + protected Iterable getKeywordStrings(Iterable queryTokens) { + return queryTokens; + } + public final AbstractListFilter getSuggestionMatchingFilter() { return suggestionMatchingFilter; } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/system/DispatchSystemDefaultImpl.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/system/DispatchSystemDefaultImpl.java index 39cdc47e11b..c72ab6f1f1d 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/system/DispatchSystemDefaultImpl.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/dispatch/client/system/DispatchSystemDefaultImpl.java @@ -1,11 +1,14 @@ package com.sap.sse.gwt.dispatch.client.system; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.google.gwt.core.client.Scheduler; import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sse.gwt.client.ServiceRoutingProvider; import com.sap.sse.gwt.dispatch.client.system.batching.AutomaticBatchingDispatch; import com.sap.sse.gwt.dispatch.client.system.caching.CachingDispatch; import com.sap.sse.gwt.dispatch.client.transport.DefaultTransport; @@ -27,8 +30,11 @@ public abstract class DispatchSystemDefaultImpl imp ProvidesServerTime { private final Logger LOG = Logger.getLogger(DispatchSystemDefaultImpl.class.getName()); private final DefaultTransport simpleDispatch; - private final DispatchSystemAsync dispatch; - + private final DispatchSystemAsync defaultDispatch; + private final Map> routingRegistry = new HashMap<>(); + private final boolean processResultsScheduled; + private final String dispatchRPCPath; + public DispatchSystemDefaultImpl(String dispatchRPCPath) { this(dispatchRPCPath, false); } @@ -43,10 +49,25 @@ public abstract class DispatchSystemDefaultImpl imp * use a {@link Scheduler} to process the results */ public DispatchSystemDefaultImpl(String dispatchRPCPath, boolean processResultsScheduled) { + this.dispatchRPCPath = dispatchRPCPath; + this.processResultsScheduled = processResultsScheduled; simpleDispatch = new DefaultTransport(dispatchRPCPath); - dispatch = new CachingDispatch(new AutomaticBatchingDispatch( - simpleDispatch, processResultsScheduled)); - LOG.finest("Started dispatch system for " + dispatchRPCPath); + defaultDispatch = new CachingDispatch(new AutomaticBatchingDispatch(simpleDispatch, processResultsScheduled)); + LOG.finest("Started default dispatch system for " + dispatchRPCPath); + } + + private DispatchSystemAsync createDispatchFor(String routing) { + final StringBuilder destinationUrlBuilder = new StringBuilder(dispatchRPCPath); + if (routing != null && !routing.isEmpty()) { + if (!dispatchRPCPath.endsWith("/")) { + destinationUrlBuilder.append("/"); + } + destinationUrlBuilder.append(routing.startsWith("/") ? routing.substring(1) : routing); + } + DefaultTransport transport = new DefaultTransport(destinationUrlBuilder.toString()); + DispatchSystemAsync dispatch = new CachingDispatch(new AutomaticBatchingDispatch( + transport, processResultsScheduled)); + return dispatch; } @Override @@ -66,7 +87,19 @@ public abstract class DispatchSystemDefaultImpl imp callback.onSuccess(result); } }; - dispatch.execute(action, wrappedCallback); + final DispatchSystemAsync dispatchToUse; + if (action instanceof ServiceRoutingProvider) { + ServiceRoutingProvider providesDispatchRoutingKey = (ServiceRoutingProvider) action; + String routingPath = providesDispatchRoutingKey.routingSuffixPath(); + + dispatchToUse = routingRegistry.computeIfAbsent(routingPath, this::createDispatchFor); + + LOG.fine("Using routed dispatch for path "+ routingPath); + } else { + LOG.fine("Using default dispatch for "+ action.getClass().getName()); + dispatchToUse = defaultDispatch; + } + dispatchToUse.execute(action, wrappedCallback); } @Override diff --git a/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF b/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF index d868170ee30..b1359749d89 100644 --- a/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF +++ b/java/com.tractrac.clientmodule/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: TracTrac Client module Bundle-SymbolicName: com.tractrac.clientmodule -Bundle-Version: 3.10.0 +Bundle-Version: 3.10.1 Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ClassPath: ., lib/TracAPI.jar @@ -41,6 +41,7 @@ Export-Package: com.tractrac.asio.lib.api, com.tractrac.dataflow.lib.prov.stored.tcp.put, com.tractrac.dataflow.lib.spi, com.tractrac.dataflow.lib.spi.authentication, + com.tractrac.datatypes.lib.api.sensor, com.tractrac.model.lib.api, com.tractrac.model.lib.api.attachment, com.tractrac.model.lib.api.data, @@ -59,10 +60,17 @@ Export-Package: com.tractrac.asio.lib.api, com.tractrac.model.lib.impl.spatial, com.tractrac.model.server.lib.api, com.tractrac.model.server.lib.api.cache, + com.tractrac.model.server.lib.api.commands, com.tractrac.model.server.lib.api.controlpassing, + com.tractrac.model.server.lib.api.devices, + com.tractrac.model.server.lib.api.gms, + com.tractrac.model.server.lib.api.loype, + com.tractrac.model.server.lib.api.notifier, com.tractrac.model.server.lib.api.position, com.tractrac.model.server.lib.api.raceobject, com.tractrac.model.server.lib.api.route, + com.tractrac.model.server.lib.api.sensor, + com.tractrac.model.server.lib.api.view, com.tractrac.osgi, com.tractrac.subscription.app.tracapi, com.tractrac.subscription.lib.api, diff --git a/java/com.tractrac.clientmodule/README.txt b/java/com.tractrac.clientmodule/README.txt index 6c8802c4d2d..90b283e5ebe 100755 --- a/java/com.tractrac.clientmodule/README.txt +++ b/java/com.tractrac.clientmodule/README.txt @@ -15,6 +15,29 @@ It contains also some files: - test.sh -> script that compiles the code in the src folder, creates the test.jar library and execute the code of the example. - Manifest.txt -> manifest used to create the test.jar file + +******************************************** +************* TracAPI 3.10.1 *************** +******************************************** + This is a final version.It fixes bugs in the implementation and it adds a some features. + It keeps the backward compatibility. + + Release date: 28/06/2018 + Build number: 35a26e503339fff26fe984617e5190a18438e10b + + 1) Features + + - Adding a method to retrieve positions by class id (Requested by Chris Terkelsen, 07/05/2018) + + 2) Bugs + + - When the client is offline, if it tries to load a race it gets a RaceLoadingException. But then, + when the client is online again, it can't reuse the same race because it is in an internal wrong + status (Reported by Thomas Scott, 19/06/2018) + - The live delay is not propagated (sometimes). This feature has been reimplemented and now the + value is attached and send with the race object using the same approach used to transmit the race + start time or the tracking start time (Reported by Axel Uhl, 19/06/2018) + ******************************************** ************* TracAPI 3.10.0 *************** ******************************************** diff --git a/java/com.tractrac.clientmodule/lib/TracAPI-src.jar b/java/com.tractrac.clientmodule/lib/TracAPI-src.jar index 31f01cdeaf2..de4c29d27cd 100755 Binary files a/java/com.tractrac.clientmodule/lib/TracAPI-src.jar and b/java/com.tractrac.clientmodule/lib/TracAPI-src.jar differ diff --git a/java/com.tractrac.clientmodule/lib/TracAPI.jar b/java/com.tractrac.clientmodule/lib/TracAPI.jar index 290f947d14e..3bc94a18dc1 100755 Binary files a/java/com.tractrac.clientmodule/lib/TracAPI.jar and b/java/com.tractrac.clientmodule/lib/TracAPI.jar differ diff --git a/java/com.tractrac.clientmodule/pom.xml b/java/com.tractrac.clientmodule/pom.xml index 83574fa3088..e1d11c5efa5 100755 --- a/java/com.tractrac.clientmodule/pom.xml +++ b/java/com.tractrac.clientmodule/pom.xml @@ -8,6 +8,6 @@ 1.0.0-SNAPSHOT com.tractrac.clientmodule - 3.10.0 + 3.10.1 eclipse-plugin diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java index 8fa4b0c4b1d..b32079cabbc 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/services/sending/MessageSendingService.java @@ -13,6 +13,7 @@ import com.sap.sailing.android.shared.R; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.services.sending.MessagePersistenceManager.MessageRestorer; import com.sap.sailing.android.shared.services.sending.MessageSenderTask.MessageSendingListener; +import com.sap.sailing.android.shared.util.NotificationHelper; import com.sap.sailing.android.shared.util.PrefUtils; import com.sap.sailing.domain.common.racelog.RaceLogServletConstants; @@ -227,9 +228,17 @@ public class MessageSendingService extends Service implements MessageSendingList if (persistenceManager.areIntentsDelayed()) { handleDelayedMessages(); } + startForeground(NotificationHelper.getNotificationId(), NotificationHelper.getNotification(this)); ExLog.i(this, TAG, "Sending Service on Create."); } + @Override + public void onDestroy() { + stopForeground(true); + ExLog.i(this, TAG, "Message Sending Service is being destroyed."); + super.onDestroy(); + } + @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent == null) { diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/fragments/preference/BasePreferenceFragment.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/fragments/preference/BasePreferenceFragment.java index 2204a0e282e..104b6bfc7a2 100644 --- a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/fragments/preference/BasePreferenceFragment.java +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/ui/fragments/preference/BasePreferenceFragment.java @@ -6,16 +6,15 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import com.sap.sailing.android.shared.ui.utils.MultiplePreferenceChangeListener; +import com.sap.sailing.android.shared.ui.views.EditSetPreference; + import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.MultiSelectListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; - -import com.sap.sailing.android.shared.ui.utils.MultiplePreferenceChangeListener; -import com.sap.sailing.android.shared.ui.views.EditSetPreference; /** * Created by I074137 on 18.09.13. @@ -27,7 +26,7 @@ public class BasePreferenceFragment extends PreferenceFragment { return (T) findPreference(getString(resourceId)); } - protected static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { + protected Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object value) { String stringValue = value.toString(); @@ -53,34 +52,28 @@ public class BasePreferenceFragment extends PreferenceFragment { } }; - protected static void bindPreferenceSummaryToValue(Preference preference) { + protected void bindPreferenceSummaryToValue(Preference preference) { addOnPreferenceChangeListener(preference, sBindPreferenceSummaryToValueListener); sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, - PreferenceManager - .getDefaultSharedPreferences(preference.getContext()) - .getString(preference.getKey(), "")); + getPreferenceManager().getSharedPreferences().getString(preference.getKey(), "")); } - protected static void bindPreferenceSummaryToInteger(Preference preference) { + protected void bindPreferenceSummaryToInteger(Preference preference) { addOnPreferenceChangeListener(preference, sBindPreferenceSummaryToValueListener); sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, - PreferenceManager - .getDefaultSharedPreferences(preference.getContext()) - .getInt(preference.getKey(), 0)); + getPreferenceManager().getSharedPreferences().getInt(preference.getKey(), 0)); } - protected static void bindPreferenceSummaryToSet(Preference preference) { + protected void bindPreferenceSummaryToSet(Preference preference) { addOnPreferenceChangeListener(preference, sBindPreferenceSummaryToValueListener); sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, - PreferenceManager - .getDefaultSharedPreferences(preference.getContext()) - .getStringSet(preference.getKey(), new HashSet())); + getPreferenceManager().getSharedPreferences().getStringSet(preference.getKey(), new HashSet())); } - protected static void bindPreferenceToCheckbox(CheckBoxPreference checkboxPreference, final Preference target) { + protected void bindPreferenceToCheckbox(CheckBoxPreference checkboxPreference, final Preference target) { target.setEnabled(checkboxPreference.isChecked()); addOnPreferenceChangeListener(checkboxPreference, new Preference.OnPreferenceChangeListener() { @Override @@ -91,7 +84,7 @@ public class BasePreferenceFragment extends PreferenceFragment { }); } - protected static void bindPreferenceToListEntry(Preference preference, final String defaultValue) { + protected void bindPreferenceToListEntry(Preference preference, final String defaultValue) { final ListPreference listPreference = (ListPreference) preference; if (listPreference.getValue() == null) { listPreference.setValue(defaultValue); @@ -111,7 +104,7 @@ public class BasePreferenceFragment extends PreferenceFragment { }); } - protected static void addOnPreferenceChangeListener(Preference preference, OnPreferenceChangeListener newListener) { + protected void addOnPreferenceChangeListener(Preference preference, OnPreferenceChangeListener newListener) { OnPreferenceChangeListener oldListener = preference.getOnPreferenceChangeListener(); if (oldListener == null) { MultiplePreferenceChangeListener multiListener = new MultiplePreferenceChangeListener(); diff --git a/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/NotificationHelper.java b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/NotificationHelper.java new file mode 100644 index 00000000000..4bc5834d710 --- /dev/null +++ b/mobile/com.sap.sailing.android.shared/src/com/sap/sailing/android/shared/util/NotificationHelper.java @@ -0,0 +1,56 @@ +package com.sap.sailing.android.shared.util; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.graphics.Bitmap; +import android.support.v4.app.NotificationCompat; + +public class NotificationHelper { + + private static final int NOTIFICATION_ID = 13062018; + private static Bitmap largeIcon; + private static CharSequence title; + private static int smallIcon; + + public static void prepareNotificationWith(CharSequence appTitle, Bitmap appIcon, int notificationIcon) { + title = appTitle; + largeIcon = appIcon; + smallIcon = notificationIcon; + } + + public static Notification getNotification(Context context) { + return getNotification(context, title, ""); + } + + public static Notification getNotification(Context context, CharSequence customTitle, String content) { + return getNotification(context, customTitle, content, null); + } + + + public static Notification getNotification(Context context, CharSequence customTitle, String content, PendingIntent intent) { + return getNotification(context, customTitle, content, intent, Notification.COLOR_DEFAULT); + } + + public static Notification getNotification(Context context, CharSequence customTitle, String content, PendingIntent intent, int color) { + NotificationCompat.Builder builder = new NotificationCompat.Builder(context) + .setContentText(content) + .setContentTitle(customTitle) + .setLargeIcon(largeIcon) + .setSmallIcon(smallIcon) + .setOngoing(true) + .setColor(color) + .setPriority(Notification.PRIORITY_HIGH); + + if (intent != null) { + builder.setContentIntent(intent); + } + + return builder.build(); + } + + + public static int getNotificationId() { + return NOTIFICATION_ID; + } +} diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/services/TrackingService.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/services/TrackingService.java index 9b6745e6c50..9228253f1e1 100755 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/services/TrackingService.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/services/TrackingService.java @@ -15,12 +15,10 @@ import org.json.JSONException; import org.json.JSONObject; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.graphics.BitmapFactory; import android.hardware.GeomagneticField; import android.location.Location; import android.location.LocationListener; @@ -33,12 +31,12 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.support.v4.app.NotificationCompat; import android.widget.Toast; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.services.sending.MessageSendingService; import com.sap.sailing.android.shared.ui.customviews.GPSQuality; +import com.sap.sailing.android.shared.util.NotificationHelper; import com.sap.sailing.android.tracking.app.BuildConfig; import com.sap.sailing.android.tracking.app.R; import com.sap.sailing.android.tracking.app.ui.activities.TrackingActivity; @@ -64,16 +62,11 @@ public class TrackingService extends Service implements LocationListener { private static final int GREAT_DISTANCE = 10; private static final int NO_DISTANCE = 0; - private NotificationManager notificationManager; private AppPreferences prefs; private GPSQualityListener gpsQualityListener; private final IBinder trackingBinder = new TrackingBinder(); - // Unique Identification Number for the Notification. - // We use it on Notification start, and to cancel it. - private int NOTIFICATION_ID = R.string.tracker_started; - public final static int UPDATE_INTERVAL_IN_MILLIS_DEFAULT = 1000; public final static String GPS_DISABLED_MESSAGE = "gpsDisabled"; private float minLocationUpdateDistanceInMeters = 0f; @@ -127,7 +120,6 @@ public class TrackingService extends Service implements LocationListener { locationsQueuedBasedOnSendingInterval = new LinkedHashMap<>(); prefs = new AppPreferences(this); - notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); } @@ -342,7 +334,7 @@ public class TrackingService extends Service implements LocationListener { @Override public void onDestroy() { stopTracking(); - notificationManager.cancel(NOTIFICATION_ID); + stopForeground(false); Toast.makeText(this, R.string.tracker_stopped, Toast.LENGTH_SHORT).show(); } @@ -387,16 +379,15 @@ public class TrackingService extends Service implements LocationListener { private void showNotification() { Intent intent = new Intent(this, TrackingActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); - PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0); - Notification notification = new NotificationCompat.Builder(this) - .setContentTitle(getText(R.string.app_name)) - .setContentText(getString(R.string.tracking_notification_text, event.name)) - .setContentIntent(pi) - .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) - .setSmallIcon(R.drawable.ic_directions_boat) - .setOngoing(true) - .build(); - notificationManager.notify(NOTIFICATION_ID, notification); + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); + + Notification notification = NotificationHelper.getNotification( + this, + getText(R.string.app_name), + getString(R.string.tracking_notification_text, event.name), + pendingIntent + ); + startForeground(NotificationHelper.getNotificationId(), notification); } public void registerGPSQualityListener(GPSQualityListener listener) { diff --git a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java index 750e8de0edf..df85fa03e80 100644 --- a/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java +++ b/mobile/com.sap.sailing.android.tracking.app/src/com/sap/sailing/android/tracking/app/ui/activities/StartActivity.java @@ -6,6 +6,7 @@ import com.sap.sailing.android.shared.data.BaseCheckinData; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.android.shared.ui.activities.AbstractStartActivity; import com.sap.sailing.android.shared.util.EulaHelper; +import com.sap.sailing.android.shared.util.NotificationHelper; import com.sap.sailing.android.tracking.app.R; import com.sap.sailing.android.tracking.app.ui.fragments.HomeFragment; import com.sap.sailing.android.tracking.app.utils.AboutHelper; @@ -19,6 +20,8 @@ import com.sap.sailing.android.tracking.app.valueobjects.MarkCheckinData; import com.sap.sailing.android.ui.fragments.AbstractHomeFragment; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.view.Menu; @@ -49,6 +52,11 @@ public class StartActivity extends AbstractStartActivity { if (!EulaHelper.with(this).isEulaAccepted()) { EulaHelper.with(this).showEulaDialog(R.style.AppTheme_AlertDialog); } + + Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); + int smallIcon = R.drawable.ic_directions_boat; + CharSequence title = getText(R.string.app_name); + NotificationHelper.prepareNotificationWith(title, largeIcon, smallIcon); } @Override diff --git a/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml b/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml index cce5b2f7dab..0ab09d05df2 100644 --- a/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml +++ b/mobile/com.sap.sailing.racecommittee.app/AndroidManifest.xml @@ -44,7 +44,9 @@ android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyDH-Dwy5yFlyBa2-VUgiZ3qGGUO8M_0MPY" /> - + @@ -69,10 +71,11 @@ + android:windowSoftInputMode="adjustPan" /> @@ -118,4 +121,4 @@ - \ No newline at end of file + diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml index 7d1cae96233..9d76b3b5bdc 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml @@ -536,6 +536,7 @@ Die App konnte keine Verbindung zu SAP Sailing hergestellt.\nPrüfen Sie bitte, ob Sie eine stabile Mobilfunkverbindung haben.\nWollen Sie es erneut versuchen? Wiederholen Beenden + Server ändern Dieser Teilnehmer wurde an einem anderen Gerät verändert und hat überschneidende Änderungen. Bitte stellen Sie sicher, dass die Daten korrekt sind. diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values/preferences.xml b/mobile/com.sap.sailing.racecommittee.app/res/values/preferences.xml index ecffc95bc95..d3fa54339b4 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values/preferences.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values/preferences.xml @@ -115,6 +115,9 @@ true protestTimeDuration 90 + protestTimeDurationCustom + protestTimeDurationCustomEditable + true allow_demo false diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml index cd8ec47c008..527d4d92de8 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml @@ -429,7 +429,7 @@ Use previously published course design? Please enter a valid number Please enter a valid pathfinder - Error scanning QR code (%i) + Error scanning QR code (%d) Malformed url Marks: %s No course available to copy @@ -565,6 +565,7 @@ The App couldn\'t establish a connection to SAP Sailing.\nPlease check if you have a stable mobile connection.\nDo you want to retry? Retry Exit + Change Server This competitor was changed from another device and you have changed the same data. Please be sure, that this data is correct now. diff --git a/mobile/com.sap.sailing.racecommittee.app/res/values/styles.xml b/mobile/com.sap.sailing.racecommittee.app/res/values/styles.xml index ade66c47b4e..142f3c77777 100644 --- a/mobile/com.sap.sailing.racecommittee.app/res/values/styles.xml +++ b/mobile/com.sap.sailing.racecommittee.app/res/values/styles.xml @@ -32,6 +32,13 @@ @color/light_colorPrimary @color/light_colorPrimaryDark @color/light_colorAccent + @style/PreferenceDialogTheme + + + - + - \ No newline at end of file + diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/AppPreferences.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/AppPreferences.java index 6163b76620b..bd09f5fe40d 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/AppPreferences.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/AppPreferences.java @@ -5,16 +5,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.preference.PreferenceManager; -import android.provider.Settings.Secure; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; - import com.google.android.gms.maps.model.LatLng; import com.sap.sailing.android.shared.logging.ExLog; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; @@ -29,6 +19,16 @@ import com.sap.sailing.racecommittee.app.domain.coursedesign.NumberOfRounds; import com.sap.sailing.racecommittee.app.domain.coursedesign.TrapezoidCourseLayouts; import com.sap.sailing.racecommittee.app.domain.coursedesign.WindWardLeeWardCourseLayouts; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.preference.PreferenceManager; +import android.provider.Settings.Secure; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + /** * Wrapper for {@link SharedPreferences} for all hidden and non-hidden preferences and state variables. */ @@ -497,13 +497,32 @@ public class AppPreferences { .getInteger(R.integer.preference_dependent_races_offset_default)); } - public int getProtestTimeDuration() { + public int getProtestTimeDurationInMinutes() { return helper.getInt(context.getString(R.string.preference_protest_time_duration_key), context.getResources() .getInteger(R.integer.preference_protest_time_duration_default)); } - public void setProtestTimeDuration(int value) { - helper.getEditor().putInt(context.getString(R.string.preference_protest_time_duration_key), value).commit(); + public int getProtestTimeDurationInMinutesCustom() { + return helper.getInt(context.getString(R.string.preference_protest_time_duration_custom_key), -1); + } + + public boolean isDefaultProtestTimeCustomEditable() { + return helper.getBoolean(context.getString(R.string.preference_protest_time_duration_custom_editable_key), + context.getResources().getBoolean(R.bool.preference_protest_time_duration_custom_editable_default)); + } + + public void setDefaultProtestTimeDurationInMinutes(int protestTimeInMinutes) { + helper.getEditor().putInt(context.getString(R.string.preference_protest_time_duration_key), protestTimeInMinutes).commit(); + } + + public void setDefaultProtestTimeDurationInMinutesCustom(int protestTimeInMinutest) { + helper.getEditor().putInt(context.getString(R.string.preference_protest_time_duration_custom_key), protestTimeInMinutest).commit(); + } + + public void setDefaultProtestTimeDurationInMinutesCustomEditable(boolean editable) { + helper.getEditor() + .putBoolean(context.getString(R.string.preference_protest_time_duration_custom_editable_key), editable) + .commit(); } public String getTheme() { diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/configuration/impl/PreferencesRegattaConfigurationLoader.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/configuration/impl/PreferencesRegattaConfigurationLoader.java index 14c128f623b..b23efef1801 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/configuration/impl/PreferencesRegattaConfigurationLoader.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/domain/configuration/impl/PreferencesRegattaConfigurationLoader.java @@ -21,6 +21,7 @@ import com.sap.sailing.domain.base.configuration.procedures.RRS26Configuration; import com.sap.sailing.domain.base.configuration.procedures.SWCStartConfiguration; import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.racecommittee.app.AppPreferences; +import com.sap.sse.common.Duration; public class PreferencesRegattaConfigurationLoader implements ConfigurationLoader { @@ -48,6 +49,7 @@ public class PreferencesRegattaConfigurationLoader implements ConfigurationLoade configuration.setDefaultRacingProcedureType(preferences.getDefaultRacingProcedureType()); configuration.setDefaultCourseDesignerMode(preferences.getDefaultCourseDesignerMode()); + configuration.setDefaultProtestTimeDuration(Duration.ONE_MINUTE.times(preferences.getProtestTimeDurationInMinutes())); RRS26ConfigurationImpl rrs26 = new RRS26ConfigurationImpl(); rrs26.setClassFlag(preferences.getRacingProcedureClassFlag(RacingProcedureType.RRS26)); @@ -104,6 +106,13 @@ public class PreferencesRegattaConfigurationLoader implements ConfigurationLoade preferences.setDefaultCourseDesignerMode(configuration.getDefaultCourseDesignerMode()); } + if (configuration.getDefaultProtestTimeDuration() != null) { + preferences.setDefaultProtestTimeDurationInMinutes((int) configuration.getDefaultProtestTimeDuration().asMinutes()); + preferences.setDefaultProtestTimeDurationInMinutesCustomEditable(false); + } else { + preferences.setDefaultProtestTimeDurationInMinutesCustomEditable(true); + } + if (configuration.getRRS26Configuration() != null) { RRS26Configuration config = configuration.getRRS26Configuration(); storeRacingProcedureConfiguration(RacingProcedureType.RRS26, config); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java index eb643430c13..a0118f13df1 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/services/RaceStateService.java @@ -9,6 +9,7 @@ import java.util.Map.Entry; import java.util.Set; import com.sap.sailing.android.shared.logging.ExLog; +import com.sap.sailing.android.shared.util.NotificationHelper; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor; import com.sap.sailing.domain.abstractlog.race.impl.RaceLogChangedVisitor; @@ -33,18 +34,16 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; import android.annotation.TargetApi; import android.app.AlarmManager; -import android.app.NotificationManager; +import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.graphics.BitmapFactory; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; import android.util.Pair; public class RaceStateService extends Service { @@ -60,8 +59,6 @@ public class RaceStateService extends Service { } } - private final static int NOTIFICATION_ID = 42; - private final IBinder mBinder = new RaceStateServiceBinder(); private AlarmManager alarmManager; @@ -75,9 +72,6 @@ public class RaceStateService extends Service { private Map>> managedIntents; - private NotificationManager notificationManager; - private NotificationCompat.Builder notificationBuilder; - @Override public void onCreate() { this.alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); @@ -87,26 +81,22 @@ public class RaceStateService extends Service { this.registeredStateEventSchedulers = new HashMap<>(); this.managedIntents = new HashMap<>(); - notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - setupNotificationBuilder(); - super.onCreate(); + + Notification notification = setupNotification(null); + startForeground(NotificationHelper.getNotificationId(), notification); ExLog.i(this, TAG, "Started."); } - private void setupNotificationBuilder() { + private Notification setupNotification(String customContent) { Intent launcherIntent = new Intent(this, LoginActivity.class); launcherIntent.setAction(Intent.ACTION_MAIN); launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); PendingIntent contentIntent = PendingIntent.getActivity(this, 0, launcherIntent, 0); - notificationBuilder = new NotificationCompat.Builder(this) - .setSmallIcon(R.drawable.ic_boat_white_24dp) - .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) - .setContentTitle(getText(R.string.service_info)) - .setContentText(getString(R.string.service_text_no_races)) - .setContentIntent(contentIntent) - .setColor(getResources().getColor(R.color.constant_sap_blue_1)) - .setOngoing(true); + CharSequence title = getText(R.string.service_info); + String content = customContent != null ? customContent : getString(R.string.service_text_no_races); + int color = getResources().getColor(R.color.constant_sap_blue_1); + return NotificationHelper.getNotification(this, title, content, contentIntent, color); } @Override @@ -130,7 +120,7 @@ public class RaceStateService extends Service { @Override public void onDestroy() { unregisterAllRaces(); - + stopForeground(false); super.onDestroy(); } @@ -282,10 +272,9 @@ public class RaceStateService extends Service { private void updateNotification() { int numRaces = managedIntents.keySet().size(); - notificationManager.notify(NOTIFICATION_ID, notificationBuilder - .setContentText(getString(R.string.service_text_num_races, numRaces)) - .build()); - startForeground(NOTIFICATION_ID, notificationBuilder.build()); + String content = getString(R.string.service_text_num_races, numRaces); + Notification notification = setupNotification(content); + startForeground(NotificationHelper.getNotificationId(), notification); } public void registerRace(ManagedRace race) { diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java old mode 100644 new mode 100755 index caf9a3de7fe..c03d9b12ab5 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/activities/LoginActivity.java @@ -6,28 +6,8 @@ import java.util.ArrayList; import java.util.Collection; import java.util.UUID; -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.app.Fragment; -import android.app.FragmentTransaction; -import android.app.LoaderManager.LoaderCallbacks; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.v4.content.LocalBroadcastManager; -import android.support.v7.app.AlertDialog; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.widget.Button; -import android.widget.Toast; - +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GooglePlayServicesUtil; import com.sap.sailing.android.shared.logging.ExLog; @@ -35,6 +15,7 @@ import com.sap.sailing.android.shared.util.AppUtils; import com.sap.sailing.android.shared.util.BroadcastManager; import com.sap.sailing.android.shared.util.EulaHelper; import com.sap.sailing.android.shared.util.NetworkHelper; +import com.sap.sailing.android.shared.util.NotificationHelper; import com.sap.sailing.android.shared.util.ViewHelper; import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.EventBase; @@ -66,6 +47,28 @@ import com.sap.sailing.racecommittee.app.utils.QRHelper; import com.sap.sailing.racecommittee.app.utils.StringHelper; import com.sap.sailing.racecommittee.app.utils.autoupdate.AutoUpdater; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.app.LoaderManager.LoaderCallbacks; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.AlertDialog; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.Button; +import android.widget.Toast; + public class LoginActivity extends BaseActivity implements EventSelectedListenerHost, CourseAreaSelectedListenerHost, PositionSelectedListenerHost, DialogListenerHost.DialogResultListener { @@ -334,6 +337,11 @@ public class LoginActivity extends BaseActivity if (!EulaHelper.with(this).isEulaAccepted()) { EulaHelper.with(this).showEulaDialog(R.style.AppTheme_AlertDialog); } + + Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); + int smallIcon = R.drawable.ic_boat_white_24dp; + CharSequence title = getText(R.string.app_name); + NotificationHelper.prepareNotificationWith(title, largeIcon, smallIcon); } @Override @@ -407,6 +415,7 @@ public class LoginActivity extends BaseActivity public void onLoadFailed(Exception reason) { dismissProgressSpinner(); + preferences.setDefaultProtestTimeDurationInMinutesCustomEditable(true); if (reason instanceof FileNotFoundException) { Toast.makeText(getApplicationContext(), getString(R.string.loading_configuration_not_found), Toast.LENGTH_LONG).show(); ExLog.w(LoginActivity.this, TAG, String.format("There seems to be no configuration for this device: %s", reason.toString())); @@ -513,11 +522,11 @@ public class LoginActivity extends BaseActivity animatorSet.start(); } - private ObjectAnimator getAlphaAnimator(@NonNull Object target) { + private ObjectAnimator getAlphaAnimator(@NonNull View target) { return ObjectAnimator.ofFloat(target, "alpha", 0f, 1f); } - private ObjectAnimator getAlphaRevAnimator(@NonNull Object target) { + private ObjectAnimator getAlphaRevAnimator(@NonNull View target) { return ObjectAnimator.ofFloat(target, "alpha", 1f, 0f); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorGoalPassingComparator.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorGoalPassingComparator.java new file mode 100644 index 00000000000..9b32291ca30 --- /dev/null +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorGoalPassingComparator.java @@ -0,0 +1,39 @@ +package com.sap.sailing.racecommittee.app.ui.comparators; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import com.sap.sailing.domain.base.Boat; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sse.common.Util; + +public class CompetitorGoalPassingComparator implements Comparator> { + + private ConcurrentMap ranking; + + public CompetitorGoalPassingComparator() { + this.ranking = new ConcurrentHashMap<>(); + } + + public ConcurrentMap updateWith(List> sortedList) { + ConcurrentMap result = new ConcurrentHashMap<>(); + Integer index = 0; + for (Util.Pair entry : sortedList) { + result.put(UUID.fromString(entry.getB()), index); + index++; + } + this.ranking = result; + return this.ranking; + } + + @Override + public int compare(Map.Entry leftCompetitor, Map.Entry rightCompetitor) { + Integer leftRank = this.ranking.get(leftCompetitor.getKey().getId()); + Integer rightRank = this.ranking.get(rightCompetitor.getKey().getId()); + return leftRank - rightRank; + } +} diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/LoginBackdrop.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/LoginBackdrop.java index 36936a68b62..5f164227d1f 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/LoginBackdrop.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/LoginBackdrop.java @@ -396,6 +396,12 @@ public class LoginBackdrop extends Fragment implements BackPressListener { getActivity().finish(); } }); + builder.setNeutralButton(R.string.hello_call_error_neutral, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + BroadcastManager.getInstance(getActivity()).addIntent(new Intent(AppConstants.INTENT_ACTION_SHOW_ONBOARDING)); + } + }); builder.setCancelable(false); builder.show(); } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/ProtestTimeDialogFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/ProtestTimeDialogFragment.java index 1d99b28da49..f88b66bbbc2 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/ProtestTimeDialogFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/dialogs/ProtestTimeDialogFragment.java @@ -5,6 +5,28 @@ import java.util.Calendar; import java.util.Date; import java.util.List; +import com.sap.sailing.android.shared.util.BitmapHelper; +import com.sap.sailing.android.shared.util.BroadcastManager; +import com.sap.sailing.android.shared.util.ScreenHelper; +import com.sap.sailing.android.shared.util.ViewHelper; +import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder; +import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet; +import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; +import com.sap.sailing.racecommittee.app.AppConstants; +import com.sap.sailing.racecommittee.app.AppPreferences; +import com.sap.sailing.racecommittee.app.R; +import com.sap.sailing.racecommittee.app.data.DataManager; +import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; +import com.sap.sailing.racecommittee.app.domain.ManagedRace; +import com.sap.sailing.racecommittee.app.utils.PreferenceHelper; +import com.sap.sailing.racecommittee.app.utils.ThemeHelper; +import com.sap.sailing.racecommittee.app.utils.TimeUtils; +import com.sap.sse.common.Duration; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.TimeRange; +import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.impl.TimeRangeImpl; + import android.annotation.TargetApi; import android.content.Context; import android.content.ContextWrapper; @@ -27,27 +49,6 @@ import android.widget.ListView; import android.widget.TextView; import android.widget.TimePicker; -import com.sap.sailing.android.shared.util.BitmapHelper; -import com.sap.sailing.android.shared.util.BroadcastManager; -import com.sap.sailing.android.shared.util.ScreenHelper; -import com.sap.sailing.android.shared.util.ViewHelper; -import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder; -import com.sap.sailing.domain.base.racegroup.RaceGroupSeriesFleet; -import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; -import com.sap.sailing.racecommittee.app.AppConstants; -import com.sap.sailing.racecommittee.app.AppPreferences; -import com.sap.sailing.racecommittee.app.R; -import com.sap.sailing.racecommittee.app.data.DataManager; -import com.sap.sailing.racecommittee.app.data.ReadonlyDataManager; -import com.sap.sailing.racecommittee.app.domain.ManagedRace; -import com.sap.sailing.racecommittee.app.utils.ThemeHelper; -import com.sap.sailing.racecommittee.app.utils.TimeUtils; -import com.sap.sse.common.Duration; -import com.sap.sse.common.TimePoint; -import com.sap.sse.common.TimeRange; -import com.sap.sse.common.impl.MillisecondsTimePoint; -import com.sap.sse.common.impl.TimeRangeImpl; - public class ProtestTimeDialogFragment extends AttachedDialogFragment implements View.OnClickListener { protected final static int MOVE_DOWN = -1; @@ -66,6 +67,7 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements private View mHome; private Button mChoose; private AppPreferences mPreferences; + private Integer mDuration = null; public ProtestTimeDialogFragment() { races = new ArrayList<>(); @@ -100,7 +102,6 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mPreferences = AppPreferences.on(getActivity()); View layout = super.onCreateView(inflater, container, savedInstanceState); if (customView == null) { layout = inflater.inflate(R.layout.protest_time_fragment, container, false); @@ -210,7 +211,6 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements } private View setupView() { - mPreferences = AppPreferences.on(getActivity()); LayoutInflater inflater = LayoutInflater.from(getActivity()); View view = inflater.inflate(R.layout.protest_time_dialog, null); mRacesList = (ListView) view.findViewById(R.id.protest_time_races_list); @@ -296,11 +296,21 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements } ReadonlyDataManager manager = DataManager.create(getActivity()); ArrayList raceIds = args.getStringArrayList(ARGS_RACE_IDS); + String raceGroup = null; if (raceIds != null) { for (String id : raceIds) { - races.add(manager.getDataStore().getRace(id)); + ManagedRace managedRace = manager.getDataStore().getRace(id); + if (raceGroup == null) { + raceGroup = managedRace.getRaceGroup().getName(); + } + races.add(managedRace); } } + if (raceGroup != null) { + mPreferences = AppPreferences.on(getActivity(), PreferenceHelper.getRegattaPrefFileName(raceGroup)); + } else { + mPreferences = AppPreferences.on(getActivity()); + } } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) @@ -309,7 +319,7 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements TimePoint startTime = TimeUtils.getTime(mTimePicker); TimePoint now = MillisecondsTimePoint.now(); for (ManagedRace race : selectedRaces) { - Duration duration = Duration.ONE_MINUTE.times(AppPreferences.on(getActivity()).getProtestTimeDuration()); + Duration duration = Duration.ONE_MINUTE.times(AppPreferences.on(getActivity()).getProtestTimeDurationInMinutes()); TimeRange protestTime = new TimeRangeImpl(startTime, startTime.plus(duration)); race.getState().setProtestTime(now, protestTime); } @@ -391,13 +401,21 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements } private void updateProtestRange() { - int duration = mPreferences.getProtestTimeDuration(); + if (mDuration == null) { + mDuration = mPreferences.getProtestTimeDurationInMinutes(); + if (mPreferences.isDefaultProtestTimeCustomEditable()) { + int custom = mPreferences.getProtestTimeDurationInMinutesCustom(); + if (custom >= 0) { + mDuration = custom; + } + } + } if (mProtestDuration != null) { - mProtestDuration.setText(getString(R.string.protest_duration, duration)); + mProtestDuration.setText(getString(R.string.protest_duration, mDuration)); } if (mProtestEndTime != null) { TimePoint startTime = TimeUtils.getTime(mTimePicker); - TimePoint endTime = startTime.plus(Duration.ONE_MINUTE.times(duration)); + TimePoint endTime = startTime.plus(Duration.ONE_MINUTE.times(mDuration)); mProtestEndTime.setText(getString(R.string.protest_end_time, TimeUtils.formatTime(endTime))); } } @@ -440,7 +458,7 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements public void onClick(View v) { FrameLayout layout = (FrameLayout) LayoutInflater.from(v.getContext()).inflate(R.layout.protest_duration, null); final EditText duration = (EditText) layout.findViewById(R.id.protest_duration); - duration.setText(String.valueOf(mPreferences.getProtestTimeDuration())); + duration.setText(String.valueOf(mDuration)); duration.setSelection(duration.length()); AlertDialog.Builder builder = new AlertDialog.Builder(new ContextWrapper(v.getContext()), R.style.AppTheme_AlertDialog); builder.setTitle(v.getContext().getString(R.string.protest_duration_dialog_title)); @@ -448,8 +466,10 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - int value = Integer.parseInt(duration.getText().toString()); - mPreferences.setProtestTimeDuration(value); + mDuration = Integer.parseInt(duration.getText().toString()); + if (mPreferences.isDefaultProtestTimeCustomEditable()) { + mPreferences.setDefaultProtestTimeDurationInMinutesCustom(mDuration); + } updateProtestRange(); } }); @@ -457,4 +477,4 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements builder.show(); } } -} \ No newline at end of file +} diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/CourseDesignerPreferenceFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/CourseDesignerPreferenceFragment.java index 26eb1d28f95..55203255515 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/CourseDesignerPreferenceFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/CourseDesignerPreferenceFragment.java @@ -1,11 +1,11 @@ package com.sap.sailing.racecommittee.app.ui.fragments.preference; -import android.os.Bundle; - import com.sap.sailing.android.shared.ui.fragments.preference.BasePreferenceFragment; import com.sap.sailing.android.shared.ui.views.EditSetPreference; import com.sap.sailing.racecommittee.app.R; +import android.os.Bundle; + public class CourseDesignerPreferenceFragment extends BasePreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/RegattaPreferenceFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/RegattaPreferenceFragment.java index 7f88a42549f..67a8d4cd015 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/RegattaPreferenceFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/preference/RegattaPreferenceFragment.java @@ -5,12 +5,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.MultiSelectListPreference; -import android.support.annotation.StringRes; -import android.text.TextUtils; - import com.sap.sailing.android.shared.ui.fragments.preference.BasePreferenceFragment; import com.sap.sailing.domain.base.configuration.RegattaConfiguration; import com.sap.sailing.domain.common.CourseDesignerMode; @@ -19,6 +13,12 @@ import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.racecommittee.app.R; import com.sap.sailing.racecommittee.app.ui.activities.PreferenceActivity; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.MultiSelectListPreference; +import android.support.annotation.StringRes; +import android.text.TextUtils; + /** *

    * This preference fragment can be redirected to display the {@link RegattaConfiguration} of a specific regatta. @@ -38,15 +38,6 @@ public class RegattaPreferenceFragment extends BasePreferenceFragment { } addPreferencesFromResource(R.xml.preference_regatta_defaults); - bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_rrs26_classflag_key)); - bindPreferenceSummaryToSet(findPreference(R.string.preference_racing_procedure_rrs26_startmode_flags_key)); - bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_swc_classflag_key)); - bindPreferenceSummaryToSet(findPreference(R.string.preference_racing_procedure_swc_startmode_flags_key)); - bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_gatestart_classflag_key)); - bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_ess_classflag_key)); - bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_basic_classflag_key)); - bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_league_classflag_key)); - setupGeneral(); setupRRS26(); setupSWC(); @@ -56,32 +47,6 @@ public class RegattaPreferenceFragment extends BasePreferenceFragment { setupLeague(); } - private void setupRRS26() { - setupStartmodeFlagsList(R.string.preference_racing_procedure_rrs26_startmode_flags_key); - setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_rrs26_classflag_key)); - } - - private void setupSWC() { - setupStartmodeFlagsList(R.string.preference_racing_procedure_swc_startmode_flags_key); - setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_swc_classflag_key)); - } - - private void setupGateStart() { - setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_gatestart_classflag_key)); - } - - private void setupESS() { - setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_ess_classflag_key)); - } - - private void setupBasic() { - setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_basic_classflag_key)); - } - - private void setupLeague() { - setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_league_classflag_key)); - } - private void setupGeneral() { setupRacingProcedureTypePreference(); setupCourseDesignerTypePreference(); @@ -89,6 +54,42 @@ public class RegattaPreferenceFragment extends BasePreferenceFragment { setupProtestTimePreference(); } + private void setupRRS26() { + setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_rrs26_classflag_key)); + bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_rrs26_classflag_key)); + + setupStartmodeFlagsList(R.string.preference_racing_procedure_rrs26_startmode_flags_key); + bindPreferenceSummaryToSet(findPreference(R.string.preference_racing_procedure_rrs26_startmode_flags_key)); + } + + private void setupSWC() { + setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_swc_classflag_key)); + bindPreferenceSummaryToSet(findPreference(R.string.preference_racing_procedure_swc_startmode_flags_key)); + + setupStartmodeFlagsList(R.string.preference_racing_procedure_swc_startmode_flags_key); + bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_swc_classflag_key)); + } + + private void setupGateStart() { + setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_gatestart_classflag_key)); + bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_gatestart_classflag_key)); + } + + private void setupESS() { + setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_ess_classflag_key)); + bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_ess_classflag_key)); + } + + private void setupBasic() { + setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_basic_classflag_key)); + bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_basic_classflag_key)); + } + + private void setupLeague() { + setupClassFlagList(this.findPreference(R.string.preference_racing_procedure_league_classflag_key)); + bindPreferenceSummaryToValue(findPreference(R.string.preference_racing_procedure_league_classflag_key)); + } + private void setupRacingProcedureTypePreference() { final ListPreference startProcedurePreference = findPreference(R.string.preference_racing_procedure_override_key); diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java index 88c881858c2..574b191e5c2 100644 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/TrackingListFragment.java @@ -44,6 +44,7 @@ import com.sap.sailing.racecommittee.app.domain.impl.LeaderboardResult; import com.sap.sailing.racecommittee.app.ui.adapters.CompetitorAndBoatAdapter; import com.sap.sailing.racecommittee.app.ui.adapters.CompetitorResultsList; import com.sap.sailing.racecommittee.app.ui.adapters.FinishListAdapter; +import com.sap.sailing.racecommittee.app.ui.comparators.CompetitorGoalPassingComparator; import com.sap.sailing.racecommittee.app.ui.comparators.CompetitorNameComparator; import com.sap.sailing.racecommittee.app.ui.comparators.CompetitorSailIdComparator; import com.sap.sailing.racecommittee.app.ui.fragments.RaceFragment; @@ -95,6 +96,8 @@ public class TrackingListFragment extends BaseFragment private static final int SORT_SAIL_NUMBER = 0; private static final int SORT_NAME = 1; + private static final int SORT_GOAL = 2; + private static final int SORT_START = 3; private RecyclerViewDragDropManager mDragDropManager; private RecyclerViewSwipeManager mSwipeManager; @@ -229,6 +232,7 @@ public class TrackingListFragment extends BaseFragment mComparators = new ArrayList<>(); mComparators.add(SORT_SAIL_NUMBER, new CompetitorSailIdComparator()); mComparators.add(SORT_NAME, new CompetitorNameComparator()); + mComparators.add(SORT_GOAL, new CompetitorGoalPassingComparator()); mComparator = mComparators.get(SORT_SAIL_NUMBER); mFinishedData = initializeFinishList(); mConfirmedData = new CompetitorResultsImpl(); @@ -296,9 +300,7 @@ public class TrackingListFragment extends BaseFragment Util.addAll(getRace().getCompetitorsAndBoats().entrySet(), mCompetitorData); mFilteredCompetitorData.clear(); mFilteredCompetitorData.addAll(mCompetitorData); - sortCompetitors(); - mCompetitorAdapter.notifyDataSetChanged(); - + loadLeaderboardResult(); initLocalData(); } @@ -399,14 +401,15 @@ public class TrackingListFragment extends BaseFragment case R.id.by_name: mComparator = mComparators.get(SORT_NAME); break; - case R.id.by_start: + mComparator = mComparators.get(SORT_START); break; - case R.id.by_goal: - loadLeaderboardResult(); + mComparator = mComparators.get(SORT_GOAL); + break; + case R.id.by_boat: + mComparator = mComparators.get(SORT_SAIL_NUMBER); break; - default: mComparator = mComparators.get(SORT_SAIL_NUMBER); @@ -503,8 +506,12 @@ public class TrackingListFragment extends BaseFragment @Override public void onLoadSucceeded(LeaderboardResult data, boolean isCached) { - if (isAdded()) { - onLoadLeaderboardSucceeded(data); + List> sortByRank = data.getResult(getRace().getName()); + if (isAdded() && sortByRank != null) { + CompetitorGoalPassingComparator comparator = (CompetitorGoalPassingComparator)mComparators.get(SORT_GOAL); + comparator.updateWith(sortByRank); + sortCompetitors(); + mCompetitorAdapter.notifyDataSetChanged(); } } })); @@ -524,27 +531,6 @@ public class TrackingListFragment extends BaseFragment mCompetitorAdapter.notifyDataSetChanged(); } - protected void onLoadLeaderboardSucceeded(LeaderboardResult data) { - final String raceName = getRace().getName(); - List> sortByRank = data.getResult(raceName); - List> sortedList = new ArrayList<>(); - if (sortByRank != null) { - for (Util.Pair item : sortByRank) { - for (Map.Entry competitor : mCompetitorData) { - if (competitor.getKey().getId().toString().equals(item.getB())) { - sortedList.add(competitor); - break; - } - } - } - } - mCompetitorData.clear(); - mCompetitorData.addAll(sortedList); - mFilteredCompetitorData.clear(); - mFilteredCompetitorData.addAll(sortedList); - mCompetitorAdapter.notifyDataSetChanged(); - } - private void deleteCompetitorsFromFinishedList(Collection validCompetitors) { List toBeDeleted = new ArrayList<>(); for (CompetitorResultWithIdImpl item : mFinishedData) { @@ -608,7 +594,7 @@ public class TrackingListFragment extends BaseFragment @Override public void onCompetitorClick(Competitor competitor) { moveCompetitorToFinishList(competitor); - for (Map.Entry entry : getRace().getCompetitorsAndBoats().entrySet()) { + for (Map.Entry entry : mFilteredCompetitorData) { if (entry.getKey().equals(competitor)) { removeCompetitorFromList(entry); break; @@ -644,11 +630,17 @@ public class TrackingListFragment extends BaseFragment private void removeCompetitorFromList(Map.Entry entry) { mCompetitorData.remove(entry); int pos = mFilteredCompetitorData.indexOf(entry); - mFilteredCompetitorData.remove(entry); - sortCompetitors(); + mFilteredCompetitorData.remove(pos); mCompetitorAdapter.notifyItemRemoved(pos); } + private void addNewCompetitorToCompetitorList(Map.Entry entry) { + mCompetitorData.add(entry); + mFilteredCompetitorData.add(entry); + sortCompetitors(); + mCompetitorAdapter.notifyItemInserted(mFilteredCompetitorData.indexOf(entry)); + } + /** * The {@code fromPosition} identifies the position of the item to be moved in the underlying {@link #mFinishedData * data model}; it shall be moved to {@code toPosition}. The item will be moved in {@link #mFinishedData} as @@ -722,13 +714,6 @@ public class TrackingListFragment extends BaseFragment .getFinishingTime(), competitorToReplaceWithAdjustedPosition.getComment(), MergeState.OK); } - private void addNewCompetitorToCompetitorList(Map.Entry entry) { - mCompetitorData.add(entry); - mFilteredCompetitorData.add(entry); - sortCompetitors(); - mCompetitorAdapter.notifyItemInserted(mFilteredCompetitorData.indexOf(entry)); - } - @Override public void onLongClick(final CompetitorResultWithIdImpl item) { final CharSequence[] maxPointsReasons = getAllMaxPointsReasons(); @@ -1141,6 +1126,8 @@ public class TrackingListFragment extends BaseFragment builder.setMessage(getString(R.string.refresh_message, string.toString())); builder.setPositiveButton(R.string.refresh_positive, null); builder.show(); + // goal passing might have changed too, so update them + loadLeaderboardResult(); } } diff --git a/wiki/howto/onboarding.md b/wiki/howto/onboarding.md index d4ecf6b9bff..b5978be5196 100644 --- a/wiki/howto/onboarding.md +++ b/wiki/howto/onboarding.md @@ -2,24 +2,24 @@ This document describes the onboarding process for a new team member (developer) -First of all, make sure you've looked at http://www.amazon.de/Patterns-Elements-Reusable-Object-Oriented-Software/dp/0201633612. That's a great book, and knowing at least some of it will help you a great deal finding your way around our solution. +First of all, make sure you've looked at [http://www.amazon.de/Patterns-Elements-Reusable-Object-Oriented-Software/dp/0201633612](http://www.amazon.de/Patterns-Elements-Reusable-Object-Oriented-Software/dp/0201633612). That's a great book, and knowing at least some of it will help you a great deal finding your way around our solution. ### Race Analysis Development Setup #### Installations -1. Eclipse (Eclipse IDE for Eclipse Committers, e.g. version 4.7.2 "Oxygen SR2"), http://www.eclipse.org +1. Eclipse (Eclipse IDE for Eclipse Committers, version 4.7.2 ["Oxygen SR2"](http://www.eclipse.org/downloads/packages/eclipse-ide-eclipse-committers/oxygen2)), [http://www.eclipse.org](http://www.eclipse.org) 2. Eclipse Extensions - * Install GWT Eclipse plugin for Eclipse (https://github.com/gwt-plugins/gwt-eclipse-plugin using http://storage.googleapis.com/gwt-eclipse-plugin/v3/release as the update site URL) - * Install Eclipse debugger for GWT SuperDevMode (master version: http://p2.sapsailing.com/p2/sdbg; public release: http://sdbg.github.io/p2) -3. Git (e.g. Git for Windows v2.12.2), http://git-scm.com / https://git-for-windows.github.io/ -4. MongoDB (e.g. Production Release 2.6.7), download: https://www.mongodb.com/ -5. RabbitMQ, download from http://www.rabbitmq.com/. Requires Erlang to be installed. RabbitMQ installer will assist in installing Erlang. Some sources report that there may be trouble with latest versions of RabbitMQ. In some cases, McAffee seems to block the installation of the latest version on SAP hardware; in other cases connection problems to newest versions have been reported. We know that version 3.6.8 works well. https://github.com/rabbitmq/rabbitmq-server/releases/tag/rabbitmq_v3_6_8 is the link. -6. JDK 1.7 (Java SE 7), http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html -7. JDK 1.8 (Java SE 8), http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html -8. Maven 3.1.1 (or higher), http://maven.apache.org -9. GWT SDK 2.8.2 (http://www.gwtproject.org/download.html) -10. Standalone Android SDK (see section "Additional steps required for Android projects"). OPTIONALLY: You may additionally install Android Studio (https://developer.android.com/tools/studio/index.html) or IntelliJ IDEA (https://www.jetbrains.com/idea/download/). + * Install GWT Eclipse plugin for Eclipse ([https://github.com/gwt-plugins/gwt-eclipse-plugin](https://github.com/gwt-plugins/gwt-eclipse-plugin) using [http://storage.googleapis.com/gwt-eclipse-plugin/v3/release](http://storage.googleapis.com/gwt-eclipse-plugin/v3/release) as the update site URL) + * Install Eclipse debugger for GWT SuperDevMode (master version: [http://p2.sapsailing.com/p2/sdbg](http://p2.sapsailing.com/p2/sdbg); public release: [http://sdbg.github.io/p2](http://sdbg.github.io/p2)) +3. Git (e.g. Git for Windows v2.12.2), [http://git-scm.com](http://git-scm.com) / [https://git-for-windows.github.io](https://git-for-windows.github.io) +4. MongoDB (e.g. Production Release 2.6.7), download: [https://www.mongodb.com/](https://www.mongodb.com/) +5. RabbitMQ, download from [http://www.rabbitmq.com](http://www.rabbitmq.com). Requires Erlang to be installed. RabbitMQ installer will assist in installing Erlang. Some sources report that there may be trouble with latest versions of RabbitMQ. In some cases, McAffee seems to block the installation of the latest version on SAP hardware; in other cases connection problems to newest versions have been reported. We know that version 3.6.8 works well. [https://github.com/rabbitmq/rabbitmq-server/releases/tag/rabbitmq_v3_6_8](https://github.com/rabbitmq/rabbitmq-server/releases/tag/rabbitmq_v3_6_8) is the link. +6. JDK 1.7 (Java SE 7), [http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html](http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html) +7. JDK 1.8 (Java SE 8), [http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) +8. Maven 3.1.1 (or higher), [http://maven.apache.org](http://maven.apache.org) +9. GWT SDK 2.8.2 ([http://www.gwtproject.org/download.html](http://www.gwtproject.org/download.html)) +10. Standalone Android SDK (see section "Additional steps required for Android projects"). OPTIONALLY: You may additionally install Android Studio ([https://developer.android.com/tools/studio/index.html](https://developer.android.com/tools/studio/index.html)) or IntelliJ IDEA ([https://www.jetbrains.com/idea/download/](https://www.jetbrains.com/idea/download/)). #### Automatic Eclipse plugin installation @@ -37,7 +37,7 @@ Be aware hat the installation may take several minutes depending on your Interne #### Further optional but recommended installations -1. Cygwin, http://www.cygwin.com/ +1. Cygwin, [http://www.cygwin.com/](http://www.cygwin.com/) 2. Eclipse Mylyn Bugzilla extension 3. kdiff3 (git tool) 4. Firebug (javascript & .css debugging, included in Firefox Developer Tools in newer versions of Firefox by default) @@ -46,13 +46,13 @@ Be aware hat the installation may take several minutes depending on your Interne 1. Git Account The primary Git repository for the project is hosted on sapsailing.com. It is mirrored on an hourly basis into SAP's internal Git/Gerrit repository, but branches from the external Git end up under the remote ``sapsailing.com`` in the internal repository, thus do not automatically merge into their branch counterparts. Conversely, commits pushed onto branches of the SAP-internal Gerrit will not by themselves end up on the external Git at sapsailing.com. - * For access to the external git at ssh://trac@sapsailing.com/home/trac/git please send your SSH public key to Axel Uhl or Simon Marcel Pamies, requesting git access. Make sure to NOT generate the key using Putty. Putty keys don't work reliably under Linux and on Windows/Cygwin environments. Use ssh-keygen in a Cygwin or Linux or MacOS/X environment instead. - * Alternatively, for access to the SAP-internal Git/Gerrit repository register yourself as a Git user in the SAP-Git under: https://git.wdf.sap.corp:8080/; ask the Git administrator (Axel Uhl) to get on the list of enabled committers + * For access to the external git at ``ssh://trac@sapsailing.com/home/trac/git`` please send your SSH public key to Axel Uhl or Simon Marcel Pamies, requesting git access. Make sure to NOT generate the key using Putty. Putty keys don't work reliably under Linux and on Windows/Cygwin environments. Use ssh-keygen in a Cygwin or Linux or MacOS/X environment instead. + * Alternatively, for access to the SAP-internal Git/Gerrit repository register yourself as a Git user in the SAP-Git under: [https://git.wdf.sap.corp:8080/](https://git.wdf.sap.corp:8080/); ask the Git administrator (Axel Uhl) to get on the list of enabled committers 2. Bugzilla * Ask the Bugzilla administrator (Frank Mittag, Axel Uhl) to create a bugzilla account for you. - * Bugzilla url: http://bugzilla.sapsailing.com/bugzilla/ + * Bugzilla url: [http://bugzilla.sapsailing.com/bugzilla/](http://bugzilla.sapsailing.com/bugzilla/) 3. Wiki - * Send a request to Axel Uhl or Simon Marcel Pamies that includes the SHA1 hash of your desired password. Obtain such an SHA1 hash for your password here: http://www.sha1-online.com/. + * Send a request to Axel Uhl or Simon Marcel Pamies that includes the SHA1 hash of your desired password. Obtain such an SHA1 hash for your password here: [http://www.sha1-online.com/](http://www.sha1-online.com/). 4. Hudson * Request a Hudson user by sending e-mail to Axel Uhl, Frank Mittag or Simon Marcel Pamies. @@ -61,7 +61,7 @@ The primary Git repository for the project is hosted on sapsailing.com. It is mi 1. Get the content of the git repository * Generate SSH Keys with "ssh-keygen -t rsa -C "" " command in Cygwin Terminal (Not with Putty!!!) * Clone the repository to your local file system from `ssh://[SAP-User]@git.wdf.sap.corp:29418/SAPSail/sapsailingcapture.git` or `ssh://[user]@sapsailing.com/home/trac/git` User "trac" has all public ssh keys. - * Please note that when using one of the newer versions of Cygwin, your Cygwin home folder setting might differ from your Windows home folder. This will likely lead to problems when issuing certain commands. For troubleshooting, take a look at the following thread: https://stackoverflow.com/questions/1494658/how-can-i-change-my-cygwin-home-folder-after-installation + * Please note that when using one of the newer versions of Cygwin, your Cygwin home folder setting might differ from your Windows home folder. This will likely lead to problems when issuing certain commands. For troubleshooting, take a look at the following thread: [https://stackoverflow.com/questions/1494658/how-can-i-change-my-cygwin-home-folder-after-installation](https://stackoverflow.com/questions/1494658/how-can-i-change-my-cygwin-home-folder-after-installation) 2. Check out the 'master' branch from the git repository. The 'master' branch is the main development branch. Please check that you start your work on this branch. 3. Setup and configure Eclipse * Make absolutely sure to import CodeFormatter.xml (from $GIT_HOME/java) into your Eclipse preferences (Preferences->Java->Code Style->Formatter) @@ -71,7 +71,7 @@ The primary Git repository for the project is hosted on sapsailing.com. It is mi * Install GWT SDK and add the SDK in Eclipse (Preferences -> GWT -> GWT Settings -> Add...) * In "Window->Preferences->GWT->Errors/Warnings, set "Missing SDK" to "Ignore" * In "Window->Preferences->General->Editors->TextEditors" check Insert Spaces for Tabs - * In "Window->Preferences->Web->HTML Files->Editor" indent using Spaces + * In "Window->Preferences->Web->HTML Files->Editor" indent using Spaces * In "Window->Preferences->General->Content Types" select on the right side CSS, now add in the lower file association list *.gss to get limited syntax highlighting and content assist in GSS files * Install Eclipse debugger for GWT SuperDevMode * Install Eclipse eGit (optional) @@ -79,8 +79,8 @@ The primary Git repository for the project is hosted on sapsailing.com. It is mi * Check that the both JDKs are available (Windows->Preferences->Java->Installed JREs) * Check that JDK 1.7 has been matched to JavaSE-1.7 and that JDK 1.8 has been matched to JavaSE-1.8 (...>Installed JREs>Execution Environments) * It is also possible to match the SAPJVM 7 or 8 to JavaSE-1.7 / JavaSE-1.8 (for profiling purposes) - * Go to Windows->Preferences->Google->Errors/Warnings and set "Missing SDK" to "Ignore" - * Import all Race Analysis projects from the `java/` subdirectory of the git main folder (make sure to import via the wizard "Git->Projects from Git" in Eclipse, and additionally make sure to scan for nested projects!) + * Go to Windows->Preferences->Google->Errors/Warnings and set "Missing SDK" to "Ignore" + * Import all Race Analysis projects from the `java/` subdirectory of the git main folder (make sure to import via the wizard "Git->Projects from Git" "Import->General->Projects from Folder or Archive" in Eclipse, and additionally make sure to scan for nested projects!) * Import all projects from the `mobile/` subdirectory of the git main folder; this in particular contains the race committee app projects * Set the Eclipse target platform to race-analysis-p2-remote.target (located in com.sap.sailing.targetplatform/definitions) * Wait until the target platform has been resolved completely @@ -92,10 +92,10 @@ The primary Git repository for the project is hosted on sapsailing.com. It is mi * Run "SailingGWT sdm" in the debug dropdown 5. Import races within the Race Analysis Suite * Choose "Security UI sdm" in the upper left corner of the "Development Mode" Tab in Eclipse and open "...Login.html" in your browser (obsolete) - * Choose "SailingGWT sdm" in the "Development Mode" Tab and open "...AdminConsole.html..." (It is normal that the first try fails. Reload the page after the first try) + * Choose "GWT Sailing SDM" in the "Development Mode" Tab and open "...AdminConsole.html..." (It is normal that the first try fails. Reload the page after the first try) * Default Login: user "admin", password "admin" * In the list on the left, click on "Connectors" - * For TracTrac Events: In the "TracTrac Connections" Form, fill in the JSON URL http://germanmaster.traclive.dk/events/event_20120905_erEuropean/jsonservice.php (all other required information will be filled in automatically) + * For TracTrac Events: In the "TracTrac Connections" Form, fill in the JSON URL [http://germanmaster.traclive.dk/events/event_20120905_erEuropean/jsonservice.php](http://germanmaster.traclive.dk/events/event_20120905_erEuropean/jsonservice.php)(all other required information will be filled in automatically) * Press "List Races" #### Git repository configuration essentials @@ -130,13 +130,13 @@ When a file has "wrong line endings" (line endings are different to what is conf To ensure that all components of the Analysis Suite are working, you should also import all Android projects (mobile/) into your workspace. There are some additional requirements to enable the build process of these projects. 1. Add the Android Development Tools (ADT) plugin to your Eclipse IDE - - In Eclipse click Help -> Install New Software -> Add and enter https://dl-ssl.google.com/android/eclipse/ + - In Eclipse click Help -> Install New Software -> Add and enter [https://dl-ssl.google.com/android/eclipse/](https://dl-ssl.google.com/android/eclipse/) - Select the Developer Tools and install - After restarting Eclipse the "Welcome to Android Development"-window should help you with installing the Android SDK - - It is also possible to download the Android SDK separately from the official Google download website. However, as of Revision 25.0.0 of the Android SDK Tools, the SDK Manager became an integrated part of Android Studio. Therefore, Revisions newer than 24.4.1 will not come with a standalone SDK Manager. Since it is absolutely essential if you want to use Eclipse, please download the Android SDK from the following link: https://dl.google.com/android/installer_r24.4.1-windows.exe + - It is also possible to download the Android SDK separately from the official Google download website. However, as of Revision 25.0.0 of the Android SDK Tools, the SDK Manager became an integrated part of Android Studio. Therefore, Revisions newer than 24.4.1 will not come with a standalone SDK Manager. Since it is absolutely essential if you want to use Eclipse, please download the Android SDK from the following link: Windows: [https://dl.google.com/android/installer_r24.4.1-windows.exe](https://dl.google.com/android/installer_r24.4.1-windows.exe) MacOS: [https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip](https://dl.google.com/android/android-sdk_r24.4.1-macosx.zip) Linux: [https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz](https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz) 2. Setup the Android SDK * In Eclipse press Window -> Android SDK Manager - * Install everything of "Tools" + * Install everything of "Tools" (hint: watchout not to update Android SDK Tools, see note below) * Install everything of "Android 3.2 API 13" * Install everything of "Android 5.1.1 API 22" * From the package "Extras", install "Android Support Repository" **or** "Android Support Library". Additionally install "Google Play Services", "Google Repository" **and** "Google USB Driver". @@ -184,7 +184,7 @@ The Android Apps can be built in Android Studio or gradle command line. Android * choose "add root" from "Unregistered VCS root detected" (you can add this later, if you missed it) 3. Setup the Android SDK * in Android Studio press Tools -> Android -> SDK Manager - * Install from section "Tools" + * Install from section "Tools" (hint: carefull not to update * Android SDK Tools * Android SDK Platform-tools * Android SDK Build-tools 22.0.1 (latest version at the time of writing) diff --git a/wiki/info/landscape/amazon-ec2-sharding-setup.md b/wiki/info/landscape/amazon-ec2-sharding-setup.md new file mode 100644 index 00000000000..c554aaf2cce --- /dev/null +++ b/wiki/info/landscape/amazon-ec2-sharding-setup.md @@ -0,0 +1,62 @@ +# Amazon EC2 for SAP Sailing Analytics + +[[_TOC_]] + +## Sharding + +#### Servers + +- For a minimum Setup at least 2 Servers are required. +- One server is required to be the master server as in usual replication situations +- All other servers are required to be replicas + + +e.g. a simple example: + + +![](sharding_instances.PNG) + +#### Routing Setup + +Multiple target groups must be defined per routing target. + +Interesting here are the groups named after leaderboards (or other discriminators). + +The names of the target groups can be defined however fitting, e.g. multiple leaderboards can be routed to each, in this example the leaderboard name is used for simplicity. + +![](sharding_target_groups.PNG) + +The interesting part is the configuration of the load balancer: + +It is recommended to have a route with a dedicated hostname going specifically to the master!, as else it might not be possible to reach the non replicated AdminConsole! + +It is required to have a default route, that can go to any server, because there are some requests that are not specific to leaderboards, and still need to be processed. + +For each Leaderboard that should not go to the default group, a entry is required. To discriminate between leaderboards, they are encoded into (mostly) all requests. + +This allows to specify a path based routing. The path are "/gwt/service/sailing/" and "/gwt/service/dispatch/" and the routing relevant suffix is leaderboard/underscore\_escaped\_leaderboardname. + + +All non a-z,A-Z,0-9 characters are replaced with \_. + +This is because limitations in the ALB do not allow the unmodified URL encoded leaderboard name. + + +In case it is unclear how the name of a leaderboard is, the REST api of the master server can be asked for this. (after creating the leaderboards) + +NAME\_OF\_LEADERBOARD\_YOU\_NEED\_TO\_KNOW\_THE\_PATH\_OF is the same name as in the admin console, the browser will do all required url encoding for this + +https://www.sharding-master.sapsailing.com/sailingserver/api/v1/leaderboards/NAME\_OF\_LEADERBOARD\_YOU\_NEED\_TO\_KNOW\_THE\_PATH\_OF/ + +eg: +https://www.sharding-master.sapsailing.com/sailingserver/api/v1/leaderboards/ESS 2016 Cardiff + +This will return a JSON document with the leaderboard name encoded near at the end: + +"ShardingLeaderboardName":"\/leaderboard\/ESS\_2016\_Cardiff", please note that the response is encoded, to get the proper name this needs to be decoded first e.g using some json parser. + +![](sharding_ALB.PNG) + +Please note that the example image is missing the dispatch related rules! + +If you need to use the REST api for other applications, we suggest to use the master server directly, as else the requests would need to be replicated to the master server anyway. \ No newline at end of file diff --git a/wiki/info/landscape/sharding_ALB.PNG b/wiki/info/landscape/sharding_ALB.PNG new file mode 100644 index 00000000000..60dd3a856c9 Binary files /dev/null and b/wiki/info/landscape/sharding_ALB.PNG differ diff --git a/wiki/info/landscape/sharding_instances.PNG b/wiki/info/landscape/sharding_instances.PNG new file mode 100644 index 00000000000..7be720fdd02 Binary files /dev/null and b/wiki/info/landscape/sharding_instances.PNG differ diff --git a/wiki/info/landscape/sharding_target_groups.PNG b/wiki/info/landscape/sharding_target_groups.PNG new file mode 100644 index 00000000000..4836c0f1468 Binary files /dev/null and b/wiki/info/landscape/sharding_target_groups.PNG differ