From a60c6ac660a84f94eee8a19ade5bb30dafc1eea8 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Tue, 9 Jul 2013 16:11:13 +0200 Subject: [PATCH 01/14] Fixed ugly bug with Youtube videos not being loaded a second time. Reason was the call sequence which kept the video container panel invisible for the Youtube API. Changed the sequence by making the panel visible before calling the Youtube API. --- .../client/media/YoutubeEmbeddedPlayer.java | 31 ++++++++++++++----- .../ui/client/media/YoutubeVideoControl.java | 20 +----------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeEmbeddedPlayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeEmbeddedPlayer.java index ac49c2dc72c..e23b68392d7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeEmbeddedPlayer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeEmbeddedPlayer.java @@ -5,7 +5,9 @@ import java.util.Date; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.SimplePanel; import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.MediaServiceAsync; @@ -17,6 +19,8 @@ import com.sap.sailing.gwt.ui.client.shared.controls.dialog.WindowBox; public class YoutubeEmbeddedPlayer extends AbstractMediaPlayer implements VideoPlayer, CloseHandler, MediaSynchAdapter { + private static int videoCounter; + private final WindowBox dialogBox; private MediaSynchControl mediaSynchControl; @@ -37,15 +41,14 @@ public class YoutubeEmbeddedPlayer extends AbstractMediaPlayer implements VideoP FlowPanel rootPanel = new FlowPanel(); rootPanel.addStyleName("video-root-panel"); - videoControl = new YoutubeVideoControl(videoTrack.url, showSynchControls); - rootPanel.add(videoControl.widget()); - - if (showSynchControls) { - mediaSynchControl = new MediaSynchControl(this, mediaService, errorReporter); - mediaSynchControl.widget().addStyleName("media-synch-control"); - rootPanel.add(mediaSynchControl.widget()); - } + Panel videoContainer = new SimplePanel(); + String videoContainerId = "videoContainer-" + videoTrack.url + ++videoCounter; + videoContainer.getElement().setId(videoContainerId); + videoContainer.getElement().setInnerText("When the Youtube video doesn't show up, click the popout button at the upper right corner to open the video in a dedicated browser window."); + + rootPanel.add(videoContainer.asWidget()); + this.dialogBox = new WindowBox(videoTrack.title, videoTrack.toString(), rootPanel, new WindowBox.PopoutHandler() { @Override @@ -56,7 +59,19 @@ public class YoutubeEmbeddedPlayer extends AbstractMediaPlayer implements VideoP }); dialogBox.addCloseHandler(this); + //first show dialog to make video container visible in DOM show(); + + //then use Youtube API to render load video into video container + videoControl = new YoutubeVideoControl(videoTrack.url, videoContainerId, showSynchControls); + + //then show media synch controls which refer to the video control + if (showSynchControls) { + mediaSynchControl = new MediaSynchControl(this, mediaService, errorReporter); + mediaSynchControl.widget().addStyleName("media-synch-control"); + rootPanel.add(mediaSynchControl.widget()); + } + } private void show() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeVideoControl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeVideoControl.java index 8f79b321985..bdfe0608409 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeVideoControl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/YoutubeVideoControl.java @@ -1,18 +1,11 @@ package com.sap.sailing.gwt.ui.client.media; import com.google.gwt.core.client.JavaScriptObject; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.SimplePanel; -import com.google.gwt.user.client.ui.Widget; public class YoutubeVideoControl { - private static int containerId; - private JavaScriptObject youtubePlayer; - private final Panel containerPanel; - private boolean deferredIsPlaying; private double deferredCurrentTime; @@ -23,15 +16,8 @@ public class YoutubeVideoControl { private double deferredPlaybackSpeed; - YoutubeVideoControl(String videoUrl, boolean showControls) { + YoutubeVideoControl(String videoUrl, String videoContainerId, boolean showControls) { - containerPanel = new SimplePanel(); - - String videoContainerId = "videoContainer-" + containerId++; - - containerPanel.getElement().setId(videoContainerId); - containerPanel.getElement().setInnerText("When the Youtube video doesn't show up, click the popout button at the upper right corner to open the video in a dedicated browser window."); - if (!isYoutubeApiInitialized()) { loadInitialYoutubePlayer(videoUrl, videoContainerId, showControls); } else { @@ -206,10 +192,6 @@ public class YoutubeVideoControl { player.setPlaybackRate(newPlaySpeedFactor); }-*/; - public Widget widget() { - return containerPanel; - } - public void setControlsVisible(boolean isVisible) { if (this.youtubePlayer != null) { nativeSetControlsVisible(isVisible); From 3ed17d3766a6ff4c63b8add09a4919769813964c Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Fri, 13 Sep 2013 09:41:23 +0200 Subject: [PATCH 02/14] - Made recordWind return a flag wether a wind fix has been considered for a tracked race - Made Expedition importer tolerant for partially filled rows, collecting partial data until a fix is complete --- .../domain/test/mock/MockedTrackedRace.java | 5 +- .../domain/tracking/DynamicTrackedRace.java | 5 +- .../tracking/impl/DynamicTrackedRaceImpl.java | 5 +- .../test/UDPExpeditionReceiverTest.java | 3 +- .../gwt/ui/test/MockedTrackedRace.java | 5 +- .../gateway/expeditionimport/CompleteRow.csv | 4 + .../expeditionimport/PositionFirst.csv | 4 + .../expeditionimport/TestWindLogParser.java | 56 ++ .../gateway/expeditionimport/WindFirst.csv | 4 + .../META-INF/MANIFEST.MF | 3 +- .../WEB-INF/web.xml | 481 +++++++++--------- .../expeditionimport/WindImportServlet.java | 78 +++ .../expeditionimport/WindLogParser.java | 158 ++++++ .../server/windimport/TestWindImport.java | 30 ++ 14 files changed, 599 insertions(+), 242 deletions(-) create mode 100644 java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/CompleteRow.csv create mode 100644 java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/PositionFirst.csv create mode 100644 java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/TestWindLogParser.java create mode 100644 java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/WindFirst.csv create mode 100644 java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java create mode 100644 java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java create mode 100644 java/com.sap.sailing.server.windimport.test/src/com/sap/sailing/server/windimport/TestWindImport.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index b48f5b37c28..5c34110fda5 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -204,9 +204,12 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public void recordWind(Wind wind, WindSource windSource) { + public boolean recordWind(Wind wind, WindSource windSource) { if (windSource.getType() == WindSourceType.EXPEDITION) { windTrack.add(wind); + return true; + } else { + return false; } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java index f40104584c1..c9c58bb47e0 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java @@ -11,7 +11,10 @@ public interface DynamicTrackedRace extends TrackedRace { void recordFix(Mark mark, GPSFix fix); - void recordWind(Wind wind, WindSource windSource); + /** + * @return True if the specified wind has been accepteed and added to this race's windtrack and database, else false. + */ + boolean recordWind(Wind wind, WindSource windSource); void removeWind(Wind wind, WindSource windSource); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java index 60eae088a25..0711fc5df08 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java @@ -594,7 +594,7 @@ DynamicTrackedRace, GPSTrackListener { } @Override - public void recordWind(Wind wind, WindSource windSource) { + public boolean recordWind(Wind wind, WindSource windSource) { // TODO check what a good filter is; remember that start/end of tracking may change over time; what if we have discarded valuable wind fixes? TimePoint startOfRace = getStartOfRace(); TimePoint startOfTracking = getStartOfTracking(); @@ -609,6 +609,9 @@ DynamicTrackedRace, GPSTrackListener { updated(/* time point */null); // wind events shouldn't advance race time triggerManeuverCacheRecalculationForAllCompetitors(); notifyListeners(wind, windSource); + return true; + } else { + return false; } } diff --git a/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java b/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java index 3a5aa78fad7..c1d6acec4c5 100755 --- a/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java +++ b/java/com.sap.sailing.expeditionconnector.test/src/com/sap/sailing/expeditionconnector/test/UDPExpeditionReceiverTest.java @@ -152,8 +152,9 @@ public class UDPExpeditionReceiverTest { ExpeditionWindTracker windTracker = new ExpeditionWindTracker(new MockedTrackedRace() { private static final long serialVersionUID = 4444197492014940699L; @Override - public void recordWind(Wind wind, WindSource windSource) { + public boolean recordWind(Wind wind, WindSource windSource) { windFixes.add(wind); + return true; } }, null, receiver, /* ExpeditionWindTrackerFactory */ null); receiver.addListener(windTracker, /* validMessagesOnly */ false); diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index bbceb71ee1d..e04329310b0 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -204,9 +204,12 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public void recordWind(Wind wind, WindSource windSource) { + public boolean recordWind(Wind wind, WindSource windSource) { if (windSource.getType() == WindSourceType.EXPEDITION) { windTrack.add(wind); + return true; + } else { + return false; } } diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/CompleteRow.csv b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/CompleteRow.csv new file mode 100644 index 00000000000..3b0a982b7a8 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/CompleteRow.csv @@ -0,0 +1,4 @@ +Utc,Bsp,Awa,Aws,Twa,Tws,Twd,Rudder2,Leeway,Set,Drift,Hdg,AirTmp,SeaTmp,Baro,Depth,Heel,Trim,Rudder,Tab,Forestay,Downhaul,MastAng,FrstyLen,MastButt,LoadStbd,LoadPort,Rake,Volts,ROT,GpsQual,GpsPDOP,GpsNum,GpsAge,GpsAlt,GpsGeoSep,GpsMode,Lat,Lon,Cog,Sog,DiffStn,Error,StbRunner,PrtRunner,Vang,Traveller,MainSheet,KeelAng,KeelHt,CanardH,OilPres,RPM1,RPM2,Board,Board2,DistToLn,RchTmToLn,RchDtToLn,GPS Time,Downhaul2,MkLat,MkLon,PortLat,PortLon,StbdLat,StbdLon,GPS HPE,RH,LeadPt,LeadSb,BkStay,User 0,User 1,User 2,User 3,User 4,User 5,User 6,User 7,User 8,User 9,User 10,User 11,User 12,User 13,User 14,User 15,User 16,User 17,User 18,User 19,User 20,User 21,User 22,User 23,User 24,User 25,User 26,User 27,User 28,User 29,User 30,User 31,TmToGun,TmToLn,TmToBurn,BelowLn,GunBlwLn,WvSigHt,WvSigPd,WvMaxHt,WvMaxPd,Slam,Motion,Mwa,Mws,Boom Pos +41451.30756,,104.1,11.2,,,,,,,,,,,,,,,,,,,,,,,,,,11.7,,,,,,,,,,315.2,0.1,,,,,,,,,,,,,,,,,,,,,,,,,,,9.54 +41451.30757,,,,,11.2,257.2,,,,,,,,,,,,,,,,,,,,,,,,2,,9,,-0.7,,,54.430272,10.172062,,,,,,,,,,,,,,,,,,,,,41451.307558,,,,,,,,9.54 +41451.30758,,104.1,11.2,,,,,,,,,,,,,,,,,,,,,,,,,,11.7,,,,,,,,,,315.2,0.1,,,,,,,,,,,,,,,,,,,,,,,,,,,9.54 diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/PositionFirst.csv b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/PositionFirst.csv new file mode 100644 index 00000000000..1bd95039d2f --- /dev/null +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/PositionFirst.csv @@ -0,0 +1,4 @@ +Utc,Bsp,Awa,Aws,Twa,Tws,Twd,Rudder2,Leeway,Set,Drift,Hdg,AirTmp,SeaTmp,Baro,Depth,Heel,Trim,Rudder,Tab,Forestay,Downhaul,MastAng,FrstyLen,MastButt,LoadStbd,LoadPort,Rake,Volts,ROT,GpsQual,GpsPDOP,GpsNum,GpsAge,GpsAlt,GpsGeoSep,GpsMode,Lat,Lon,Cog,Sog,DiffStn,Error,StbRunner,PrtRunner,Vang,Traveller,MainSheet,KeelAng,KeelHt,CanardH,OilPres,RPM1,RPM2,Board,Board2,DistToLn,RchTmToLn,RchDtToLn,GPS Time,Downhaul2,MkLat,MkLon,PortLat,PortLon,StbdLat,StbdLon,GPS HPE,RH,LeadPt,LeadSb,BkStay,User 0,User 1,User 2,User 3,User 4,User 5,User 6,User 7,User 8,User 9,User 10,User 11,User 12,User 13,User 14,User 15,User 16,User 17,User 18,User 19,User 20,User 21,User 22,User 23,User 24,User 25,User 26,User 27,User 28,User 29,User 30,User 31,TmToGun,TmToLn,TmToBurn,BelowLn,GunBlwLn,WvSigHt,WvSigPd,WvMaxHt,WvMaxPd,Slam,Motion,Mwa,Mws,Boom Pos +41451.30755,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,9,,-0.7,,,54.430272,10.172062,,,,,,,,,,,,,,,,,,,,,41451.307558,,,,,,,,9.54 +41451.30756,,104.1,11.2,,,,,,,,,,,,,,,,,,,,,,,,,,11.7,,,,,,,,,,315.2,0.1,,,,,,,,,,,,,,,,,,,,,,,,,,,9.54 +41451.30757,,,,,11.2,257.2,,,,,,12.6,,1017.4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9.54 diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/TestWindLogParser.java b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/TestWindLogParser.java new file mode 100644 index 00000000000..a0631593221 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/TestWindLogParser.java @@ -0,0 +1,56 @@ +package com.sap.sailing.server.gateway.expeditionimport; + +import java.util.List; + +import org.junit.Test; + +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.server.gateway.expeditionimport.WindLogParser; + +import static org.junit.Assert.*; + +import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.number.IsCloseTo.*; + +public class TestWindLogParser { + + private static final double DOUBLE_TOLERANCE = 1e-7; + + @Test + public void testCompleteRow() throws Exception { + List windImport = WindLogParser.importWind(getClass().getResourceAsStream("CompleteRow.csv")); + assertThat(windImport.size(), is(1)); + Wind wind = windImport.get(0); + assertWind(wind, 11.2, 257.2, 54.430272, 10.172062); + } + + @Test + public void testWindFirst() throws Exception { + List windImport = WindLogParser.importWind(getClass().getResourceAsStream("WindFirst.csv")); + assertThat(windImport.size(), is(1)); + Wind wind = windImport.get(0); + assertWind(wind, 11.2, 257.2, 54.430272, 10.172062); + } + + @Test + public void testPositionFirst() throws Exception { + List windImport = WindLogParser.importWind(getClass().getResourceAsStream("PositionFirst.csv")); + assertThat(windImport.size(), is(1)); + Wind wind = windImport.get(0); + assertWind(wind, 11.2, 257.2, 54.430272, 10.172062); + } + + private void assertWind(Wind wind, double knots, double degrees, double lat, double lon) { + assertThat(wind.getKnots(), closeTo(knots, DOUBLE_TOLERANCE)); + assertThat(wind.getFrom().getDegrees(), closeTo(degrees, DOUBLE_TOLERANCE)); + assertThat(wind.getPosition().getLatDeg(), closeTo(lat, DOUBLE_TOLERANCE)); + assertThat(wind.getPosition().getLngDeg(), closeTo(lon, DOUBLE_TOLERANCE)); + } + +// @Test +// public void test2013Jun26_0() throws Exception { +// List windImport = WindLogParser.importWind(getClass().getResourceAsStream("2013Jun26_0.csv")); +// assertThat(windImport.size(), is(85586)); +// } + +} diff --git a/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/WindFirst.csv b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/WindFirst.csv new file mode 100644 index 00000000000..40bd4ea120d --- /dev/null +++ b/java/com.sap.sailing.server.gateway.test/src/com/sap/sailing/server/gateway/expeditionimport/WindFirst.csv @@ -0,0 +1,4 @@ +Utc,Bsp,Awa,Aws,Twa,Tws,Twd,Rudder2,Leeway,Set,Drift,Hdg,AirTmp,SeaTmp,Baro,Depth,Heel,Trim,Rudder,Tab,Forestay,Downhaul,MastAng,FrstyLen,MastButt,LoadStbd,LoadPort,Rake,Volts,ROT,GpsQual,GpsPDOP,GpsNum,GpsAge,GpsAlt,GpsGeoSep,GpsMode,Lat,Lon,Cog,Sog,DiffStn,Error,StbRunner,PrtRunner,Vang,Traveller,MainSheet,KeelAng,KeelHt,CanardH,OilPres,RPM1,RPM2,Board,Board2,DistToLn,RchTmToLn,RchDtToLn,GPS Time,Downhaul2,MkLat,MkLon,PortLat,PortLon,StbdLat,StbdLon,GPS HPE,RH,LeadPt,LeadSb,BkStay,User 0,User 1,User 2,User 3,User 4,User 5,User 6,User 7,User 8,User 9,User 10,User 11,User 12,User 13,User 14,User 15,User 16,User 17,User 18,User 19,User 20,User 21,User 22,User 23,User 24,User 25,User 26,User 27,User 28,User 29,User 30,User 31,TmToGun,TmToLn,TmToBurn,BelowLn,GunBlwLn,WvSigHt,WvSigPd,WvMaxHt,WvMaxPd,Slam,Motion,Mwa,Mws,Boom Pos +41451.30755,,,,,11.2,257.2,,,,,,12.6,,1017.4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9.54 +41451.30756,,104.1,11.2,,,,,,,,,,,,,,,,,,,,,,,,,,11.7,,,,,,,,,,315.2,0.1,,,,,,,,,,,,,,,,,,,,,,,,,,,9.54 +41451.30757,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,9,,-0.7,,,54.430272,10.172062,,,,,,,,,,,,,,,,,,,,,41451.307558,,,,,,,,9.54 diff --git a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF index fa56a3620d8..a9d5728dc78 100644 --- a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF @@ -36,7 +36,8 @@ Require-Bundle: com.sap.sailing.domain, org.eclipse.jetty.websocket.common;bundle-version="9.0.4", javax.validation;bundle-version="1.0.0", com.sun.jersey;bundle-version="1.17.0", - org.objectweb.asm;bundle-version="3.3.1" + org.objectweb.asm;bundle-version="3.3.1", + org.apache.commons.fileupload;bundle-version="1.2.2" Bundle-ClassPath: . Web-ContextPath: /sailingserver Export-Package: com.sap.sailing.server.gateway diff --git a/java/com.sap.sailing.server.gateway/WEB-INF/web.xml b/java/com.sap.sailing.server.gateway/WEB-INF/web.xml index 6055572f575..61f8c859ef0 100644 --- a/java/com.sap.sailing.server.gateway/WEB-INF/web.xml +++ b/java/com.sap.sailing.server.gateway/WEB-INF/web.xml @@ -1,236 +1,245 @@ - - - - - AdminApp - com.sap.sailing.server.gateway.impl.AdminApp - - - AdminApp - /admin - - - - RaceGroups - com.sap.sailing.server.gateway.impl.rc.RaceGroupJsonExportServlet - - - RaceGroups - /rc/racegroups - - - - RCAddToRaceLog - com.sap.sailing.server.gateway.impl.rc.AddEntryToRaceLogJsonPostServlet - - - RCAddToRaceLog - /rc/racelog - - - - RCMarks - com.sap.sailing.server.gateway.impl.rc.MarksJsonExportServlet - - - RCMarks - /rc/marks - - - - RCMarkPositions - com.sap.sailing.server.gateway.impl.rc.MarkPositionsJsonExportServlet - - - RCMarkPositions - /rc/markpositions - - - - RCCurrentCourse - com.sap.sailing.server.gateway.impl.rc.CourseJsonExportServlet - - - RCCurrentCourse - /rc/currentcourse - - - - RCCompetitors - com.sap.sailing.server.gateway.impl.rc.CompetitorsJsonExportServlet - - - RCCompetitors - /rc/competitors - - - - - - RegattaAndRaceDataJsonGetServlet - com.sap.sailing.server.gateway.impl.RegattaAndRaceDataJsonGetServlet - - - RegattaAndRaceDataJsonGetServlet - /moderator - - - - ExpeditionThroughHttpPostServlet - com.sap.sailing.server.gateway.impl.ExpeditionThroughHttpPostServlet - - - ExpeditionThroughHttpPostServlet - /expedition - - - - EventsJsonGetServlet - com.sap.sailing.server.gateway.impl.EventsJsonGetServlet - - - EventsJsonGetServlet - /events - - - - EventRaceStatesServlet - com.sap.sailing.server.gateway.impl.EventRaceStatesJsonGetServlet - - - EventRaceStatesServlet - /eventRaceStates - - - - RegattasJsonGetServlet - com.sap.sailing.server.gateway.impl.RegattasJsonGetServlet - - - RegattasJsonGetServlet - /regattas - - - RegattaJsonGetServlet - com.sap.sailing.server.gateway.impl.RegattaJsonGetServlet - - - RegattaJsonGetServlet - /regatta - - - - LeaderboardsJsonGetServlet - com.sap.sailing.server.gateway.impl.LeaderboardsJsonGetServlet - - - LeaderboardsJsonGetServlet - /leaderboards - - - - LeaderboardJsonGetServlet - com.sap.sailing.server.gateway.impl.LeaderboardJsonGetServlet - - - LeaderboardJsonGetServlet - /leaderboard - - - - LeaderboardGroupJsonGetServlet - com.sap.sailing.server.gateway.impl.LeaderboardGroupJsonGetServlet - - - LeaderboardGroupJsonGetServlet - /leaderboardgroup - - - - LeaderboardGroupsJsonGetServlet - com.sap.sailing.server.gateway.impl.LeaderboardGroupsJsonGetServlet - - - LeaderboardGroupsJsonGetServlet - /leaderboardgroups - - - - WindJsonGetServlet - com.sap.sailing.server.gateway.impl.WindJsonGetServlet - - - WindJsonGetServlet - /wind - - - - ExpeditionWindMeasureStatusGetServlet - com.sap.sailing.server.gateway.impl.ExpeditionWindMeasureStatusGetServlet - - - ExpeditionWindMeasureStatusGetServlet - /expeditionWindStatus - - - - MasterDataByLeaderboardGroupJsonGetServlet - com.sap.sailing.server.gateway.impl.MasterDataByLeaderboardGroupJsonGetServlet - - - MasterDataByLeaderboardGroupJsonGetServlet - /masterdata/leaderboardgroups - - - - TrackFilesExportPostServlet - com.sap.sailing.server.gateway.impl.TrackFilesExportPostServlet - - - TrackFilesExportPostServlet - /trackfiles - - - - websockettest - com.sap.sailing.server.gateway.impl.WebSocketTestServlet - - - - websockettest - /websockettest - - - - Jersey REST API - com.sap.sailing.server.gateway.jaxrs.RestServletContainer - - javax.ws.rs.Application - com.sap.sailing.server.gateway.jaxrs.api.RestApiApplication - - 1 - - - Jersey REST API - /api/* - - - - Jersey REST SPI - com.sap.sailing.server.gateway.jaxrs.RestServletContainer - - javax.ws.rs.Application - com.sap.sailing.server.gateway.jaxrs.spi.RestSpiApplication - - 1 - - - Jersey REST SPI - /spi/* - - - + + + + + AdminApp + com.sap.sailing.server.gateway.impl.AdminApp + + + AdminApp + /admin + + + + RaceGroups + com.sap.sailing.server.gateway.impl.rc.RaceGroupJsonExportServlet + + + RaceGroups + /rc/racegroups + + + + RCAddToRaceLog + com.sap.sailing.server.gateway.impl.rc.AddEntryToRaceLogJsonPostServlet + + + RCAddToRaceLog + /rc/racelog + + + + RCMarks + com.sap.sailing.server.gateway.impl.rc.MarksJsonExportServlet + + + RCMarks + /rc/marks + + + + RCMarkPositions + com.sap.sailing.server.gateway.impl.rc.MarkPositionsJsonExportServlet + + + RCMarkPositions + /rc/markpositions + + + + RCCurrentCourse + com.sap.sailing.server.gateway.impl.rc.CourseJsonExportServlet + + + RCCurrentCourse + /rc/currentcourse + + + + RCCompetitors + com.sap.sailing.server.gateway.impl.rc.CompetitorsJsonExportServlet + + + RCCompetitors + /rc/competitors + + + + + + RegattaAndRaceDataJsonGetServlet + com.sap.sailing.server.gateway.impl.RegattaAndRaceDataJsonGetServlet + + + RegattaAndRaceDataJsonGetServlet + /moderator + + + + ExpeditionThroughHttpPostServlet + com.sap.sailing.server.gateway.impl.ExpeditionThroughHttpPostServlet + + + ExpeditionThroughHttpPostServlet + /expedition + + + + EventsJsonGetServlet + com.sap.sailing.server.gateway.impl.EventsJsonGetServlet + + + EventsJsonGetServlet + /events + + + + EventRaceStatesServlet + com.sap.sailing.server.gateway.impl.EventRaceStatesJsonGetServlet + + + EventRaceStatesServlet + /eventRaceStates + + + + RegattasJsonGetServlet + com.sap.sailing.server.gateway.impl.RegattasJsonGetServlet + + + RegattasJsonGetServlet + /regattas + + + RegattaJsonGetServlet + com.sap.sailing.server.gateway.impl.RegattaJsonGetServlet + + + RegattaJsonGetServlet + /regatta + + + + LeaderboardsJsonGetServlet + com.sap.sailing.server.gateway.impl.LeaderboardsJsonGetServlet + + + LeaderboardsJsonGetServlet + /leaderboards + + + + LeaderboardJsonGetServlet + com.sap.sailing.server.gateway.impl.LeaderboardJsonGetServlet + + + LeaderboardJsonGetServlet + /leaderboard + + + + LeaderboardGroupJsonGetServlet + com.sap.sailing.server.gateway.impl.LeaderboardGroupJsonGetServlet + + + LeaderboardGroupJsonGetServlet + /leaderboardgroup + + + + LeaderboardGroupsJsonGetServlet + com.sap.sailing.server.gateway.impl.LeaderboardGroupsJsonGetServlet + + + LeaderboardGroupsJsonGetServlet + /leaderboardgroups + + + + WindJsonGetServlet + com.sap.sailing.server.gateway.impl.WindJsonGetServlet + + + WindJsonGetServlet + /wind + + + + WindImportServlet + com.sap.sailing.server.gateway.expeditionimport.WindImportServlet + + + WindImportServlet + /expedition-import + + + + ExpeditionWindMeasureStatusGetServlet + com.sap.sailing.server.gateway.impl.ExpeditionWindMeasureStatusGetServlet + + + ExpeditionWindMeasureStatusGetServlet + /expeditionWindStatus + + + + MasterDataByLeaderboardGroupJsonGetServlet + com.sap.sailing.server.gateway.impl.MasterDataByLeaderboardGroupJsonGetServlet + + + MasterDataByLeaderboardGroupJsonGetServlet + /masterdata/leaderboardgroups + + + + TrackFilesExportPostServlet + com.sap.sailing.server.gateway.impl.TrackFilesExportPostServlet + + + TrackFilesExportPostServlet + /trackfiles + + + + websockettest + com.sap.sailing.server.gateway.impl.WebSocketTestServlet + + + + websockettest + /websockettest + + + + Jersey REST API + com.sap.sailing.server.gateway.jaxrs.RestServletContainer + + javax.ws.rs.Application + com.sap.sailing.server.gateway.jaxrs.api.RestApiApplication + + 1 + + + Jersey REST API + /api/* + + + + Jersey REST SPI + com.sap.sailing.server.gateway.jaxrs.RestServletContainer + + javax.ws.rs.Application + com.sap.sailing.server.gateway.jaxrs.spi.RestSpiApplication + + 1 + + + Jersey REST SPI + /spi/* + + + diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java new file mode 100644 index 00000000000..f352cec62ba --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java @@ -0,0 +1,78 @@ +package com.sap.sailing.server.gateway.expeditionimport; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; + +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.tracking.DynamicTrackedRace; +import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.server.gateway.SailingServerHttpServlet; + +public class WindImportServlet extends SailingServerHttpServlet { + + private static final long serialVersionUID = 1L; + + private static final WindSource EXPEDITION_WIND_SOURCE = new WindSourceImpl(WindSourceType.EXPEDITION); + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (!ServletFileUpload.isMultipartContent(req)) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + + try { + + Iterable allRegattas = getService().getAllRegattas(); + + Collection files = readInput(req); + for (FileItem file : files) { + List windFixes = WindLogParser.importWind(file.getInputStream()); + for (Regatta regatta : allRegattas) { + DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta); + Iterable allRaceDefinitions = regatta.getAllRaces(); + for (RaceDefinition raceDefinition : allRaceDefinitions) { + DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(raceDefinition); + for (Wind wind : windFixes) { + trackedRace.recordWind(wind, EXPEDITION_WIND_SOURCE); + } + } + } + } + } catch (FileUploadException e) { + throw new IOException(e); + } + } + + private Collection readInput(HttpServletRequest req) throws FileUploadException { + Collection files = new ArrayList(); + // http://commons.apache.org/fileupload/using.html + FileItemFactory factory = new DiskFileItemFactory(); + ServletFileUpload upload = new ServletFileUpload(factory); + @SuppressWarnings("unchecked") + List items = upload.parseRequest(req); + for (FileItem item : items) { + if (!item.isFormField()) + files.add(item); + } + return files; + } +} diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java new file mode 100644 index 00000000000..178cef60a74 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java @@ -0,0 +1,158 @@ +package com.sap.sailing.server.gateway.expeditionimport; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.List; +import java.util.TimeZone; +import java.util.regex.Pattern; + +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.domain.tracking.impl.WindImpl; + +public class WindLogParser { + + /** + * Collects partial wind data until everything is there to create a Wind instance. + * @author D047974 + * + */ + private static class WindBuffer { + + /** + * The 1900 Date System http://support.microsoft.com/kb/180162/en-us + */ + private static final Calendar EXCEL_EPOCH_START = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + static { + EXCEL_EPOCH_START.set(1899, Calendar.DECEMBER, 30, 0, 0, 0); + } + private static final int MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; + + private TimePoint timePoint; + private Position position; + private SpeedWithBearing trueWindSpeedWithBearing; + + public void updateTime(String time_ExcelEpoch) { + double excelTimeStamp = Double.parseDouble(time_ExcelEpoch); + long millisecondsSinceExcelEpochStart = (long) (MILLISECONDS_PER_DAY * excelTimeStamp); + this.timePoint = new MillisecondsTimePoint(EXCEL_EPOCH_START.getTimeInMillis() + millisecondsSinceExcelEpochStart); + + } + + public void updateWindData(String trueWindSpeedData, String trueWindDirectionData) { + if (!trueWindSpeedData.trim().isEmpty() && !trueWindDirectionData.trim().isEmpty()) { + + double trueWindSpeed = Double.parseDouble(trueWindSpeedData); + double trueWindDirection = Double.parseDouble(trueWindDirectionData); + Bearing trueWindBearing = new DegreeBearingImpl(trueWindDirection + 180); + this.trueWindSpeedWithBearing = new KnotSpeedWithBearingImpl(trueWindSpeed, trueWindBearing); + } + } + + public void updatePosition(String latData, String lonData) { + if (!latData.trim().isEmpty() && !lonData.trim().isEmpty()) { + double lat = Double.parseDouble(latData); + double lon = Double.parseDouble(lonData); + + this.position = new DegreePosition(lat, lon); + } + } + + public Wind createWindIfReady() { + if ((timePoint != null) && (position != null) && (trueWindSpeedWithBearing != null)) { + WindImpl result = new WindImpl(position, timePoint, trueWindSpeedWithBearing); + reset(); + return result; + } else { + return null; + } + } + + private void reset() { + timePoint = null; + position = null; + trueWindSpeedWithBearing = null; + } + + } + + private static final int CSV_INDEX_TIME_STAMP = 0; + private static final int CSV_INDEX_TRUE_WIND_SPEED = 5; + private static final int CSV_INDEX_TRUE_WIND_DIRECTION = 6; + private static final int CSV_INDEX_LAT = 37; + private static final int CSV_INDEX_LONG = 38; + + private static final int CSV_MAX_INDEX = Collections.max(Arrays.asList(CSV_INDEX_TIME_STAMP, CSV_INDEX_TRUE_WIND_SPEED, CSV_INDEX_TRUE_WIND_DIRECTION, CSV_INDEX_LAT, CSV_INDEX_LONG)); + + private static final Pattern LINE_PATTERN = Pattern.compile(","); + + /** + * See ExpeditionMessage, UDPExpeditionReceiver + * @param windStream + * @return + * @throws IOException + */ + public static List importWind(InputStream windStream) throws IOException { + BufferedReader csvReader = new BufferedReader(new InputStreamReader(windStream)); + String headerLine = csvReader.readLine(); + assertHeaderFormat(headerLine); + + List result = new ArrayList(); + String dataLine; + WindBuffer windBuffer = new WindBuffer(); + while ((dataLine = csvReader.readLine()) != null) { + String[] data = parseDataLine(dataLine); + + if (data != null) { + windBuffer.updateTime(data[CSV_INDEX_TIME_STAMP]); + windBuffer.updateWindData(data[CSV_INDEX_TRUE_WIND_SPEED], data[CSV_INDEX_TRUE_WIND_DIRECTION]); + windBuffer.updatePosition(data[CSV_INDEX_LAT], data[CSV_INDEX_LONG]); + + Wind wind = windBuffer.createWindIfReady(); + if (wind != null) { + result.add(wind); + } + } + } + + return result; + } + + private static void assertHeaderFormat(String headerLine) { + //supposed to be Utc,Bsp,Awa,Aws,Twa,Tws,Twd,Rudder2,Leeway,Set,Drift,Hdg,AirTmp,SeaTmp,Baro,Depth,Heel,Trim,Rudder,Tab,Forestay,Downhaul,MastAng,FrstyLen,MastButt,LoadStbd,LoadPort,Rake,Volts,ROT,GpsQual,GpsPDOP,GpsNum,GpsAge,GpsAltitude,GpsGeoSep,GpsMode,Lat,Lon,Cog,Sog,DiffStn,Error,StbRunner,PrtRunner,Vang,Traveller,MainSheet,KeelAng,KeelHt,CanardH,OilPres,RPM1,RPM2,Dagger Bd,Boom Pos,DistToLn,RchTmToLn,RchDtToLn,GPS Time,Downhaul2,MkLat,MkLon,PortLat,PortLon,StbdLat,StbdLon,GPS HPE,RH,LeadPt,LeadSb,BkStay,User 0,User 1,User 2,User 3,User 4,User 5,User 6,User 7,User 8,User 9,User 10,User 11,User 12,User 13,User 14,User 15,User 16,User 17,User 18,User 19,User 20,User 21,User 22,User 23,User 24,User 25,User 26,User 27,User 28,User 29,User 30,User 31,TmToGun,TmToLn,TmToBurn,BelowLn,GunBlwLn,WvSigHt,WvSigPd,WvMaxHt,WvMaxPd,Slam,Motion + String[] columns = LINE_PATTERN.split(headerLine); + if ("Utc".equals(columns[CSV_INDEX_TIME_STAMP]) && "Tws".equals(columns[CSV_INDEX_TRUE_WIND_SPEED]) && "Twd".equals(columns[CSV_INDEX_TRUE_WIND_DIRECTION]) && "Lat".equals(columns[CSV_INDEX_LAT]) && "Lon".equals(columns[CSV_INDEX_LONG])) { + return; + } else { + throw new RuntimeException("Unexpected csv header for Expedition wind import: " + headerLine); + } + } + + private static String[] parseDataLine(String dataLine) { + String[] data = LINE_PATTERN.split(dataLine); + if (data.length >= CSV_MAX_INDEX) { + // if (!data[CSV_INDEX_TIME_STAMP].trim().isEmpty() + // && !data[CSV_INDEX_TRUE_WIND_SPEED].trim().isEmpty() + // && !data[CSV_INDEX_TRUE_WIND_DIRECTION].trim().isEmpty() + // && !data[CSV_INDEX_LAT].trim().isEmpty() + // && !data[CSV_INDEX_LONG].trim().isEmpty()) { + return data; + // } + } + //else + return null; + } +} diff --git a/java/com.sap.sailing.server.windimport.test/src/com/sap/sailing/server/windimport/TestWindImport.java b/java/com.sap.sailing.server.windimport.test/src/com/sap/sailing/server/windimport/TestWindImport.java new file mode 100644 index 00000000000..60664af785b --- /dev/null +++ b/java/com.sap.sailing.server.windimport.test/src/com/sap/sailing/server/windimport/TestWindImport.java @@ -0,0 +1,30 @@ +package com.sap.sailing.server.windimport; + +import java.util.List; + +import org.junit.Test; + +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.server.windimport.WindImport.WindSequence; + +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; + +public class TestWindImport { + + @Test + public void testImport() throws Exception { + List windImport = WindImport.importWind(getClass().getResourceAsStream("Wind_2012Nov23_2.csv")); + assertThat(windImport.size(), is(11042)); + Wind wind_0 = windImport.get(0); + } + + @Test + public void test2013Jun26_0() throws Exception { + List windImport = WindImport.importWind(getClass().getResourceAsStream("2013Jun26_0.csv")); + int fiveMinutes_seconds = 5 * 60; + List windSequences = WindImport.splitWindSequences(windImport, fiveMinutes_seconds); + assertThat(windSequences.size(), is(12)); + } + +} From 56e398a9430e7e6af271097877d60dc27cf9f6ed Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Wed, 18 Sep 2013 09:01:32 +0200 Subject: [PATCH 03/14] Adding races affected by wind import to servlet response. --- .../expeditionimport/WindImportServlet.java | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java index f352cec62ba..bf200f74204 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java @@ -17,6 +17,8 @@ import org.apache.commons.fileupload.servlet.ServletFileUpload; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.WindSourceImpl; @@ -26,11 +28,56 @@ import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.server.gateway.SailingServerHttpServlet; public class WindImportServlet extends SailingServerHttpServlet { - + private static final long serialVersionUID = 1L; private static final WindSource EXPEDITION_WIND_SOURCE = new WindSourceImpl(WindSourceType.EXPEDITION); + static class WindImportResult { + + private final RegattaIdentifier regattaIdentifier; + private final RegattaAndRaceIdentifier raceIdentifier; + private int count; + private Wind firstWind; + private Wind lastWind; + + public void update(Wind newWind) { + count++; + if (firstWind == null || newWind.getTimePoint().before(firstWind.getTimePoint())) { + firstWind = newWind; + } + if (lastWind == null || newWind.getTimePoint().after(lastWind.getTimePoint())) { + lastWind = newWind; + } + } + + public RegattaIdentifier getRegattaIdentifier() { + return regattaIdentifier; + } + + public RegattaAndRaceIdentifier getRaceIdentifier() { + return raceIdentifier; + } + + public WindImportResult(RegattaIdentifier regattaIdentifier, RegattaAndRaceIdentifier raceIdentifier) { + this.regattaIdentifier = regattaIdentifier; + this.raceIdentifier = raceIdentifier; + } + + public int getCount() { + return count; + } + + public Wind getFirstWind() { + return firstWind; + } + + public Wind getLastWind() { + return lastWind; + } + + } + @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (!ServletFileUpload.isMultipartContent(req)) { @@ -41,6 +88,8 @@ public class WindImportServlet extends SailingServerHttpServlet { try { + List windImportResults = new ArrayList(); + Iterable allRegattas = getService().getAllRegattas(); Collection files = readInput(req); @@ -51,8 +100,12 @@ public class WindImportServlet extends SailingServerHttpServlet { Iterable allRaceDefinitions = regatta.getAllRaces(); for (RaceDefinition raceDefinition : allRaceDefinitions) { DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(raceDefinition); + WindImportResult windImportResult = new WindImportResult(regatta.getRegattaIdentifier(), trackedRace.getRaceIdentifier()); + windImportResults.add(windImportResult); for (Wind wind : windFixes) { - trackedRace.recordWind(wind, EXPEDITION_WIND_SOURCE); + if (trackedRace.recordWind(wind, EXPEDITION_WIND_SOURCE)) { + windImportResult.update(wind); + } } } } From 612700d7f3ae3ad918507e77cb3b361e360e81a8 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Thu, 10 Oct 2013 12:11:52 +0200 Subject: [PATCH 04/14] Initial working UI, includes parsing and sending boat id . Still missing: - apply to selected races only --- .../gwt/ui/adminconsole/WindPanel.java | 93 ++++++++++++++++++- .../expeditionimport/WindImportServlet.java | 38 +++++--- 2 files changed, 117 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index 4752dc2b985..0863fb39618 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -11,11 +11,15 @@ import java.util.Set; import com.google.gwt.cell.client.ActionCell; import com.google.gwt.cell.client.ActionCell.Delegate; import com.google.gwt.core.client.GWT; +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.i18n.client.NumberFormat; +import com.google.gwt.regexp.shared.MatchResult; +import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.ColumnSortEvent.Handler; import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; @@ -25,10 +29,14 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CaptionPanel; import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.FileUpload; import com.google.gwt.user.client.ui.FormPanel; import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.TabPanel; +import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.view.client.ListDataProvider; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; @@ -93,10 +101,12 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower this.setWidget(mainPanel); trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher, - raceSelectionProvider, stringMessages, false); + raceSelectionProvider, stringMessages, /*multiselection*/ false); mainPanel.add(trackedRacesListComposite); raceSelectionProvider.addRaceSelectionChangeListener(this); + createWindImportPanel(mainPanel, stringMessages); + windCaptionPanel = new CaptionPanel(stringMessages.wind()); windCaptionPanel.setVisible(false); mainPanel.add(windCaptionPanel); @@ -222,6 +232,84 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower windFixesDisplayPanel.add(rawWindFixesTable); } + private void createWindImportPanel(Panel mainPanel, final StringMessages stringMessages) { + CaptionPanel importPanel = new CaptionPanel("Import Wind from Expedition"); + Panel importPanelContent = new VerticalPanel(); + importPanel.add(importPanelContent); + + final FormPanel form = new FormPanel(); + form.setMethod(FormPanel.METHOD_POST); + form.setEncoding(FormPanel.ENCODING_MULTIPART); + form.setAction(GWT.getHostPageBaseURL()+"/../../sailingserver/expedition-import"); + + VerticalPanel formContentPanel = new VerticalPanel(); + + final TextBox boatIdTextBox = new TextBox(); + boatIdTextBox.setName("boatId"); + + final FileUpload fileUpload = new FileUpload(); + fileUpload.setName("upload"); + fileUpload.addChangeHandler(new ChangeHandler() { + + @Override + public void onChange(ChangeEvent event) { + String fileName = fileUpload.getFilename(); + + RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches file names like "2013Jun26_0.csv" where 0 indicates the boat id. + MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); + String boatId; + if (match.getGroupCount() > 0) { + boatId = match.getGroup(1); + } else { + boatId = ""; + } + boatIdTextBox.setText(boatId); + } + }); + + formContentPanel.add(fileUpload); + + HorizontalPanel boatIdPanel = new HorizontalPanel(); + boatIdPanel.add(new Label("Boat Id")); + boatIdPanel.add(boatIdTextBox); + + formContentPanel.add(boatIdPanel); + + Button submitButton = new Button("Upload", new ClickHandler() + { + @Override + public void onClick(ClickEvent event) { + form.submit(); + } + }); + formContentPanel.add(submitButton); + + final Panel importResultPanel = new VerticalPanel(); + + form.addSubmitHandler(new FormPanel.SubmitHandler() { + public void onSubmit(SubmitEvent event) { + // This event is fired just before the form is submitted. We can take + // this opportunity to perform validation. +// if (condition isn't met) { +// event.cancel(); +// } + } + }); + form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() { + public void onSubmitComplete(SubmitCompleteEvent event) { + importResultPanel.clear(); + TextBox importResult = new TextBox(); + importResult.setText(event.getResults()); + importResultPanel.add(importResult); + } + }); + form.add(formContentPanel); + importPanelContent.add(form); + importPanelContent.add(importResultPanel); + + mainPanel.add(importPanel); + } + private void showWindSettingDialog(RaceDTO race, CoursePositionsDTO course) { AddWindFixDialog windSettingDialog = new AddWindFixDialog(race, course, stringMessages, new DialogCallback() { @@ -362,8 +450,9 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower private RegattaAndRaceIdentifier getSelectedRace() { List selectedRaces = raceSelectionProvider.getSelectedRaces(); - if(selectedRaces.isEmpty()) + if(selectedRaces.isEmpty() || selectedRaces.size() > 1) { return null; + } return selectedRaces.get(0); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java index bf200f74204..a3d04777a63 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java @@ -2,7 +2,6 @@ package com.sap.sailing.server.gateway.expeditionimport; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import javax.servlet.ServletException; @@ -21,7 +20,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; -import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.Wind; @@ -31,8 +30,11 @@ public class WindImportServlet extends SailingServerHttpServlet { private static final long serialVersionUID = 1L; - private static final WindSource EXPEDITION_WIND_SOURCE = new WindSourceImpl(WindSourceType.EXPEDITION); - + static class Upload { + public String boatId; + public final List files = new ArrayList(); + } + static class WindImportResult { private final RegattaIdentifier regattaIdentifier; @@ -88,12 +90,14 @@ public class WindImportServlet extends SailingServerHttpServlet { try { + Upload upload = readInput(req); + WindSource windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, upload.boatId); + List windImportResults = new ArrayList(); Iterable allRegattas = getService().getAllRegattas(); - Collection files = readInput(req); - for (FileItem file : files) { + for (FileItem file : upload.files) { List windFixes = WindLogParser.importWind(file.getInputStream()); for (Regatta regatta : allRegattas) { DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta); @@ -103,29 +107,39 @@ public class WindImportServlet extends SailingServerHttpServlet { WindImportResult windImportResult = new WindImportResult(regatta.getRegattaIdentifier(), trackedRace.getRaceIdentifier()); windImportResults.add(windImportResult); for (Wind wind : windFixes) { - if (trackedRace.recordWind(wind, EXPEDITION_WIND_SOURCE)) { + if (trackedRace.recordWind(wind, windSource)) { windImportResult.update(wind); } } } } } + + // Use text/html to prevent browsers from wrapping the response body, + // see "Handling File Upload Responses in GWT" at http://www.artofsolving.com/node/50 + resp.setContentType("text/html;charset=UTF-8"); + resp.getWriter().append(windImportResults.toString()); } catch (FileUploadException e) { throw new IOException(e); } } - private Collection readInput(HttpServletRequest req) throws FileUploadException { - Collection files = new ArrayList(); + private Upload readInput(HttpServletRequest req) throws FileUploadException { + Upload result = new Upload(); // http://commons.apache.org/fileupload/using.html FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); @SuppressWarnings("unchecked") List items = upload.parseRequest(req); for (FileItem item : items) { - if (!item.isFormField()) - files.add(item); + if (item.isFormField()) { + if ("boatId".equals(item.getFieldName())) { + result.boatId = item.getString(); + } + } else { + result.files.add(item); + } } - return files; + return result; } } From 7f111e2392c084d55b3fde6f4f17d43a328bf7d3 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Fri, 11 Oct 2013 17:05:25 +0200 Subject: [PATCH 05/14] Working expedition wind import. Still missing: - import for selected subset of races - UI beautification --- .../gwt/ui/adminconsole/WindPanel.java | 799 +++++++++--------- .../expeditionimport/WindImportServlet.java | 212 +++-- 2 files changed, 535 insertions(+), 476 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index 0863fb39618..4a88d7e1a5b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -11,6 +11,7 @@ import java.util.Set; import com.google.gwt.cell.client.ActionCell; import com.google.gwt.cell.client.ActionCell.Delegate; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.JsArray; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; @@ -45,6 +46,7 @@ import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.dto.RaceDTO; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor; +import com.sap.sailing.gwt.ui.adminconsole.WindImportResult.RaceEntry; import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; @@ -69,466 +71,465 @@ import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO; * */ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower, RaceSelectionChangeListener { - private final SailingServiceAsync sailingService; - private final ErrorReporter errorReporter; - private final StringMessages stringMessages; - private final IdentityColumn removeColumn; - private final TextColumn timeColumn; - private final TextColumn speedInKnotsColumn; - private final TextColumn windDirectionInDegColumn; - private final TextColumn positionColumn; - private final TrackedRacesListComposite trackedRacesListComposite; - private final RaceSelectionProvider raceSelectionProvider; - private final WindSourcesToExcludeSelectorPanel windSourcesToExcludeSelectorPanel; - private final CheckBox raceIsKnownToStartUpwindBox; - private final CaptionPanel windCaptionPanel; - private final VerticalPanel windFixesDisplayPanel; - private final Label windSourceLabel; - private final ListDataProvider rawWindFixesDataProvider; - private final CellTable rawWindFixesTable; - private final VerticalPanel windFixPanel; - - public WindPanel(final SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - ErrorReporter errorReporter, RegattaRefresher regattaRefresher, final StringMessages stringMessages) { - this.sailingService = sailingService; - this.errorReporter = errorReporter; - this.stringMessages = stringMessages; - this.raceSelectionProvider = new RaceSelectionModel(); - windSourcesToExcludeSelectorPanel = new WindSourcesToExcludeSelectorPanel(sailingService, stringMessages, errorReporter); - - VerticalPanel mainPanel = new VerticalPanel(); - mainPanel.setSize("100%", "100%"); - this.setWidget(mainPanel); + + private static final String EXPEDITON_IMPORT_PARAMETER_BOAT_ID = "boatId"; - trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher, - raceSelectionProvider, stringMessages, /*multiselection*/ false); - mainPanel.add(trackedRacesListComposite); - raceSelectionProvider.addRaceSelectionChangeListener(this); + private static final String URL_SAILINGSERVER_EXPEDITION_IMPORT = "/../../sailingserver/expedition-import"; + + private final SailingServiceAsync sailingService; + private final ErrorReporter errorReporter; + private final StringMessages stringMessages; + private final IdentityColumn removeColumn; + private final TextColumn timeColumn; + private final TextColumn speedInKnotsColumn; + private final TextColumn windDirectionInDegColumn; + private final TextColumn positionColumn; + private final TrackedRacesListComposite trackedRacesListComposite; + private final RaceSelectionProvider raceSelectionProvider; + private final WindSourcesToExcludeSelectorPanel windSourcesToExcludeSelectorPanel; + private final CheckBox raceIsKnownToStartUpwindBox; + private final CaptionPanel windCaptionPanel; + private final VerticalPanel windFixesDisplayPanel; + private final Label windSourceLabel; + private final ListDataProvider rawWindFixesDataProvider; + private final CellTable rawWindFixesTable; + private final VerticalPanel windFixPanel; - createWindImportPanel(mainPanel, stringMessages); + public WindPanel(final SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, RegattaRefresher regattaRefresher, final StringMessages stringMessages) { + this.sailingService = sailingService; + this.errorReporter = errorReporter; + this.stringMessages = stringMessages; + this.raceSelectionProvider = new RaceSelectionModel(); + windSourcesToExcludeSelectorPanel = new WindSourcesToExcludeSelectorPanel(sailingService, stringMessages, errorReporter); - windCaptionPanel = new CaptionPanel(stringMessages.wind()); - windCaptionPanel.setVisible(false); - mainPanel.add(windCaptionPanel); + VerticalPanel mainPanel = new VerticalPanel(); + mainPanel.setSize("100%", "100%"); + this.setWidget(mainPanel); - TabPanel tabPanel = new TabPanel(); - tabPanel.setAnimationEnabled(true); - windCaptionPanel.add(tabPanel); - tabPanel.setSize("95%", "95%"); + trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages, /*multiselection*/false); + mainPanel.add(trackedRacesListComposite); + raceSelectionProvider.addRaceSelectionChangeListener(this); - windFixesDisplayPanel = new VerticalPanel(); - Button addWindFixButton = new Button(stringMessages.actionAddWindData()+ "..."); - addWindFixButton.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - RegattaAndRaceIdentifier selectedRace = getSelectedRace(); - if(selectedRace != null) { - final RaceDTO race = trackedRacesListComposite.getRaceByIdentifier(selectedRace); - sailingService.getCoursePositions(selectedRace, race.trackedRace.startOfTracking, new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - showWindSettingDialog(race, null); - } + windCaptionPanel = new CaptionPanel(stringMessages.wind()); + windCaptionPanel.setVisible(false); + mainPanel.add(windCaptionPanel); - @Override - public void onSuccess(final CoursePositionsDTO result) { - showWindSettingDialog(race, result); - } - }); - } - } - }); - windFixesDisplayPanel.add(addWindFixButton); + TabPanel tabPanel = new TabPanel(); + tabPanel.setAnimationEnabled(true); + windCaptionPanel.add(tabPanel); + tabPanel.setSize("95%", "95%"); - final VerticalPanel windSourcesPanel = new VerticalPanel(); - windSourcesPanel.setSpacing(10); - tabPanel.add(windSourcesPanel, stringMessages.windSourcesUsed()); - tabPanel.add(windFixesDisplayPanel, "Wind fixes"); - tabPanel.selectTab(0); - - raceIsKnownToStartUpwindBox = new CheckBox(stringMessages.raceIsKnownToStartUpwind()); - windSourcesPanel.add(raceIsKnownToStartUpwindBox); + windFixesDisplayPanel = new VerticalPanel(); + Button addWindFixButton = new Button(stringMessages.actionAddWindData() + "..."); + addWindFixButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + RegattaAndRaceIdentifier selectedRace = getSelectedRace(); + if (selectedRace != null) { + final RaceDTO race = trackedRacesListComposite.getRaceByIdentifier(selectedRace); + sailingService.getCoursePositions(selectedRace, race.trackedRace.startOfTracking, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + showWindSettingDialog(race, null); + } - windSourcesPanel.add(windSourcesToExcludeSelectorPanel); - raceIsKnownToStartUpwindBox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - setRaceIsKnownToStartUpwind(); - } - }); - - windFixPanel = new VerticalPanel(); - windSourcesPanel.add(windFixPanel); - - windSourceLabel = new Label(); - windFixesDisplayPanel.add(windSourceLabel); + @Override + public void onSuccess(final CoursePositionsDTO result) { + showWindSettingDialog(race, result); + } + }); + } + } + }); + windFixesDisplayPanel.add(addWindFixButton); - // table for the raw wind fixes - removeColumn = new IdentityColumn(new ActionCell(stringMessages.remove(), new Delegate() { - @Override - public void execute(final WindDTO wind) { - List selectedRaces = raceSelectionProvider.getSelectedRaces(); - final RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(selectedRaces.size()-1); - sailingService.removeWind(raceIdentifier, wind, new AsyncCallback() { - @Override - public void onSuccess(Void result) { - // remove row from underlying list: - rawWindFixesDataProvider.getList().remove(wind); - } - @Override - public void onFailure(Throwable caught) { - WindPanel.this.errorReporter.reportError( - WindPanel.this.stringMessages.errorSettingWindForRace()+ " "+raceIdentifier - + ": "+ caught.getMessage()); - } - }); - } - })); - timeColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - return DateAndTimeFormatterUtil.formatDateAndTime(new Date(object.measureTimepoint)); - } - }; - speedInKnotsColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - return ""+object.trueWindSpeedInKnots; - } - }; - windDirectionInDegColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - return ""+object.trueWindFromDeg; - } - }; - positionColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - String result = ""; - if(object.position != null) { - result = "Lat: " + object.position.latDeg + ", Lon: " + object.position.lngDeg; - } - return result; - } - }; - timeColumn.setSortable(true); - speedInKnotsColumn.setSortable(true); - windDirectionInDegColumn.setSortable(true); + final VerticalPanel windSourcesPanel = new VerticalPanel(); + windSourcesPanel.setSpacing(10); + tabPanel.add(windSourcesPanel, stringMessages.windSourcesUsed()); + tabPanel.add(windFixesDisplayPanel, "Wind fixes"); + tabPanel.selectTab(0); - AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); - rawWindFixesTable = new CellTable(/* pageSize */10000, tableRes); - rawWindFixesTable.addColumn(timeColumn, "Time"); - rawWindFixesTable.addColumn(speedInKnotsColumn, "Speed (kn)"); - rawWindFixesTable.addColumn(windDirectionInDegColumn, "From (deg)"); - rawWindFixesTable.addColumn(positionColumn, "Position"); - rawWindFixesTable.addColumn(removeColumn, stringMessages.actions()); - rawWindFixesDataProvider = new ListDataProvider(); - rawWindFixesDataProvider.addDataDisplay(rawWindFixesTable); - Handler columnSortHandler = getWindTableColumnSortHandler(rawWindFixesDataProvider.getList(), timeColumn, - speedInKnotsColumn, windDirectionInDegColumn); - rawWindFixesTable.addColumnSortHandler(columnSortHandler); - rawWindFixesTable.getColumnSortList().push(timeColumn); - windFixesDisplayPanel.add(rawWindFixesTable); - } + raceIsKnownToStartUpwindBox = new CheckBox(stringMessages.raceIsKnownToStartUpwind()); + windSourcesPanel.add(raceIsKnownToStartUpwindBox); + + windSourcesPanel.add(windSourcesToExcludeSelectorPanel); + raceIsKnownToStartUpwindBox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + setRaceIsKnownToStartUpwind(); + } + }); + + windFixPanel = new VerticalPanel(); + windSourcesPanel.add(windFixPanel); + + windSourceLabel = new Label(); + windFixesDisplayPanel.add(windSourceLabel); + + // table for the raw wind fixes + removeColumn = new IdentityColumn(new ActionCell(stringMessages.remove(), new Delegate() { + @Override + public void execute(final WindDTO wind) { + List selectedRaces = raceSelectionProvider.getSelectedRaces(); + final RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(selectedRaces.size() - 1); + sailingService.removeWind(raceIdentifier, wind, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + // remove row from underlying list: + rawWindFixesDataProvider.getList().remove(wind); + } + + @Override + public void onFailure(Throwable caught) { + WindPanel.this.errorReporter.reportError(WindPanel.this.stringMessages.errorSettingWindForRace() + " " + raceIdentifier + ": " + caught.getMessage()); + } + }); + } + })); + timeColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + return DateAndTimeFormatterUtil.formatDateAndTime(new Date(object.measureTimepoint)); + } + }; + speedInKnotsColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + return "" + object.trueWindSpeedInKnots; + } + }; + windDirectionInDegColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + return "" + object.trueWindFromDeg; + } + }; + positionColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + String result = ""; + if (object.position != null) { + result = "Lat: " + object.position.latDeg + ", Lon: " + object.position.lngDeg; + } + return result; + } + }; + timeColumn.setSortable(true); + speedInKnotsColumn.setSortable(true); + windDirectionInDegColumn.setSortable(true); + + AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); + rawWindFixesTable = new CellTable(/* pageSize */10000, tableRes); + rawWindFixesTable.addColumn(timeColumn, "Time"); + rawWindFixesTable.addColumn(speedInKnotsColumn, "Speed (kn)"); + rawWindFixesTable.addColumn(windDirectionInDegColumn, "From (deg)"); + rawWindFixesTable.addColumn(positionColumn, "Position"); + rawWindFixesTable.addColumn(removeColumn, stringMessages.actions()); + rawWindFixesDataProvider = new ListDataProvider(); + rawWindFixesDataProvider.addDataDisplay(rawWindFixesTable); + Handler columnSortHandler = getWindTableColumnSortHandler(rawWindFixesDataProvider.getList(), timeColumn, speedInKnotsColumn, windDirectionInDegColumn); + rawWindFixesTable.addColumnSortHandler(columnSortHandler); + rawWindFixesTable.getColumnSortList().push(timeColumn); + windFixesDisplayPanel.add(rawWindFixesTable); + + createWindImportPanel(mainPanel, stringMessages); + + } private void createWindImportPanel(Panel mainPanel, final StringMessages stringMessages) { CaptionPanel importPanel = new CaptionPanel("Import Wind from Expedition"); Panel importPanelContent = new VerticalPanel(); importPanel.add(importPanelContent); - + final FormPanel form = new FormPanel(); form.setMethod(FormPanel.METHOD_POST); form.setEncoding(FormPanel.ENCODING_MULTIPART); - form.setAction(GWT.getHostPageBaseURL()+"/../../sailingserver/expedition-import"); - + form.setAction(GWT.getHostPageBaseURL() + URL_SAILINGSERVER_EXPEDITION_IMPORT); + VerticalPanel formContentPanel = new VerticalPanel(); - + final TextBox boatIdTextBox = new TextBox(); - boatIdTextBox.setName("boatId"); - + boatIdTextBox.setName(EXPEDITON_IMPORT_PARAMETER_BOAT_ID); + final FileUpload fileUpload = new FileUpload(); fileUpload.setName("upload"); fileUpload.addChangeHandler(new ChangeHandler() { - + @Override public void onChange(ChangeEvent event) { String fileName = fileUpload.getFilename(); - RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches file names like "2013Jun26_0.csv" where 0 indicates the boat id. - MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); - String boatId; - if (match.getGroupCount() > 0) { - boatId = match.getGroup(1); - } else { - boatId = ""; - } - boatIdTextBox.setText(boatId); + RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches file names like "2013Jun26_0.csv" where 0 indicates the boat id. + MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); + String boatId; + if (match.getGroupCount() > 0) { + boatId = match.getGroup(1); + } else { + boatId = ""; + } + boatIdTextBox.setText(boatId); } }); - + formContentPanel.add(fileUpload); - + HorizontalPanel boatIdPanel = new HorizontalPanel(); boatIdPanel.add(new Label("Boat Id")); boatIdPanel.add(boatIdTextBox); - + formContentPanel.add(boatIdPanel); - - Button submitButton = new Button("Upload", new ClickHandler() - { + + Button submitButton = new Button("Upload", new ClickHandler() { @Override public void onClick(ClickEvent event) { - form.submit(); + form.submit(); } }); formContentPanel.add(submitButton); - + final Panel importResultPanel = new VerticalPanel(); - - form.addSubmitHandler(new FormPanel.SubmitHandler() { - public void onSubmit(SubmitEvent event) { - // This event is fired just before the form is submitted. We can take - // this opportunity to perform validation. -// if (condition isn't met) { -// event.cancel(); -// } - } - }); - form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() { - public void onSubmitComplete(SubmitCompleteEvent event) { - importResultPanel.clear(); - TextBox importResult = new TextBox(); - importResult.setText(event.getResults()); - importResultPanel.add(importResult); - } - }); + + form.addSubmitHandler(new FormPanel.SubmitHandler() { + public void onSubmit(SubmitEvent event) { + // This event is fired just before the form is submitted. We can take + // this opportunity to perform validation. + // if (condition isn't met) { + // event.cancel(); + // } + } + }); + form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() { + public void onSubmitComplete(SubmitCompleteEvent event) { + importResultPanel.clear(); + String windImportResultJson = event.getResults(); + + WindImportResult windImportResult = WindImportResult.fromJson(windImportResultJson); + JsArray raceEntries = windImportResult.getRaceEntries(); + Label resultHeader = new Label("Imported wind to " + raceEntries.length() + " races from: " + windImportResult.getFirst() + " to: " + windImportResult.getLast()); + importResultPanel.add(resultHeader); + if (windImportResult.getError() != null) { + Label errorText = new Label("Error: " + windImportResult.getError()); + importResultPanel.add(errorText); + } + for (int i = 0; i < raceEntries.length(); i++) { + RaceEntry raceEntry = raceEntries.get(i); + Label entryText = new Label("Race: " + raceEntry.getRaceName() + " (" + raceEntry.getRegattaName() + + ") - " + raceEntry.getCount() + " fixes from: " + raceEntry.getFirst() + " to:" + raceEntry.getLast()); + importResultPanel.add(entryText); + } + } + }); form.add(formContentPanel); importPanelContent.add(form); importPanelContent.add(importResultPanel); - - mainPanel.add(importPanel); + + mainPanel.add(importPanel); } - private void showWindSettingDialog(RaceDTO race, CoursePositionsDTO course) { - AddWindFixDialog windSettingDialog = new AddWindFixDialog(race, course, stringMessages, - new DialogCallback() { - @Override - public void cancel() { - } + private void showWindSettingDialog(RaceDTO race, CoursePositionsDTO course) { + AddWindFixDialog windSettingDialog = new AddWindFixDialog(race, course, stringMessages, new DialogCallback() { + @Override + public void cancel() { + } - @Override - public void ok(final WindDTO result) { - addWindFix(result); - } - }); - windSettingDialog.show(); - } + @Override + public void ok(final WindDTO result) { + addWindFix(result); + } + }); + windSettingDialog.show(); + } - private void addWindFix(final WindDTO wind) { - final RegattaAndRaceIdentifier raceIdentifier = getSelectedRace(); - sailingService.setWind(raceIdentifier, wind, new AsyncCallback() { - @Override - public void onSuccess(Void result) { - showWind(raceIdentifier); - } + private void addWindFix(final WindDTO wind) { + final RegattaAndRaceIdentifier raceIdentifier = getSelectedRace(); + sailingService.setWind(raceIdentifier, wind, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + showWind(raceIdentifier); + } - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error adding a wind fix for race " + raceIdentifier + ": " + caught.getMessage()); - } - }); - } - - @Override - public void fillRegattas(List result) { - trackedRacesListComposite.fillRegattas(result); - } + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error adding a wind fix for race " + raceIdentifier + ": " + caught.getMessage()); + } + }); + } - @Override - public void showWind(final RegattaAndRaceIdentifier raceIdentifier) { - sailingService.getWindSourcesInfo(raceIdentifier, - new AsyncCallback() { - @Override - public void onSuccess(final WindInfoForRaceDTO result) { - if (result != null) { - updateWindSourcesToExclude(result, raceIdentifier); - raceIsKnownToStartUpwindBox.setValue(result.raceIsKnownToStartUpwind); - - // load the raw wind fixes - sailingService.getRawWindFixes(raceIdentifier, null, - new AsyncCallback() { - @Override - public void onSuccess(WindInfoForRaceDTO result) { - if (result != null) { - udapteRawWindFixes(result); - } else { - // no wind fixes known for untracked race - clearWindSources(); - clearWindFixes(); - } - } + @Override + public void fillRegattas(List result) { + trackedRacesListComposite.fillRegattas(result); + } - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error reading wind fixes: " + caught.getMessage()); - } - }); - - } else { - // no wind sources known for untracked race - clearWindSources(); - clearWindFixes(); - } - } + @Override + public void showWind(final RegattaAndRaceIdentifier raceIdentifier) { + sailingService.getWindSourcesInfo(raceIdentifier, new AsyncCallback() { + @Override + public void onSuccess(final WindInfoForRaceDTO result) { + if (result != null) { + updateWindSourcesToExclude(result, raceIdentifier); + raceIsKnownToStartUpwindBox.setValue(result.raceIsKnownToStartUpwind); - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error reading wind source information: " + caught.getMessage()); - } - }); - } + // load the raw wind fixes + sailingService.getRawWindFixes(raceIdentifier, null, new AsyncCallback() { + @Override + public void onSuccess(WindInfoForRaceDTO result) { + if (result != null) { + udapteRawWindFixes(result); + } else { + // no wind fixes known for untracked race + clearWindSources(); + clearWindFixes(); + } + } - private void updateWindSourcesToExclude(WindInfoForRaceDTO result, RegattaAndRaceIdentifier raceIdentifier) { - windSourcesToExcludeSelectorPanel.update(raceIdentifier, result.windTrackInfoByWindSource.keySet(), result.windSourcesToExclude); - } + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error reading wind fixes: " + caught.getMessage()); + } + }); - private void udapteRawWindFixes(WindInfoForRaceDTO result) { - for (Map.Entry e : result.windTrackInfoByWindSource.entrySet()) { - if (e.getKey().getType() == WindSourceType.WEB || e.getKey().getType() == WindSourceType.EXPEDITION) { - windSourceLabel.setText(stringMessages.windSource() + ": " + e.getKey()); - if (e.getKey().getType() == WindSourceType.WEB) { - // only the WEB wind source is editable, hence has a "Remove" column - // rawWindFixesTable.removeColumn(col); - } - rawWindFixesDataProvider.getList().clear(); - rawWindFixesDataProvider.getList().addAll(e.getValue().windFixes); - } - } - } - - private Handler getWindTableColumnSortHandler(List list, TextColumn timeColumn, - TextColumn speedInKnotsColumn, TextColumn windDirectionInDegColumn) { - ListHandler result = new ListHandler(list); - result.setComparator(timeColumn, new Comparator() { - @Override - public int compare(WindDTO o1, WindDTO o2) { - return o1.measureTimepoint < o2.measureTimepoint ? -1 : o1.measureTimepoint == o2.measureTimepoint ? 0 : 1; - } - }); - result.setComparator(speedInKnotsColumn, new Comparator() { - @Override - public int compare(WindDTO o1, WindDTO o2) { - return o1.trueWindSpeedInKnots < o2.trueWindSpeedInKnots ? -1 : - o1.trueWindSpeedInKnots == o2.trueWindSpeedInKnots ? 0 : 1; - } - }); - result.setComparator(windDirectionInDegColumn, new Comparator() { - @Override - public int compare(WindDTO o1, WindDTO o2) { - return o1.trueWindFromDeg < o2.trueWindFromDeg ? -1 : - o1.trueWindFromDeg == o2.trueWindFromDeg ? 0 : 1; - } - }); - return result; - } + } else { + // no wind sources known for untracked race + clearWindSources(); + clearWindFixes(); + } + } - private void setRaceIsKnownToStartUpwind() { - final RegattaAndRaceIdentifier selectedRace = getSelectedRace(); - sailingService.setRaceIsKnownToStartUpwind(selectedRace, - raceIsKnownToStartUpwindBox.getValue(), new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError(WindPanel.this.stringMessages.errorWhileTryingToSetWindSourceForRace()+ - " "+selectedRace+": "+caught.getMessage()); - } - @Override - public void onSuccess(Void result) { - } - }); - } + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error reading wind source information: " + caught.getMessage()); + } + }); + } - private RegattaAndRaceIdentifier getSelectedRace() { - List selectedRaces = raceSelectionProvider.getSelectedRaces(); - if(selectedRaces.isEmpty() || selectedRaces.size() > 1) { - return null; - } - - return selectedRaces.get(0); - } + private void updateWindSourcesToExclude(WindInfoForRaceDTO result, RegattaAndRaceIdentifier raceIdentifier) { + windSourcesToExcludeSelectorPanel.update(raceIdentifier, result.windTrackInfoByWindSource.keySet(), result.windSourcesToExclude); + } - private void clearWindSources() { - final Set emptySet = Collections.emptySet(); - windSourcesToExcludeSelectorPanel.update(null, emptySet, emptySet); - } + private void udapteRawWindFixes(WindInfoForRaceDTO result) { + for (Map.Entry e : result.windTrackInfoByWindSource.entrySet()) { + if (e.getKey().getType() == WindSourceType.WEB || e.getKey().getType() == WindSourceType.EXPEDITION) { + windSourceLabel.setText(stringMessages.windSource() + ": " + e.getKey()); + if (e.getKey().getType() == WindSourceType.WEB) { + // only the WEB wind source is editable, hence has a "Remove" column + // rawWindFixesTable.removeColumn(col); + } + rawWindFixesDataProvider.getList().clear(); + rawWindFixesDataProvider.getList().addAll(e.getValue().windFixes); + } + } + } - private void clearWindFixes() { - } - - private void showWindFixesList(RegattaAndRaceIdentifier selectedRace, RaceDTO raceDTO) { - List windSourceTypeNames = new ArrayList(); - windSourceTypeNames.add(WindSourceType.COMBINED.name()); - sailingService.getAveragedWindInfo(selectedRace, raceDTO.startOfRace, 30000L, 100, windSourceTypeNames, new AsyncCallback() { + private Handler getWindTableColumnSortHandler(List list, TextColumn timeColumn, TextColumn speedInKnotsColumn, TextColumn windDirectionInDegColumn) { + ListHandler result = new ListHandler(list); + result.setComparator(timeColumn, new Comparator() { + @Override + public int compare(WindDTO o1, WindDTO o2) { + return o1.measureTimepoint < o2.measureTimepoint ? -1 : o1.measureTimepoint == o2.measureTimepoint ? 0 : 1; + } + }); + result.setComparator(speedInKnotsColumn, new Comparator() { + @Override + public int compare(WindDTO o1, WindDTO o2) { + return o1.trueWindSpeedInKnots < o2.trueWindSpeedInKnots ? -1 : o1.trueWindSpeedInKnots == o2.trueWindSpeedInKnots ? 0 : 1; + } + }); + result.setComparator(windDirectionInDegColumn, new Comparator() { + @Override + public int compare(WindDTO o1, WindDTO o2) { + return o1.trueWindFromDeg < o2.trueWindFromDeg ? -1 : o1.trueWindFromDeg == o2.trueWindFromDeg ? 0 : 1; + } + }); + return result; + } - @Override - public void onFailure(Throwable caught) { - } + private void setRaceIsKnownToStartUpwind() { + final RegattaAndRaceIdentifier selectedRace = getSelectedRace(); + sailingService.setRaceIsKnownToStartUpwind(selectedRace, raceIsKnownToStartUpwindBox.getValue(), new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError(WindPanel.this.stringMessages.errorWhileTryingToSetWindSourceForRace() + " " + selectedRace + ": " + caught.getMessage()); + } - @Override - public void onSuccess(WindInfoForRaceDTO result) { - windFixPanel.clear(); - - for (WindSourceType input : new WindSourceType[]{WindSourceType.COMBINED}) { - windFixPanel.add(new HTML(" ")); - windFixPanel.add(new Label(stringMessages.windFixListingDescription() + " " + input.name())); - WindTrackInfoDTO windTrackInfo = result.windTrackInfoByWindSource.get(new WindSourceImpl(input)); - if (windTrackInfo != null && windTrackInfo.windFixes.size()>=7) { - NumberFormat formatter = NumberFormat.getFormat(".##"); - for (WindDTO windFix : windTrackInfo.windFixes.subList(0, 3)) { - windFixPanel.add(new Label(""+formatter.format(windFix.trueWindFromDeg)+" (deg) "+ - formatter.format(windFix.trueWindSpeedInKnots)+" (kt) "+ - formatter.format(windFix.position.latDeg) + " (lat) "+ - formatter.format(windFix.position.lngDeg) + " (lng) " + - new Date(windFix.measureTimepoint))); - } - // These fixes must not necessarily be the real last ones. This especially holds for long races. - for (WindDTO windFix : windTrackInfo.windFixes.subList(windTrackInfo.windFixes.size()-4, windTrackInfo.windFixes.size()-1)) { - windFixPanel.add(new Label(""+formatter.format(windFix.trueWindFromDeg)+" (deg) "+ - formatter.format(windFix.trueWindSpeedInKnots)+" (kt) "+ - formatter.format(windFix.position.latDeg) + " (lat) "+ - formatter.format(windFix.position.lngDeg) + " (lng) " + - new Date(windFix.measureTimepoint))); - } - } else { - windFixPanel.add(new Label(stringMessages.noWindFixesAvailable())); - } - } - } - }); - } - - private void updateWindDisplay() { - RegattaAndRaceIdentifier selectedRace = getSelectedRace(); - RaceDTO raceDTO = selectedRace != null ? trackedRacesListComposite.getRaceByIdentifier(selectedRace) : null; + @Override + public void onSuccess(Void result) { + } + }); + } - if (selectedRace != null && raceDTO != null && raceDTO.trackedRace != null) { - windCaptionPanel.setVisible(true); - windCaptionPanel.setCaptionText(stringMessages.wind() + ": " + selectedRace.getRaceName()); + private RegattaAndRaceIdentifier getSelectedRace() { + List selectedRaces = raceSelectionProvider.getSelectedRaces(); + if (selectedRaces.isEmpty() || selectedRaces.size() > 1) { + return null; + } - showWind(selectedRace); - showWindFixesList(selectedRace, raceDTO); - } else { - windCaptionPanel.setVisible(false); - windCaptionPanel.setCaptionText(stringMessages.wind()); + return selectedRaces.get(0); + } - clearWindSources(); - clearWindFixes(); - } - - } + private void clearWindSources() { + final Set emptySet = Collections.emptySet(); + windSourcesToExcludeSelectorPanel.update(null, emptySet, emptySet); + } - @Override - public void onRaceSelectionChange(List selectedRaces) { - updateWindDisplay(); - } + private void clearWindFixes() { + } + + private void showWindFixesList(RegattaAndRaceIdentifier selectedRace, RaceDTO raceDTO) { + List windSourceTypeNames = new ArrayList(); + windSourceTypeNames.add(WindSourceType.COMBINED.name()); + sailingService.getAveragedWindInfo(selectedRace, raceDTO.startOfRace, 30000L, 100, windSourceTypeNames, new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + } + + @Override + public void onSuccess(WindInfoForRaceDTO result) { + windFixPanel.clear(); + + for (WindSourceType input : new WindSourceType[] { WindSourceType.COMBINED }) { + windFixPanel.add(new HTML(" ")); + windFixPanel.add(new Label(stringMessages.windFixListingDescription() + " " + input.name())); + WindTrackInfoDTO windTrackInfo = result.windTrackInfoByWindSource.get(new WindSourceImpl(input)); + if (windTrackInfo != null && windTrackInfo.windFixes.size() >= 7) { + NumberFormat formatter = NumberFormat.getFormat(".##"); + for (WindDTO windFix : windTrackInfo.windFixes.subList(0, 3)) { + windFixPanel.add(new Label("" + formatter.format(windFix.trueWindFromDeg) + " (deg) " + formatter.format(windFix.trueWindSpeedInKnots) + " (kt) " + formatter.format(windFix.position.latDeg) + " (lat) " + formatter.format(windFix.position.lngDeg) + " (lng) " + new Date(windFix.measureTimepoint))); + } + // These fixes must not necessarily be the real last ones. This especially holds for long races. + for (WindDTO windFix : windTrackInfo.windFixes.subList(windTrackInfo.windFixes.size() - 4, windTrackInfo.windFixes.size() - 1)) { + windFixPanel.add(new Label("" + formatter.format(windFix.trueWindFromDeg) + " (deg) " + formatter.format(windFix.trueWindSpeedInKnots) + " (kt) " + formatter.format(windFix.position.latDeg) + " (lat) " + formatter.format(windFix.position.lngDeg) + " (lng) " + new Date(windFix.measureTimepoint))); + } + } else { + windFixPanel.add(new Label(stringMessages.noWindFixesAvailable())); + } + } + } + }); + } + + private void updateWindDisplay() { + RegattaAndRaceIdentifier selectedRace = getSelectedRace(); + RaceDTO raceDTO = selectedRace != null ? trackedRacesListComposite.getRaceByIdentifier(selectedRace) : null; + + if (selectedRace != null && raceDTO != null && raceDTO.trackedRace != null) { + windCaptionPanel.setVisible(true); + windCaptionPanel.setCaptionText(stringMessages.wind() + ": " + selectedRace.getRaceName()); + + showWind(selectedRace); + showWindFixesList(selectedRace, raceDTO); + } else { + windCaptionPanel.setVisible(false); + windCaptionPanel.setCaptionText(stringMessages.wind()); + + clearWindSources(); + clearWindFixes(); + } + + } + + @Override + public void onRaceSelectionChange(List selectedRaces) { + updateWindDisplay(); + } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java index a3d04777a63..7678cd06d8f 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java @@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.expeditionimport; import java.io.IOException; import java.util.ArrayList; +import java.util.Date; import java.util.List; import javax.servlet.ServletException; @@ -13,11 +14,11 @@ import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; @@ -25,121 +26,178 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.server.gateway.SailingServerHttpServlet; +import com.sap.sailing.server.gateway.expeditionimport.WindImportServlet.WindImportResult.RaceEntry; public class WindImportServlet extends SailingServerHttpServlet { - + private static final long serialVersionUID = 1L; - + static class Upload { public String boatId; public final List files = new ArrayList(); } - + static class WindImportResult { - private final RegattaIdentifier regattaIdentifier; - private final RegattaAndRaceIdentifier raceIdentifier; + private Date first; + private Date last; + public String error; + + public final List raceEntries = new ArrayList(); + + public Date getFirst() { + return first; + } + + public Date getLast() { + return last; + } + + public void update(Wind newWind) { + Date newDate = newWind.getTimePoint().asDate(); + if (this.first == null || newDate.before(this.first)) { + this.first = newDate; + } + if (this.last == null || newDate.after(this.last)) { + this.last = newDate; + } + } + + RaceEntry addRaceEntry(String regattaName, String raceName) { + RaceEntry raceEntry = new RaceEntry(regattaName, raceName); + raceEntries.add(raceEntry); + return raceEntry; + } + + static class RaceEntry { + + public final String regattaName; + public final String raceName; private int count; - private Wind firstWind; - private Wind lastWind; + private Date first; + private Date last; + + private RaceEntry(String regattaName, String raceName) { + this.regattaName = regattaName; + this.raceName = raceName; + } public void update(Wind newWind) { count++; - if (firstWind == null || newWind.getTimePoint().before(firstWind.getTimePoint())) { - firstWind = newWind; + Date newDate = newWind.getTimePoint().asDate(); + if (this.first == null || newDate.before(this.first)) { + this.first = newDate; } - if (lastWind == null || newWind.getTimePoint().after(lastWind.getTimePoint())) { - lastWind = newWind; + if (this.last == null || newDate.after(this.last)) { + this.last = newDate; } } - public RegattaIdentifier getRegattaIdentifier() { - return regattaIdentifier; - } - - public RegattaAndRaceIdentifier getRaceIdentifier() { - return raceIdentifier; - } - - public WindImportResult(RegattaIdentifier regattaIdentifier, RegattaAndRaceIdentifier raceIdentifier) { - this.regattaIdentifier = regattaIdentifier; - this.raceIdentifier = raceIdentifier; - } - public int getCount() { return count; } - public Wind getFirstWind() { - return firstWind; + public Date getFirst() { + return first; } - public Wind getLastWind() { - return lastWind; + public Date getLast() { + return last; } - + + private JSONObject json() { + JSONObject result = new JSONObject(); + result.put("regattaName", regattaName); + result.put("raceName", raceName); + result.put("count", getCount()); + result.put("first", getFirst().getTime()); + result.put("last", getLast().getTime()); + return result; + } + } + + public JSONObject json() { + JSONObject result = new JSONObject(); + + result.put("first", getFirst().getTime()); + result.put("last", getLast().getTime()); + result.put("error", error); + + JSONArray raceEntriesJson = new JSONArray(); + for (RaceEntry raceEntry : raceEntries) { + raceEntriesJson.add(raceEntry.json()); + } + result.put("raceEntries", raceEntriesJson); + + + return result; + + } + } - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (!ServletFileUpload.isMultipartContent(req)) { - resp.sendError(HttpServletResponse.SC_BAD_REQUEST); - return; - } - + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if (!ServletFileUpload.isMultipartContent(req)) { + resp.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + WindImportResult windImportResult = new WindImportResult(); + try { - + Upload upload = readInput(req); WindSource windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, upload.boatId); - - List windImportResults = new ArrayList(); - + Iterable allRegattas = getService().getAllRegattas(); for (FileItem file : upload.files) { List windFixes = WindLogParser.importWind(file.getInputStream()); - for (Regatta regatta : allRegattas) { - DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta); - Iterable allRaceDefinitions = regatta.getAllRaces(); - for (RaceDefinition raceDefinition : allRaceDefinitions) { - DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(raceDefinition); - WindImportResult windImportResult = new WindImportResult(regatta.getRegattaIdentifier(), trackedRace.getRaceIdentifier()); - windImportResults.add(windImportResult); - for (Wind wind : windFixes) { - if (trackedRace.recordWind(wind, windSource)) { - windImportResult.update(wind); + if (windFixes.size() > 0) { + windImportResult.update(windFixes.get(0)); + windImportResult.update(windFixes.get(windFixes.size() - 1)); + for (Regatta regatta : allRegattas) { + DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta); + Iterable allRaceDefinitions = regatta.getAllRaces(); + for (RaceDefinition raceDefinition : allRaceDefinitions) { + DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(raceDefinition); + RaceEntry raceEntry = windImportResult.addRaceEntry(regatta.getName(), trackedRace.getRace().getName()); + for (Wind wind : windFixes) { + if (trackedRace.recordWind(wind, windSource)) { + raceEntry.update(wind); + } } } } } } - + // Use text/html to prevent browsers from wrapping the response body, // see "Handling File Upload Responses in GWT" at http://www.artofsolving.com/node/50 - resp.setContentType("text/html;charset=UTF-8"); - resp.getWriter().append(windImportResults.toString()); - } catch (FileUploadException e) { - throw new IOException(e); + resp.setContentType("text/html;charset=UTF-8"); + resp.getWriter().append(windImportResult.json().toJSONString()); + } catch (Exception e) { + windImportResult.error = e.toString(); } - } + } - private Upload readInput(HttpServletRequest req) throws FileUploadException { - Upload result = new Upload(); - // http://commons.apache.org/fileupload/using.html - FileItemFactory factory = new DiskFileItemFactory(); - ServletFileUpload upload = new ServletFileUpload(factory); - @SuppressWarnings("unchecked") - List items = upload.parseRequest(req); - for (FileItem item : items) { - if (item.isFormField()) { - if ("boatId".equals(item.getFieldName())) { - result.boatId = item.getString(); - } - } else { - result.files.add(item); - } - } - return result; - } + private Upload readInput(HttpServletRequest req) throws FileUploadException { + Upload result = new Upload(); + // http://commons.apache.org/fileupload/using.html + FileItemFactory factory = new DiskFileItemFactory(); + ServletFileUpload upload = new ServletFileUpload(factory); + @SuppressWarnings("unchecked") + List items = upload.parseRequest(req); + for (FileItem item : items) { + if (item.isFormField()) { + if ("boatId".equals(item.getFieldName())) { + result.boatId = item.getString(); + } + } else { + result.files.add(item); + } + } + return result; + } } From 4128a36882a7ef2ae9245a2fa85683a1619cbf97 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Fri, 11 Oct 2013 17:55:54 +0200 Subject: [PATCH 06/14] Slightly enhanced layout. Still missing: apply wind to selected race(s) only. --- .../gwt/ui/adminconsole/WindPanel.java | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index 4a88d7e1a5b..c381fb22a5c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -33,6 +33,7 @@ import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FileUpload; import com.google.gwt.user.client.ui.FormPanel; import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Panel; @@ -237,16 +238,30 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower } private void createWindImportPanel(Panel mainPanel, final StringMessages stringMessages) { - CaptionPanel importPanel = new CaptionPanel("Import Wind from Expedition"); - Panel importPanelContent = new VerticalPanel(); - importPanel.add(importPanelContent); - + + /* + * To style the "browse" button of the file upload widget + * see http://www.shauninman.com/archive/2007/09/10/styling_file_inputs_with_css_and_the_dom + */ + + CaptionPanel windImportRootPanel = new CaptionPanel("Import Wind from Expedition"); + mainPanel.add(windImportRootPanel); + VerticalPanel windImportContentPanel = new VerticalPanel(); + windImportRootPanel.add(windImportContentPanel); final FormPanel form = new FormPanel(); + windImportContentPanel.add(form); + VerticalPanel formContentPanel = new VerticalPanel(); + form.add(formContentPanel); + HorizontalPanel inputPanel = new HorizontalPanel(); + formContentPanel.add(inputPanel); + final Panel importResultPanel = new VerticalPanel(); + windImportContentPanel.add(importResultPanel); + + form.setMethod(FormPanel.METHOD_POST); form.setEncoding(FormPanel.ENCODING_MULTIPART); form.setAction(GWT.getHostPageBaseURL() + URL_SAILINGSERVER_EXPEDITION_IMPORT); - VerticalPanel formContentPanel = new VerticalPanel(); final TextBox boatIdTextBox = new TextBox(); boatIdTextBox.setName(EXPEDITON_IMPORT_PARAMETER_BOAT_ID); @@ -271,13 +286,16 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower } }); - formContentPanel.add(fileUpload); + inputPanel.add(fileUpload); - HorizontalPanel boatIdPanel = new HorizontalPanel(); - boatIdPanel.add(new Label("Boat Id")); - boatIdPanel.add(boatIdTextBox); - - formContentPanel.add(boatIdPanel); + Label boatIdLabel = new Label("Boat Id:"); + inputPanel.add(boatIdLabel); + inputPanel.add(boatIdTextBox); + boatIdLabel.setWordWrap(false); + inputPanel.setSpacing(5); + inputPanel.setCellVerticalAlignment(fileUpload, HasVerticalAlignment.ALIGN_MIDDLE); + inputPanel.setCellVerticalAlignment(boatIdLabel, HasVerticalAlignment.ALIGN_MIDDLE); + inputPanel.setCellVerticalAlignment(boatIdTextBox, HasVerticalAlignment.ALIGN_MIDDLE); Button submitButton = new Button("Upload", new ClickHandler() { @Override @@ -287,8 +305,6 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower }); formContentPanel.add(submitButton); - final Panel importResultPanel = new VerticalPanel(); - form.addSubmitHandler(new FormPanel.SubmitHandler() { public void onSubmit(SubmitEvent event) { // This event is fired just before the form is submitted. We can take @@ -319,11 +335,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower } } }); - form.add(formContentPanel); - importPanelContent.add(form); - importPanelContent.add(importResultPanel); - mainPanel.add(importPanel); } private void showWindSettingDialog(RaceDTO race, CoursePositionsDTO course) { From 229823303bce1bfec6def8bff779cf26df50aa7a Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Mon, 14 Oct 2013 09:59:52 +0200 Subject: [PATCH 07/14] Added trimming to boat id handling. --- .../gateway/expeditionimport/WindImportServlet.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java index 7678cd06d8f..97c3d9330da 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java @@ -21,6 +21,7 @@ import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; @@ -148,7 +149,12 @@ public class WindImportServlet extends SailingServerHttpServlet { try { Upload upload = readInput(req); - WindSource windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, upload.boatId); + WindSource windSource; + if ((upload.boatId != null) && (upload.boatId.trim().length() > 0)) { + windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, upload.boatId.trim()); + } else { + windSource = new WindSourceImpl(WindSourceType.EXPEDITION); + } Iterable allRegattas = getService().getAllRegattas(); From 9d9c90134cbb36b2a861ec19055c3877f5903e2f Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Mon, 14 Oct 2013 17:31:19 +0200 Subject: [PATCH 08/14] Added missing wrapper class WindImportResult --- .../gwt/ui/adminconsole/WindImportResult.java | 38 +++++++++++++++++++ .../gwt/ui/adminconsole/WindPanel.java | 23 ++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java new file mode 100644 index 00000000000..2be559fc859 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java @@ -0,0 +1,38 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.JsArray; +import com.google.gwt.core.client.JsDate; + +public class WindImportResult extends JavaScriptObject { + + protected WindImportResult() { + }; + + static public final native WindImportResult fromJson(String json) /*-{return eval('('+json+')');}-*/; + + public final native String getError() /*-{return this.error;}-*/; + + public final native JsDate getFirst() /*-{return new Date(this.first);}-*/; + + public final native JsDate getLast() /*-{return new Date(this.last);}-*/; + + public final native JsArray getRaceEntries() /*-{return this.raceEntries;}-*/; + + static class RaceEntry extends JavaScriptObject { + + protected RaceEntry() {}; + + public final native String getRegattaName() /*-{return this.regattaName;}-*/; + + public final native String getRaceName() /*-{return this.raceName;}-*/; + + public final native int getCount() /*-{return this.count;}-*/; + + public final native JsDate getFirst() /*-{return new Date(this.first);}-*/; + + public final native JsDate getLast() /*-{return new Date(this.last);}-*/; + + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index c381fb22a5c..59fdbdb546d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -107,7 +107,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower mainPanel.setSize("100%", "100%"); this.setWidget(mainPanel); - trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages, /*multiselection*/false); + trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages, /*multiselection*/true); mainPanel.add(trackedRacesListComposite); raceSelectionProvider.addRaceSelectionChangeListener(this); @@ -297,6 +297,27 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower inputPanel.setCellVerticalAlignment(boatIdLabel, HasVerticalAlignment.ALIGN_MIDDLE); inputPanel.setCellVerticalAlignment(boatIdTextBox, HasVerticalAlignment.ALIGN_MIDDLE); + + final TextBox raceIdTextBox = new TextBox(); + raceIdTextBox.setName("races"); + inputPanel.add(raceIdTextBox); + raceSelectionProvider.addRaceSelectionChangeListener(new RaceSelectionChangeListener() { + + @Override + public void onRaceSelectionChange(List selectedRaces) { + StringBuilder races = new StringBuilder(); + for (RegattaAndRaceIdentifier raceIdentifier : selectedRaces) { + races.append(raceIdentifier.toString()); + races.append('|'); + } + raceIdTextBox.setText(races.toString()); + } + }); + + + + + Button submitButton = new Button("Upload", new ClickHandler() { @Override public void onClick(ClickEvent event) { From 48a1a6c187f4cc565b61c9178980bf5c407c6f3d Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Tue, 15 Oct 2013 11:25:40 +0200 Subject: [PATCH 09/14] Added seletion-aware wind import --- .../gwt/ui/adminconsole/RaceSelection.java | 30 ++++++ .../gwt/ui/adminconsole/WindImportResult.java | 2 +- .../gwt/ui/adminconsole/WindPanel.java | 71 +++++++------ .../expeditionimport/WindImportServlet.java | 99 ++++++++++++------- .../expeditionimport/WindLogParser.java | 3 + .../sailing/server/RacingEventService.java | 7 +- .../server/impl/RacingEventServiceImpl.java | 13 +-- 7 files changed, 148 insertions(+), 77 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceSelection.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceSelection.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceSelection.java new file mode 100644 index 00000000000..9ac6cd60f19 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceSelection.java @@ -0,0 +1,30 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.core.client.JavaScriptObject; + +public class RaceSelection extends JavaScriptObject { + + protected RaceSelection() {}; + + public static final native RaceSelection create() /*-{return [];}-*/; + + public final native String toJson() /*-{return JSON.stringify(this);}-*/; + + public final native void addRace(RaceEntry race) /*-{ + + this.push(race); + + }-*/; + + public static class RaceEntry extends JavaScriptObject { + + protected RaceEntry() {}; + + public static final native RaceEntry create() /*-{return {};}-*/; + + public final native void setRaceName(String raceName) /*-{this.race = raceName;}-*/; + + public final native void setRegatteName(String regatteName) /*-{this.regatta = regatteName;}-*/; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java index 2be559fc859..34517031ece 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindImportResult.java @@ -9,7 +9,7 @@ public class WindImportResult extends JavaScriptObject { protected WindImportResult() { }; - static public final native WindImportResult fromJson(String json) /*-{return eval('('+json+')');}-*/; + static public final native WindImportResult fromJson(String json) /*-{return JSON.parse(json);}-*/; public final native String getError() /*-{return this.error;}-*/; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index 59fdbdb546d..337b8135107 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -26,6 +26,7 @@ import com.google.gwt.user.cellview.client.ColumnSortEvent.Handler; import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; import com.google.gwt.user.cellview.client.IdentityColumn; import com.google.gwt.user.cellview.client.TextColumn; +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.CaptionPanel; @@ -34,6 +35,7 @@ import com.google.gwt.user.client.ui.FileUpload; import com.google.gwt.user.client.ui.FormPanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HasVerticalAlignment; +import com.google.gwt.user.client.ui.Hidden; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Panel; @@ -265,6 +267,14 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower final TextBox boatIdTextBox = new TextBox(); boatIdTextBox.setName(EXPEDITON_IMPORT_PARAMETER_BOAT_ID); + final Button submitButton = new Button("Upload", new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + importResultPanel.clear(); + form.submit(); + } + }); + submitButton.setEnabled(false); final FileUpload fileUpload = new FileUpload(); fileUpload.setName("upload"); @@ -273,6 +283,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower @Override public void onChange(ChangeEvent event) { String fileName = fileUpload.getFilename(); + submitButton.setEnabled((fileName != null) && (fileUpload.getFilename().trim().length() > 0)); RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches file names like "2013Jun26_0.csv" where 0 indicates the boat id. MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); @@ -283,6 +294,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower boatId = ""; } boatIdTextBox.setText(boatId); + } }); @@ -297,42 +309,37 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower inputPanel.setCellVerticalAlignment(boatIdLabel, HasVerticalAlignment.ALIGN_MIDDLE); inputPanel.setCellVerticalAlignment(boatIdTextBox, HasVerticalAlignment.ALIGN_MIDDLE); + final Hidden hiddenRacesField = new Hidden("races"); + inputPanel.add(hiddenRacesField); - final TextBox raceIdTextBox = new TextBox(); - raceIdTextBox.setName("races"); - inputPanel.add(raceIdTextBox); - raceSelectionProvider.addRaceSelectionChangeListener(new RaceSelectionChangeListener() { - - @Override - public void onRaceSelectionChange(List selectedRaces) { - StringBuilder races = new StringBuilder(); - for (RegattaAndRaceIdentifier raceIdentifier : selectedRaces) { - races.append(raceIdentifier.toString()); - races.append('|'); - } - raceIdTextBox.setText(races.toString()); - } - }); - - - - - - Button submitButton = new Button("Upload", new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - form.submit(); - } - }); formContentPanel.add(submitButton); form.addSubmitHandler(new FormPanel.SubmitHandler() { public void onSubmit(SubmitEvent event) { - // This event is fired just before the form is submitted. We can take - // this opportunity to perform validation. - // if (condition isn't met) { - // event.cancel(); - // } + if ((fileUpload.getFilename() != null) && (fileUpload.getFilename().trim().length() > 0)) { + List selectedRaces = raceSelectionProvider.getSelectedRaces(); + String warningMessage; + if (selectedRaces.size() > 0) { + warningMessage = "Do you really want to import wind into the selected " + selectedRaces.size() + " races?"; + RaceSelection raceSelection = RaceSelection.create(); + for (int i = 0; i < selectedRaces.size(); i++) { + RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(i); + RaceSelection.RaceEntry raceEntry = RaceSelection.RaceEntry.create(); + raceEntry.setRaceName(raceIdentifier.getRaceName()); + raceEntry.setRegatteName(raceIdentifier.getRegattaName()); + raceSelection.addRace(raceEntry); + } + hiddenRacesField.setValue(raceSelection.toJson()); + } else { + warningMessage = "Do you really want to import wind into all races?"; + hiddenRacesField.setValue(null); + } + if (!Window.confirm(warningMessage)) { + event.cancel(); + } + } else { + event.cancel(); + } } }); form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() { @@ -342,7 +349,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower WindImportResult windImportResult = WindImportResult.fromJson(windImportResultJson); JsArray raceEntries = windImportResult.getRaceEntries(); - Label resultHeader = new Label("Imported wind to " + raceEntries.length() + " races from: " + windImportResult.getFirst() + " to: " + windImportResult.getLast()); + Label resultHeader = new Label("Imported wind from: " + windImportResult.getFirst() + " to: " + windImportResult.getLast() + " into " + raceEntries.length() + " races."); importResultPanel.add(resultHeader); if (windImportResult.getError() != null) { Label errorText = new Label("Error: " + windImportResult.getError()); diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java index 97c3d9330da..45e5a7b8c34 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindImportServlet.java @@ -16,15 +16,18 @@ import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; import com.sap.sailing.domain.tracking.DynamicTrackedRace; -import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.server.gateway.SailingServerHttpServlet; import com.sap.sailing.server.gateway.expeditionimport.WindImportServlet.WindImportResult.RaceEntry; @@ -33,11 +36,12 @@ public class WindImportServlet extends SailingServerHttpServlet { private static final long serialVersionUID = 1L; - static class Upload { + static class UploadRequest { public String boatId; public final List files = new ArrayList(); + public List races = new ArrayList();; } - + static class WindImportResult { private Date first; @@ -111,8 +115,12 @@ public class WindImportServlet extends SailingServerHttpServlet { result.put("regattaName", regattaName); result.put("raceName", raceName); result.put("count", getCount()); - result.put("first", getFirst().getTime()); - result.put("last", getLast().getTime()); + if (getFirst() != null) { + result.put("first", getFirst().getTime()); + } + if (getLast() != null) { + result.put("last", getLast().getTime()); + } return result; } } @@ -120,13 +128,19 @@ public class WindImportServlet extends SailingServerHttpServlet { public JSONObject json() { JSONObject result = new JSONObject(); - result.put("first", getFirst().getTime()); - result.put("last", getLast().getTime()); + if (getFirst() != null) { + result.put("first", getFirst().getTime()); + } + if (getLast() != null) { + result.put("last", getLast().getTime()); + } result.put("error", error); JSONArray raceEntriesJson = new JSONArray(); for (RaceEntry raceEntry : raceEntries) { - raceEntriesJson.add(raceEntry.json()); + if (raceEntry.count > 0) { + raceEntriesJson.add(raceEntry.json()); + } } result.put("raceEntries", raceEntriesJson); @@ -138,9 +152,9 @@ public class WindImportServlet extends SailingServerHttpServlet { } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (!ServletFileUpload.isMultipartContent(req)) { - resp.sendError(HttpServletResponse.SC_BAD_REQUEST); + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + if (!ServletFileUpload.isMultipartContent(request)) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); return; } @@ -148,31 +162,41 @@ public class WindImportServlet extends SailingServerHttpServlet { try { - Upload upload = readInput(req); + UploadRequest uploadRequest = readRequest(request); WindSource windSource; - if ((upload.boatId != null) && (upload.boatId.trim().length() > 0)) { - windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, upload.boatId.trim()); - } else { + if (uploadRequest.boatId == null) { windSource = new WindSourceImpl(WindSourceType.EXPEDITION); + } else { + windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, uploadRequest.boatId); + } + + List trackedRaces = new ArrayList(); + if (uploadRequest.races.size() > 0) { + for (RegattaAndRaceIdentifier raceEntry : uploadRequest.races) { + DynamicTrackedRace trackedRace = getService().getTrackedRace(raceEntry); + if (trackedRace != null) { + trackedRaces.add(trackedRace); + } + } + } else { + for (Regatta regatta : getService().getAllRegattas()) { + for (RaceDefinition raceDefinition : regatta.getAllRaces()) { + trackedRaces.add(getService().getTrackedRegatta(regatta).getTrackedRace(raceDefinition)); + } + } } - Iterable allRegattas = getService().getAllRegattas(); - - for (FileItem file : upload.files) { + for (FileItem file : uploadRequest.files) { List windFixes = WindLogParser.importWind(file.getInputStream()); if (windFixes.size() > 0) { windImportResult.update(windFixes.get(0)); windImportResult.update(windFixes.get(windFixes.size() - 1)); - for (Regatta regatta : allRegattas) { - DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta); - Iterable allRaceDefinitions = regatta.getAllRaces(); - for (RaceDefinition raceDefinition : allRaceDefinitions) { - DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(raceDefinition); - RaceEntry raceEntry = windImportResult.addRaceEntry(regatta.getName(), trackedRace.getRace().getName()); - for (Wind wind : windFixes) { - if (trackedRace.recordWind(wind, windSource)) { - raceEntry.update(wind); - } + for (DynamicTrackedRace trackedRace : trackedRaces) { + RegattaAndRaceIdentifier raceIdentifier = trackedRace.getRaceIdentifier(); + RaceEntry raceEntry = windImportResult.addRaceEntry(raceIdentifier.getRegattaName(), raceIdentifier.getRaceName()); + for (Wind wind : windFixes) { + if (trackedRace.recordWind(wind, windSource)) { + raceEntry.update(wind); } } } @@ -181,26 +205,31 @@ public class WindImportServlet extends SailingServerHttpServlet { // Use text/html to prevent browsers from wrapping the response body, // see "Handling File Upload Responses in GWT" at http://www.artofsolving.com/node/50 - resp.setContentType("text/html;charset=UTF-8"); - resp.getWriter().append(windImportResult.json().toJSONString()); } catch (Exception e) { windImportResult.error = e.toString(); } + response.setContentType("text/html;charset=UTF-8"); + response.getWriter().append(windImportResult.json().toJSONString()); } - private Upload readInput(HttpServletRequest req) throws FileUploadException { - Upload result = new Upload(); + private UploadRequest readRequest(HttpServletRequest req) throws FileUploadException, ParseException { + UploadRequest result = new UploadRequest(); // http://commons.apache.org/fileupload/using.html FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); @SuppressWarnings("unchecked") List items = upload.parseRequest(req); for (FileItem item : items) { - if (item.isFormField()) { + if (item.isFormField() && (item.getString() != null) && (item.getString().trim().length() > 0)) { if ("boatId".equals(item.getFieldName())) { - result.boatId = item.getString(); + result.boatId = item.getString().trim(); + } else if ("races".equals(item.getFieldName())) { + JSONArray races = (JSONArray) new JSONParser().parse(item.getString().trim()); + for (Object raceEntry : races) { + result.races.add(new RegattaNameAndRaceName((String) ((JSONObject) raceEntry).get("regatta"), (String) ((JSONObject ) raceEntry).get("race"))); + } } - } else { + } else if (item.getSize() > 0) { result.files.add(item); } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java index 178cef60a74..6900f3128b1 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/expeditionimport/WindLogParser.java @@ -132,6 +132,9 @@ public class WindLogParser { } private static void assertHeaderFormat(String headerLine) { + if (headerLine == null) { + throw new NullPointerException("Empty header line."); + } //supposed to be Utc,Bsp,Awa,Aws,Twa,Tws,Twd,Rudder2,Leeway,Set,Drift,Hdg,AirTmp,SeaTmp,Baro,Depth,Heel,Trim,Rudder,Tab,Forestay,Downhaul,MastAng,FrstyLen,MastButt,LoadStbd,LoadPort,Rake,Volts,ROT,GpsQual,GpsPDOP,GpsNum,GpsAge,GpsAltitude,GpsGeoSep,GpsMode,Lat,Lon,Cog,Sog,DiffStn,Error,StbRunner,PrtRunner,Vang,Traveller,MainSheet,KeelAng,KeelHt,CanardH,OilPres,RPM1,RPM2,Dagger Bd,Boom Pos,DistToLn,RchTmToLn,RchDtToLn,GPS Time,Downhaul2,MkLat,MkLon,PortLat,PortLon,StbdLat,StbdLon,GPS HPE,RH,LeadPt,LeadSb,BkStay,User 0,User 1,User 2,User 3,User 4,User 5,User 6,User 7,User 8,User 9,User 10,User 11,User 12,User 13,User 14,User 15,User 16,User 17,User 18,User 19,User 20,User 21,User 22,User 23,User 24,User 25,User 26,User 27,User 28,User 29,User 30,User 31,TmToGun,TmToLn,TmToBurn,BelowLn,GunBlwLn,WvSigHt,WvSigPd,WvMaxHt,WvMaxPd,Slam,Motion String[] columns = LINE_PATTERN.split(headerLine); if ("Utc".equals(columns[CSV_INDEX_TIME_STAMP]) && "Tws".equals(columns[CSV_INDEX_TRUE_WIND_SPEED]) && "Twd".equals(columns[CSV_INDEX_TRUE_WIND_DIRECTION]) && "Lat".equals(columns[CSV_INDEX_LAT]) && "Lon".equals(columns[CSV_INDEX_LONG])) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java index 16c33416bdc..d1f31e0d0e1 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java @@ -47,6 +47,7 @@ import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; +import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.RaceListener; import com.sap.sailing.domain.tracking.RaceTracker; import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters; @@ -86,9 +87,9 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch @Override RaceDefinition getRace(RegattaAndRaceIdentifier raceIdentifier); - TrackedRace getTrackedRace(Regatta regatta, RaceDefinition race); + DynamicTrackedRace getTrackedRace(Regatta regatta, RaceDefinition race); - TrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier); + DynamicTrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier); /** * Obtains an unmodifiable map of the leaderboard configured in this service keyed by their names. @@ -307,7 +308,7 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch */ void removeRegatta(Regatta regatta) throws MalformedURLException, IOException, InterruptedException; - TrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier); + DynamicTrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier); /** * Obtains an unmodifiable map of the leaderboard groups configured in this service keyed by their names. diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index f2069d30ef7..1edb9b96502 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -90,6 +90,7 @@ import com.sap.sailing.domain.swisstimingadapter.SailMasterConnector; import com.sap.sailing.domain.swisstimingadapter.SailMasterMessage; import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory; import com.sap.sailing.domain.swisstimingadapter.persistence.SwissTimingAdapterPersistence; +import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.GPSFix; import com.sap.sailing.domain.tracking.GPSFixMoving; @@ -1433,11 +1434,11 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen } @Override - public TrackedRace getTrackedRace(Regatta regatta, RaceDefinition race) { + public DynamicTrackedRace getTrackedRace(Regatta regatta, RaceDefinition race) { return getOrCreateTrackedRegatta(regatta).getTrackedRace(race); } - private TrackedRace getExistingTrackedRace(Regatta regatta, RaceDefinition race) { + private DynamicTrackedRace getExistingTrackedRace(Regatta regatta, RaceDefinition race) { return getOrCreateTrackedRegatta(regatta).getExistingTrackedRace(race); } @@ -1495,8 +1496,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen } @Override - public TrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier) { - TrackedRace result = null; + public DynamicTrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier) { + DynamicTrackedRace result = null; Regatta regatta = regattasByName.get(raceIdentifier.getRegattaName()); if (regatta != null) { DynamicTrackedRegatta trackedRegatta = regattaTrackingCache.get(regatta); @@ -1511,9 +1512,9 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen } @Override - public TrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier) { + public DynamicTrackedRace getExistingTrackedRace(RegattaAndRaceIdentifier raceIdentifier) { Regatta regatta = getRegattaByName(raceIdentifier.getRegattaName()); - TrackedRace trackedRace = null; + DynamicTrackedRace trackedRace = null; if (regatta != null) { RaceDefinition race = regatta.getRaceByName(raceIdentifier.getRaceName()); trackedRace = getOrCreateTrackedRegatta(regatta).getExistingTrackedRace(race); From bdd3961bda47f236b4c2078fcb163fa409b92130 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Tue, 15 Oct 2013 14:07:47 +0200 Subject: [PATCH 10/14] Added localisation. --- .../gwt/ui/adminconsole/WindPanel.java | 39 ++++++++++--------- .../sailing/gwt/ui/client/StringMessages.java | 9 +++++ .../gwt/ui/client/StringMessages.properties | 8 ++++ .../ui/client/StringMessages_de.properties | 8 ++++ 4 files changed, 46 insertions(+), 18 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index 337b8135107..6d2953e88af 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -75,6 +75,8 @@ import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO; */ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower, RaceSelectionChangeListener { + private static final String EXPEDITON_IMPORT_PARAMETER_RACES = "races"; + private static final String EXPEDITON_IMPORT_PARAMETER_BOAT_ID = "boatId"; private static final String URL_SAILINGSERVER_EXPEDITION_IMPORT = "/../../sailingserver/expedition-import"; @@ -246,7 +248,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower * see http://www.shauninman.com/archive/2007/09/10/styling_file_inputs_with_css_and_the_dom */ - CaptionPanel windImportRootPanel = new CaptionPanel("Import Wind from Expedition"); + CaptionPanel windImportRootPanel = new CaptionPanel(stringMessages.windImport_Title()); mainPanel.add(windImportRootPanel); VerticalPanel windImportContentPanel = new VerticalPanel(); windImportRootPanel.add(windImportContentPanel); @@ -267,7 +269,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower final TextBox boatIdTextBox = new TextBox(); boatIdTextBox.setName(EXPEDITON_IMPORT_PARAMETER_BOAT_ID); - final Button submitButton = new Button("Upload", new ClickHandler() { + final Button submitButton = new Button(stringMessages.windImport_Upload(), new ClickHandler() { @Override public void onClick(ClickEvent event) { importResultPanel.clear(); @@ -282,16 +284,18 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower @Override public void onChange(ChangeEvent event) { + importResultPanel.clear(); String fileName = fileUpload.getFilename(); - submitButton.setEnabled((fileName != null) && (fileUpload.getFilename().trim().length() > 0)); + boolean isValidFileName = (fileName != null) && (fileUpload.getFilename().trim().length() > 0); + submitButton.setEnabled(isValidFileName); - RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches file names like "2013Jun26_0.csv" where 0 indicates the boat id. - MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); - String boatId; - if (match.getGroupCount() > 0) { - boatId = match.getGroup(1); - } else { - boatId = ""; + String boatId = ""; + if (isValidFileName) { + RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches typical expedition log file names like "2013Jun26_0.csv" where 0 as group[1] indicates the boat id. + MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); + if (match.getGroupCount() > 0) { + boatId = match.getGroup(1); + } } boatIdTextBox.setText(boatId); @@ -300,7 +304,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower inputPanel.add(fileUpload); - Label boatIdLabel = new Label("Boat Id:"); + Label boatIdLabel = new Label(stringMessages.windImport_BoatId()); inputPanel.add(boatIdLabel); inputPanel.add(boatIdTextBox); boatIdLabel.setWordWrap(false); @@ -309,7 +313,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower inputPanel.setCellVerticalAlignment(boatIdLabel, HasVerticalAlignment.ALIGN_MIDDLE); inputPanel.setCellVerticalAlignment(boatIdTextBox, HasVerticalAlignment.ALIGN_MIDDLE); - final Hidden hiddenRacesField = new Hidden("races"); + final Hidden hiddenRacesField = new Hidden(EXPEDITON_IMPORT_PARAMETER_RACES); inputPanel.add(hiddenRacesField); formContentPanel.add(submitButton); @@ -320,7 +324,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower List selectedRaces = raceSelectionProvider.getSelectedRaces(); String warningMessage; if (selectedRaces.size() > 0) { - warningMessage = "Do you really want to import wind into the selected " + selectedRaces.size() + " races?"; + warningMessage = stringMessages.windImport_SelectedRacesWarning(selectedRaces.size()); RaceSelection raceSelection = RaceSelection.create(); for (int i = 0; i < selectedRaces.size(); i++) { RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(i); @@ -331,7 +335,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower } hiddenRacesField.setValue(raceSelection.toJson()); } else { - warningMessage = "Do you really want to import wind into all races?"; + warningMessage = stringMessages.windImport_AllRacesWarning(); hiddenRacesField.setValue(null); } if (!Window.confirm(warningMessage)) { @@ -349,16 +353,15 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower WindImportResult windImportResult = WindImportResult.fromJson(windImportResultJson); JsArray raceEntries = windImportResult.getRaceEntries(); - Label resultHeader = new Label("Imported wind from: " + windImportResult.getFirst() + " to: " + windImportResult.getLast() + " into " + raceEntries.length() + " races."); + Label resultHeader = new Label(stringMessages.windImport_ResultHeader(String.valueOf(windImportResult.getFirst()), String.valueOf(windImportResult.getLast()), raceEntries.length())); importResultPanel.add(resultHeader); if (windImportResult.getError() != null) { - Label errorText = new Label("Error: " + windImportResult.getError()); + Label errorText = new Label(stringMessages.windImport_ResultError(windImportResult.getError())); importResultPanel.add(errorText); } for (int i = 0; i < raceEntries.length(); i++) { RaceEntry raceEntry = raceEntries.get(i); - Label entryText = new Label("Race: " + raceEntry.getRaceName() + " (" + raceEntry.getRegattaName() - + ") - " + raceEntry.getCount() + " fixes from: " + raceEntry.getFirst() + " to:" + raceEntry.getLast()); + Label entryText = new Label(stringMessages.windImport_ResultEntry(raceEntry.getRaceName(), raceEntry.getRegattaName(), raceEntry.getCount(), String.valueOf(raceEntry.getFirst()), String.valueOf(raceEntry.getLast()))); importResultPanel.add(entryText); } } 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 0a81277140c..836398a715e 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 @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.client; import com.google.gwt.i18n.client.Messages; +import com.google.gwt.safehtml.shared.SafeHtml; public interface StringMessages extends Messages { String helloWorld(); @@ -750,4 +751,12 @@ public interface StringMessages extends Messages { String speedOverGroundFiveSecondsBeforeStart(); String distanceToLineFiveSecondsBeforeStartTooltip(); String speedOverGroundFiveSecondsBeforeStartTooltip(); + SafeHtml windImport_Upload(); + SafeHtml windImport_Title(); + String windImport_BoatId(); + String windImport_SelectedRacesWarning(int selectedRacesCount); + String windImport_AllRacesWarning(); + String windImport_ResultHeader(String firstWindDate, String lastWindDate, int affectedRacesCount); + String windImport_ResultError(String errorMessage); + String windImport_ResultEntry(String raceName, String regattaName, int windFixesCount, String firstWindDate, String lastWindDate); } 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 6e45da7a4a1..855ee209ee7 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 @@ -744,3 +744,11 @@ polarDiagramButton=Polar Diagram timeSinceLastPositionFix=Last GPS timeSinceLastPositionFixTooltip=Time since last received GPS position trackingQuality=Tracking quality +windImport_Upload=Upload +windImport_Title=Import Wind from Expedition +windImport_BoatId=Boat Id: +windImport_SelectedRacesWarning=Do you really want to import wind into the selected {0} races?\nOnly races with applicable time window will be affected anyway. +windImport_AllRacesWarning=Do you really want to import wind into all races?\nOnly races with applicable time window will be affected anyway. +windImport_ResultHeader=Imported wind from: {0} to: {1}. {2} races affected. +windImport_ResultEntry=Race: {0} ({1}) - {2} fixes from: {3} to: {4} +windImport_ResultError=Error: {0} 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 89e96dfd4f1..50e0f984d0d 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 @@ -740,3 +740,11 @@ polarDiagramButton=Polardiagramm timeSinceLastPositionFix=Letztes GPS timeSinceLastPositionFixTooltip=Zeit seit der letzten empfangenen GPS-Position trackingQuality=Tracking-Qualität +windImport_Upload=Abschicken +windImport_Title=Expedition Wind Import +windImport_BoatId=Boot Id: +windImport_SelectedRacesWarning=Möchten Sie den Wind für die {0} ausgewählten Rennen importieren?\nEs sind nur solche Rennen betroffen, deren Zeitraum mit der Windmessung überlappt. +windImport_AllRacesWarning=Möchten Sie den Wind für sämtliche Rennen importieren?\nEs sind nur solche Rennen betroffen, deren Zeitraum mit der Windmessung überlappt. +windImport_ResultHeader=Wind wurde importiert von: {0} bis: {1}. {2} Rennen betroffen. +windImport_ResultEntry=Rennen: {0} ({1}) - {2} Messwerte von: {3} bis: {4} +windImport_ResultError=Fehler: {0} From 9ea8066d3f37ab7ed758f7d910fc44e36c411d5d Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Wed, 16 Oct 2013 15:38:48 +0200 Subject: [PATCH 11/14] Fixed erronous occurence of SafeHTML in StringMessages --- .../java/com/sap/sailing/gwt/ui/client/StringMessages.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 836398a715e..471515680b6 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 @@ -1,7 +1,6 @@ package com.sap.sailing.gwt.ui.client; import com.google.gwt.i18n.client.Messages; -import com.google.gwt.safehtml.shared.SafeHtml; public interface StringMessages extends Messages { String helloWorld(); @@ -751,8 +750,8 @@ public interface StringMessages extends Messages { String speedOverGroundFiveSecondsBeforeStart(); String distanceToLineFiveSecondsBeforeStartTooltip(); String speedOverGroundFiveSecondsBeforeStartTooltip(); - SafeHtml windImport_Upload(); - SafeHtml windImport_Title(); + String windImport_Upload(); + String windImport_Title(); String windImport_BoatId(); String windImport_SelectedRacesWarning(int selectedRacesCount); String windImport_AllRacesWarning(); From e2a6c5193f1e66b868f88b0b3343c5f037c03830 Mon Sep 17 00:00:00 2001 From: Jens Rommel Date: Wed, 16 Oct 2013 16:11:31 +0200 Subject: [PATCH 12/14] Re-converted tabs to space. --- .../gwt/ui/adminconsole/WindPanel.java | 883 +++++++++--------- .../sailing/gwt/ui/client/StringMessages.java | 16 +- 2 files changed, 451 insertions(+), 448 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index 6d2953e88af..3024b34f672 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -74,505 +74,508 @@ import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO; * */ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower, RaceSelectionChangeListener { - - private static final String EXPEDITON_IMPORT_PARAMETER_RACES = "races"; - private static final String EXPEDITON_IMPORT_PARAMETER_BOAT_ID = "boatId"; + private static final String EXPEDITON_IMPORT_PARAMETER_RACES = "races"; - private static final String URL_SAILINGSERVER_EXPEDITION_IMPORT = "/../../sailingserver/expedition-import"; - - private final SailingServiceAsync sailingService; - private final ErrorReporter errorReporter; - private final StringMessages stringMessages; - private final IdentityColumn removeColumn; - private final TextColumn timeColumn; - private final TextColumn speedInKnotsColumn; - private final TextColumn windDirectionInDegColumn; - private final TextColumn positionColumn; - private final TrackedRacesListComposite trackedRacesListComposite; - private final RaceSelectionProvider raceSelectionProvider; - private final WindSourcesToExcludeSelectorPanel windSourcesToExcludeSelectorPanel; - private final CheckBox raceIsKnownToStartUpwindBox; - private final CaptionPanel windCaptionPanel; - private final VerticalPanel windFixesDisplayPanel; - private final Label windSourceLabel; - private final ListDataProvider rawWindFixesDataProvider; - private final CellTable rawWindFixesTable; - private final VerticalPanel windFixPanel; + private static final String EXPEDITON_IMPORT_PARAMETER_BOAT_ID = "boatId"; - public WindPanel(final SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, RegattaRefresher regattaRefresher, final StringMessages stringMessages) { - this.sailingService = sailingService; - this.errorReporter = errorReporter; - this.stringMessages = stringMessages; - this.raceSelectionProvider = new RaceSelectionModel(); - windSourcesToExcludeSelectorPanel = new WindSourcesToExcludeSelectorPanel(sailingService, stringMessages, errorReporter); + private static final String URL_SAILINGSERVER_EXPEDITION_IMPORT = "/../../sailingserver/expedition-import"; - VerticalPanel mainPanel = new VerticalPanel(); - mainPanel.setSize("100%", "100%"); - this.setWidget(mainPanel); + private final SailingServiceAsync sailingService; + private final ErrorReporter errorReporter; + private final StringMessages stringMessages; + private final IdentityColumn removeColumn; + private final TextColumn timeColumn; + private final TextColumn speedInKnotsColumn; + private final TextColumn windDirectionInDegColumn; + private final TextColumn positionColumn; + private final TrackedRacesListComposite trackedRacesListComposite; + private final RaceSelectionProvider raceSelectionProvider; + private final WindSourcesToExcludeSelectorPanel windSourcesToExcludeSelectorPanel; + private final CheckBox raceIsKnownToStartUpwindBox; + private final CaptionPanel windCaptionPanel; + private final VerticalPanel windFixesDisplayPanel; + private final Label windSourceLabel; + private final ListDataProvider rawWindFixesDataProvider; + private final CellTable rawWindFixesTable; + private final VerticalPanel windFixPanel; - trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages, /*multiselection*/true); - mainPanel.add(trackedRacesListComposite); - raceSelectionProvider.addRaceSelectionChangeListener(this); + public WindPanel(final SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, + ErrorReporter errorReporter, RegattaRefresher regattaRefresher, final StringMessages stringMessages) { + this.sailingService = sailingService; + this.errorReporter = errorReporter; + this.stringMessages = stringMessages; + this.raceSelectionProvider = new RaceSelectionModel(); + windSourcesToExcludeSelectorPanel = new WindSourcesToExcludeSelectorPanel(sailingService, stringMessages, errorReporter); - windCaptionPanel = new CaptionPanel(stringMessages.wind()); - windCaptionPanel.setVisible(false); - mainPanel.add(windCaptionPanel); + VerticalPanel mainPanel = new VerticalPanel(); + mainPanel.setSize("100%", "100%"); + this.setWidget(mainPanel); - TabPanel tabPanel = new TabPanel(); - tabPanel.setAnimationEnabled(true); - windCaptionPanel.add(tabPanel); - tabPanel.setSize("95%", "95%"); + trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher, + raceSelectionProvider, stringMessages, /*multiselection*/true); + mainPanel.add(trackedRacesListComposite); + raceSelectionProvider.addRaceSelectionChangeListener(this); - windFixesDisplayPanel = new VerticalPanel(); - Button addWindFixButton = new Button(stringMessages.actionAddWindData() + "..."); - addWindFixButton.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - RegattaAndRaceIdentifier selectedRace = getSelectedRace(); - if (selectedRace != null) { - final RaceDTO race = trackedRacesListComposite.getRaceByIdentifier(selectedRace); - sailingService.getCoursePositions(selectedRace, race.trackedRace.startOfTracking, new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - showWindSettingDialog(race, null); - } + windCaptionPanel = new CaptionPanel(stringMessages.wind()); + windCaptionPanel.setVisible(false); + mainPanel.add(windCaptionPanel); - @Override - public void onSuccess(final CoursePositionsDTO result) { - showWindSettingDialog(race, result); - } - }); - } - } - }); - windFixesDisplayPanel.add(addWindFixButton); + TabPanel tabPanel = new TabPanel(); + tabPanel.setAnimationEnabled(true); + windCaptionPanel.add(tabPanel); + tabPanel.setSize("95%", "95%"); - final VerticalPanel windSourcesPanel = new VerticalPanel(); - windSourcesPanel.setSpacing(10); - tabPanel.add(windSourcesPanel, stringMessages.windSourcesUsed()); - tabPanel.add(windFixesDisplayPanel, "Wind fixes"); - tabPanel.selectTab(0); + windFixesDisplayPanel = new VerticalPanel(); + Button addWindFixButton = new Button(stringMessages.actionAddWindData() + "..."); + addWindFixButton.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + RegattaAndRaceIdentifier selectedRace = getSelectedRace(); + if (selectedRace != null) { + final RaceDTO race = trackedRacesListComposite.getRaceByIdentifier(selectedRace); + sailingService.getCoursePositions(selectedRace, race.trackedRace.startOfTracking, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + showWindSettingDialog(race, null); + } - raceIsKnownToStartUpwindBox = new CheckBox(stringMessages.raceIsKnownToStartUpwind()); - windSourcesPanel.add(raceIsKnownToStartUpwindBox); + @Override + public void onSuccess(final CoursePositionsDTO result) { + showWindSettingDialog(race, result); + } + }); + } + } + }); + windFixesDisplayPanel.add(addWindFixButton); - windSourcesPanel.add(windSourcesToExcludeSelectorPanel); - raceIsKnownToStartUpwindBox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - setRaceIsKnownToStartUpwind(); - } - }); + final VerticalPanel windSourcesPanel = new VerticalPanel(); + windSourcesPanel.setSpacing(10); + tabPanel.add(windSourcesPanel, stringMessages.windSourcesUsed()); + tabPanel.add(windFixesDisplayPanel, "Wind fixes"); + tabPanel.selectTab(0); - windFixPanel = new VerticalPanel(); - windSourcesPanel.add(windFixPanel); + raceIsKnownToStartUpwindBox = new CheckBox(stringMessages.raceIsKnownToStartUpwind()); + windSourcesPanel.add(raceIsKnownToStartUpwindBox); - windSourceLabel = new Label(); - windFixesDisplayPanel.add(windSourceLabel); + windSourcesPanel.add(windSourcesToExcludeSelectorPanel); + raceIsKnownToStartUpwindBox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + setRaceIsKnownToStartUpwind(); + } + }); - // table for the raw wind fixes - removeColumn = new IdentityColumn(new ActionCell(stringMessages.remove(), new Delegate() { - @Override - public void execute(final WindDTO wind) { - List selectedRaces = raceSelectionProvider.getSelectedRaces(); - final RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(selectedRaces.size() - 1); - sailingService.removeWind(raceIdentifier, wind, new AsyncCallback() { - @Override - public void onSuccess(Void result) { - // remove row from underlying list: - rawWindFixesDataProvider.getList().remove(wind); - } + windFixPanel = new VerticalPanel(); + windSourcesPanel.add(windFixPanel); - @Override - public void onFailure(Throwable caught) { - WindPanel.this.errorReporter.reportError(WindPanel.this.stringMessages.errorSettingWindForRace() + " " + raceIdentifier + ": " + caught.getMessage()); - } - }); - } - })); - timeColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - return DateAndTimeFormatterUtil.formatDateAndTime(new Date(object.measureTimepoint)); - } - }; - speedInKnotsColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - return "" + object.trueWindSpeedInKnots; - } - }; - windDirectionInDegColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - return "" + object.trueWindFromDeg; - } - }; - positionColumn = new TextColumn() { - @Override - public String getValue(WindDTO object) { - String result = ""; - if (object.position != null) { - result = "Lat: " + object.position.latDeg + ", Lon: " + object.position.lngDeg; - } - return result; - } - }; - timeColumn.setSortable(true); - speedInKnotsColumn.setSortable(true); - windDirectionInDegColumn.setSortable(true); + windSourceLabel = new Label(); + windFixesDisplayPanel.add(windSourceLabel); - AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); - rawWindFixesTable = new CellTable(/* pageSize */10000, tableRes); - rawWindFixesTable.addColumn(timeColumn, "Time"); - rawWindFixesTable.addColumn(speedInKnotsColumn, "Speed (kn)"); - rawWindFixesTable.addColumn(windDirectionInDegColumn, "From (deg)"); - rawWindFixesTable.addColumn(positionColumn, "Position"); - rawWindFixesTable.addColumn(removeColumn, stringMessages.actions()); - rawWindFixesDataProvider = new ListDataProvider(); - rawWindFixesDataProvider.addDataDisplay(rawWindFixesTable); - Handler columnSortHandler = getWindTableColumnSortHandler(rawWindFixesDataProvider.getList(), timeColumn, speedInKnotsColumn, windDirectionInDegColumn); - rawWindFixesTable.addColumnSortHandler(columnSortHandler); - rawWindFixesTable.getColumnSortList().push(timeColumn); - windFixesDisplayPanel.add(rawWindFixesTable); + // table for the raw wind fixes + removeColumn = new IdentityColumn(new ActionCell(stringMessages.remove(), new Delegate() { + @Override + public void execute(final WindDTO wind) { + List selectedRaces = raceSelectionProvider.getSelectedRaces(); + final RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(selectedRaces.size() - 1); + sailingService.removeWind(raceIdentifier, wind, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + // remove row from underlying list: + rawWindFixesDataProvider.getList().remove(wind); + } - createWindImportPanel(mainPanel, stringMessages); + @Override + public void onFailure(Throwable caught) { + WindPanel.this.errorReporter.reportError(WindPanel.this.stringMessages.errorSettingWindForRace() + + " " + raceIdentifier + ": " + caught.getMessage()); + } + }); + } + })); + timeColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + return DateAndTimeFormatterUtil.formatDateAndTime(new Date(object.measureTimepoint)); + } + }; + speedInKnotsColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + return "" + object.trueWindSpeedInKnots; + } + }; + windDirectionInDegColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + return "" + object.trueWindFromDeg; + } + }; + positionColumn = new TextColumn() { + @Override + public String getValue(WindDTO object) { + String result = ""; + if (object.position != null) { + result = "Lat: " + object.position.latDeg + ", Lon: " + object.position.lngDeg; + } + return result; + } + }; + timeColumn.setSortable(true); + speedInKnotsColumn.setSortable(true); + windDirectionInDegColumn.setSortable(true); - } + AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class); + rawWindFixesTable = new CellTable(/* pageSize */10000, tableRes); + rawWindFixesTable.addColumn(timeColumn, "Time"); + rawWindFixesTable.addColumn(speedInKnotsColumn, "Speed (kn)"); + rawWindFixesTable.addColumn(windDirectionInDegColumn, "From (deg)"); + rawWindFixesTable.addColumn(positionColumn, "Position"); + rawWindFixesTable.addColumn(removeColumn, stringMessages.actions()); + rawWindFixesDataProvider = new ListDataProvider(); + rawWindFixesDataProvider.addDataDisplay(rawWindFixesTable); + Handler columnSortHandler = getWindTableColumnSortHandler(rawWindFixesDataProvider.getList(), timeColumn, speedInKnotsColumn, windDirectionInDegColumn); + rawWindFixesTable.addColumnSortHandler(columnSortHandler); + rawWindFixesTable.getColumnSortList().push(timeColumn); + windFixesDisplayPanel.add(rawWindFixesTable); - private void createWindImportPanel(Panel mainPanel, final StringMessages stringMessages) { - - /* - * To style the "browse" button of the file upload widget - * see http://www.shauninman.com/archive/2007/09/10/styling_file_inputs_with_css_and_the_dom - */ - - CaptionPanel windImportRootPanel = new CaptionPanel(stringMessages.windImport_Title()); - mainPanel.add(windImportRootPanel); - VerticalPanel windImportContentPanel = new VerticalPanel(); - windImportRootPanel.add(windImportContentPanel); - final FormPanel form = new FormPanel(); - windImportContentPanel.add(form); - VerticalPanel formContentPanel = new VerticalPanel(); - form.add(formContentPanel); - HorizontalPanel inputPanel = new HorizontalPanel(); - formContentPanel.add(inputPanel); - final Panel importResultPanel = new VerticalPanel(); - windImportContentPanel.add(importResultPanel); + createWindImportPanel(mainPanel, stringMessages); - - form.setMethod(FormPanel.METHOD_POST); - form.setEncoding(FormPanel.ENCODING_MULTIPART); - form.setAction(GWT.getHostPageBaseURL() + URL_SAILINGSERVER_EXPEDITION_IMPORT); + } + private void createWindImportPanel(Panel mainPanel, final StringMessages stringMessages) { - final TextBox boatIdTextBox = new TextBox(); - boatIdTextBox.setName(EXPEDITON_IMPORT_PARAMETER_BOAT_ID); - final Button submitButton = new Button(stringMessages.windImport_Upload(), new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - importResultPanel.clear(); - form.submit(); - } - }); - submitButton.setEnabled(false); + /* + * To style the "browse" button of the file upload widget + * see http://www.shauninman.com/archive/2007/09/10/styling_file_inputs_with_css_and_the_dom + */ - final FileUpload fileUpload = new FileUpload(); - fileUpload.setName("upload"); - fileUpload.addChangeHandler(new ChangeHandler() { + CaptionPanel windImportRootPanel = new CaptionPanel(stringMessages.windImport_Title()); + mainPanel.add(windImportRootPanel); + VerticalPanel windImportContentPanel = new VerticalPanel(); + windImportRootPanel.add(windImportContentPanel); + final FormPanel form = new FormPanel(); + windImportContentPanel.add(form); + VerticalPanel formContentPanel = new VerticalPanel(); + form.add(formContentPanel); + HorizontalPanel inputPanel = new HorizontalPanel(); + formContentPanel.add(inputPanel); + final Panel importResultPanel = new VerticalPanel(); + windImportContentPanel.add(importResultPanel); - @Override - public void onChange(ChangeEvent event) { - importResultPanel.clear(); - String fileName = fileUpload.getFilename(); - boolean isValidFileName = (fileName != null) && (fileUpload.getFilename().trim().length() > 0); - submitButton.setEnabled(isValidFileName); + form.setMethod(FormPanel.METHOD_POST); + form.setEncoding(FormPanel.ENCODING_MULTIPART); + form.setAction(GWT.getHostPageBaseURL() + URL_SAILINGSERVER_EXPEDITION_IMPORT); - String boatId = ""; - if (isValidFileName) { - RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches typical expedition log file names like "2013Jun26_0.csv" where 0 as group[1] indicates the boat id. - MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); - if (match.getGroupCount() > 0) { - boatId = match.getGroup(1); - } - } - boatIdTextBox.setText(boatId); - - } - }); + final TextBox boatIdTextBox = new TextBox(); + boatIdTextBox.setName(EXPEDITON_IMPORT_PARAMETER_BOAT_ID); + final Button submitButton = new Button(stringMessages.windImport_Upload(), new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + importResultPanel.clear(); + form.submit(); + } + }); + submitButton.setEnabled(false); - inputPanel.add(fileUpload); + final FileUpload fileUpload = new FileUpload(); + fileUpload.setName("upload"); + fileUpload.addChangeHandler(new ChangeHandler() { - Label boatIdLabel = new Label(stringMessages.windImport_BoatId()); - inputPanel.add(boatIdLabel); - inputPanel.add(boatIdTextBox); - boatIdLabel.setWordWrap(false); - inputPanel.setSpacing(5); - inputPanel.setCellVerticalAlignment(fileUpload, HasVerticalAlignment.ALIGN_MIDDLE); - inputPanel.setCellVerticalAlignment(boatIdLabel, HasVerticalAlignment.ALIGN_MIDDLE); - inputPanel.setCellVerticalAlignment(boatIdTextBox, HasVerticalAlignment.ALIGN_MIDDLE); + @Override + public void onChange(ChangeEvent event) { + importResultPanel.clear(); + String fileName = fileUpload.getFilename(); + boolean isValidFileName = (fileName != null) && (fileUpload.getFilename().trim().length() > 0); + submitButton.setEnabled(isValidFileName); - final Hidden hiddenRacesField = new Hidden(EXPEDITON_IMPORT_PARAMETER_RACES); - inputPanel.add(hiddenRacesField); - - formContentPanel.add(submitButton); + String boatId = ""; + if (isValidFileName) { + RegExp EXPEDITION_EXPORT_FILE_PATTERN = RegExp.compile("^.*_([0-9]+)\\.csv"); //matches typical expedition log file names like "2013Jun26_0.csv" where 0 as group[1] indicates the boat id. + MatchResult match = EXPEDITION_EXPORT_FILE_PATTERN.exec(fileName); + if (match.getGroupCount() > 0) { + boatId = match.getGroup(1); + } + } + boatIdTextBox.setText(boatId); - form.addSubmitHandler(new FormPanel.SubmitHandler() { - public void onSubmit(SubmitEvent event) { - if ((fileUpload.getFilename() != null) && (fileUpload.getFilename().trim().length() > 0)) { - List selectedRaces = raceSelectionProvider.getSelectedRaces(); - String warningMessage; - if (selectedRaces.size() > 0) { - warningMessage = stringMessages.windImport_SelectedRacesWarning(selectedRaces.size()); - RaceSelection raceSelection = RaceSelection.create(); - for (int i = 0; i < selectedRaces.size(); i++) { - RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(i); - RaceSelection.RaceEntry raceEntry = RaceSelection.RaceEntry.create(); - raceEntry.setRaceName(raceIdentifier.getRaceName()); - raceEntry.setRegatteName(raceIdentifier.getRegattaName()); - raceSelection.addRace(raceEntry); - } - hiddenRacesField.setValue(raceSelection.toJson()); - } else { - warningMessage = stringMessages.windImport_AllRacesWarning(); - hiddenRacesField.setValue(null); - } - if (!Window.confirm(warningMessage)) { - event.cancel(); - } - } else { - event.cancel(); - } - } - }); - form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() { - public void onSubmitComplete(SubmitCompleteEvent event) { - importResultPanel.clear(); - String windImportResultJson = event.getResults(); + } + }); - WindImportResult windImportResult = WindImportResult.fromJson(windImportResultJson); - JsArray raceEntries = windImportResult.getRaceEntries(); - Label resultHeader = new Label(stringMessages.windImport_ResultHeader(String.valueOf(windImportResult.getFirst()), String.valueOf(windImportResult.getLast()), raceEntries.length())); - importResultPanel.add(resultHeader); - if (windImportResult.getError() != null) { - Label errorText = new Label(stringMessages.windImport_ResultError(windImportResult.getError())); - importResultPanel.add(errorText); - } - for (int i = 0; i < raceEntries.length(); i++) { - RaceEntry raceEntry = raceEntries.get(i); - Label entryText = new Label(stringMessages.windImport_ResultEntry(raceEntry.getRaceName(), raceEntry.getRegattaName(), raceEntry.getCount(), String.valueOf(raceEntry.getFirst()), String.valueOf(raceEntry.getLast()))); - importResultPanel.add(entryText); - } - } - }); + inputPanel.add(fileUpload); - } + Label boatIdLabel = new Label(stringMessages.windImport_BoatId()); + inputPanel.add(boatIdLabel); + inputPanel.add(boatIdTextBox); + boatIdLabel.setWordWrap(false); + inputPanel.setSpacing(5); + inputPanel.setCellVerticalAlignment(fileUpload, HasVerticalAlignment.ALIGN_MIDDLE); + inputPanel.setCellVerticalAlignment(boatIdLabel, HasVerticalAlignment.ALIGN_MIDDLE); + inputPanel.setCellVerticalAlignment(boatIdTextBox, HasVerticalAlignment.ALIGN_MIDDLE); - private void showWindSettingDialog(RaceDTO race, CoursePositionsDTO course) { - AddWindFixDialog windSettingDialog = new AddWindFixDialog(race, course, stringMessages, new DialogCallback() { - @Override - public void cancel() { - } + final Hidden hiddenRacesField = new Hidden(EXPEDITON_IMPORT_PARAMETER_RACES); + inputPanel.add(hiddenRacesField); - @Override - public void ok(final WindDTO result) { - addWindFix(result); - } - }); - windSettingDialog.show(); - } + formContentPanel.add(submitButton); - private void addWindFix(final WindDTO wind) { - final RegattaAndRaceIdentifier raceIdentifier = getSelectedRace(); - sailingService.setWind(raceIdentifier, wind, new AsyncCallback() { - @Override - public void onSuccess(Void result) { - showWind(raceIdentifier); - } + form.addSubmitHandler(new FormPanel.SubmitHandler() { + public void onSubmit(SubmitEvent event) { + if ((fileUpload.getFilename() != null) && (fileUpload.getFilename().trim().length() > 0)) { + List selectedRaces = raceSelectionProvider.getSelectedRaces(); + String warningMessage; + if (selectedRaces.size() > 0) { + warningMessage = stringMessages.windImport_SelectedRacesWarning(selectedRaces.size()); + RaceSelection raceSelection = RaceSelection.create(); + for (int i = 0; i < selectedRaces.size(); i++) { + RegattaAndRaceIdentifier raceIdentifier = selectedRaces.get(i); + RaceSelection.RaceEntry raceEntry = RaceSelection.RaceEntry.create(); + raceEntry.setRaceName(raceIdentifier.getRaceName()); + raceEntry.setRegatteName(raceIdentifier.getRegattaName()); + raceSelection.addRace(raceEntry); + } + hiddenRacesField.setValue(raceSelection.toJson()); + } else { + warningMessage = stringMessages.windImport_AllRacesWarning(); + hiddenRacesField.setValue(null); + } + if (!Window.confirm(warningMessage)) { + event.cancel(); + } + } else { + event.cancel(); + } + } + }); + form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() { + public void onSubmitComplete(SubmitCompleteEvent event) { + importResultPanel.clear(); + String windImportResultJson = event.getResults(); - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error adding a wind fix for race " + raceIdentifier + ": " + caught.getMessage()); - } - }); - } + WindImportResult windImportResult = WindImportResult.fromJson(windImportResultJson); + JsArray raceEntries = windImportResult.getRaceEntries(); + Label resultHeader = new Label(stringMessages.windImport_ResultHeader(String.valueOf(windImportResult.getFirst()), + String.valueOf(windImportResult.getLast()), raceEntries.length())); + importResultPanel.add(resultHeader); + if (windImportResult.getError() != null) { + Label errorText = new Label(stringMessages.windImport_ResultError(windImportResult.getError())); + importResultPanel.add(errorText); + } + for (int i = 0; i < raceEntries.length(); i++) { + RaceEntry raceEntry = raceEntries.get(i); + Label entryText = new Label(stringMessages.windImport_ResultEntry(raceEntry.getRaceName(), raceEntry.getRegattaName(), + raceEntry.getCount(), String.valueOf(raceEntry.getFirst()), String.valueOf(raceEntry.getLast()))); + importResultPanel.add(entryText); + } + } + }); - @Override - public void fillRegattas(List result) { - trackedRacesListComposite.fillRegattas(result); - } + } - @Override - public void showWind(final RegattaAndRaceIdentifier raceIdentifier) { - sailingService.getWindSourcesInfo(raceIdentifier, new AsyncCallback() { - @Override - public void onSuccess(final WindInfoForRaceDTO result) { - if (result != null) { - updateWindSourcesToExclude(result, raceIdentifier); - raceIsKnownToStartUpwindBox.setValue(result.raceIsKnownToStartUpwind); + private void showWindSettingDialog(RaceDTO race, CoursePositionsDTO course) { + AddWindFixDialog windSettingDialog = new AddWindFixDialog(race, course, stringMessages, new DialogCallback() { + @Override + public void cancel() { + } - // load the raw wind fixes - sailingService.getRawWindFixes(raceIdentifier, null, new AsyncCallback() { - @Override - public void onSuccess(WindInfoForRaceDTO result) { - if (result != null) { - udapteRawWindFixes(result); - } else { - // no wind fixes known for untracked race - clearWindSources(); - clearWindFixes(); - } - } + @Override + public void ok(final WindDTO result) { + addWindFix(result); + } + }); + windSettingDialog.show(); + } - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error reading wind fixes: " + caught.getMessage()); - } - }); + private void addWindFix(final WindDTO wind) { + final RegattaAndRaceIdentifier raceIdentifier = getSelectedRace(); + sailingService.setWind(raceIdentifier, wind, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + showWind(raceIdentifier); + } - } else { - // no wind sources known for untracked race - clearWindSources(); - clearWindFixes(); - } - } + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error adding a wind fix for race " + raceIdentifier + ": " + caught.getMessage()); + } + }); + } - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error reading wind source information: " + caught.getMessage()); - } - }); - } + @Override + public void fillRegattas(List result) { + trackedRacesListComposite.fillRegattas(result); + } - private void updateWindSourcesToExclude(WindInfoForRaceDTO result, RegattaAndRaceIdentifier raceIdentifier) { - windSourcesToExcludeSelectorPanel.update(raceIdentifier, result.windTrackInfoByWindSource.keySet(), result.windSourcesToExclude); - } + @Override + public void showWind(final RegattaAndRaceIdentifier raceIdentifier) { + sailingService.getWindSourcesInfo(raceIdentifier, new AsyncCallback() { + @Override + public void onSuccess(final WindInfoForRaceDTO result) { + if (result != null) { + updateWindSourcesToExclude(result, raceIdentifier); + raceIsKnownToStartUpwindBox.setValue(result.raceIsKnownToStartUpwind); - private void udapteRawWindFixes(WindInfoForRaceDTO result) { - for (Map.Entry e : result.windTrackInfoByWindSource.entrySet()) { - if (e.getKey().getType() == WindSourceType.WEB || e.getKey().getType() == WindSourceType.EXPEDITION) { - windSourceLabel.setText(stringMessages.windSource() + ": " + e.getKey()); - if (e.getKey().getType() == WindSourceType.WEB) { - // only the WEB wind source is editable, hence has a "Remove" column - // rawWindFixesTable.removeColumn(col); - } - rawWindFixesDataProvider.getList().clear(); - rawWindFixesDataProvider.getList().addAll(e.getValue().windFixes); - } - } - } + // load the raw wind fixes + sailingService.getRawWindFixes(raceIdentifier, null, new AsyncCallback() { + @Override + public void onSuccess(WindInfoForRaceDTO result) { + if (result != null) { + udapteRawWindFixes(result); + } else { + // no wind fixes known for untracked race + clearWindSources(); + clearWindFixes(); + } + } - private Handler getWindTableColumnSortHandler(List list, TextColumn timeColumn, TextColumn speedInKnotsColumn, TextColumn windDirectionInDegColumn) { - ListHandler result = new ListHandler(list); - result.setComparator(timeColumn, new Comparator() { - @Override - public int compare(WindDTO o1, WindDTO o2) { - return o1.measureTimepoint < o2.measureTimepoint ? -1 : o1.measureTimepoint == o2.measureTimepoint ? 0 : 1; - } - }); - result.setComparator(speedInKnotsColumn, new Comparator() { - @Override - public int compare(WindDTO o1, WindDTO o2) { - return o1.trueWindSpeedInKnots < o2.trueWindSpeedInKnots ? -1 : o1.trueWindSpeedInKnots == o2.trueWindSpeedInKnots ? 0 : 1; - } - }); - result.setComparator(windDirectionInDegColumn, new Comparator() { - @Override - public int compare(WindDTO o1, WindDTO o2) { - return o1.trueWindFromDeg < o2.trueWindFromDeg ? -1 : o1.trueWindFromDeg == o2.trueWindFromDeg ? 0 : 1; - } - }); - return result; - } + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error reading wind fixes: " + caught.getMessage()); + } + }); - private void setRaceIsKnownToStartUpwind() { - final RegattaAndRaceIdentifier selectedRace = getSelectedRace(); - sailingService.setRaceIsKnownToStartUpwind(selectedRace, raceIsKnownToStartUpwindBox.getValue(), new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError(WindPanel.this.stringMessages.errorWhileTryingToSetWindSourceForRace() + " " + selectedRace + ": " + caught.getMessage()); - } + } else { + // no wind sources known for untracked race + clearWindSources(); + clearWindFixes(); + } + } - @Override - public void onSuccess(Void result) { - } - }); - } + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error reading wind source information: " + caught.getMessage()); + } + }); + } - private RegattaAndRaceIdentifier getSelectedRace() { - List selectedRaces = raceSelectionProvider.getSelectedRaces(); - if (selectedRaces.isEmpty() || selectedRaces.size() > 1) { - return null; - } + private void updateWindSourcesToExclude(WindInfoForRaceDTO result, RegattaAndRaceIdentifier raceIdentifier) { + windSourcesToExcludeSelectorPanel.update(raceIdentifier, result.windTrackInfoByWindSource.keySet(), result.windSourcesToExclude); + } - return selectedRaces.get(0); - } + private void udapteRawWindFixes(WindInfoForRaceDTO result) { + for (Map.Entry e : result.windTrackInfoByWindSource.entrySet()) { + if (e.getKey().getType() == WindSourceType.WEB || e.getKey().getType() == WindSourceType.EXPEDITION) { + windSourceLabel.setText(stringMessages.windSource() + ": " + e.getKey()); + if (e.getKey().getType() == WindSourceType.WEB) { + // only the WEB wind source is editable, hence has a "Remove" column + // rawWindFixesTable.removeColumn(col); + } + rawWindFixesDataProvider.getList().clear(); + rawWindFixesDataProvider.getList().addAll(e.getValue().windFixes); + } + } + } - private void clearWindSources() { - final Set emptySet = Collections.emptySet(); - windSourcesToExcludeSelectorPanel.update(null, emptySet, emptySet); - } + private Handler getWindTableColumnSortHandler(List list, TextColumn timeColumn, TextColumn speedInKnotsColumn, TextColumn windDirectionInDegColumn) { + ListHandler result = new ListHandler(list); + result.setComparator(timeColumn, new Comparator() { + @Override + public int compare(WindDTO o1, WindDTO o2) { + return o1.measureTimepoint < o2.measureTimepoint ? -1 : o1.measureTimepoint == o2.measureTimepoint ? 0 : 1; + } + }); + result.setComparator(speedInKnotsColumn, new Comparator() { + @Override + public int compare(WindDTO o1, WindDTO o2) { + return o1.trueWindSpeedInKnots < o2.trueWindSpeedInKnots ? -1 : o1.trueWindSpeedInKnots == o2.trueWindSpeedInKnots ? 0 : 1; + } + }); + result.setComparator(windDirectionInDegColumn, new Comparator() { + @Override + public int compare(WindDTO o1, WindDTO o2) { + return o1.trueWindFromDeg < o2.trueWindFromDeg ? -1 : o1.trueWindFromDeg == o2.trueWindFromDeg ? 0 : 1; + } + }); + return result; + } - private void clearWindFixes() { - } + private void setRaceIsKnownToStartUpwind() { + final RegattaAndRaceIdentifier selectedRace = getSelectedRace(); + sailingService.setRaceIsKnownToStartUpwind(selectedRace, raceIsKnownToStartUpwindBox.getValue(), new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError(WindPanel.this.stringMessages.errorWhileTryingToSetWindSourceForRace() + " " + selectedRace + ": " + caught.getMessage()); + } - private void showWindFixesList(RegattaAndRaceIdentifier selectedRace, RaceDTO raceDTO) { - List windSourceTypeNames = new ArrayList(); - windSourceTypeNames.add(WindSourceType.COMBINED.name()); - sailingService.getAveragedWindInfo(selectedRace, raceDTO.startOfRace, 30000L, 100, windSourceTypeNames, new AsyncCallback() { + @Override + public void onSuccess(Void result) { + } + }); + } - @Override - public void onFailure(Throwable caught) { - } + private RegattaAndRaceIdentifier getSelectedRace() { + List selectedRaces = raceSelectionProvider.getSelectedRaces(); + if (selectedRaces.isEmpty() || selectedRaces.size() > 1) { + return null; + } - @Override - public void onSuccess(WindInfoForRaceDTO result) { - windFixPanel.clear(); + return selectedRaces.get(0); + } - for (WindSourceType input : new WindSourceType[] { WindSourceType.COMBINED }) { - windFixPanel.add(new HTML(" ")); - windFixPanel.add(new Label(stringMessages.windFixListingDescription() + " " + input.name())); - WindTrackInfoDTO windTrackInfo = result.windTrackInfoByWindSource.get(new WindSourceImpl(input)); - if (windTrackInfo != null && windTrackInfo.windFixes.size() >= 7) { - NumberFormat formatter = NumberFormat.getFormat(".##"); - for (WindDTO windFix : windTrackInfo.windFixes.subList(0, 3)) { - windFixPanel.add(new Label("" + formatter.format(windFix.trueWindFromDeg) + " (deg) " + formatter.format(windFix.trueWindSpeedInKnots) + " (kt) " + formatter.format(windFix.position.latDeg) + " (lat) " + formatter.format(windFix.position.lngDeg) + " (lng) " + new Date(windFix.measureTimepoint))); - } - // These fixes must not necessarily be the real last ones. This especially holds for long races. - for (WindDTO windFix : windTrackInfo.windFixes.subList(windTrackInfo.windFixes.size() - 4, windTrackInfo.windFixes.size() - 1)) { - windFixPanel.add(new Label("" + formatter.format(windFix.trueWindFromDeg) + " (deg) " + formatter.format(windFix.trueWindSpeedInKnots) + " (kt) " + formatter.format(windFix.position.latDeg) + " (lat) " + formatter.format(windFix.position.lngDeg) + " (lng) " + new Date(windFix.measureTimepoint))); - } - } else { - windFixPanel.add(new Label(stringMessages.noWindFixesAvailable())); - } - } - } - }); - } + private void clearWindSources() { + final Set emptySet = Collections.emptySet(); + windSourcesToExcludeSelectorPanel.update(null, emptySet, emptySet); + } - private void updateWindDisplay() { - RegattaAndRaceIdentifier selectedRace = getSelectedRace(); - RaceDTO raceDTO = selectedRace != null ? trackedRacesListComposite.getRaceByIdentifier(selectedRace) : null; + private void clearWindFixes() { + } - if (selectedRace != null && raceDTO != null && raceDTO.trackedRace != null) { - windCaptionPanel.setVisible(true); - windCaptionPanel.setCaptionText(stringMessages.wind() + ": " + selectedRace.getRaceName()); + private void showWindFixesList(RegattaAndRaceIdentifier selectedRace, RaceDTO raceDTO) { + List windSourceTypeNames = new ArrayList(); + windSourceTypeNames.add(WindSourceType.COMBINED.name()); + sailingService.getAveragedWindInfo(selectedRace, raceDTO.startOfRace, 30000L, 100, windSourceTypeNames, new AsyncCallback() { - showWind(selectedRace); - showWindFixesList(selectedRace, raceDTO); - } else { - windCaptionPanel.setVisible(false); - windCaptionPanel.setCaptionText(stringMessages.wind()); + @Override + public void onFailure(Throwable caught) { + } - clearWindSources(); - clearWindFixes(); - } + @Override + public void onSuccess(WindInfoForRaceDTO result) { + windFixPanel.clear(); - } + for (WindSourceType input : new WindSourceType[] { WindSourceType.COMBINED }) { + windFixPanel.add(new HTML(" ")); + windFixPanel.add(new Label(stringMessages.windFixListingDescription() + " " + input.name())); + WindTrackInfoDTO windTrackInfo = result.windTrackInfoByWindSource.get(new WindSourceImpl(input)); + if (windTrackInfo != null && windTrackInfo.windFixes.size() >= 7) { + NumberFormat formatter = NumberFormat.getFormat(".##"); + for (WindDTO windFix : windTrackInfo.windFixes.subList(0, 3)) { + windFixPanel.add(new Label("" + formatter.format(windFix.trueWindFromDeg) + " (deg) " + formatter.format(windFix.trueWindSpeedInKnots) + " (kt) " + formatter.format(windFix.position.latDeg) + " (lat) " + formatter.format(windFix.position.lngDeg) + " (lng) " + new Date(windFix.measureTimepoint))); + } + // These fixes must not necessarily be the real last ones. This especially holds for long races. + for (WindDTO windFix : windTrackInfo.windFixes.subList(windTrackInfo.windFixes.size() - 4, windTrackInfo.windFixes.size() - 1)) { + windFixPanel.add(new Label("" + formatter.format(windFix.trueWindFromDeg) + " (deg) " + formatter.format(windFix.trueWindSpeedInKnots) + " (kt) " + formatter.format(windFix.position.latDeg) + " (lat) " + formatter.format(windFix.position.lngDeg) + " (lng) " + new Date(windFix.measureTimepoint))); + } + } else { + windFixPanel.add(new Label(stringMessages.noWindFixesAvailable())); + } + } + } + }); + } - @Override - public void onRaceSelectionChange(List selectedRaces) { - updateWindDisplay(); - } + private void updateWindDisplay() { + RegattaAndRaceIdentifier selectedRace = getSelectedRace(); + RaceDTO raceDTO = selectedRace != null ? trackedRacesListComposite.getRaceByIdentifier(selectedRace) : null; + + if (selectedRace != null && raceDTO != null && raceDTO.trackedRace != null) { + windCaptionPanel.setVisible(true); + windCaptionPanel.setCaptionText(stringMessages.wind() + ": " + selectedRace.getRaceName()); + + showWind(selectedRace); + showWindFixesList(selectedRace, raceDTO); + } else { + windCaptionPanel.setVisible(false); + windCaptionPanel.setCaptionText(stringMessages.wind()); + + clearWindSources(); + clearWindFixes(); + } + + } + + @Override + public void onRaceSelectionChange(List selectedRaces) { + updateWindDisplay(); + } } 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 471515680b6..5774fa9a959 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 @@ -750,12 +750,12 @@ public interface StringMessages extends Messages { String speedOverGroundFiveSecondsBeforeStart(); String distanceToLineFiveSecondsBeforeStartTooltip(); String speedOverGroundFiveSecondsBeforeStartTooltip(); - String windImport_Upload(); - String windImport_Title(); - String windImport_BoatId(); - String windImport_SelectedRacesWarning(int selectedRacesCount); - String windImport_AllRacesWarning(); - String windImport_ResultHeader(String firstWindDate, String lastWindDate, int affectedRacesCount); - String windImport_ResultError(String errorMessage); - String windImport_ResultEntry(String raceName, String regattaName, int windFixesCount, String firstWindDate, String lastWindDate); + String windImport_Upload(); + String windImport_Title(); + String windImport_BoatId(); + String windImport_SelectedRacesWarning(int selectedRacesCount); + String windImport_AllRacesWarning(); + String windImport_ResultHeader(String firstWindDate, String lastWindDate, int affectedRacesCount); + String windImport_ResultError(String errorMessage); + String windImport_ResultEntry(String raceName, String regattaName, int windFixesCount, String firstWindDate, String lastWindDate); } From fff87d28eddd8f25f7ef05b71b9a34d4c8838c54 Mon Sep 17 00:00:00 2001 From: Lennart Hensler Date: Wed, 16 Oct 2013 16:20:43 +0200 Subject: [PATCH 13/14] Added toolips to the wind chart settings dialog (resolving bug 1558) --- .../sailing/gwt/ui/client/StringMessages.java | 8 ++++++++ .../gwt/ui/client/StringMessages.properties | 8 ++++++++ .../gwt/ui/client/StringMessages_de.properties | 8 ++++++++ .../gwt/ui/client/WindSourceTypeFormatter.java | 18 ++++++++++++++++++ .../WindChartSettingsDialogComponent.java | 4 ++++ 5 files changed, 46 insertions(+) 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 0a81277140c..1a61390db7a 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 @@ -750,4 +750,12 @@ public interface StringMessages extends Messages { String speedOverGroundFiveSecondsBeforeStart(); String distanceToLineFiveSecondsBeforeStartTooltip(); String speedOverGroundFiveSecondsBeforeStartTooltip(); + String showWindSpeedSeriesTooltip(); + String showWindDirectionSeriesTooltip(); + String combinedWindSourceTypeTooltip(); + String courseBasedWindSourceTypeTooltip(); + String trackBasedEstimationWindSourceTypeTooltip(); + String expeditionWindSourceTypeTooltip(); + String webWindSourceTypeTooltip(); + String raceCommitteeWindSourceTypeTooltip(); } 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 6e45da7a4a1..a2e93e45d9d 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 @@ -744,3 +744,11 @@ polarDiagramButton=Polar Diagram timeSinceLastPositionFix=Last GPS timeSinceLastPositionFixTooltip=Time since last received GPS position trackingQuality=Tracking quality +showWindSpeedSeriesTooltip=Toggles the visibility of the wind speed for all data sources +showWindDirectionSeriesTooltip=Toggles the visibility of the wind direction for all data sources +combinedWindSourceTypeTooltip=Combines all other wind sources to one data set +courseBasedWindSourceTypeTooltip=A value based on the course layout (under the assumption, that the race starts with an upwind leg) +trackBasedEstimationWindSourceTypeTooltip=Calculated data from the competitor paths +expeditionWindSourceTypeTooltip=Data captured from mobile measuring systems +webWindSourceTypeTooltip=Manually setted data values +raceCommitteeWindSourceTypeTooltip=Data manually setted from the race officers \ 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 89e96dfd4f1..ada3bed6368 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 @@ -740,3 +740,11 @@ polarDiagramButton=Polardiagramm timeSinceLastPositionFix=Letztes GPS timeSinceLastPositionFixTooltip=Zeit seit der letzten empfangenen GPS-Position trackingQuality=Tracking-Qualität +showWindSpeedSeriesTooltip=Setzt die Sichtbarkeit der Windgeschwindigkeit aller Datenquellen +showWindDirectionSeriesTooltip=Setzt die Sichtbarkeit der Windrichtung aller Datenquellen +combinedWindSourceTypeTooltip=Kombiniert alle Datenquellen zu einem Datensatz +courseBasedWindSourceTypeTooltip=Ein Datenwert basierend auf dem Kurslayout (unter der Annahme, dass das Rennen gegen den Wind startet) +trackBasedEstimationWindSourceTypeTooltip=Berechnet die Daten aus dem gefahrenen Kurs der Teilnehmer +expeditionWindSourceTypeTooltip=Von mobilen Messanlagen erfasste Daten +webWindSourceTypeTooltip=Manuell gesetzte Datenwerte +raceCommitteeWindSourceTypeTooltip=Von den Rennaufsehern manuell gesetzte Daten \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/WindSourceTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/WindSourceTypeFormatter.java index ae47d16e59b..b04b73d98aa 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/WindSourceTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/WindSourceTypeFormatter.java @@ -22,6 +22,24 @@ public class WindSourceTypeFormatter { return null; } + public static String tooltipFor(WindSourceType windSourceType, StringMessages stringMessages) { + switch (windSourceType) { + case COMBINED: + return stringMessages.combinedWindSourceTypeTooltip(); + case COURSE_BASED: + return stringMessages.courseBasedWindSourceTypeTooltip(); + case TRACK_BASED_ESTIMATION: + return stringMessages.trackBasedEstimationWindSourceTypeTooltip(); + case EXPEDITION: + return stringMessages.expeditionWindSourceTypeTooltip(); + case WEB: + return stringMessages.webWindSourceTypeTooltip(); + case RACECOMMITTEE: + return stringMessages.raceCommitteeWindSourceTypeTooltip(); + } + return null; + } + public static String format(WindSource windSource, StringMessages stringMessages) { return format(windSource.getType(), stringMessages)+(windSource.getId() == null ? "" : (" "+windSource.getId().toString())); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChartSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChartSettingsDialogComponent.java index 2007b074f62..51b144494b8 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChartSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChartSettingsDialogComponent.java @@ -65,6 +65,7 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent grid.setWidget(0, 1, windSpeedSourcesPanel); showWindSpeedSeriesCheckbox = dialog.createCheckbox(stringMessages.showWindSpeedSeries()); + showWindSpeedSeriesCheckbox.setTitle(stringMessages.showWindSpeedSeriesTooltip()); showWindSpeedSeriesCheckbox.setValue(initialSettings.isShowWindSpeedSeries()); showWindSpeedSeriesCheckbox.addValueChangeHandler(new ValueChangeHandler() { @Override @@ -78,6 +79,7 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent windSpeedSourcesPanel.add(showWindSpeedSeriesCheckbox); showWindDirectionsSeriesCheckbox = dialog.createCheckbox(stringMessages.showWindDirectionSeries()); + showWindDirectionsSeriesCheckbox.setTitle(stringMessages.showWindDirectionSeriesTooltip()); showWindDirectionsSeriesCheckbox.setValue(initialSettings.isShowWindDirectionsSeries()); showWindDirectionsSeriesCheckbox.addValueChangeHandler(new ValueChangeHandler() { @Override @@ -92,6 +94,7 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent for (WindSourceType windSourceType : WindSourceType.values()) { CheckBox checkbox = dialog.createCheckbox(WindSourceTypeFormatter.format(windSourceType, stringMessages)); + checkbox.setTitle(WindSourceTypeFormatter.tooltipFor(windSourceType, stringMessages)); checkbox.setEnabled(initialSettings.isShowWindDirectionsSeries()); windDirectionsCheckboxes.put(windSourceType, checkbox); checkbox.setValue(initialSettings.getWindDirectionSourcesToDisplay().contains(windSourceType)); @@ -101,6 +104,7 @@ public class WindChartSettingsDialogComponent implements SettingsDialogComponent for (WindSourceType windSourceType : WindSourceType.values()) { CheckBox checkbox = dialog.createCheckbox(WindSourceTypeFormatter.format(windSourceType, stringMessages)); + checkbox.setTitle(WindSourceTypeFormatter.tooltipFor(windSourceType, stringMessages)); checkbox.setEnabled(initialSettings.isShowWindSpeedSeries() && windSourceType.useSpeed()); windSpeedCheckboxes.put(windSourceType, checkbox); checkbox.setValue(initialSettings.getWindSpeedSourcesToDisplay().contains(windSourceType)); From 4094263f33e7ab5742714460b2435a6df616d89e Mon Sep 17 00:00:00 2001 From: Lennart Hensler Date: Wed, 16 Oct 2013 17:41:44 +0200 Subject: [PATCH 14/14] Fixed some typos in the wind chart settings tooltips --- .../com/sap/sailing/gwt/ui/client/StringMessages.properties | 6 +++--- .../sap/sailing/gwt/ui/client/StringMessages_de.properties | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) 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 a2e93e45d9d..1b6727fae0c 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 @@ -748,7 +748,7 @@ showWindSpeedSeriesTooltip=Toggles the visibility of the wind speed for all data showWindDirectionSeriesTooltip=Toggles the visibility of the wind direction for all data sources combinedWindSourceTypeTooltip=Combines all other wind sources to one data set courseBasedWindSourceTypeTooltip=A value based on the course layout (under the assumption, that the race starts with an upwind leg) -trackBasedEstimationWindSourceTypeTooltip=Calculated data from the competitor paths +trackBasedEstimationWindSourceTypeTooltip=Calculated data from the competitor tracks expeditionWindSourceTypeTooltip=Data captured from mobile measuring systems -webWindSourceTypeTooltip=Manually setted data values -raceCommitteeWindSourceTypeTooltip=Data manually setted from the race officers \ No newline at end of file +webWindSourceTypeTooltip=Manually set data values +raceCommitteeWindSourceTypeTooltip=Data manually set from the race officers \ 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 ada3bed6368..f1faa79f927 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 @@ -747,4 +747,4 @@ courseBasedWindSourceTypeTooltip=Ein Datenwert basierend auf dem Kurslayout (unt trackBasedEstimationWindSourceTypeTooltip=Berechnet die Daten aus dem gefahrenen Kurs der Teilnehmer expeditionWindSourceTypeTooltip=Von mobilen Messanlagen erfasste Daten webWindSourceTypeTooltip=Manuell gesetzte Datenwerte -raceCommitteeWindSourceTypeTooltip=Von den Rennaufsehern manuell gesetzte Daten \ No newline at end of file +raceCommitteeWindSourceTypeTooltip=Von den Wettfahrtleitern manuell gesetzte Daten \ No newline at end of file