README.md
... ...
@@ -1,4 +1,5 @@
1 1
# SAP Sailing Analytics
2
+
2 3
The SAP Sailing Analytics platform as seen on [sapsailing.com](https://sapsailing.com)
3 4
4 5
Sailing provides the perfect platform for SAP to showcase solutions and help the sport run like never before. SAP’s involvement in the sport has transformed the sailing experience by providing tools, which:
build.gradle
... ...
@@ -50,9 +50,9 @@ allprojects {
50 50
ext {
51 51
// regular
52 52
buildTools = "30.0.3"
53
- compileSdk = 31
53
+ compileSdk = 33
54 54
minSdk = 16
55
- targetSdk = 31
55
+ targetSdk = 33
56 56
57 57
sourceCompatibilityVersion = JavaVersion.VERSION_1_8
58 58
targetCompatibilityVersion = JavaVersion.VERSION_1_8
configuration/httpd/cgi-bin/github.cgi
... ...
@@ -0,0 +1,20 @@
1
+#!/bin/bash
2
+echo "
3
+"
4
+BODY=$( cat )
5
+echo "${BODY}" >/tmp/github-hook-body
6
+REF=$( echo "${BODY}" | jq -r '.ref' )
7
+PUSHER=$( echo "${BODY}" | jq -r '.pusher.email' )
8
+logger -t github-cgi "ref ia $REF, pusher was $PUSHER"
9
+# For testing:
10
+#if [ "${PUSHER}" = "axel.uhl@sap.com" -a "${REF}" = "refs/heads/translation" ]; then
11
+if [ "${PUSHER}" = "tmsatsls+github.tools.sap_service-tip-git@sap.com" -a "${REF}" = "refs/heads/translation" ]; then
12
+ echo "Identified a push to refs/heads/translation by ${PUSHER}."
13
+ echo "Fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git"
14
+ logger -t github-cgi "fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git"
15
+ cd /home/wiki/gitwiki
16
+ sudo -u wiki git fetch sapsailing translation:translation 2>&1
17
+ sudo -u wiki git push origin translation:translation 2>&1
18
+else
19
+ echo "Either pusher was not tmsatsls+github.tools.sap_service-tip-git@sap.com or ref was not refs/heads/translation. Not triggering."
20
+fi
configuration/httpd/conf.d/000-macros.conf
... ...
@@ -1,57 +1,9 @@
1 1
# ARCHIVE, based on i3.2xlarge, 64GB RAM and 1.9TB swap
2
-#Define ARCHIVE_IP 172.31.24.90
3
-#Define ARCHIVE_IP 172.31.24.90
4
-#Define ARCHIVE_IP 172.31.20.25
5
-#Define ARCHIVE_IP 172.31.16.178
6
-#Define ARCHIVE_IP 172.31.25.130
7
-#Define ARCHIVE_IP 172.31.25.246
8
-#Define ARCHIVE_IP 172.31.24.192
9
-#Define ARCHIVE_IP 172.31.22.16
10
-# Failover (hanging in full gc):
11
-#Define ARCHIVE_IP 172.31.29.138
12
-# Production (hanging in full gc):
13
-#Define ARCHIVE_IP 172.31.18.201
14
-# New Candidate, not really ready...
15
-#Define ARCHIVE_IP 172.31.28.255
16
-#Define ARCHIVE_IP 172.31.21.159
17
-#Define ARCHIVE_IP 172.31.19.41
18
-#Define ARCHIVE_IP 172.31.29.206
19
-#Define ARCHIVE_IP 172.31.25.194
20
-#Define ARCHIVE_IP 172.31.17.130
21
-#Define ARCHIVE_IP 172.31.20.156
22
-#Define ARCHIVE_IP 172.31.28.168
23
-#Define ARCHIVE_IP 172.31.23.135
24
-#Define ARCHIVE_IP 172.31.16.245
25
-#Define ARCHIVE_IP 172.31.29.3
26
-#Define ARCHIVE_IP 172.31.20.157
27
-#Define ARCHIVE_IP 172.31.17.35
28
-#Define ARCHIVE_IP 172.31.28.236
29
-#Define ARCHIVE_IP 172.31.16.207
30
-#Define ARCHIVE_IP 172.31.26.1
31
-#Define ARCHIVE_IP 172.31.21.3
32
-#Define ARCHIVE_IP 172.31.28.73
33
-#Define ARCHIVE_IP 172.31.17.113
34
-#Define ARCHIVE_IP 172.31.30.211
35
-#Define ARCHIVE_IP 172.31.20.222
36
-#Define ARCHIVE_IP 172.31.21.139
37
-#Define ARCHIVE_IP 172.31.23.47
38
-#Define ARCHIVE_IP 172.31.20.62
39
-#Define ARCHIVE_IP 172.31.20.131
40
-#Define ARCHIVE_IP 172.31.47.208
41
-#Define ARCHIVE_IP 172.31.41.14
42
-#Define ARCHIVE_IP 172.31.34.59
43
-#Define ARCHIVE_IP 172.31.46.41
44
-#Define ARCHIVE_IP 172.31.44.232
45
-#Define ARCHIVE_IP 172.31.46.28
46
-#Define ARCHIVE_IP 172.31.45.169
47
-#Define ARCHIVE_IP 172.31.45.31
48
-#Define ARCHIVE_IP 172.31.44.175
49 2
#Define ARCHIVE_IP 172.31.36.140
50 3
Define ARCHIVE_IP 172.31.41.241
51 4
Define ARCHIVE_FAILOVER_IP 172.31.47.152
52 5
Define PRODUCTION
53 6
54
-
55 7
<Macro ArchiveRewrite>
56 8
Use Rewrite ${PRODUCTION} 8888
57 9
</Macro>
configuration/httpd/conf.d/001-events.conf
... ...
@@ -21,8 +21,8 @@ Use Status 172.31.42.171 internal-server-status
21 21
Use Status 127.0.0.1 internal-server-status
22 22
23 23
# Uncomment the following and update the internal IP to what your Archive server candidate is
24
-Use Plain archive-failover.sapsailing.com 172.31.44.175 8888
25
-Use Plain archive-candidate.sapsailing.com 172.31.46.176 8888
24
+Use Plain archive-failover.sapsailing.com 172.31.40.12 8888
25
+Use Plain archive-candidate.sapsailing.com 172.31.46.203 8888
26 26
27 27
Use Plain my-master.sapsailing.com 172.31.24.34 8888
28 28
configuration/httpd/conf.d/004-git-ssl.conf
... ...
@@ -3,6 +3,7 @@ ServerName git.sapsailing.com
3 3
SetEnv GIT_PROJECT_ROOT /home/trac/git
4 4
SetEnv GIT_HTTP_EXPORT_ALL
5 5
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
6
+ScriptAlias /hooks/ /var/www/cgi-bin/
6 7
7 8
<Location /git/ >
8 9
Options +ExecCGI
... ...
@@ -127,6 +128,10 @@ SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt
127 128
128 129
<Directory "/var/www/cgi-bin">
129 130
SSLOptions +StdEnvVars
131
+ AllowOverride None
132
+ Options FollowSymLinks
133
+ Order allow,deny
134
+ Allow from all
130 135
</Directory>
131 136
132 137
# SSL Protocol Adjustments:
configuration/httpd/conf.d/49erEuros2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Event-ARCHIVE 49erEuros2023.sapsailing.com ebe42774-76e1-4f9d-8e3a-829cc263088e
configuration/httpd/conf.d/bundesliga2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Home-ARCHIVE bundesliga2023.sapsailing.com
configuration/httpd/conf.d/danishleague2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Home-ARCHIVE danishleague2023.sapsailing.com
configuration/httpd/conf.d/finalregionalny2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Home-ARCHIVE finalregionalny2023.sapsailing.com
configuration/httpd/conf.d/frenchleague2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Event-ARCHIVE frenchleague2023.sapsailing.com 289ee506-e8de-42e3-b726-f339fdba3b31
configuration/httpd/conf.d/norwegianleague2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Home-ARCHIVE norwegianleague2023.sapsailing.com
configuration/httpd/conf.d/scl2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Home-ARCHIVE scl2023.sapsailing.com
configuration/httpd/conf.d/switchoverArchive.sh
... ...
@@ -26,7 +26,7 @@ setProductionMainIfNotSet() {
26 26
#currently unhealthy
27 27
#set production to archive
28 28
logger -t archive "Healthy: setting production to main archive"
29
- sed -i -E "s/Define PRODUCTION .*/Define PRODUCTION \${ARCHIVE_IP}/" ${MACROS_PATH}
29
+ sed -i -E "s/^Define PRODUCTION .*/Define PRODUCTION \${ARCHIVE_IP}/" ${MACROS_PATH}
30 30
notify "Healthy: main archive online"
31 31
else
32 32
#If already healthy then no reload or notification occurs.
... ...
@@ -46,7 +46,7 @@ then
46 46
then
47 47
#set production to failover if not already. Separate if statement in case the curl statement
48 48
#fails but the production is already set to point to the backup
49
- sed -i -E "s/Define PRODUCTION .*/Define PRODUCTION \${ARCHIVE_FAILOVER_IP}/" ${MACROS_PATH}
49
+ sed -i -E "s/^Define PRODUCTION .*/Define PRODUCTION \${ARCHIVE_FAILOVER_IP}/" ${MACROS_PATH}
50 50
logger -t archive "Unhealthy: second check failed, switching to failover"
51 51
systemctl reload httpd
52 52
notify-operators "Unhealthy: main archive offline"
configuration/httpd/conf.d/switchoverArchive.sh.orig
... ...
@@ -0,0 +1,72 @@
1
+#!/bin/bash
2
+MACROS_PATH=$1
3
+TIMEOUT1=2
4
+TIMEOUT2=9
5
+#Purpose: Script is used to switch to the failover archive if the primary is unhealthy by altering the macros
6
+#definitions and then reloading.
7
+
8
+#These next lines get the current ip values for the archive and failover, plus they store the value of production,
9
+#which is a variable pointing to either the primary or failover value.
10
+archiveIp="$(sed -n -E 's/^Define ARCHIVE_IP (.*)/\1/p' ${MACROS_PATH} | tr -d '[:space:]')"
11
+failoverIp="$(sed -n -E 's/^Define ARCHIVE_FAILOVER_IP (.*)/\1/p' ${MACROS_PATH} | tr -d '[:space:]')"
12
+productionIp="$(sed -n -E 's/^Define PRODUCTION (.*)/\1/p' ${MACROS_PATH} | tr -d '[:space:]')"
13
+#Checks if the macro.conf is set as healthy or unhealthy currently.
14
+if [[ "${productionIp}" == "\${ARCHIVE_IP}" ]]
15
+then
16
+ alreadyHealthy=1
17
+ logger -t archive "currently healthy"
18
+else
19
+ alreadyHealthy=0
20
+ logger -t archive "currently unhealthy"
21
+fi
22
+#Sets the production value to point to the variable defining the main archive IP, provided it isn't already set.
23
+setProductionMainIfNotSet() {
24
+ if [[ $alreadyHealthy -eq 0 ]]
25
+ then
26
+ #currently unhealthy
27
+ #set production to archive
28
+ logger -t archive "Healthy: setting production to main archive"
29
+<<<<<<< HEAD
30
+ sed -i -E "s/Define PRODUCTION .*/Define PRODUCTION \${ARCHIVE_IP}/" ${MACROS_PATH}
31
+ notify "Healthy: main archive online"
32
+=======
33
+ sed -i -E "s/^Define PRODUCTION .*/Define PRODUCTION \${ARCHIVE_IP}/" ${MACROS_PATH}
34
+ systemctl reload httpd
35
+ {
36
+ echo "To: ${EMAIL}"
37
+ echo Subject: Healthy
38
+ echo
39
+ echo Healthy: main archive online
40
+ } | /usr/sbin/sendmail -t
41
+>>>>>>> ec87e54291196f3ae8381b9b5df50c24b264238f
42
+ else
43
+ #If already healthy then no reload or notification occurs.
44
+ logger -t archive "Healthy: already set, no change needed"
45
+ fi
46
+
47
+}
48
+logger -t archive "begin check"
49
+curl -s --connect-timeout ${TIMEOUT1} "http://${archiveIp}:8888/gwt/status" >> /dev/null
50
+if [[ $? -ne 0 ]]
51
+then
52
+ logger -t archive "first check failed"
53
+ curl -s --connect-timeout ${TIMEOUT2} "http://${archiveIp}:8888/gwt/status" >> /dev/null
54
+ if [[ $? -ne 0 ]]
55
+ then
56
+ if [[ $alreadyHealthy -eq 1 ]]
57
+ then
58
+ #set production to failover if not already. Separate if statement in case the curl statement
59
+ #fails but the production is already set to point to the backup
60
+ sed -i -E "s/^Define PRODUCTION .*/Define PRODUCTION \${ARCHIVE_FAILOVER_IP}/" ${MACROS_PATH}
61
+ logger -t archive "Unhealthy: second check failed, switching to failover"
62
+ systemctl reload httpd
63
+ notify-operators "Unhealthy: main archive offline"
64
+ else
65
+ logger -t archive "Unhealthy: second check still fails, failover already in use"
66
+ fi
67
+ else
68
+ setProductionMainIfNotSet
69
+ fi
70
+else
71
+ setProductionMainIfNotSet
72
+fi
configuration/httpd/conf.d/trojmiejskaliga2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Home-ARCHIVE trojmiejskaliga2023.sapsailing.com
configuration/httpd/conf.d/wordpress.conf.bak
... ...
@@ -0,0 +1,16 @@
1
+# The problem with this is that Wordpress seems to redirect to https,
2
+# leading to an endless redirect loop when the ALB then receives the
3
+# https request, does the SSL handling and forwards as an http request
4
+# to here again...
5
+<VirtualHost *:80>
6
+ ServerName blog.sapsailing.com
7
+ CustomLog logs/blog_log combined
8
+ DocumentRoot /home/wordpress/wordpress
9
+ <Directory /home/wordpress/wordpress>
10
+ Options Indexes FollowSymLinks MultiViews
11
+ AllowOverride All
12
+ Order allow,deny
13
+ allow from all
14
+ </Directory>
15
+</VirtualHost>
16
+
configuration/httpd/conf.d/wow2023.sapsailing.com.conf
... ...
@@ -0,0 +1 @@
1
+Use Home-ARCHIVE wow2023.sapsailing.com
configuration/imageupgrade_functions.sh
... ...
@@ -55,12 +55,12 @@ clean_httpd_logs() {
55 55
echo "Clearing httpd logs" >>/var/log/sailing.err
56 56
service httpd stop
57 57
rm -rf /var/log/httpd/*
58
- rm /etc/httpd/conf.d/001-internals.conf
58
+ rm -f /etc/httpd/conf.d/001-internals.conf
59 59
}
60 60
61 61
clean_startup_logs() {
62 62
echo "Clearing bootstrap logs" >>/var/log/sailing.err
63
- rm /var/log/sailing*
63
+ rm -f /var/log/sailing*
64 64
# Ensure that upon the next boot the reboot indicator is not present, indicating that it's the first boot
65 65
rm "${REBOOT_INDICATOR}"
66 66
}
... ...
@@ -95,7 +95,7 @@ finalize() {
95 95
else
96 96
# Only clean ${LOGON_USER_HOME}/.ssh directory and /tmp/image-upgrade-finished if the next step is shutdown / image creation
97 97
clean_root_ssh_dir_and_tmp
98
- rm /var/log/sailing.err
98
+ rm -f /var/log/sailing.err
99 99
shutdown -h now &
100 100
fi
101 101
}
configuration/tractrac-json-urls
... ...
@@ -392,15 +392,45 @@ https://event3.tractrac.com/events/event_20221211_TestOceanR/jsonservice.php
392 392
https://event3.tractrac.com/events/event_20230223_TheOceanRa/jsonservice.php
393 393
https://event3.tractrac.com/events/event_20230328_TrofeoSARP/jsonservice.php
394 394
https://event3.tractrac.com/events/event_20230329_AllianzReg/jsonservice.php
395
+https://event3.tractrac.com/events/event_20230329_SailingCha1/jsonservice.php
395 396
https://event3.tractrac.com/events/event_20230420_TheOceanRa/jsonservice.php
396 397
https://event3.tractrac.com/events/event_20230421_FrenchOlym/jsonservice.php
398
+https://event3.tractrac.com/events/event_20230427_SailingCha1/jsonservice.php
399
+https://event3.tractrac.com/events/event_20230505_NorskSeils/jsonservice.php
400
+https://event3.tractrac.com/events/event_20230511_SegelBunde/jsonservice.php
401
+https://event3.tractrac.com/events/event_20230517_Sejlsports/jsonservice.php
402
+https://event3.tractrac.com/events/event_20230520_Sejlsports/jsonservice.php
397 403
https://event3.tractrac.com/events/event_20230603_TheOceanRa/jsonservice.php
398 404
https://event3.tractrac.com/events/event_20230607_HelgaCup/jsonservice.php
405
+https://event3.tractrac.com/events/event_20230609_Sejlsports1/jsonservice.php
406
+https://event3.tractrac.com/events/event_20230614_SailingCha1/jsonservice.php
399 407
https://event3.tractrac.com/events/event_20230614_TheOceanRa/jsonservice.php
408
+https://event3.tractrac.com/events/event_20230616_Sejlsports/jsonservice.php
409
+https://event3.tractrac.com/events/event_20230621_NorskSeils/jsonservice.php
400 410
https://event3.tractrac.com/events/event_20230629_TheOceanRa/jsonservice.php
401 411
https://event3.tractrac.com/events/event_20230706_ParisOlymp/jsonservice.php
412
+https://event3.tractrac.com/events/event_20230721_SailingCha1/jsonservice.php
413
+https://event3.tractrac.com/events/event_20230727_SegelBunde/jsonservice.php
414
+https://event3.tractrac.com/events/event_20230801_NorskSeils/jsonservice.php
402 415
https://event3.tractrac.com/events/event_20230809_AllianzPar/jsonservice.php
403 416
https://event3.tractrac.com/events/event_20230809_AllianzSai1/jsonservice.php
417
+https://event3.tractrac.com/events/event_20230812_CopyofSejl/jsonservice.php
418
+https://event3.tractrac.com/events/event_20230812_Sejlsports/jsonservice.php
419
+https://event3.tractrac.com/events/event_20230818_SegelBunde/jsonservice.php
420
+https://event3.tractrac.com/events/event_20230818_Ungdomslig/jsonservice.php
421
+https://event3.tractrac.com/events/event_20230826_Sejlsports/jsonservice.php
422
+https://event3.tractrac.com/events/event_20230831_SailingCha1/jsonservice.php
423
+https://event3.tractrac.com/events/event_20230902_Sejlsports/jsonservice.php
424
+https://event3.tractrac.com/events/event_20230913_SailingCha1/jsonservice.php
425
+https://event3.tractrac.com/events/event_20230914_SegelBunde/jsonservice.php
426
+https://event3.tractrac.com/events/event_20230922_NorskSeils/jsonservice.php
427
+https://event3.tractrac.com/events/event_20230923_XYachtsSta/jsonservice.php
428
+https://event3.tractrac.com/events/event_20230930_Ungdomslig/jsonservice.php
429
+https://event3.tractrac.com/events/event_20231005_CopyofSege/jsonservice.php
430
+https://event3.tractrac.com/events/event_20231011_LigueNatio/jsonservice.php
431
+https://event3.tractrac.com/events/event_20231012_MesternesM/jsonservice.php
432
+https://event3.tractrac.com/events/event_20231018_SegelBunde/jsonservice.php
433
+https://event3.tractrac.com/events/event_20231105_Europeaner/jsonservice.php
404 434
https://event.tractrac.com/events/event_20170727_Travemnder2/jsonservice.php
405 435
https://event.tractrac.com/events/event_20171013_DSLPokalGl/jsonservice.php
406 436
https://event.tractrac.com/events/event_20181012_MstarnasMs/jsonservice.php
... ...
@@ -475,6 +505,7 @@ https://event.tractrac.com/events/event_20211017_KornatiCup/jsonservice.php
475 505
https://event.tractrac.com/events/event_20211115_ererFxNacr/jsonservice.php
476 506
https://event.tractrac.com/events/event_20211210_YouthSaili/jsonservice.php
477 507
https://event.tractrac.com/events/event_20220626_KielerWoch/jsonservice.php
508
+https://event.tractrac.com/events/event_20230324_SAILINGCha/jsonservice.php
478 509
https://event.tractrac.com/events/event_20230531_ERERFXNACR/jsonservice.php
479 510
https://event.tractrac.com/events/event_20230611_KielerWoch/jsonservice.php
480 511
https://event.tractrac.com/events/event_20230622_KielerWoch/jsonservice.php
docker/docker-compose.yml
... ...
@@ -1,7 +1,7 @@
1 1
version: "3.9"
2 2
services:
3 3
sailing-analytics:
4
- image: "docker.sapsailing.com:443/sapsailing:latest"
4
+ image: "docker.sapsailing.com/sapsailing:latest"
5 5
ports:
6 6
- "8888:8888"
7 7
- "6666:6666"
home.md
... ...
@@ -26,6 +26,7 @@ SAP is at the center of today’s technology revolution, developing innovations
26 26
* [[User Management|wiki/info/landscape/usermanagement]]
27 27
* [[Development Environment|wiki/info/landscape/development-environment]]
28 28
* [[Production Environment|wiki/info/landscape/production-environment]]
29
+ * [[Internationalization (i18n)|wiki/howto/development/i18n]]
29 30
* [[Typical Development Scenarios|wiki/info/landscape/typical-development-scenarios]]
30 31
* [[RaceLog Tracking Server Architecture|wiki/info/landscape/server]]
31 32
* Environment Overview [[PDF|wiki/info/mobile/event-tracking/architecture.pdf]] | [[SVG|wiki/info/mobile/event-tracking/architecture.svg]]
java/com.sap.sailing.competitorimport/src/com/sap/sailing/competitorimport/CompetitorProvider.java
... ...
@@ -1,6 +1,7 @@
1 1
package com.sap.sailing.competitorimport;
2 2
3 3
import java.io.IOException;
4
+import java.net.URISyntaxException;
4 5
import java.util.Locale;
5 6
import java.util.Map;
6 7
import java.util.Set;
... ...
@@ -32,7 +33,7 @@ public interface CompetitorProvider extends Named {
32 33
* competitor names. Should the value for a key be <code>null</code>, competitors for the event may still
33 34
* be available, only they may not be keyed per regatta in that case.
34 35
*/
35
- Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException;
36
+ Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException, URISyntaxException;
36 37
37 38
/**
38 39
* Obtains competitor records from the source of import
... ...
@@ -44,7 +45,7 @@ public interface CompetitorProvider extends Named {
44 45
* <code>eventName</code>, or a regatta name as provided in the value set for the key
45 46
* <code>eventName</code> as returned by {@link #getHasCompetitorsForRegattasInEvent()}.
46 47
*/
47
- Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName) throws JAXBException, IOException;
48
+ Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName) throws JAXBException, IOException, URISyntaxException;
48 49
49 50
/**
50 51
* A hint for users about this competitor provider; ideally mapped to the user's locale with i18n support.
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages.properties
... ...
@@ -24,7 +24,7 @@ Team=Team
24 24
SearchTag=Search Tag
25 25
LeaderboardSailingDomainRetrieverChain=Leaderboards
26 26
RaceSailingDomainRetrieverChain=Races
27
-LegSailingDomainRetrieverChain=Legs
27
+LegOfCompetitorSailingDomainRetrieverChain=Legs of Competitors
28 28
GPSFixSailingDomainRetrieverChain=GPS Fixes
29 29
BravoFixSailingDomainRetrieverChain=Bravo Fixes
30 30
BravoFixTrackSailingDomainRetrieverChain=Bravo Tracks
... ...
@@ -254,4 +254,6 @@ CompetitorDaySailingDomainRetrieverChain=Competitor-Day for race
254 254
LowestSpeed=Lowest Speed
255 255
SpeedChange=Speed Change
256 256
MaximumTurningRate=Maximum turning rate (°/s)
257
-AverageTurningRate=Average turning rate (°/s)
... ...
\ No newline at end of file
0
+AverageTurningRate=Average turning rate (°/s)
1
+LengthOfTheLeg=Length of the Leg
2
+LegSailingDomainRetrieverChain=Leg
... ...
\ No newline at end of file
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_cs.properties
... ...
@@ -24,7 +24,7 @@ Team=Tým
24 24
SearchTag=Hledací znak
25 25
LeaderboardSailingDomainRetrieverChain=Výsledkové tabule
26 26
RaceSailingDomainRetrieverChain=Rozjížďky
27
-LegSailingDomainRetrieverChain=Úseky
27
+LegOfCompetitorSailingDomainRetrieverChain=Úseky závodníků
28 28
GPSFixSailingDomainRetrieverChain=Záznamy polohy GPS
29 29
BravoFixSailingDomainRetrieverChain=Záznamy polohy Bravo
30 30
BravoFixTrackSailingDomainRetrieverChain=Dráhy Bravo
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Nejnižší rychlost
255 255
SpeedChange=Změna rychlosti
256 256
MaximumTurningRate=Maximální tempo změny kurzu (°/s)
257 257
AverageTurningRate=Průměrné tempo změny kurzu (°/s)
258
+LengthOfTheLeg=Délka úseku
259
+LegSailingDomainRetrieverChain=Úsek
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_da.properties
... ...
@@ -24,7 +24,7 @@ Team=Team
24 24
SearchTag=Søgetag
25 25
LeaderboardSailingDomainRetrieverChain=Ranglister
26 26
RaceSailingDomainRetrieverChain=Sejladser
27
-LegSailingDomainRetrieverChain=Ben
27
+LegOfCompetitorSailingDomainRetrieverChain=Ben for konkurrenter
28 28
GPSFixSailingDomainRetrieverChain=GPS-registreringer
29 29
BravoFixSailingDomainRetrieverChain=Bravo-registreringer
30 30
BravoFixTrackSailingDomainRetrieverChain=Bravo-spor
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Laveste hastighed
255 255
SpeedChange=Hastighedsændring
256 256
MaximumTurningRate=Maks. drejehastighed (°/s)
257 257
AverageTurningRate=Gennemsnitlig drejehastighed (°/s)
258
+LengthOfTheLeg=Længde på benet
259
+LegSailingDomainRetrieverChain=Ben
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_de.properties
... ...
@@ -10,8 +10,8 @@ Acronym=Abkürzung
10 10
SailID=Segelnummer
11 11
BoatName=Bootsname
12 12
Color=Farbe
13
-LegNumber=Nummer des Schenkels
14
-LegType=Art des Schenkels
13
+LegNumber=Nummer des Bahnschenkels
14
+LegType=Art des Bahnschenkels
15 15
Year=Jahr
16 16
Regatta=Regatta
17 17
CourseArea=Regattabahn
... ...
@@ -21,7 +21,7 @@ RaceColumn=Rennspalte
21 21
Race=Rennen
22 22
LeaderboardSailingDomainRetrieverChain=Leaderboards
23 23
RaceSailingDomainRetrieverChain=Rennen
24
-LegSailingDomainRetrieverChain=Schenkel
24
+LegOfCompetitorSailingDomainRetrieverChain=Bahnschenkel eines Teilnehmers
25 25
GPSFixSailingDomainRetrieverChain=GPS-Fixes
26 26
BravoFixSailingDomainRetrieverChain=Bravo-Fixes
27 27
BravoFixTrackSailingDomainRetrieverChain=Bravo Messreihen
... ...
@@ -35,9 +35,9 @@ RaceOfCompetitorSailingDomainRetrieverChain=Rennen eines Teilnehmers
35 35
ManeuverSailingDomainRetrieverChain=Manöver
36 36
LeaderboardGroup=Ranglisten Gruppe
37 37
Leaderboard=Rangliste
38
-Leg=Schenkel
38
+Leg=Bahnschenkel
39 39
MarkPassing=Tonnenrundung
40
-LegOfCompetitor=Schenkel eines Teilnehmers
40
+LegOfCompetitor=Bahnschenkel eines Teilnehmers
41 41
GpsFix=GPS-Fix
42 42
BravoFix=Bravo-Fix
43 43
LineLengthAtStart=Länge der Startlinie zum Startzeitpunkt
... ...
@@ -117,7 +117,7 @@ RankThirtySecondsAfterStart=Absolute Platzierung 30s nach dem Startzeitpunkt
117 117
WindwardDistanceToAdvantageousEndOfLineAtStartOfRace=Windwärtige Distanz zum Bevorteilten Ende der Startlinie zum Start des Rennens
118 118
WindwardDistanceToAdvantageousEndOfLineAtStartOfCompetitor=Windwärtige Distanz zum Bevorteilten Ende der Startlinie zum Start des Teilnehmers
119 119
AbsoluteWindwardDistanceToStarboardSideAtStartOfCompetitor=Absolute Windwärtige Distanz zur Steuerbordseite beim Start des Teilnehmers
120
-RankAfterHalfOfTheFirstLeg=Platzierung nach der Hälfte des ersten Schenkels
120
+RankAfterHalfOfTheFirstLeg=Platzierung nach der Hälfte des ersten Bahnschenkels
121 121
AdvantageousEndOfLine=Bevorteiltes Ende der Startlinie
122 122
AdvantageOfStarboardSideOfStartLine=Vorteil der Steuerbord-Seite der Startlinie
123 123
TrueWindAngleOfStartLineSeenFromStarboardSide=Winkel der Startlinie zum wahren Wind vom Steuerbord-Ende aus gesehen
... ...
@@ -257,4 +257,6 @@ CompetitorDaySailingDomainRetrieverChain=Teilnehmer-Tag für Wettfahrt
257 257
LowestSpeed=Niedrigste Geschwindigkeit
258 258
SpeedChange=Geschwindigkeits-Änderung
259 259
MaximumTurningRate=Maximale Drehgeschwindigkeit (°/s)
260
-AverageTurningRate=Durchschnittliche Drehgeschwindigkeit (°/s)
... ...
\ No newline at end of file
0
+AverageTurningRate=Durchschnittliche Drehgeschwindigkeit (°/s)
1
+LengthOfTheLeg=Länge eines Bahnschenkels
2
+LegSailingDomainRetrieverChain=Bahnschenkel
... ...
\ No newline at end of file
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_es.properties
... ...
@@ -24,7 +24,7 @@ Team=Equipo
24 24
SearchTag=Etiqueta de búsqueda
25 25
LeaderboardSailingDomainRetrieverChain=Clasificaciones
26 26
RaceSailingDomainRetrieverChain=Carreras
27
-LegSailingDomainRetrieverChain=Tramos
27
+LegOfCompetitorSailingDomainRetrieverChain=Tramos de competidores
28 28
GPSFixSailingDomainRetrieverChain=Puntos de GPS
29 29
BravoFixSailingDomainRetrieverChain=Puntos de Bravo
30 30
BravoFixTrackSailingDomainRetrieverChain=Seguimientos Bravo
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Velocidad más baja
255 255
SpeedChange=Cambio de velocidad
256 256
MaximumTurningRate=Velocidad máxima de giro (°/s)
257 257
AverageTurningRate=Velocidad media de giro (°/s)
258
+LengthOfTheLeg=Longitud del tramo
259
+LegSailingDomainRetrieverChain=Tramo
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_fr.properties
... ...
@@ -24,7 +24,7 @@ Team=Équipe
24 24
SearchTag=Balise de recherche
25 25
LeaderboardSailingDomainRetrieverChain=Palmarès
26 26
RaceSailingDomainRetrieverChain=Courses
27
-LegSailingDomainRetrieverChain=Portions de parcours
27
+LegOfCompetitorSailingDomainRetrieverChain=Portions de parcours des concurrents
28 28
GPSFixSailingDomainRetrieverChain=Positions GPS
29 29
BravoFixSailingDomainRetrieverChain=Positions Bravo
30 30
BravoFixTrackSailingDomainRetrieverChain=Enregistrements Bravo
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Vitesse la plus basse
255 255
SpeedChange=Changement de vitesse
256 256
MaximumTurningRate=Vitesse angulaire maximale (°/s)
257 257
AverageTurningRate=Vitesse angulaire moyenne (°/s)
258
+LengthOfTheLeg=Longueur de la portion de parcours
259
+LegSailingDomainRetrieverChain=Portion de parcours
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_it.properties
... ...
@@ -24,7 +24,7 @@ Team=Squadra
24 24
SearchTag=Tag di ricerca
25 25
LeaderboardSailingDomainRetrieverChain=Classifiche
26 26
RaceSailingDomainRetrieverChain=Gare
27
-LegSailingDomainRetrieverChain=Tratte
27
+LegOfCompetitorSailingDomainRetrieverChain=Tratte dei concorrenti
28 28
GPSFixSailingDomainRetrieverChain=Punti nave GPS
29 29
BravoFixSailingDomainRetrieverChain=Punti nave Bravo
30 30
BravoFixTrackSailingDomainRetrieverChain=Tracce Bravo
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Velocità più bassa
255 255
SpeedChange=Cambio di velocità
256 256
MaximumTurningRate=Velocità di virata massima (°/s)
257 257
AverageTurningRate=Velocità di virata media (°/s)
258
+LengthOfTheLeg=Lunghezza della tratta
259
+LegSailingDomainRetrieverChain=Tratta
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ja.properties
... ...
@@ -24,7 +24,7 @@ Team=チーム
24 24
SearchTag=検索タグ
25 25
LeaderboardSailingDomainRetrieverChain=リーダーボード
26 26
RaceSailingDomainRetrieverChain=レース
27
-LegSailingDomainRetrieverChain=レグ
27
+LegOfCompetitorSailingDomainRetrieverChain=競技者のレグ
28 28
GPSFixSailingDomainRetrieverChain=GPS フィックス
29 29
BravoFixSailingDomainRetrieverChain=Bravo フィックス
30 30
BravoFixTrackSailingDomainRetrieverChain=Bravo トラック
... ...
@@ -255,3 +255,5 @@ LowestSpeed=最低速度
255 255
SpeedChange=速度変更
256 256
MaximumTurningRate=最大回転率 (°/s)
257 257
AverageTurningRate=平均回転率 (°/s)
258
+LengthOfTheLeg=レグの長さ
259
+LegSailingDomainRetrieverChain=レグ
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_pt.properties
... ...
@@ -24,7 +24,7 @@ Team=Equipe
24 24
SearchTag=Etiqueta de pesquisa
25 25
LeaderboardSailingDomainRetrieverChain=Painéis de classificação
26 26
RaceSailingDomainRetrieverChain=Corridas
27
-LegSailingDomainRetrieverChain=Pernas
27
+LegOfCompetitorSailingDomainRetrieverChain=Pernas de competidores
28 28
GPSFixSailingDomainRetrieverChain=Pontos fixos de GPS
29 29
BravoFixSailingDomainRetrieverChain=Pontos fixos Bravo
30 30
BravoFixTrackSailingDomainRetrieverChain=Pistas Bravo
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Velocidade mais baixa
255 255
SpeedChange=Mudança de velocidade
256 256
MaximumTurningRate=Taxa de viragem máxima (°/s)
257 257
AverageTurningRate=Taxa de viragem média (°/s)
258
+LengthOfTheLeg=Comprimento da perna
259
+LegSailingDomainRetrieverChain=Perna
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_ru.properties
... ...
@@ -24,7 +24,7 @@ Team=Команда
24 24
SearchTag=Тег поиска
25 25
LeaderboardSailingDomainRetrieverChain=Таблицы лидеров
26 26
RaceSailingDomainRetrieverChain=Гонки
27
-LegSailingDomainRetrieverChain=Отрезки
27
+LegOfCompetitorSailingDomainRetrieverChain=Отрезки участников
28 28
GPSFixSailingDomainRetrieverChain=Замеры GPS
29 29
BravoFixSailingDomainRetrieverChain=Замеры Bravo
30 30
BravoFixTrackSailingDomainRetrieverChain=Отслеживания Bravo
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Наименьшая скорость
255 255
SpeedChange=Изменение скорости
256 256
MaximumTurningRate=Максимальная скорость поворота (°/c)
257 257
AverageTurningRate=Средняя скорость поворота (°/c)
258
+LengthOfTheLeg=Длина отрезка
259
+LegSailingDomainRetrieverChain=Отрезок
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_sl.properties
... ...
@@ -24,7 +24,7 @@ Team=Ekipa
24 24
SearchTag=Oznaka iskanja
25 25
LeaderboardSailingDomainRetrieverChain=Lestvice vodilnih
26 26
RaceSailingDomainRetrieverChain=Plovi
27
-LegSailingDomainRetrieverChain=Stranice
27
+LegOfCompetitorSailingDomainRetrieverChain=Stranice tekmovalcev
28 28
GPSFixSailingDomainRetrieverChain=GPS-položaj
29 29
BravoFixSailingDomainRetrieverChain=Bravo-položaji
30 30
BravoFixTrackSailingDomainRetrieverChain=Bravo-sledi
... ...
@@ -255,3 +255,5 @@ LowestSpeed=Najnižja hitrost
255 255
SpeedChange=Sprememba hitrosti
256 256
MaximumTurningRate=Maksimalna hitrost obračanja (°/s)
257 257
AverageTurningRate=Povprečna hitrost obračanja (°/s)
258
+LengthOfTheLeg=Dolžina stranice
259
+LegSailingDomainRetrieverChain=Stranica
java/com.sap.sailing.datamining/resources/stringmessages/Sailing_StringMessages_zh.properties
... ...
@@ -24,7 +24,7 @@ Team=团队
24 24
SearchTag=搜索标记
25 25
LeaderboardSailingDomainRetrieverChain=积分榜
26 26
RaceSailingDomainRetrieverChain=比赛轮次
27
-LegSailingDomainRetrieverChain=航段
27
+LegOfCompetitorSailingDomainRetrieverChain=参赛队的航段
28 28
GPSFixSailingDomainRetrieverChain=GPS 修复
29 29
BravoFixSailingDomainRetrieverChain=Bravo 修复
30 30
BravoFixTrackSailingDomainRetrieverChain=Bravo 跟踪
... ...
@@ -249,9 +249,11 @@ IRM=IRM 代码(OCS、DNC、DNF 等)
249 249
XTE=交叉轨道误差
250 250
AbsoluteXTE=绝对交叉轨道误差
251 251
Day=天
252
-CompetitorDay=参赛队日
253
-CompetitorDaySailingDomainRetrieverChain=比赛轮次的参赛队日
252
+CompetitorDay=参赛队天
253
+CompetitorDaySailingDomainRetrieverChain=比赛轮次的参赛队天
254 254
LowestSpeed=最低速度
255 255
SpeedChange=速度变化
256 256
MaximumTurningRate=最大回转率 (°/s)
257 257
AverageTurningRate=平均回转率 (°/s)
258
+LengthOfTheLeg=航段长度
259
+LegSailingDomainRetrieverChain=航段
java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/SailingDataRetrievalChainDefinitions.java
... ...
@@ -98,9 +98,14 @@ public class SailingDataRetrievalChainDefinitions {
98 98
HasFoilingSegmentContext.class, FoilingSegmentsDataMiningSettings.class, FoilingSegmentsDataMiningSettings.createDefaultSettings(), "FoilingSegments");
99 99
dataRetrieverChainDefinitions.add(foilingSegmentsRetrieverChainDefinition);
100 100
//
101
+ final DataRetrieverChainDefinition<RacingEventService, HasTrackedLegContext> legRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
102
+ trackedRaceRetrieverChainDefinition, HasTrackedLegContext.class, "LegSailingDomainRetrieverChain");
103
+ legRetrieverChainDefinition.endWith(TrackedRaceRetrievalProcessor.class, TrackedLegRetrievalProcessor.class,
104
+ HasTrackedLegContext.class, "Leg");
105
+ dataRetrieverChainDefinitions.add(legRetrieverChainDefinition);
106
+ //
101 107
final DataRetrieverChainDefinition<RacingEventService, HasTrackedLegOfCompetitorContext> legOfCompetitorRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
102
- trackedRaceRetrieverChainDefinition, HasTrackedLegOfCompetitorContext.class, "LegSailingDomainRetrieverChain");
103
- legOfCompetitorRetrieverChainDefinition.addAfter(TrackedRaceRetrievalProcessor.class, TrackedLegRetrievalProcessor.class, HasTrackedLegContext.class, "Leg");
108
+ legRetrieverChainDefinition, HasTrackedLegOfCompetitorContext.class, "LegOfCompetitorSailingDomainRetrieverChain");
104 109
legOfCompetitorRetrieverChainDefinition.endWith(TrackedLegRetrievalProcessor.class, TrackedLegOfCompetitorRetrievalProcessor.class,
105 110
HasTrackedLegOfCompetitorContext.class, "LegOfCompetitor");
106 111
dataRetrieverChainDefinitions.add(legOfCompetitorRetrieverChainDefinition);
java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasTrackedLegContext.java
... ...
@@ -2,8 +2,10 @@ package com.sap.sailing.datamining.data;
2 2
3 3
import com.sap.sailing.domain.common.LegType;
4 4
import com.sap.sailing.domain.tracking.TrackedLeg;
5
+import com.sap.sse.common.Distance;
5 6
import com.sap.sse.datamining.annotations.Connector;
6 7
import com.sap.sse.datamining.annotations.Dimension;
8
+import com.sap.sse.datamining.annotations.Statistic;
7 9
8 10
public interface HasTrackedLegContext {
9 11
... ...
@@ -16,5 +18,6 @@ public interface HasTrackedLegContext {
16 18
public LegType getLegType();
17 19
@Dimension(messageKey="LegNumber", ordinal=7)
18 20
public int getLegNumber();
19
-
21
+ @Statistic(messageKey = "LengthOfTheLeg", resultDecimals = 2)
22
+ Distance getLegLength();
20 23
}
... ...
\ No newline at end of file
java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/TrackedLegWithContext.java
... ...
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.LegType;
6 6
import com.sap.sailing.domain.common.NoWindException;
7 7
import com.sap.sailing.domain.tracking.TrackedLeg;
8 8
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
9
+import com.sap.sse.common.Distance;
9 10
import com.sap.sse.common.TimePoint;
10 11
import com.sap.sse.common.impl.MillisecondsTimePoint;
11 12
... ...
@@ -97,5 +98,9 @@ public class TrackedLegWithContext implements HasTrackedLegContext {
97 98
}
98 99
return at;
99 100
}
100
-
101
+
102
+ @Override
103
+ public Distance getLegLength() {
104
+ return getTrackedLeg().getWindwardDistance();
105
+ }
101 106
}
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DataImportProgressImpl.java
... ...
@@ -55,7 +55,7 @@ public class DataImportProgressImpl implements DataImportProgress {
55 55
@Override
56 56
public void setCurrentSubProgress(DataImportSubProgress subProgress) {
57 57
if (subProgress != currentSubProgress) {
58
- logger.info("Master data import with operation ID "+getOperationId()+" moving from stage "+currentSubProgressPct+" to "+subProgress);
58
+ logger.info("Master data import with operation ID "+getOperationId()+" moving from stage "+currentSubProgress+" to "+subProgress);
59 59
}
60 60
currentSubProgress = subProgress;
61 61
}
java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/Activator.java
... ...
@@ -41,8 +41,6 @@ public class Activator implements BundleActivator {
41 41
private static final Logger logger = Logger.getLogger(Activator.class.getName());
42 42
private static Activator INSTANCE;
43 43
44
- private static final String DEFAULT_CLIENT_ID = "d29eae61621af3057db0e638232a027e96b1d2291b1b89a1481dfcac075b0bf4";
45
- private static final String DEFAULT_CLIENT_SECRET = "537dbd14a84fcb470c91d85e8c4f8f7a356ac5ffc8727594d1bfe900ee5942ef";
46 44
private static final String DEFAULT_REDIRECT_PROTOCOL = "https";
47 45
private static final String DEFAULT_REDIRECT_HOST = "www.sapsailing.com";
48 46
private static final String DEFAULT_REDIRECT_PORT = null; // port==null means default
... ...
@@ -60,8 +58,8 @@ public class Activator implements BundleActivator {
60 58
61 59
public Activator() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
62 60
logger.info(getClass().getName()+" constructor");
63
- final String clientId = System.getProperty(CLIENT_ID_PROPERTY_NAME, DEFAULT_CLIENT_ID);
64
- final String clientSecret = System.getProperty(CLIENT_SECRET_PROPERTY_NAME, DEFAULT_CLIENT_SECRET);
61
+ final String clientId = System.getProperty(CLIENT_ID_PROPERTY_NAME);
62
+ final String clientSecret = System.getProperty(CLIENT_SECRET_PROPERTY_NAME);
65 63
final String clientRedirectProtocol = System.getProperty(CLIENT_REDIRECT_PROTOCOL_PROPERTY_NAME, DEFAULT_REDIRECT_PROTOCOL);
66 64
final String clientRedirectHost = System.getProperty(CLIENT_REDIRECT_HOST_PROPERTY_NAME, DEFAULT_REDIRECT_HOST);
67 65
final String clientRedirectPort = System.getProperty(CLIENT_REDIRECT_PORT_PROPERTY_NAME, DEFAULT_REDIRECT_PORT);
java/com.sap.sailing.domain.swisstimingadapter/META-INF/MANIFEST.MF
... ...
@@ -17,7 +17,8 @@ Require-Bundle: com.sap.sailing.domain,
17 17
com.sap.sailing.xrr.schema,
18 18
com.sap.sailing.domain.shared.android,
19 19
com.sun.xml.bind.jaxb-impl;bundle-version="2.3.0",
20
- com.sap.sse.security.common
20
+ com.sap.sse.security.common,
21
+ com.sap.sailing.manage2sail
21 22
Export-Package: com.sap.sailing.domain.swisstimingadapter,
22 23
com.sap.sailing.domain.swisstimingadapter.impl;x-friends:="com.sap.sailing.server.test,com.sap.sailing.mongodb.test,com.sap.sailing.domain.swisstimingadapter.persistence"
23 24
Import-Package: com.sap.sailing.xrr.schema,
java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/SwissTimingAdapter.java
... ...
@@ -3,7 +3,6 @@ package com.sap.sailing.domain.swisstimingadapter;
3 3
import java.io.IOException;
4 4
import java.net.UnknownHostException;
5 5
import java.text.ParseException;
6
-import java.util.List;
7 6
8 7
import javax.xml.bind.JAXBException;
9 8
... ...
@@ -19,8 +18,6 @@ public interface SwissTimingAdapter {
19 18
String NAME = "SwissTiming";
20 19
String DEFAULT_URL = null;
21 20
22
- List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname, int port) throws InterruptedException, UnknownHostException, IOException, ParseException;
23
-
24 21
/**
25 22
* @param regattaToAddTo
26 23
* if <code>null</code>, an existing regatta by the name of the TracTrac event with the boat class name
java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/AbstractSailMasterConnector.java
... ...
@@ -4,6 +4,7 @@ import java.io.IOException;
4 4
import java.io.InputStream;
5 5
import java.io.OutputStream;
6 6
import java.net.SocketException;
7
+import java.net.URISyntaxException;
7 8
import java.net.UnknownHostException;
8 9
import java.text.DateFormat;
9 10
import java.text.DecimalFormat;
... ...
@@ -568,7 +569,7 @@ public abstract class AbstractSailMasterConnector extends SailMasterTransceiverI
568 569
+ " which seems to be OK. I think we're connected in "+this+"!");
569 570
}
570 571
}
571
- } catch (IOException e) {
572
+ } catch (IOException | URISyntaxException e) {
572 573
logger.log(Level.INFO, "Exception trying to establish connection in "+this+". Trying again in 1s.", e);
573 574
disconnectAndWaitABit();
574 575
}
... ...
@@ -576,7 +577,7 @@ public abstract class AbstractSailMasterConnector extends SailMasterTransceiverI
576 577
}
577 578
}
578 579
579
- protected abstract void connect() throws IOException;
580
+ protected abstract void connect() throws IOException, URISyntaxException;
580 581
581 582
protected abstract boolean isConnected() throws IOException;
582 583
java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorForUrlDownload.java
... ...
@@ -4,6 +4,7 @@ import java.io.IOException;
4 4
import java.io.InputStream;
5 5
import java.io.OutputStream;
6 6
import java.net.HttpURLConnection;
7
+import java.net.URISyntaxException;
7 8
import java.net.URL;
8 9
import java.net.URLConnection;
9 10
import java.text.ParseException;
... ...
@@ -12,6 +13,7 @@ import java.util.logging.Logger;
12 13
import com.sap.sailing.domain.base.BoatClass;
13 14
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
14 15
import com.sap.sailing.domain.swisstimingadapter.MessageType;
16
+import com.sap.sailing.manage2sail.Activator;
15 17
import com.sap.sse.util.HttpUrlConnectionHelper;
16 18
17 19
/**
... ...
@@ -67,9 +69,9 @@ public class SailMasterConnectorForUrlDownload extends AbstractSailMasterConnect
67 69
}
68 70
69 71
@Override
70
- protected void connect() throws IOException {
72
+ protected void connect() throws IOException, URISyntaxException {
71 73
logger.info("Connecting to "+raceDataUrl);
72
- urlConnection = HttpUrlConnectionHelper.redirectConnection(raceDataUrl);
74
+ urlConnection = HttpUrlConnectionHelper.redirectConnection(Activator.getInstance().addAccessTokenToManage2SailUrl(raceDataUrl));
73 75
}
74 76
75 77
@Override
java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SwissTimingAdapterImpl.java
... ...
@@ -4,8 +4,6 @@ import java.io.IOException;
4 4
import java.io.InputStream;
5 5
import java.net.URL;
6 6
import java.net.URLConnection;
7
-import java.net.UnknownHostException;
8
-import java.text.ParseException;
9 7
import java.util.ArrayList;
10 8
import java.util.HashMap;
11 9
import java.util.List;
... ...
@@ -59,14 +57,6 @@ public class SwissTimingAdapterImpl implements SwissTimingAdapter {
59 57
}
60 58
61 59
@Override
62
- public List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> getSwissTimingRaceRecords(String hostname,
63
- int port) throws InterruptedException, UnknownHostException, IOException, ParseException {
64
- List<com.sap.sailing.domain.swisstimingadapter.RaceRecord> result = new ArrayList<com.sap.sailing.domain.swisstimingadapter.RaceRecord>();
65
- // TODO --> Frank: this needs to come from Manage2Sail and its JSON document somehow...
66
- return result;
67
- }
68
-
69
- @Override
70 60
public RaceHandle addSwissTimingRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, String raceID,
71 61
String raceName, String raceDescription, BoatClass boatClass, String hostname, int port,
72 62
StartList startList, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds,
java/com.sap.sailing.domain.test/META-INF/MANIFEST.MF
... ...
@@ -35,7 +35,9 @@ Require-Bundle: com.sap.sailing.domain,
35 35
org.mongodb.driver-sync;bundle-version="4.3.1",
36 36
org.apache.commons.math3;bundle-version="3.5.0",
37 37
org.apache.shiro.core;bundle-version="1.2.2",
38
- org.hamcrest;bundle-version="2.2.0"
38
+ org.hamcrest;bundle-version="2.2.0",
39
+ net.bytebuddy.byte-buddy;bundle-version="1.12.18",
40
+ net.bytebuddy.byte-buddy-agent;bundle-version="1.12.18"
39 41
Export-Package: com.sap.sailing.domain.test,
40 42
com.sap.sailing.domain.test.mock
41 43
Import-Package: org.apache.commons.math;version="2.1.0",
java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/MarkPositionReceiver.java
... ...
@@ -82,34 +82,50 @@ public class MarkPositionReceiver extends AbstractReceiverWithQueue<IControl, IP
82 82
83 83
@Override
84 84
protected void handleEvent(Util.Triple<IControl, IPosition, Integer> event) {
85
- final String markCourseArea = event.getA() == null ? null : event.getA().getCourseArea();
86
- final String raceCourseArea = tractracRace.getCourseArea();
87
- // if mark and race both specify a course area, accept the mark position only
88
- // if the course area matches:
89
- if (markCourseArea == null || raceCourseArea == null || markCourseArea.equals(raceCourseArea)) {
90
- if (received++ % 1000 == 0) {
91
- System.out.print("M");
92
- if ((received / 1000 + 1) % 80 == 0) {
93
- System.out.println();
94
- }
85
+ /* TODO bug 5919 (https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=5919)
86
+ *
87
+ * We would like to exclude mark positions coming from marks that do not belong
88
+ * to the race for which this receiver is subscribed. The IControl interface offers
89
+ * a getCourseArea() method, and so does IRace. It would seem natural to do the following:
90
+ *
91
+ final String markCourseArea = event.getA() == null ? null : event.getA().getCourseArea();
92
+ final String raceCourseArea = tractracRace.getCourseArea();
93
+ // if mark and race both specify a course area, accept the mark position only
94
+ // if the course area matches:
95
+ if (markCourseArea == null || raceCourseArea == null || markCourseArea.equals(raceCourseArea)) {
96
+ *
97
+ * However, TracAPI updates Control objects' fields from the race data currently being loaded.
98
+ * Hence, if multiple races are loaded concurrently, as is typically the case upon any server
99
+ * re-start, particularly of course a larger archive of many events, the IControl.getCourseArea()
100
+ * method will return unpredictable values as the IControl object for marks with the same ID
101
+ * will be shared across all of TracAPI, and hence values from any random race being loaded
102
+ * may end up in the courseArea field.
103
+ *
104
+ * Therefore, for the time being, we cannot use this criterion to filter mark fixes. Of course,
105
+ * it would also help if TracTrac / TracAPI filtered those already on the server side.
106
+ */
107
+ if (received++ % 1000 == 0) {
108
+ System.out.print("M");
109
+ if ((received / 1000 + 1) % 80 == 0) {
110
+ System.out.println();
95 111
}
96
- if (!singleMarksEnsuredAlready) {
97
- singleMarksEnsuredAlready = true;
98
- ensureAllSingleMarksOfCourseAreaAreCreated(tractracRace);
99
- }
100
- Mark mark = getDomainFactory().getMark(new ControlPointAdapter(event.getA()), event.getC());
101
- DynamicTrackedRace trackedRace = getTrackedRace(tractracRace);
102
- if (trackedRace != null) {
103
- GPSFixMoving markPosition = getDomainFactory().createGPSFixMoving(event.getB());
104
- if (getSimulator() != null) {
105
- getSimulator().scheduleMarkPosition(mark, markPosition);
106
- } else {
107
- trackedRace.recordFix(mark, markPosition);
108
- }
112
+ }
113
+ if (!singleMarksEnsuredAlready) {
114
+ singleMarksEnsuredAlready = true;
115
+ ensureAllSingleMarksOfCourseAreaAreCreated(tractracRace);
116
+ }
117
+ Mark mark = getDomainFactory().getMark(new ControlPointAdapter(event.getA()), event.getC());
118
+ DynamicTrackedRace trackedRace = getTrackedRace(tractracRace);
119
+ if (trackedRace != null) {
120
+ GPSFixMoving markPosition = getDomainFactory().createGPSFixMoving(event.getB());
121
+ if (getSimulator() != null) {
122
+ getSimulator().scheduleMarkPosition(mark, markPosition);
109 123
} else {
110
- logger.warning("Couldn't find tracked race for race " + tractracRace.getName()
111
- + ". Dropping mark position event " + event);
124
+ trackedRace.recordFix(mark, markPosition);
112 125
}
126
+ } else {
127
+ logger.warning("Couldn't find tracked race for race " + tractracRace.getName()
128
+ + ". Dropping mark position event " + event);
113 129
}
114 130
}
115 131
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java
... ...
@@ -416,6 +416,9 @@ public interface TrackedRace
416 416
* Obtains estimated interpolated wind information for a given position and time point. The information is taken
417 417
* from all wind sources available except for those listed in <code>windSourcesToExclude</code>, using the
418 418
* confidences of the wind values provided by the various sources during averaging.
419
+ *
420
+ * @return the wind vector for the time and place given; or {@code null} if no wind speed/direction can be
421
+ * determined, e.g., because there is no non-excluded wind source available
419 422
*/
420 423
Wind getWind(Position p, TimePoint at, Set<WindSource> windSourcesToExclude);
421 424
... ...
@@ -750,6 +753,9 @@ public interface TrackedRace
750 753
* and time point <code>at</code>, using the particular wind source's averaging interval. The confidences delivered
751 754
* by each wind source are used during computing the averaged result across the wind sources. The result has the
752 755
* averaged confidence attached.
756
+ *
757
+ * @return the wind vector for the time and place given, with a confidence attached; or {@code null} if no wind
758
+ * speed/direction can be determined, e.g., because there is no non-excluded wind source available
753 759
*/
754 760
WindWithConfidence<Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
755 761
Set<WindSource> windSourcesToExclude);
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/qrcode/QRCodePlace.java
... ...
@@ -7,6 +7,7 @@ import java.util.logging.Logger;
7 7
8 8
import com.google.gwt.place.shared.PlaceTokenizer;
9 9
import com.google.gwt.user.client.Window;
10
+import com.sap.sailing.domain.common.BranchIOConstants;
10 11
import com.sap.sailing.domain.common.MailInvitationType;
11 12
import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants;
12 13
import com.sap.sse.common.Util.Pair;
... ...
@@ -29,10 +30,14 @@ import com.sap.sse.gwt.client.AbstractBasePlace;
29 30
*/
30 31
public class QRCodePlace extends AbstractBasePlace {
31 32
private static final Logger logger = Logger.getLogger(QRCodePlace.class.getName());
32
- public static final String PARAM_REGATTA_NAME = "regatta_name";
33
- public static final String PARAM_REGATTA_SECRET = "secret";
33
+ private static final String PARAM_REGATTA_NAME = "regatta_name";
34
+ private static final String PARAM_REGATTA_SECRET = "secret";
34 35
private static final String PARAM_MODE = "mode";
35
- public static final String PARAM_SERVER = "server";
36
+ private static final String PARAM_SERVER = "server";
37
+ private static final String PARAM_SERVER_URL = "server_url";
38
+ private static final String PARAM_DEVICE_CONFIG_ID = "device_config_identifier";
39
+ private static final String PARAM_DEVICE_CONFIG_UUID = "device_config_uuid";
40
+ private static final String PARAM_TOKEN = "token";
36 41
37 42
private UUID eventId;
38 43
private UUID competitorId;
... ...
@@ -44,6 +49,10 @@ public class QRCodePlace extends AbstractBasePlace {
44 49
private InvitationMode mode;
45 50
private String rawCheckInUrl;
46 51
private String targetServer;
52
+ private String serverUrl;
53
+ private String deviceConfigIdentifier;
54
+ private String deviceConfigUuid;
55
+ private String token;
47 56
48 57
public enum InvitationMode {
49 58
COMPETITOR(MailInvitationType.SailInsight1),
... ...
@@ -74,6 +83,12 @@ public class QRCodePlace extends AbstractBasePlace {
74 83
if (publicRegattaName == null || regattaRegistrationLinkSecret == null || targetServer == null) {
75 84
logger.severe("Missing parameter for regatta, secret or server");
76 85
}
86
+ } else if (isRaceManagerAppRequest()) {
87
+ serverUrl = Window.Location.getParameter(PARAM_SERVER_URL);
88
+ targetServer = serverUrl;
89
+ deviceConfigIdentifier = Window.Location.getParameter(PARAM_DEVICE_CONFIG_ID);
90
+ deviceConfigUuid = Window.Location.getParameter(PARAM_DEVICE_CONFIG_UUID);
91
+ token = Window.Location.getParameter(PARAM_TOKEN);
77 92
} else {
78 93
rawCheckInUrl = Window.Location.getParameter(DeviceMappingConstants.URL_CHECKIN_URL);
79 94
if (rawCheckInUrl != null) {
... ...
@@ -176,12 +191,28 @@ public class QRCodePlace extends AbstractBasePlace {
176 191
}
177 192
178 193
public String getPublicInviteBranchIOUrl(MailInvitationType mailInvitationType) {
179
- return mailInvitationType.getBranchIOopenRegattaURL() + "?" + QRCodePlace.PARAM_REGATTA_NAME + "="
180
- + encodeUrl(publicRegattaName) + "&" + QRCodePlace.PARAM_REGATTA_SECRET + "="
181
- + encodeUrl(regattaRegistrationLinkSecret) + "&" + QRCodePlace.PARAM_SERVER + "="
194
+ return mailInvitationType.getBranchIOopenRegattaURL() + "?" + PARAM_REGATTA_NAME + "="
195
+ + encodeUrl(publicRegattaName) + "&" + PARAM_REGATTA_SECRET + "="
196
+ + encodeUrl(regattaRegistrationLinkSecret) + "&" + PARAM_SERVER + "="
182 197
+ encodeUrl(targetServer);
183 198
}
184 199
200
+ public String getRaceManagerAppUrl() {
201
+ String url;
202
+ if (serverUrl != null && deviceConfigIdentifier != null && deviceConfigUuid != null) {
203
+ url = BranchIOConstants.RACEMANAGER_APP_BRANCH_QUICK_LINK
204
+ + "?" + PARAM_SERVER_URL + "=" + encodeUrl(serverUrl)
205
+ + "&" + PARAM_DEVICE_CONFIG_ID + "=" + encodeUrl(deviceConfigIdentifier)
206
+ + "&" + PARAM_DEVICE_CONFIG_UUID + "=" + encodeUrl(deviceConfigUuid);
207
+ if (token != null) {
208
+ url += "&" + PARAM_TOKEN + "=" + encodeUrl(token);
209
+ }
210
+ } else {
211
+ url = null;
212
+ }
213
+ return url;
214
+ }
215
+
185 216
public InvitationMode getMode() {
186 217
return mode;
187 218
}
... ...
@@ -227,7 +258,9 @@ public class QRCodePlace extends AbstractBasePlace {
227 258
return "QRCodePlace [eventId=" + eventId + ", competitorId=" + competitorId + ", boatId=" + boatId + ", markId="
228 259
+ markId + ", leaderboardName=" + leaderboardName + ", publicRegattaName=" + publicRegattaName
229 260
+ ", regattaRegistrationLinkSecret=" + regattaRegistrationLinkSecret + ", mode=" + mode
230
- + ", rawCheckInUrl=" + rawCheckInUrl + ", targetServer=" + targetServer + "]";
261
+ + ", rawCheckInUrl=" + rawCheckInUrl + ", targetServer=" + targetServer + ", serverUrl=" + serverUrl
262
+ + ", deviceConfigIdentifier=" + deviceConfigIdentifier + ", deviceConfigUuid=" + deviceConfigUuid
263
+ + ", token=" + token + "]";
231 264
}
232 265
233 266
public static class Tokenizer implements PlaceTokenizer<QRCodePlace> {
... ...
@@ -257,8 +290,19 @@ public class QRCodePlace extends AbstractBasePlace {
257 290
* @return true if it is a public invite request (former PUBLIC_INVITE or PUBLIC_INVITE3 mode)
258 291
*/
259 292
static boolean isPublicInviteRequest() {
260
- return Window.Location.getParameter(QRCodePlace.PARAM_REGATTA_NAME) != null
261
- && Window.Location.getParameter(QRCodePlace.PARAM_REGATTA_SECRET) != null
262
- && Window.Location.getParameter(QRCodePlace.PARAM_SERVER) != null;
293
+ return Window.Location.getParameter(PARAM_REGATTA_NAME) != null
294
+ && Window.Location.getParameter(PARAM_REGATTA_SECRET) != null
295
+ && Window.Location.getParameter(PARAM_SERVER) != null;
296
+ }
297
+
298
+ /**
299
+ * Check if available URL parameter are indicating a public invite request.
300
+ *
301
+ * @return true if it is a public invite request (former PUBLIC_INVITE or PUBLIC_INVITE3 mode)
302
+ */
303
+ static boolean isRaceManagerAppRequest() {
304
+ return Window.Location.getParameter(PARAM_SERVER_URL) != null
305
+ && Window.Location.getParameter(PARAM_DEVICE_CONFIG_ID) != null
306
+ && Window.Location.getParameter(PARAM_DEVICE_CONFIG_UUID) != null;
263 307
}
264 308
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/qrcode/QRCodePresenter.java
... ...
@@ -142,6 +142,10 @@ public class QRCodePresenter {
142 142
// as the event is most likely displayed on a different server anyway, do not load additional data
143 143
dataCollector = new DataCollector(view);
144 144
dataCollector.proceedIfFinished();
145
+ } else if (QRCodePlace.isRaceManagerAppRequest()) {
146
+ logger.info("QR Code for race manager app to be shown");
147
+ dataCollector = new DataCollector(view);
148
+ dataCollector.proceedIfFinished();
145 149
} else {
146 150
logger.severe("QR Code cannot be created. Request type cannot be identified.");
147 151
}
... ...
@@ -293,6 +297,9 @@ public class QRCodePresenter {
293 297
logger.info("About to show QR Code for public regatta invite");
294 298
view.showPublic(place.getPublicRegattaName(),
295 299
place.getPublicInviteBranchIOUrl(place.getMode().getMailInvitationType()));
300
+ } else if (QRCodePlace.isRaceManagerAppRequest()) {
301
+ logger.info("About to show QR Code for race manager app");
302
+ view.showRaceManagerApp(place.getRaceManagerAppUrl());
296 303
} else {
297 304
logger.severe("Cannot identify request type.");
298 305
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/qrcode/QRCodeView.java
... ...
@@ -89,8 +89,24 @@ public class QRCodeView extends Composite {
89 89
90 90
public void showPublic(String publicRegattaName, String publicInviteBranchIOUrl) {
91 91
logger.info("Showing QR Code for public regatta invite");
92
- titleDivUi.setInnerText(StringMessages.INSTANCE.qrCodeTitleOpenRegatta(publicRegattaName));
93
- showQrCodeForURL(publicInviteBranchIOUrl);
92
+ if (publicInviteBranchIOUrl != null) {
93
+ titleDivUi.setInnerText(StringMessages.INSTANCE.qrCodeTitleOpenRegatta(publicRegattaName));
94
+ showQrCodeForURL(publicInviteBranchIOUrl);
95
+ } else {
96
+ titleDivUi.setInnerText(StringMessages.INSTANCE.qrCodeTitleErrorCreatingQrCode());
97
+ }
98
+ }
99
+
100
+ public void showRaceManagerApp(String raceManagerAppBranchIOUrl) {
101
+ logger.info("Showing QR Code for race manager app");
102
+ titleDivUi.setInnerText(StringMessages.INSTANCE.qrCodeTitleOpenRaceManagerApp());
103
+ if (raceManagerAppBranchIOUrl != null) {
104
+ subtitleDivUi.setInnerText(StringMessages.INSTANCE.qrCodeSubTitleOpenRaceManagerApp());
105
+ showQrCodeForURL(raceManagerAppBranchIOUrl);
106
+ } else {
107
+ titleDivUi.setInnerText(StringMessages.INSTANCE.qrCodeTitleErrorCreatingQrCode());
108
+ }
109
+
94 110
}
95 111
96 112
public void showBouyTender(QRCodeEvent event, String leaderboardName, String branchIoUrl) {
... ...
@@ -103,7 +119,11 @@ public class QRCodeView extends Composite {
103 119
} else {
104 120
logger.info("No event available");
105 121
}
106
- showQrCodeForURL(branchIoUrl);
122
+ if (branchIoUrl != null) {
123
+ showQrCodeForURL(branchIoUrl);
124
+ } else {
125
+ titleDivUi.setInnerText(StringMessages.INSTANCE.qrCodeTitleErrorCreatingQrCode());
126
+ }
107 127
showEventImageIfPossible(event);
108 128
}
109 129
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
... ...
@@ -4,6 +4,12 @@
4 4
<div id="mainContent">
5 5
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
6 6
<div class="innerContent">
7
+ <h5 class="articleSubheadline">November 2023</h5>
8
+ <ul class="bulletList">
9
+ <li>Added statistics for length of a leg to the Data Mining module.</li>
10
+ <li>Added tool tips to the drop-down lists for selecting the competitor chart data type
11
+ and the data type for displaying as the tail color on the map</li>
12
+ </ul>
7 13
<h5 class="articleSubheadline">October 2023</h5>
8 14
<ul class="bulletList">
9 15
<li>Competitor charts sometimes wouldn't update properly in live mode. This has now been fixed.</li>
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetBoatPositionsAction.java
... ...
@@ -13,10 +13,11 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
13 13
import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO;
14 14
import com.sap.sse.common.TimePoint;
15 15
import com.sap.sse.common.TimeRange;
16
+import com.sap.sse.common.Util.Pair;
16 17
import com.sap.sse.common.impl.TimeRangeImpl;
17 18
import com.sap.sse.gwt.client.async.TimeRangeAsyncAction;
18 19
19
-public class GetBoatPositionsAction implements TimeRangeAsyncAction<CompactBoatPositionsDTO, String> {
20
+public class GetBoatPositionsAction implements TimeRangeAsyncAction<CompactBoatPositionsDTO, Pair<String, DetailType>> {
20 21
private final SailingServiceAsync sailingService;
21 22
private final RegattaAndRaceIdentifier raceIdentifier;
22 23
private final Map<CompetitorDTO, Date> from;
... ...
@@ -42,27 +43,27 @@ public class GetBoatPositionsAction implements TimeRangeAsyncAction<CompactBoatP
42 43
}
43 44
44 45
@Override
45
- public void execute(Map<String, TimeRange> timeRanges, AsyncCallback<CompactBoatPositionsDTO> callback) {
46
+ public void execute(Map<Pair<String, DetailType>, TimeRange> timeRanges, AsyncCallback<CompactBoatPositionsDTO> callback) {
46 47
final Map<String, Date> fromByCompetitorIdAsString = new HashMap<String, Date>();
47 48
final Map<String, Date> toByCompetitorIdAsString = new HashMap<String, Date>();
48
- for (final Map.Entry<String, TimeRange> entry : timeRanges.entrySet()) {
49
+ for (final Map.Entry<Pair<String, DetailType>, TimeRange> entry : timeRanges.entrySet()) {
49 50
final Date from = entry.getValue().from().asDate();
50 51
final Date to = entry.getValue().to().asDate();
51
- fromByCompetitorIdAsString.put(entry.getKey(), from);
52
- toByCompetitorIdAsString.put(entry.getKey(), to);
52
+ fromByCompetitorIdAsString.put(entry.getKey().getA(), from);
53
+ toByCompetitorIdAsString.put(entry.getKey().getA(), to);
53 54
}
54 55
sailingService.getBoatPositions(raceIdentifier, fromByCompetitorIdAsString, toByCompetitorIdAsString,
55 56
extrapolate, detailType, leaderboardName, leaderboardGroupName, leaderboardGroupId, callback);
56 57
}
57 58
58 59
@Override
59
- public Map<String, TimeRange> getTimeRanges() {
60
- final Map<String, TimeRange> timeRangeByCompetitorId = new HashMap<>(from.size());
60
+ public Map<Pair<String, DetailType>, TimeRange> getTimeRanges() {
61
+ final Map<Pair<String, DetailType>, TimeRange> timeRangeByCompetitorId = new HashMap<>(from.size());
61 62
for (final Map.Entry<CompetitorDTO, Date> entry : from.entrySet()) {
62 63
final Date fromDate = entry.getValue();
63 64
final Date toDate = to.get(entry.getKey());
64 65
if (fromDate != null && toDate != null) {
65
- timeRangeByCompetitorId.put(entry.getKey().getIdAsString(),
66
+ timeRangeByCompetitorId.put(new Pair<>(entry.getKey().getIdAsString(), detailType),
66 67
new TimeRangeImpl(TimePoint.of(fromDate), TimePoint.of(toDate), /* toIsInclusive */ true));
67 68
}
68 69
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetBoatPositionsCallback.java
... ...
@@ -2,10 +2,13 @@ package com.sap.sailing.gwt.ui.actions;
2 2
3 3
import java.util.ArrayList;
4 4
import java.util.Comparator;
5
+import java.util.HashMap;
5 6
import java.util.List;
6 7
import java.util.Map;
8
+import java.util.Map.Entry;
7 9
8 10
import com.google.gwt.user.client.rpc.AsyncCallback;
11
+import com.sap.sailing.domain.common.DetailType;
9 12
import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO;
10 13
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType;
11 14
import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegTypeIterable;
... ...
@@ -17,25 +20,35 @@ import com.sap.sse.common.impl.MultiTimeRangeImpl;
17 20
import com.sap.sse.gwt.client.async.TimeRangeAsyncCallback;
18 21
19 22
/**
20
- * The third type parameter ({@link String}) for {@link TimeRangeAsyncCallback} represents the stringified
21
- * competitor IDs which form the key for the result structure.
23
+ * The third type parameter ({@link Pair<String, DetailType>}) for {@link TimeRangeAsyncCallback} represents the stringified
24
+ * competitor IDs and the DetailType which form the key for the result structure.
22 25
*/
23 26
public class GetBoatPositionsCallback
24
- implements TimeRangeAsyncCallback<CompactBoatPositionsDTO, GPSFixDTOWithSpeedWindTackAndLegTypeIterable, String> {
27
+ implements TimeRangeAsyncCallback<CompactBoatPositionsDTO, GPSFixDTOWithSpeedWindTackAndLegTypeIterable, Pair<String, DetailType>> {
28
+ private final DetailType detailType;
25 29
private final AsyncCallback<CompactBoatPositionsDTO> callback;
26 30
27
- public GetBoatPositionsCallback(AsyncCallback<CompactBoatPositionsDTO> callback) {
31
+ public GetBoatPositionsCallback(DetailType detailType, AsyncCallback<CompactBoatPositionsDTO> callback) {
32
+ this.detailType = detailType;
28 33
this.callback = callback;
29 34
}
30 35
31 36
@Override
32
- public Map<String, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> unzipResult(CompactBoatPositionsDTO result) {
33
- return result.getBoatPositions();
37
+ public Map<Pair<String, DetailType>, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> unzipResult(CompactBoatPositionsDTO result) {
38
+ final Map<Pair<String, DetailType>, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> unzippedResult = new HashMap<>();
39
+ for (final Entry<String, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> i : result.getBoatPositions().entrySet()) {
40
+ unzippedResult.put(new Pair<>(i.getKey(), detailType), i.getValue());
41
+ }
42
+ return unzippedResult;
34 43
}
35 44
36 45
@Override
37
- public CompactBoatPositionsDTO zipSubResults(Map<String, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> subResultMap) {
38
- return CompactBoatPositionsDTO.fromCompetitorIds(subResultMap);
46
+ public CompactBoatPositionsDTO zipSubResults(Map<Pair<String, DetailType>, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> subResultMap) {
47
+ final Map<String, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> convertedSubResultMap = new HashMap<>();
48
+ for (final Entry<Pair<String, DetailType>, GPSFixDTOWithSpeedWindTackAndLegTypeIterable> i : subResultMap.entrySet()) {
49
+ convertedSubResultMap.put(i.getKey().getA(), i.getValue());
50
+ }
51
+ return CompactBoatPositionsDTO.fromCompetitorIds(convertedSubResultMap);
39 52
}
40 53
41 54
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetCompetitorsRaceDataAction.java
... ...
@@ -17,9 +17,10 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
17 17
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
18 18
import com.sap.sse.common.TimePoint;
19 19
import com.sap.sse.common.TimeRange;
20
+import com.sap.sse.common.Util.Pair;
20 21
import com.sap.sse.gwt.client.async.TimeRangeAsyncAction;
21 22
22
-public class GetCompetitorsRaceDataAction implements TimeRangeAsyncAction<CompetitorsRaceDataDTO, CompetitorDTO> {
23
+public class GetCompetitorsRaceDataAction implements TimeRangeAsyncAction<CompetitorsRaceDataDTO, Pair<CompetitorDTO, DetailType>> {
23 24
private final SailingServiceAsync sailingService;
24 25
private final RegattaAndRaceIdentifier raceIdentifier;
25 26
private final List<CompetitorDTO> competitors;
... ...
@@ -47,28 +48,36 @@ public class GetCompetitorsRaceDataAction implements TimeRangeAsyncAction<Compet
47 48
}
48 49
49 50
@Override
50
- public void execute(Map<CompetitorDTO, TimeRange> timeRanges, AsyncCallback<CompetitorsRaceDataDTO> callback) {
51
+ public void execute(Map<Pair<CompetitorDTO, DetailType>, TimeRange> timeRanges, AsyncCallback<CompetitorsRaceDataDTO> callback) {
51 52
if (timeRanges != null && !timeRanges.isEmpty()) {
53
+ DetailType detailType = null;
54
+ final List<CompetitorDTO> competitors = new ArrayList<>();
52 55
TimeRange timeRange = null;
53
- for (final Entry<CompetitorDTO, TimeRange> e : timeRanges.entrySet()) {
56
+ for (final Entry<Pair<CompetitorDTO, DetailType>, TimeRange> e : timeRanges.entrySet()) {
57
+ competitors.add(e.getKey().getA());
58
+ if (detailType == null) {
59
+ detailType = e.getKey().getB();
60
+ } else if (detailType != e.getKey().getB()) {
61
+ throw new IllegalArgumentException("The detail types must be the same; got "+detailType+" and "+e.getKey().getB());
62
+ }
54 63
timeRange = timeRange == null ? e.getValue() : timeRange.extend(e.getValue());
55 64
}
56 65
GWT.log("Calling getCompetitorsRaceData("+raceIdentifier+", "+new ArrayList<>(timeRanges.keySet())+
57 66
", "+timeRange.from()+", "+timeRange.to()+", "+stepSizeInMs+", "+detailType+", "+leaderboardGroupName+", "+
58 67
leaderboardGroupId+", "+leaderboardName);
59
- sailingService.getCompetitorsRaceData(raceIdentifier, new ArrayList<>(timeRanges.keySet()), timeRange.from().asDate(),
68
+ sailingService.getCompetitorsRaceData(raceIdentifier, competitors, timeRange.from().asDate(),
60 69
timeRange.to().asDate(), stepSizeInMs, detailType, leaderboardGroupName, leaderboardGroupId,
61 70
leaderboardName, callback);
62 71
}
63 72
}
64 73
65 74
@Override
66
- public Map<CompetitorDTO, TimeRange> getTimeRanges() {
75
+ public Map<Pair<CompetitorDTO, DetailType>, TimeRange> getTimeRanges() {
67 76
// we'll use the same time range for all competitors
68 77
final TimeRange timeRange = TimeRange.create(TimePoint.of(fromDate), TimePoint.of(toDate));
69
- final Map<CompetitorDTO, TimeRange> result = new HashMap<>();
78
+ final Map<Pair<CompetitorDTO, DetailType>, TimeRange> result = new HashMap<>();
70 79
for (final CompetitorDTO competitor : competitors) {
71
- result.put(competitor, timeRange);
80
+ result.put(new Pair<>(competitor, detailType), timeRange);
72 81
}
73 82
return result;
74 83
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractCompetitorRegistrationDialog.java
... ...
@@ -32,6 +32,7 @@ public abstract class AbstractCompetitorRegistrationDialog extends DataEntryDial
32 32
Consumer<Pair<CompetitorRegistrationsPanel, AsyncCallback<Collection<CompetitorDTO>>>> registeredCompetitorsRetriever) {
33 33
super(stringMessages.registerCompetitors(), /* messsage */null, okButtonMessage, stringMessages.cancel(),
34 34
validator, callback);
35
+ this.ensureDebugId("registerCompetitorsDialog");
35 36
this.errorReporter = errorReporter;
36 37
this.stringMessages = stringMessages;
37 38
this.sailingService = sailingServiceWrite;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/BaseQRIdentifierWidget.java
... ...
@@ -57,6 +57,7 @@ public abstract class BaseQRIdentifierWidget implements IsWidget {
57 57
error = new Label();
58 58
error.setStyleName("errorLabel");
59 59
url = new Label();
60
+ url.ensureDebugId("QRIdentifierURL");
60 61
VerticalPanel panel = new VerticalPanel();
61 62
panel.add(inputGrid);
62 63
panel.add(qrCodeComposite);
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorRegistrationsPanel.java
... ...
@@ -111,6 +111,7 @@ public class CompetitorRegistrationsPanel extends FlowPanel implements BusyDispl
111 111
}
112 112
});
113 113
addCompetitorWithBoatButton = new Button(stringMessages.add(stringMessages.competitorWithBoat()));
114
+ addCompetitorWithBoatButton.ensureDebugId("addCompetitorWithBoatButton");
114 115
addCompetitorWithBoatButton.addClickHandler(new ClickHandler() {
115 116
@Override
116 117
public void onClick(ClickEvent event) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorToBoatMappingsDialog.java
... ...
@@ -69,6 +69,7 @@ public class CompetitorToBoatMappingsDialog extends DataEntryDialog<Map<Competit
69 69
this.competitorTable = new CompactCompetitorTableWrapper<>(sailingServiceWrite, stringMessages, errorReporter,
70 70
/* multiSelection */ false, /* enablePager */ true, userService);
71 71
this.boatTable = new CompactBoatTableWrapper<>(sailingServiceWrite, stringMessages, errorReporter, /* multiSelection */ false, /* enablePager */ true);
72
+ this.boatTable.asWidget().ensureDebugId("competitorToBoatMappingBoatTable");
72 73
ImagesBarColumn<CompetitorDTO, CompactCompetitorConfigImagesBarCell> competitorActionColumn = new ImagesBarColumn<>(new CompactCompetitorConfigImagesBarCell(stringMessages));
73 74
competitorActionColumn.setFieldUpdater(new FieldUpdater<CompetitorDTO, String>() {
74 75
@Override
... ...
@@ -80,6 +81,7 @@ public class CompetitorToBoatMappingsDialog extends DataEntryDialog<Map<Competit
80 81
}
81 82
});
82 83
competitorTable.addColumn(competitorActionColumn, stringMessages.actions());
84
+ competitorTable.asWidget().ensureDebugId("competitorToBoatMappingCompetitorTable");
83 85
refreshableBoatSelectionModel = boatTable.getSelectionModel();
84 86
refreshableCompetitorSelectionModel = competitorTable.getSelectionModel();
85 87
boatListHandler = new SelectionChangeEvent.Handler() {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceConfigurationDetailComposite.java
... ...
@@ -111,6 +111,7 @@ public class DeviceConfigurationDetailComposite extends Composite {
111 111
fillCourseAreaListBox(eventsById.get(selectedValue==null||selectedValue.isEmpty() ? null : UUID.fromString(selectedValue)));
112 112
});
113 113
captionPanel = new CaptionPanel(stringMessages.configuration());
114
+ captionPanel.ensureDebugId("DeviceConfigurationDetailComposite");
114 115
VerticalPanel verticalPanel = new VerticalPanel();
115 116
contentPanel = new VerticalPanel();
116 117
cloneButton = new Button(stringMessages.save() + " + " + stringMessages.clone());
... ...
@@ -426,6 +427,7 @@ public class DeviceConfigurationDetailComposite extends Composite {
426 427
427 428
private Button createQrCodeButton() {
428 429
Button qrCodeButton = new Button(stringMessages.qrSync());
430
+ qrCodeButton.ensureDebugId("DeviceConfigurationDetailsQrCodeButton");
429 431
qrCodeButton.addClickHandler(new ClickHandler() {
430 432
@Override
431 433
public void onClick(ClickEvent event) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceConfigurationListComposite.java
... ...
@@ -120,6 +120,7 @@ public class DeviceConfigurationListComposite extends Composite {
120 120
CellTable<DeviceConfigurationWithSecurityDTO> table = new BaseCelltable<DeviceConfigurationWithSecurityDTO>(
121 121
/* pageSize */10000, tableResource);
122 122
configurationsDataProvider.addDataDisplay(table);
123
+ table.ensureDebugId("DeviceConfigurationList");
123 124
table.setWidth("100%");
124 125
ListHandler<DeviceConfigurationWithSecurityDTO> columnSortHandler = new ListHandler<DeviceConfigurationWithSecurityDTO>(
125 126
configurationsDataProvider.getList());
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceConfigurationPanel.java
... ...
@@ -79,6 +79,7 @@ public class DeviceConfigurationPanel extends SimplePanel implements DeviceConfi
79 79
HorizontalPanel deviceManagementControlPanel = new HorizontalPanel();
80 80
deviceManagementControlPanel.setSpacing(5);
81 81
addConfigurationButton = new Button(stringMessages.addConfiguration());
82
+ addConfigurationButton.ensureDebugId("addDeviceConfigurationButton");
82 83
addConfigurationButton.addClickHandler(new ClickHandler() {
83 84
@Override
84 85
public void onClick(ClickEvent event) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/DeviceConfigurationQRIdentifierDialog.java
... ...
@@ -90,6 +90,7 @@ public class DeviceConfigurationQRIdentifierDialog extends DialogBox {
90 90
panel.add(widget);
91 91
panel.add(actionPanel);
92 92
setWidget(panel);
93
+ this.ensureDebugId("DeviceConfigurationQRIdentifierDialog");
93 94
}
94 95
95 96
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaDetailsComposite.java
... ...
@@ -171,6 +171,7 @@ public class RegattaDetailsComposite extends Composite {
171 171
});
172 172
}
173 173
});
174
+ button.ensureDebugId("registrationLinkWithQRCodeOpenButton");
174 175
grid.setWidget(row , 1, button);
175 176
return button;
176 177
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogAddDeviceMappingDialog.java
... ...
@@ -88,6 +88,7 @@ public class RegattaLogAddDeviceMappingDialog extends DataEntryDialogWithDateTim
88 88
return null;
89 89
}
90 90
}, true, callback);
91
+ this.ensureDebugId("RegattaLogAddDeviceMappingDialog");
91 92
this.stringMessages = stringMessages;
92 93
this.sailingService = sailingServiceWrite;
93 94
this.regattaRegisterSecret = regattaRegisterSecret;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RegattaLogTrackingDeviceMappingsDialog.java
... ...
@@ -84,6 +84,7 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog<Void
84 84
final StringMessages stringMessages, final ErrorReporter errorReporter, final String leaderboardName,
85 85
final String regattaRegistrationSecret, DialogCallback<Void> callback) {
86 86
super(stringMessages.mapDevices(), /*message*/ null, stringMessages.ok(), stringMessages.cancel(), /*validator*/ null, callback);
87
+ this.ensureDebugId("regattaLogTrackingDeviceMappingsDialog");
87 88
this.stringMessages = stringMessages;
88 89
this.sailingServiceWrite = sailingServiceWrite;
89 90
this.userService = userService;
... ...
@@ -99,6 +100,7 @@ public class RegattaLogTrackingDeviceMappingsDialog extends DataEntryDialog<Void
99 100
FlowPanel mainPanel = new FlowPanel();
100 101
HorizontalPanel buttonPanel = new HorizontalPanel();
101 102
Button addMappingButton = new Button(stringMessages.add());
103
+ addMappingButton.ensureDebugId("addMappingButton");
102 104
addMappingButton.addClickHandler(new ClickHandler() {
103 105
@Override
104 106
public void onClick(ClickEvent event) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ResultImportUrlAddDialog.java
... ...
@@ -1,6 +1,7 @@
1 1
package com.sap.sailing.gwt.ui.adminconsole;
2 2
3 3
import com.google.gwt.user.client.rpc.AsyncCallback;
4
+import com.google.gwt.user.client.ui.FocusWidget;
4 5
import com.google.gwt.user.client.ui.TextBox;
5 6
import com.google.gwt.user.client.ui.Widget;
6 7
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
... ...
@@ -68,6 +69,11 @@ public class ResultImportUrlAddDialog extends DataEntryDialog<UrlDTO> {
68 69
}
69 70
70 71
@Override
72
+ protected FocusWidget getInitialFocusWidget() {
73
+ return textBox;
74
+ }
75
+
76
+ @Override
71 77
protected Widget getAdditionalWidget() {
72 78
return textBox;
73 79
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SelectNameForNewDeviceConfigurationDialog.java
... ...
@@ -42,8 +42,10 @@ public class SelectNameForNewDeviceConfigurationDialog extends DataEntryDialog<S
42 42
Validator<String> validator, com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<String> callback) {
43 43
super(stringMessages.createDeviceConfiguration(), stringMessages.forWhichDeviceShouldConfigurationApply(),
44 44
stringMessages.create(), stringMessages.cancel(), validator, callback);
45
+ this.ensureDebugId("SelectNameForNewDeviceConfigurationDialog");
45 46
this.stringMessages = stringMessages;
46 47
this.identifierBox = createTextBox("");
48
+ this.identifierBox.ensureDebugId("raceManagerDeviceName");
47 49
}
48 50
49 51
@Override
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/StructureImportManagementPanel.java
... ...
@@ -37,6 +37,7 @@ import com.sap.sailing.domain.common.dto.BoatClassDTO;
37 37
import com.sap.sailing.domain.common.impl.MeterDistance;
38 38
import com.sap.sailing.gwt.ui.adminconsole.StructureImportListComposite.RegattaStructureProvider;
39 39
import com.sap.sailing.gwt.ui.adminconsole.places.AdminConsoleView.Presenter;
40
+import com.sap.sailing.gwt.ui.adminconsole.swisstiming.SwissTimingEventIdUrlUtil;
40 41
import com.sap.sailing.gwt.ui.client.Displayer;
41 42
import com.sap.sailing.gwt.ui.client.SailingServiceWriteAsync;
42 43
import com.sap.sailing.gwt.ui.client.StringMessages;
... ...
@@ -131,8 +132,8 @@ public class StructureImportManagementPanel extends SimplePanel implements Regat
131 132
eventIDTextBox.addChangeHandler(new ChangeHandler() {
132 133
@Override
133 134
public void onChange(ChangeEvent event) {
134
- jsonURLTextBox.setText("http://manage2sail.com/api/public/links/event/" + eventIDTextBox.getValue()
135
- + "?accesstoken=bDAv8CwsTM94ujZ&mediaType=json");
135
+ final String url = SwissTimingEventIdUrlUtil.getUrlFromEventId(eventIDTextBox.getValue());
136
+ jsonURLTextBox.setText(url);
136 137
}
137 138
});
138 139
eventIDTextBox.ensureDebugId("eventIDTextBox");
... ...
@@ -140,8 +141,7 @@ public class StructureImportManagementPanel extends SimplePanel implements Regat
140 141
jsonURLTextBox = new TextBox();
141 142
jsonURLTextBox.ensureDebugId("JsonURLTextBox");
142 143
jsonURLTextBox.setVisibleLength(100);
143
- jsonURLTextBox.getElement().setPropertyString("placeholder",
144
- "http://manage2sail.com/api/public/links/event/d30883d3-2876-4d7e-af49-891af6cbae1b?accesstoken=bDAv8CwsTM94ujZ&mediaType=json");
144
+ jsonURLTextBox.getElement().setPropertyString("placeholder", SwissTimingEventIdUrlUtil.getUrlFromEventId("d30883d3-2876-4d7e-af49-891af6cbae1b"));
145 145
listRegattasButton = new Button(this.stringMessages.listRegattas());
146 146
importDetailsButton = new Button(this.stringMessages.importRegattas());
147 147
importDetailsButton.setEnabled(false);
... ...
@@ -258,14 +258,13 @@ public class StructureImportManagementPanel extends SimplePanel implements Regat
258 258
if (eventIDTextBox.getValue() == null || eventIDTextBox.getValue().length() == 0) {
259 259
jsonURL = jsonURLTextBox.getValue();
260 260
} else {
261
- jsonURL = "http://manage2sail.com/api/public/links/event/" + eventIDTextBox.getValue()
262
- + "?accesstoken=bDAv8CwsTM94ujZ&mediaType=json";
261
+ jsonURL = SwissTimingEventIdUrlUtil.getUrlFromEventId(eventIDTextBox.getValue());
263 262
}
264 263
if (jsonURL == null || jsonURL.length() == 0) {
265 264
errorReporter.reportError(stringMessages.pleaseEnterNonEmptyUrl());
266 265
} else {
267 266
busyIndicator.setBusy(true);
268
- sailingServiceWrite.getRegattas(jsonURL, new AsyncCallback<Iterable<RegattaDTO>>() {
267
+ sailingServiceWrite.getManage2SailRegattas(jsonURL, new AsyncCallback<Iterable<RegattaDTO>>() {
269 268
@Override
270 269
public void onFailure(Throwable caught) {
271 270
busyIndicator.setBusy(false);
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java
... ...
@@ -435,7 +435,6 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
435 435
if (selectedRegatta != null) {
436 436
regattaIdentifier = new RegattaName(selectedRegatta.getName());
437 437
}
438
-
439 438
// Check if the assigned regatta makes sense
440 439
if (checkBoatClassOK(selectedRegatta, selectedRaces)) {
441 440
sailingServiceWrite.trackWithSwissTiming(/* regattaToAddTo */ regattaIdentifier, selectedRaces, hostname, port==null?0:port,
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingConnectionTableWrapper.java
... ...
@@ -66,7 +66,6 @@ public class SwissTimingConnectionTableWrapper extends
66 66
this.stringMessagesClient = stringMessages;
67 67
this.sailingServiceAsync = sailingServiceWriteAsync;
68 68
final ListHandler<SwissTimingConfigurationWithSecurityDTO> swissTimingConectionColumnListHandler = getColumnSortHandler();
69
-
70 69
// table
71 70
final TextColumn<SwissTimingConfigurationWithSecurityDTO> swissTimingConnectionNameColumn = new AbstractSortableTextColumn<SwissTimingConfigurationWithSecurityDTO>(
72 71
dto -> dto.getName(), swissTimingConectionColumnListHandler);
... ...
@@ -85,7 +84,6 @@ public class SwissTimingConnectionTableWrapper extends
85 84
dto -> dto.getUpdateUsername(), swissTimingConectionColumnListHandler);
86 85
final TextColumn<SwissTimingConfigurationWithSecurityDTO> swissTimingConnectionCreatorNameColumn = new AbstractSortableTextColumn<SwissTimingConfigurationWithSecurityDTO>(
87 86
dto -> dto.getCreatorName(), swissTimingConectionColumnListHandler);
88
-
89 87
final HasPermissions type = SecuredDomainType.SWISS_TIMING_ACCOUNT;
90 88
final AccessControlledActionsColumn<SwissTimingConfigurationWithSecurityDTO, DefaultActionsImagesBarCell> actionColumn = create(
91 89
new DefaultActionsImagesBarCell(stringMessages), userService);
... ...
@@ -113,7 +111,6 @@ public class SwissTimingConnectionTableWrapper extends
113 111
}
114 112
}, userService, errorReporter).show();
115 113
});
116
-
117 114
actionColumn.addAction(DefaultActionsImagesBarCell.ACTION_DELETE, DefaultActions.DELETE, dto -> {
118 115
sailingServiceWriteAsync.deleteSwissTimingConfigurations(Collections.singletonList(dto), new AsyncCallback<Void>() {
119 116
@Override
... ...
@@ -127,19 +124,16 @@ public class SwissTimingConnectionTableWrapper extends
127 124
}
128 125
});
129 126
});
130
-
131 127
final EditOwnershipDialog.DialogConfig<SwissTimingConfigurationWithSecurityDTO> configOwnership = EditOwnershipDialog
132 128
.create(userService.getUserManagementWriteService(), type, dto -> refreshSwissTimingConnectionList(),
133 129
stringMessages);
134
-
135 130
final EditACLDialog.DialogConfig<SwissTimingConfigurationWithSecurityDTO> configACL = EditACLDialog.create(
136 131
userService.getUserManagementWriteService(), type, dto -> dto.getAccessControlList(), stringMessages);
137
-
138 132
actionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_OWNERSHIP, DefaultActions.CHANGE_OWNERSHIP,
139 133
configOwnership::openOwnershipDialog);
140 134
actionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL,
141 135
u -> configACL.openDialog(u));
142
-
136
+ // filter field:
143 137
filterField = new LabeledAbstractFilterablePanel<SwissTimingConfigurationWithSecurityDTO>(
144 138
new Label(stringMessagesClient.filterSwissTimingConnections()),
145 139
new ArrayList<SwissTimingConfigurationWithSecurityDTO>(), dataProvider, stringMessages) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/swisstiming/SwissTimingEventIdUrlUtil.java
... ...
@@ -2,8 +2,9 @@ package com.sap.sailing.gwt.ui.adminconsole.swisstiming;
2 2
3 3
public final class SwissTimingEventIdUrlUtil {
4 4
private static final String EVENT_ID_PATTERN = "[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}";
5
- private static final String MANAGE2SAIL_API_BASE_URL = "http://manage2sail.com/api/public/links/event/";
6
- private static final String MANAGE2SAIL_API_ACCESS_TOKEN = "?accesstoken=bDAv8CwsTM94ujZ";
5
+ private static final String MANAGE2SAIL_API_BASE_URL = "https://manage2sail.com/api/public/links/event/";
6
+ private static final String MANAGE2SAIL_API_ACCESS_TOKEN = "bDAv8CwsTM94ujZ";
7
+ private static final String MANAGE2SAIL_API_ACCESS_TOKEN_URL_PARAM = "?accesstoken="+MANAGE2SAIL_API_ACCESS_TOKEN;
7 8
private static final String MANAGE2SAIL_API_APPENDIX = "&mediaType=json&includeRaces=true";
8 9
9 10
/**
... ...
@@ -13,7 +14,7 @@ public final class SwissTimingEventIdUrlUtil {
13 14
* @return The event ID inferred from the Json Url.
14 15
*/
15 16
public static String getEventIdFromUrl(String eventUrl) {
16
- if (eventUrl.matches("http://manage2sail.com/.*" + EVENT_ID_PATTERN + ".*")) {
17
+ if (eventUrl.matches("https?://manage2sail.com/.*" + EVENT_ID_PATTERN + ".*")) {
17 18
return eventUrl.replaceFirst(".*(" + EVENT_ID_PATTERN + ").*", "$1");
18 19
}
19 20
return null;
... ...
@@ -27,7 +28,7 @@ public final class SwissTimingEventIdUrlUtil {
27 28
public static String getUrlFromEventId(String eventIdText) {
28 29
if (eventIdText.matches(".*" + EVENT_ID_PATTERN + ".*")) {
29 30
final String inferredEventId = eventIdText.replaceFirst(".*(" + EVENT_ID_PATTERN + ").*", "$1");
30
- return MANAGE2SAIL_API_BASE_URL + inferredEventId + MANAGE2SAIL_API_ACCESS_TOKEN + MANAGE2SAIL_API_APPENDIX;
31
+ return MANAGE2SAIL_API_BASE_URL + inferredEventId + MANAGE2SAIL_API_ACCESS_TOKEN_URL_PARAM + MANAGE2SAIL_API_APPENDIX;
31 32
}
32 33
return null;
33 34
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java
... ...
@@ -380,8 +380,6 @@ public class DetailTypeFormatter {
380 380
return stringMessages.currentHeelInDegree();
381 381
case BRAVO_RACE_PITCH_IN_DEGREES:
382 382
return stringMessages.currentPitchInDegree();
383
- default:
384
- break;
385 383
}
386 384
return detailType.name();
387 385
}
... ...
@@ -597,8 +595,6 @@ public class DetailTypeFormatter {
597 595
case EXPEDITION_LEG_JIB_CAR_STBD:
598 596
case EXPEDITION_LEG_MAST_BUTT:
599 597
return "";
600
- default:
601
- break;
602 598
}
603 599
// Throwing an exception to get notified if an implementation of
604 600
// a detail type is missing.
... ...
@@ -852,11 +848,55 @@ public class DetailTypeFormatter {
852 848
case EXPEDITION_RACE_TWS:
853 849
case EXPEDITION_RACE_VMG:
854 850
case EXPEDITION_RACE_VMG_TARG_VMG_DELTA:
851
+ case BRAVO_RACE_HEEL_IN_DEGREES:
852
+ case BRAVO_RACE_PITCH_IN_DEGREES:
853
+ case EXPEDITION_LEG_AWA:
854
+ case EXPEDITION_LEG_AWS:
855
+ case EXPEDITION_LEG_BARO:
856
+ case EXPEDITION_LEG_BOAT_SPEED:
857
+ case EXPEDITION_LEG_COG:
858
+ case EXPEDITION_LEG_COURSE:
859
+ case EXPEDITION_LEG_DISTANCE_BELOW_LINE:
860
+ case EXPEDITION_LEG_DISTANCE_TO_COMMITTEE_BOAT:
861
+ case EXPEDITION_LEG_DISTANCE_TO_PIN:
862
+ case EXPEDITION_LEG_DIST_TO_PORT_LAYLINE:
863
+ case EXPEDITION_LEG_DIST_TO_STB_LAYLINE:
864
+ case EXPEDITION_LEG_FORESTAY_LOAD:
865
+ case EXPEDITION_LEG_HEADING:
866
+ case EXPEDITION_LEG_JIB_CAR_PORT:
867
+ case EXPEDITION_LEG_JIB_CAR_STBD:
868
+ case EXPEDITION_LEG_LINE_SQUARE_FOR_WIND_DIRECTION:
869
+ case EXPEDITION_LEG_LOAD_P:
870
+ case EXPEDITION_LEG_LOAD_S:
871
+ case EXPEDITION_LEG_MAST_BUTT:
872
+ case EXPEDITION_LEG_RAKE:
873
+ case EXPEDITION_LEG_RATE_OF_TURN:
874
+ case EXPEDITION_LEG_RUDDER_ANGLE:
875
+ case EXPEDITION_LEG_SOG:
876
+ case EXPEDITION_LEG_TARGET_HEEL:
877
+ case EXPEDITION_LEG_TARG_BOAT_SPEED:
878
+ case EXPEDITION_LEG_TARG_TWA:
879
+ case EXPEDITION_LEG_TIME_TO_BURN_TO_COMMITTEE_BOAT:
880
+ case EXPEDITION_LEG_TIME_TO_BURN_TO_LINE:
881
+ case EXPEDITION_LEG_TIME_TO_BURN_TO_PIN:
882
+ case EXPEDITION_LEG_TIME_TO_COMMITTEE_BOAT:
883
+ case EXPEDITION_LEG_TIME_TO_GUN:
884
+ case EXPEDITION_LEG_TIME_TO_PIN:
885
+ case EXPEDITION_LEG_TIME_TO_PORT_LAYLINE:
886
+ case EXPEDITION_LEG_TIME_TO_STB_LAYLINE:
887
+ case EXPEDITION_LEG_TWA:
888
+ case EXPEDITION_LEG_TWD:
889
+ case EXPEDITION_LEG_TWS:
890
+ case EXPEDITION_LEG_VMG:
891
+ case EXPEDITION_LEG_VMG_TARG_VMG_DELTA:
892
+ case EXPEDITION_RACE_BARO:
893
+ case EXPEDITION_RACE_JIB_CAR_PORT:
894
+ case EXPEDITION_RACE_JIB_CAR_STBD:
895
+ case EXPEDITION_RACE_LOAD_P:
896
+ case EXPEDITION_RACE_LOAD_S:
897
+ case EXPEDITION_RACE_MAST_BUTT:
855 898
return "";
856
- default:
857
- break;
858 899
}
859
-
860 900
return "";
861 901
}
862 902
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java
... ...
@@ -52,8 +52,8 @@ import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImp
52 52
import com.sap.sailing.domain.common.windfinder.SpotDTO;
53 53
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
54 54
import com.sap.sailing.expeditionconnector.ExpeditionDeviceConfiguration;
55
-import com.sap.sailing.gwt.ui.shared.BearingWithConfidenceDTO;
56 55
import com.sap.sailing.gwt.ui.shared.AccountWithSecurityDTO;
56
+import com.sap.sailing.gwt.ui.shared.BearingWithConfidenceDTO;
57 57
import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO;
58 58
import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO;
59 59
import com.sap.sailing.gwt.ui.shared.CompetitorProviderDTO;
... ...
@@ -364,7 +364,7 @@ public interface SailingService extends RemoteService, RemoteReplicationService
364 364
* @param manage2SailJsonUrl
365 365
* the URL pointing to a Manage2Sail JSON document that contains the link to the XRR document
366 366
*/
367
- Iterable<RegattaDTO> getRegattas(String manage2SailJsonUrl) throws UnauthorizedException;
367
+ Iterable<RegattaDTO> getManage2SailRegattas(String manage2SailJsonUrl) throws Exception;
368 368
369 369
boolean doesRegattaLogContainCompetitors(String name)
370 370
throws UnauthorizedException, DoesNotHaveRegattaLogException, NotFoundException;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java
... ...
@@ -387,12 +387,13 @@ public interface SailingServiceAsync extends RemoteReplicationServiceAsync {
387 387
void getEventsForLeaderboard(String leaderboardName, AsyncCallback<Collection<EventDTO>> callback);
388 388
389 389
/**
390
- * Imports regatta structure definitions from an ISAF XRR document
390
+ * Imports regatta structure definitions from an ISAF XRR document provided through SwissTiming's Manage2Sail.
391
+ * The back-end will enhance the URL with the necessary API access token if none is provided in the URL.
391 392
*
392 393
* @param manage2SailJsonUrl
393 394
* the URL pointing to a Manage2Sail JSON document that contains the link to the XRR document
394 395
*/
395
- void getRegattas(String manage2SailJsonUrl, AsyncCallback<Iterable<RegattaDTO>> asyncCallback);
396
+ void getManage2SailRegattas(String manage2SailJsonUrl, AsyncCallback<Iterable<RegattaDTO>> asyncCallback);
396 397
397 398
/**
398 399
* Returns mark passings for the competitor. Using the {@code waitForCalculations} parameter callers can control
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
... ...
@@ -62,7 +62,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
62 62
String leaderboardName();
63 63
String pleaseEnterAName();
64 64
String pleaseEnterABoatClass();
65
- String discardRacesFromHowManyStartedRacesOn();
65
+ String applyDiscardsBasedOnNumberOfScoredRaces(int maxNumberOfDiscardedResults);
66 66
String leaderboardWithThisNameAlreadyExists();
67 67
String discardThresholdsMustBeNumeric();
68 68
String discardThresholdsMustBeAscending();
... ...
@@ -2078,6 +2078,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
2078 2078
String qrCodeTitle(String leaderboardName);
2079 2079
String qrCodeSubtitle(String eventName, String eventLocationAndVenue);
2080 2080
String qrCodeTitleOpenRegatta(String regattaName);
2081
+ String qrCodeTitleOpenRaceManagerApp();
2082
+ String qrCodeSubTitleOpenRaceManagerApp();
2083
+ String qrCodeTitleErrorCreatingQrCode();
2081 2084
String qrCodeUnsecureServerRedirect(String hostName);
2082 2085
String qrCodeUnsecureServerRedirectTitle(String hostName);
2083 2086
String qrCodeInfoMessage();
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
... ...
@@ -52,7 +52,6 @@ unlink=Unlink
52 52
leaderboardName=Leaderboard Name
53 53
pleaseEnterAName=Please enter a name
54 54
pleaseEnterABoatClass=Please enter a boat class
55
-discardRacesFromHowManyStartedRacesOn=Discard one or more races, starting with how many started races there are
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Configure discards. Enter into box X, the number of scored races required to apply the Xth discard. Maximum discards: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Leaderboard with this name already exists
58 57
discardThresholdsMustBeNumeric=Discard thresholds must be numeric
... ...
@@ -2114,6 +2113,9 @@ qrCodeTitleBouy=Welcome as a bouy tender for the regatta {0}
2114 2113
qrCodeTitle=Welcome to the regatta {0}
2115 2114
qrCodeSubtitle={0} - {1}
2116 2115
qrCodeTitleOpenRegatta=Welcome to the public regatta {0}!
2116
+qrCodeTitleOpenRaceManagerApp=Welcome to the Race Manager App registration
2117
+qrCodeSubTitleOpenRaceManagerApp=The Race Manager app is only available on Android devices
2118
+qrCodeTitleErrorCreatingQrCode=Data is incorrect or out of date. Cannot create QR-Code.
2117 2119
qrCodeUnsecureServerRedirect=The given link will redirect to the unknown site {0}, confirm to navigate there
2118 2120
qrCodeUnsecureServerRedirectTitle={0} Unknown server
2119 2121
qrCodeInfoMessage=Please scan this QR Code with your mobile device to proceed with the registration
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_cs.properties
... ...
@@ -52,7 +52,6 @@ unlink=Odpojit
52 52
leaderboardName=Název výsledkové tabule
53 53
pleaseEnterAName=Zadejte název
54 54
pleaseEnterABoatClass=Zadejte lodní třídu
55
-discardRacesFromHowManyStartedRacesOn=Vyřadit jednu nebo více rozjížděk počínaje počtem odstartovaných rozjížděk
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Nakonfigurujte vyřazení. Zadejte do pole X počet bodovaných rozjížděk vyžadovaných k použití X-tého vyřazení. Maximum vyřazení: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Výsledková tabule s tímto názvem již existuje
58 57
discardThresholdsMustBeNumeric=Prahy vyřazení musejí být numerické
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_da.properties
... ...
@@ -52,7 +52,6 @@ unlink=Fjern tilknytning
52 52
leaderboardName=Ranglistenavn
53 53
pleaseEnterAName=Indtast et navn
54 54
pleaseEnterABoatClass=Indtast en bådklasse
55
-discardRacesFromHowManyStartedRacesOn=Opgiv en eller flere kapsejladser, startende med hvor mange startede kapsejladser der er
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Konfigurer opgivelser. Indtast i felt X antallet af pointgivne kapsejladser, der kræves for at anvende den X''te opgivelse. Maks. antal opgivelser: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Rangliste med dette navn findes allerede
58 57
discardThresholdsMustBeNumeric=Opgivelsesgrænser skal være numeriske
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
... ...
@@ -22,7 +22,7 @@ racesAndTracking=Wettfahrten/Tracking
22 22
tracked=getracked
23 23
time=Zeit
24 24
linkedRaces=Verknüpfte Rennen
25
-timeTooltip=Zeit, die der Teilnehmer auf diesem Schenkel gesegelt ist. Beim ersten Schenkel zählt die Zeit ab Überquerung der Startlinie.
25
+timeTooltip=Zeit, die der Teilnehmer auf diesem Bahnschenkel gesegelt ist. Beim ersten Bahnschenkel zählt die Zeit ab Überquerung der Startlinie.
26 26
playSpeed=Abspielgeschwindigkeit
27 27
playSpeedHelp="1" für normale Abspielgeschwindigkeit, "2" für doppelte Geschwindigkeit usw.
28 28
playModeLive=Live
... ...
@@ -52,7 +52,6 @@ unlink=Unlink
52 52
leaderboardName=Name der Rangliste
53 53
pleaseEnterAName=Bitte geben Sie einen Namen ein.
54 54
pleaseEnterABoatClass=Bitte geben Sie eine Bootsklasse ein.
55
-discardRacesFromHowManyStartedRacesOn=Discard one more race starting with how many started races
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Streicher konfigurieren: in Feld X die Anzahl gewerteter Wettfahrten eingeben, ab welcher der X-te Streicher angewendet werden soll. Maximal {0} Streicher.
57 56
leaderboardWithThisNameAlreadyExists=Eine Rangliste mit diesem Namen existiert bereits.
58 57
discardThresholdsMustBeNumeric=Discard thresholds must be numeric
... ...
@@ -77,10 +76,10 @@ expand=+
77 76
leg=L
78 77
distanceInMeters=Distanz
79 78
averageSpeedInKnots=\u2205 FüG
80
-averageSpeedInKnotsTooltip=Die durchschnittliche Fahrt über Grund auf diesem Schenkel,\nberechnet durch die Division der gefahrenen Distanz durch die benötigte Zeit.
79
+averageSpeedInKnotsTooltip=Die durchschnittliche Fahrt über Grund auf diesem Bahnschenkel,\nberechnet durch die Division der gefahrenen Distanz durch die benötigte Zeit.
81 80
raceAverageSpeedInKnotsTooltip=Die durchschnittliche Fahrt über Grund in diesem Rennen,\nberechnet durch die Division der gefahrenen Distanz durch die benötigte Zeit.
82 81
rankGain=Platz +/-
83
-rankGainTooltip=Die Differenz zwischen der Platzierung im aktuellen und im vorangegangenen Schenkel
82
+rankGainTooltip=Die Differenz zwischen der Platzierung im aktuellen und im vorangegangenen Bahnschenkel
84 83
sapSailingAnalytics=Sailing Analytics
85 84
leaderboard=Rangliste
86 85
leaderboards=Ranglisten
... ...
@@ -96,7 +95,7 @@ gapToLeaderInSecondsTooltip=Schätzt die (berechnete) Ankunftszeit an der jetzig
96 95
velocityMadeGoodInKnots=VMG
97 96
velocityMadeGoodInKnotsTooltip=Die effektive Geschwindigkeit zum Ziel; auf der Kreuz die windwärtige Geschwindigkeit; auf der Vorwindstrecke die leewärtige Geschwindigkeit
98 97
windwardDistanceToGoInMeters=Abstand in Windrichtung zur nächsten Bahnmarke
99
-windwardDistanceToGoInMetersTooltip=Windwärtige / leewärtige Distanz des Teilnehmerns bis zum Ende des Schenkels auf Kreuz bzw. Vorwindstrecke; Strecke entlang des Schenkels bei Holeschlägen
98
+windwardDistanceToGoInMetersTooltip=Windwärtige / leewärtige Distanz des Teilnehmerns bis zum Ende des Schenkels auf Kreuz bzw. Vorwindstrecke; Strecke entlang des Bahnschenkels bei Holeschlägen
100 99
windwardDistanceToCompetitorFarthestAheadInMeters=Rückstand
101 100
windwardDistanceToCompetitorFarthestAheadInMetersTooltip=Windwärtiger Abstand zum Boot, das bisher am weitesten windwärts gesegelt ist.\nFür Handicap-Wertungen ist das nicht notwendigerweise der nach Verrechnung in Führung liegende Teilnehmer.
102 101
name=Name
... ...
@@ -152,7 +151,7 @@ timing=Timing
152 151
raceDetailsToShow=Renndetails
153 152
tracking=Tracking
154 153
overallDetailsToShow=Regatta-Details
155
-legDetailsToShow=Schenkeldetails
154
+legDetailsToShow=Bahnschenkeldetails
156 155
showCarryColumn=Spalte Übertrag
157 156
showCarryColumnCheckbox=Spalte Übertrag anzeigen
158 157
showCarryColumnTitle=Spalte Übertrag anzeigen
... ...
@@ -166,7 +165,7 @@ selectedRaces=Welche Rennen möchten Sie angezeigt bekommen?
166 165
lengthInSeconds=Länge (s)
167 166
stressTest=Stresstest
168 167
numberOfManeuvers=Manöver
169
-numberOfManeuversTooltip=Die Anzahl der gesegelten Manöver in diesem Schenkel
168
+numberOfManeuversTooltip=Die Anzahl der gesegelten Manöver in diesem Bahnschenkel
170 169
numberOfManeuversInRaceTooltip=Die Anzahl der gesegelten Manöver in diesem Rennen
171 170
tacks=Wenden
172 171
jibes=Halsen
... ...
@@ -176,7 +175,7 @@ maneuverTypes=Manöver
176 175
chooseChart=Diagramm wählen
177 176
raceNameEmpty=Rennname leer
178 177
distanceTraveled=Zurückgelegte Strecke
179
-distanceTraveledTooltip=Die zurückgelegt Strecke vom Start des Schenkels bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls der Schenkel noch nicht beendet ist.
178
+distanceTraveledTooltip=Die zurückgelegt Strecke vom Start des Schenkels bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls der Bahnschenkel noch nicht beendet ist.
180 179
raceDistanceTraveledTooltip=Die zurückgelegte Strecke vom Start des Rennens, bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls das Rennen noch nicht beendet ist.
181 180
velocityMadeGoodLong=Velocity Made Good
182 181
speedOverGroundLong=Speed Over Ground
... ...
@@ -298,9 +297,9 @@ leaderboardGroups=Ranglisten-Gruppen
298 297
filterLeaderboardGroupsByName=Ranglisten-Gruppen nach Name filtern
299 298
createNewLeaderboardGroup=Neue Ranglisten-Gruppe erstellen
300 299
leaderboardGroupConfiguration=Ranglistengruppen-Konfiguration
301
-legs=Schenkel
302
-currentLeg=Aktueller Schenkel
303
-currentLegTooltip=Schenkel auf dem sich der Teilnehmer aktuell befindet
300
+legs=Bahnschenkel
301
+currentLeg=Aktueller Bahnschenkel
302
+currentLegTooltip=Bahnschenkel auf dem sich der Teilnehmer aktuell befindet
304 303
leaderboardGroup=Ranglisten-Gruppe
305 304
pleaseEnterNonEmptyDescription=Bitte eine nicht-leere Beschreibung eingeben
306 305
groupWithThisNameAlreadyExists=Ranglisten-Gruppe mit diesem Namen existiert bereits
... ...
@@ -367,11 +366,11 @@ replication=Replikation
367 366
averageAbsoluteCrossTrackErrorInMeters=\u2205 XTE
368 367
averageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert.
369 368
averageSignedCrossTrackErrorInMeters=\u2205 XTE +/-
370
-averageSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert.
369
+averageSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Bahnschenkel, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert.
371 370
currentAbsoluteCrossTrackErrorInMeters=\u2205 XTE
372
-currentAbsoluteCrossTrackErrorInMetersTooltip=Die momentane Querabweichung zur Kursmittellinie in diesem Schenkel; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; nur verfügbar, wenn der Teilnehmer zum angefragten Zeitpunkt in einem/dem Bahnschenkel segelt.
371
+currentAbsoluteCrossTrackErrorInMetersTooltip=Die momentane Querabweichung zur Kursmittellinie in diesem Bahnschenkel; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; nur verfügbar, wenn der Teilnehmer zum angefragten Zeitpunkt in einem/dem Bahnschenkel segelt.
373 372
currentSignedCrossTrackErrorInMeters=\u2205 XTE +/-
374
-currentSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten; nur verfügbar, wenn der Teilnehmer zum angefragten Zeitpunkt in einem/dem Bahnschenkel segelt.
373
+currentSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Bahnschenkel, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten; nur verfügbar, wenn der Teilnehmer zum angefragten Zeitpunkt in einem/dem Bahnschenkel segelt.
375 374
raceAverageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen in diesem Rennen; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert.
376 375
raceAverageSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen diesem Rennen, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten.
377 376
helpLines=Hilfslinien
... ...
@@ -859,7 +858,7 @@ polarDiagramButton=Polardiagramm
859 858
ratioBetweenTimeSinceLastPositionFixAndAverageSamplingInterval=GPS Verzug
860 859
ratioBetweenTimeSinceLastPositionFixAndAverageSamplingIntervalTooltip=Verhältnis zwischen dem Alter der zuletzt empfangenen GPS-Position und dem durchschnittlichen Abtastintervall
861 860
trackingQuality=Tracking-Qualität
862
-legType=Art des Schenkels
861
+legType=Art des Bahnschenkels
863 862
sailID=Segelnummer
864 863
seriesLeaderboard=Rangliste der Serie
865 864
regattaLeaderboards=Ranglisten der Regatta
... ...
@@ -1274,7 +1273,7 @@ orMultipleEmails=oder mehrere E-Mails (Komma-separiert)
1274 1273
courseOverGroundTrueDegreesTooltip=Rechtweisender Kurs über Grund in Grad
1275 1274
courseOverGroundTrueDegrees=KüG
1276 1275
distanceIncludingGateStartInMeters=Distanz (m. Torstart)
1277
-distanceTraveledIncludingGateStartTooltip=Die zurückgelegt Strecke vom Start des Schenkels bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls der Schenkel noch nicht beendet ist.\nEnthält der Schenkel einen Torstart, so wird die Distanz vom Pin End bis zur Startposition des Teilnehmers hinzugezählt,\num so vergleichbare Distanzen auch für Teilneher zu erhalten, die zu verschiedenen Zeiten gestartet sind.
1276
+distanceTraveledIncludingGateStartTooltip=Die zurückgelegt Strecke vom Start des Bahnschenkels bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls der Bahnschenkel noch nicht beendet ist.\nEnthält der Bahnschenkel einen Torstart, so wird die Distanz vom Pin End bis zur Startposition des Teilnehmers hinzugezählt,\num so vergleichbare Distanzen auch für Teilneher zu erhalten, die zu verschiedenen Zeiten gestartet sind.
1278 1277
raceDistanceTraveledIncludingGateStartTooltip=Die zurückgelegte Strecke vom Start des Rennens, bis zum Ende\noder bis zum aktuellen Zeitpunkt, falls das Rennen noch nicht beendet ist. Bei einem Torstart wird die Distanz vom Pin End bis zur Startposition des Teilnehmers hinzugezählt,\num so vergleichbare Distanzen auch für Teilneher zu erhalten, die zu verschiedenen Zeiten gestartet sind.
1279 1278
dashboardHeader=Dashboard
1280 1279
dashboardNoWindBotAvailableHeader=Wind Bot nicht verfügbar.
... ...
@@ -1329,7 +1328,7 @@ eventsCount[one]={0,number} Veranstaltung
1329 1328
competitorsCount={0,number} Teams
1330 1329
courseAreaName=Regattabahn: {0}
1331 1330
competitorsCount[one]={0,number} Team
1332
-currentOfTotalLegs=Schenkel {0,number}/{1,number}
1331
+currentOfTotalLegs=Bahnschenkel {0,number}/{1,number}
1333 1332
clone=Clone
1334 1333
changed=Changed
1335 1334
matcher=Matcher
... ...
@@ -2101,6 +2100,9 @@ qrCodeTitle=Willkommen zu der Regatta {0}
2101 2100
qrCodeTitleBouy=Herzlich Willkommen als Tonnenleger für die Regatta {0}
2102 2101
qrCodeSubtitle={0} - {1}
2103 2102
qrCodeTitleOpenRegatta=Willkommen zur öffentlichen Regatta {0}!
2103
+qrCodeTitleOpenRaceManagerApp=Willkommen bei der Race Manager App Registrierung
2104
+qrCodeSubTitleOpenRaceManagerApp=Die Race Manager-App ist nur auf Android-Geräten verfügbar
2105
+qrCodeTitleErrorCreatingQrCode=Die Daten sind falsch oder veraltet. Der QR-Code kann nicht erstellt werden.
2104 2106
qrCodeUnsecureServerRedirect=Der gescannte Link verweist auf die unbekannte Seite: {0}, betätigen zum fortsetzen
2105 2107
qrCodeUnsecureServerRedirectTitle={0} ist ein unbekannter Server
2106 2108
qrCodeInfoMessage=Bitte den QR Code mit dem Smartphone scannen, um mit der Registrierung fortzufahren!
... ...
@@ -2248,7 +2250,7 @@ errorAssigningCertificates: Fehler beim Zuweisen von Messbriefen zu Booten: {0}
2248 2250
insertedAndReplacedAndRemovedCertificateAssignments=Neue Messbriefzuweisungen: {0}. Geänderte Zuweisungen: {1}. Gelöschte Zuweisungen: {2}.
2249 2251
spreadTotalDistanceProportionallyAcrossLegs=Gesamtdistanz proportional auf Bahnschenkel verteilen
2250 2252
trackedTwaInDegrees=Getrackter TWA (°)
2251
-trackedDistanceInNauticalMiles=Getrackte Schenkel-Distanz (NM)
2253
+trackedDistanceInNauticalMiles=Getrackte Bahnschenkel-Distanz (NM)
2252 2254
useTrackedData=Tracking-Werte verwenden
2253 2255
totalDistance=Gesamtdistanz (NM)
2254 2256
setAllLegsToType=Typ für alle Bahnschenkel setzen
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties
... ...
@@ -52,7 +52,6 @@ unlink=Quitar enlace
52 52
leaderboardName=Nombre de tabla de clasificación
53 53
pleaseEnterAName=Indique un nombre
54 54
pleaseEnterABoatClass=Indique una clase de embarcación
55
-discardRacesFromHowManyStartedRacesOn=Descarte una o más pruebas comenzando por cuántas pruebas iniciadas hay
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Configurar descartadas. Introduzca en la casilla X el número de pruebas puntuadas necesario para aplicar el descarte X. Máximo de descarte: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Ya hay una tabla de clasificación con este nombre.
58 57
discardThresholdsMustBeNumeric=Los umbrales de rechazo deben ser numéricos
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties
... ...
@@ -52,7 +52,6 @@ unlink=Annuler Lier
52 52
leaderboardName=Nom du palmarès
53 53
pleaseEnterAName=Saisissez un nom.
54 54
pleaseEnterABoatClass=Saisissez une catégorie de bateau.
55
-discardRacesFromHowManyStartedRacesOn=Éliminez une ou plusieurs courses, en partant du nombre de courses déjà lancées.
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Configurez les éliminations. Dans la case X, saisissez le nombre de courses (avec classement) requis pour que l''élimination au Xe s''applique. Nombre d''éliminations au maximum : {0}.
57 56
leaderboardWithThisNameAlreadyExists=Un palmarès de ce nom existe déjà.
58 57
discardThresholdsMustBeNumeric=Les seuils d''élimination doivent être numériques.
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties
... ...
@@ -52,7 +52,6 @@ unlink=Annulla collegamento
52 52
leaderboardName=Nome classifica
53 53
pleaseEnterAName=Inserire un nome
54 54
pleaseEnterABoatClass=Inserire una classe della barca
55
-discardRacesFromHowManyStartedRacesOn=Eliminare una o più gare, iniziando dal numero di gare iniziate
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Configurare le eliminazioni. Inserire nella casella X il numero di gare portate a termine richiesto per applicare la X° eliminazione. Numero massimo di eliminazioni: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Esiste già una classifica con questo nome
58 57
discardThresholdsMustBeNumeric=Le soglie di scarto devono essere numeriche
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties
... ...
@@ -52,7 +52,6 @@ unlink=リンク解除
52 52
leaderboardName=リーダーボード名
53 53
pleaseEnterAName=名称を入力してください
54 54
pleaseEnterABoatClass=艇種を入力してください
55
-discardRacesFromHowManyStartedRacesOn=1 つ以上のレースを除外 (スタート済みレースがいくつ存在しているかから開始)
56 55
applyDiscardsBasedOnNumberOfScoredRaces=除外を設定します。X 番目の除外を適用するために必要な得点済みレースの数を X ボックスに入力します。最大除外数: {0}。
57 56
leaderboardWithThisNameAlreadyExists=この名称のリーダーボードは登録されています
58 57
discardThresholdsMustBeNumeric=破棄しきい値は数値でなければなりません
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties
... ...
@@ -52,7 +52,6 @@ unlink=Eliminar link
52 52
leaderboardName=Nome do painel de classificação
53 53
pleaseEnterAName=Insira um nome
54 54
pleaseEnterABoatClass=Insira uma classe de barcos
55
-discardRacesFromHowManyStartedRacesOn=Rejeitar uma ou mais corridas, começando com quantas corridas iniciadas existem
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Configure as rejeições. Insira no campo de seleção X o número de corridas pontuadas necessárias para aplicar a Xª rejeição. Máximo de rejeições: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Já existe um painel de classificação com este nome
58 57
discardThresholdsMustBeNumeric=Os limites do descarte devem ser numéricos
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties
... ...
@@ -52,7 +52,6 @@ unlink=Отменить связывание
52 52
leaderboardName=Название таблицы лидеров
53 53
pleaseEnterAName=Введите название
54 54
pleaseEnterABoatClass=Введите класс лодки
55
-discardRacesFromHowManyStartedRacesOn=Исключите одну или несколько гонок, исходя из числа стартов в гонках
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Настройте исключение. Введите в поле X, число оцениваемых гонок, необходимое для применения исключения X. Максимум исключений: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Таблица лидеров с таким названием уже существует
58 57
discardThresholdsMustBeNumeric=Пороги исключения должны быть числовыми
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_sl.properties
... ...
@@ -52,7 +52,6 @@ unlink=Odstrani povezavo
52 52
leaderboardName=Ime lestvice vodilnih
53 53
pleaseEnterAName=Vnesite ime
54 54
pleaseEnterABoatClass=Vnesite kategorijo jadrnice
55
-discardRacesFromHowManyStartedRacesOn=Odstranite enega ali več plovov, ki se začne s številom razpoložljivih plovov
56 55
applyDiscardsBasedOnNumberOfScoredRaces=Konfigurirajte odstranitve. V okvirček vnesite X, število točkovanih plovov, potrebnih za uporabo X.  odstranitve. Največje število odstranitev: {0}.
57 56
leaderboardWithThisNameAlreadyExists=Lestvica vodilnih s tem imenom že obstaja
58 57
discardThresholdsMustBeNumeric=Mejne vrednosti odstranitve morajo biti številske
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties
... ...
@@ -52,7 +52,6 @@ unlink=取消链接
52 52
leaderboardName=积分榜名称
53 53
pleaseEnterAName=请输入名称
54 54
pleaseEnterABoatClass=请输入船只级别
55
-discardRacesFromHowManyStartedRacesOn=从已开始的比赛轮次数开始再放弃一个或多个比赛轮次
56 55
applyDiscardsBasedOnNumberOfScoredRaces=配置放弃次数。在方框中输入 X,即申请第 X 次放弃所需的得分比赛次数。最大放弃次数:{0}。
57 56
leaderboardWithThisNameAlreadyExists=具有此名称的积分榜已存在
58 57
discardThresholdsMustBeNumeric=放弃临界值必须为数字
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java
... ...
@@ -121,9 +121,9 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
121 121
*/
122 122
private long effectiveStepSize = -1;
123 123
124
- private final TimeRangeActionsExecutor<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, CompetitorDTO> timeRangeActionsExecutorForCompetitorRaceDataForPrimary;
124
+ private final TimeRangeActionsExecutor<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, Pair<CompetitorDTO, DetailType>> timeRangeActionsExecutorForCompetitorRaceDataForPrimary;
125 125
126
- private final TimeRangeActionsExecutor<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, CompetitorDTO> timeRangeActionsExecutorForCompetitorRaceDataForSecondary;
126
+ private final TimeRangeActionsExecutor<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, Pair<CompetitorDTO, DetailType>> timeRangeActionsExecutorForCompetitorRaceDataForSecondary;
127 127
128 128
AbstractCompetitorRaceChart(Component<?> parent, ComponentContext<?> context, SailingServiceAsync sailingService,
129 129
AsyncActionsExecutor asyncActionsExecutor,
... ...
@@ -281,13 +281,13 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
281 281
282 282
private void doLoadDataForCompetitorsAndDataType(final Date from, final Date to, final boolean append,
283 283
ArrayList<CompetitorDTO> competitorsToLoad, final DetailType selectedDataTypeToRetrieve, final TimingHolder tholder,
284
- TimeRangeActionsExecutor<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, CompetitorDTO> timeRangeActionExecutor) {
284
+ TimeRangeActionsExecutor<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, Pair<CompetitorDTO, DetailType>> timeRangeActionExecutor) {
285 285
long effectiveStepSize = getEffectiveStepSize(from, to);
286 286
GetCompetitorsRaceDataAction getCompetitorsRaceDataAction = new GetCompetitorsRaceDataAction(sailingService,
287 287
selectedRaceIdentifier, competitorsToLoad, from, to, effectiveStepSize, selectedDataTypeToRetrieve,
288 288
leaderboardGroupName, leaderboardGroupId, leaderboardName);
289
- TimeRangeAsyncCallback<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, CompetitorDTO> dataLoadedCallback =
290
- new TimeRangeAsyncCallback<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, CompetitorDTO>() {
289
+ TimeRangeAsyncCallback<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, Pair<CompetitorDTO, DetailType>> dataLoadedCallback =
290
+ new TimeRangeAsyncCallback<CompetitorsRaceDataDTO, Triple<CompetitorRaceDataDTO, Date, Date>, Pair<CompetitorDTO, DetailType>>() {
291 291
@Override
292 292
public void onSuccess(final CompetitorsRaceDataDTO result) {
293 293
GWT.log("Received complete result for getCompetitorsRaceData request "+
... ...
@@ -316,17 +316,17 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
316 316
}
317 317
318 318
@Override
319
- public Map<CompetitorDTO, Triple<CompetitorRaceDataDTO, Date, Date>> unzipResult(CompetitorsRaceDataDTO result) {
320
- final Map<CompetitorDTO, Triple<CompetitorRaceDataDTO, Date, Date>> unzipped = new HashMap<>();
319
+ public Map<Pair<CompetitorDTO, DetailType>, Triple<CompetitorRaceDataDTO, Date, Date>> unzipResult(CompetitorsRaceDataDTO result) {
320
+ final Map<Pair<CompetitorDTO, DetailType>, Triple<CompetitorRaceDataDTO, Date, Date>> unzipped = new HashMap<>();
321 321
for (final CompetitorRaceDataDTO competitorRaceData : result.getAllRaceData()) {
322
- unzipped.put(competitorRaceData.getCompetitor(), new Triple<>(competitorRaceData,
323
- result.getRequestedFromTime(), result.getRequestedToTime()));
322
+ unzipped.put(new Pair<>(competitorRaceData.getCompetitor(), result.getDetailType()),
323
+ new Triple<>(competitorRaceData, result.getRequestedFromTime(), result.getRequestedToTime()));
324 324
}
325 325
return unzipped;
326 326
}
327 327
328 328
@Override
329
- public CompetitorsRaceDataDTO zipSubResults(Map<CompetitorDTO, Triple<CompetitorRaceDataDTO, Date, Date>> subResultMap) {
329
+ public CompetitorsRaceDataDTO zipSubResults(Map<Pair<CompetitorDTO, DetailType>, Triple<CompetitorRaceDataDTO, Date, Date>> subResultMap) {
330 330
final DetailType detailType = subResultMap.values().stream().findFirst()
331 331
.map(competitorRaceDataWithDates -> competitorRaceDataWithDates.getA().getDetailType()).orElse(null);
332 332
final Date from = subResultMap.values().stream().findFirst()
... ...
@@ -334,8 +334,8 @@ public abstract class AbstractCompetitorRaceChart<SettingsType extends ChartSett
334 334
final Date to = subResultMap.values().stream().findFirst()
335 335
.map(competitorRaceDataWithDates -> competitorRaceDataWithDates.getC()).orElse(null);
336 336
final Map<CompetitorDTO, CompetitorRaceDataDTO> raceDataPerCompetitor = new HashMap<>();
337
- for (final Entry<CompetitorDTO, Triple<CompetitorRaceDataDTO, Date, Date>> e : subResultMap.entrySet()) {
338
- raceDataPerCompetitor.put(e.getKey(), e.getValue().getA());
337
+ for (final Entry<Pair<CompetitorDTO, DetailType>, Triple<CompetitorRaceDataDTO, Date, Date>> e : subResultMap.entrySet()) {
338
+ raceDataPerCompetitor.put(e.getKey().getA(), e.getValue().getA());
339 339
}
340 340
return new CompetitorsRaceDataDTO(detailType, from, to, raceDataPerCompetitor);
341 341
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChartSettingsComponent.java
... ...
@@ -4,6 +4,8 @@ import java.util.ArrayList;
4 4
import java.util.Collections;
5 5
import java.util.List;
6 6
7
+import com.google.gwt.dom.client.NodeList;
8
+import com.google.gwt.dom.client.OptionElement;
7 9
import com.google.gwt.user.client.ui.FocusWidget;
8 10
import com.google.gwt.user.client.ui.Label;
9 11
import com.google.gwt.user.client.ui.ListBox;
... ...
@@ -14,6 +16,7 @@ import com.sap.sailing.gwt.ui.client.DetailTypeComparator;
14 16
import com.sap.sailing.gwt.ui.client.DetailTypeFormatter;
15 17
import com.sap.sailing.gwt.ui.client.StringMessages;
16 18
import com.sap.sse.common.Util;
19
+import com.sap.sse.gwt.client.DOMUtils;
17 20
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
18 21
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
19 22
... ...
@@ -49,18 +52,26 @@ public class MultiCompetitorRaceChartSettingsComponent extends
49 52
Label chartSelectionLabel = new Label(stringMessages.chooseChart());
50 53
mainPanel.add(chartSelectionLabel);
51 54
chartFirstTypeSelectionListBox = dialog.createListBox(/* isMultiSelect */false);
55
+ final NodeList<OptionElement> firstTypeOptions = DOMUtils.getOptions(chartFirstTypeSelectionListBox);
52 56
chartSecondTypeSelectionListBox = dialog.createListBox(/* isMultiSelect */false);
57
+ final NodeList<OptionElement> secondTypeOptions = DOMUtils.getOptions(chartSecondTypeSelectionListBox);
53 58
//add empty values, required, if a non available value is saved as default in the settings. Eg. rideheight, which is only valid for foiling races
54 59
chartSecondTypeSelectionListBox.addItem("--", "--");
55 60
int i = 0;
56
-
61
+ // sort detail types alphabetically first:
57 62
List<DetailType> sortedAvailableDetailTypes = new ArrayList<DetailType>();
58 63
Util.addAll(availableDetailsTypes, sortedAvailableDetailTypes);
59 64
Collections.sort(sortedAvailableDetailTypes, new DetailTypeComparator());
60
-
65
+ // then add them to the list boxes, including their tool tips
61 66
for (DetailType detailType : sortedAvailableDetailTypes) {
62
- chartFirstTypeSelectionListBox.addItem(DetailTypeFormatter.format(detailType), detailType.name());
63
- chartSecondTypeSelectionListBox.addItem(DetailTypeFormatter.format(detailType), detailType.name());
67
+ final String detailTypeString = DetailTypeFormatter.format(detailType);
68
+ final String tooltip = DetailTypeFormatter.getTooltip(detailType);
69
+ chartFirstTypeSelectionListBox.addItem(detailTypeString, detailType.name());
70
+ chartSecondTypeSelectionListBox.addItem(detailTypeString, detailType.name());
71
+ if (Util.hasLength(tooltip)) {
72
+ firstTypeOptions.getItem(firstTypeOptions.getLength()-1).setTitle(tooltip);
73
+ secondTypeOptions.getItem(secondTypeOptions.getLength()-1).setTitle(tooltip);
74
+ }
64 75
if (detailType == initialFirstDetailType) {
65 76
chartFirstTypeSelectionListBox.setSelectedIndex(i);
66 77
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java
... ...
@@ -851,7 +851,6 @@ public class FixesAndTails {
851 851
protected Double getDetailValueAt(CompetitorDTO competitorDTO, int index) {
852 852
final Trigger<Integer> firstShownFixForCompetitor = firstShownFix.get(competitorDTO);
853 853
int indexOfFirstShownFix = (firstShownFixForCompetitor == null || firstShownFixForCompetitor.get() == null) ? -1 : firstShownFixForCompetitor.get();
854
-
855 854
try {
856 855
return getFixes(competitorDTO).get(indexOfFirstShownFix + index).detailValue;
857 856
} catch (IndexOutOfBoundsException e) {
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java
... ...
@@ -19,6 +19,8 @@ import java.util.function.Consumer;
19 19
import com.google.gwt.core.client.GWT;
20 20
import com.google.gwt.core.client.Scheduler;
21 21
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
22
+import com.google.gwt.dom.client.NodeList;
23
+import com.google.gwt.dom.client.OptionElement;
22 24
import com.google.gwt.dom.client.Style;
23 25
import com.google.gwt.dom.client.Style.Unit;
24 26
import com.google.gwt.event.dom.client.ChangeEvent;
... ...
@@ -161,6 +163,7 @@ import com.sap.sse.common.impl.DegreeBearingImpl;
161 163
import com.sap.sse.common.impl.MillisecondsTimePoint;
162 164
import com.sap.sse.common.impl.RGBColor;
163 165
import com.sap.sse.common.impl.TimeRangeImpl;
166
+import com.sap.sse.gwt.client.DOMUtils;
164 167
import com.sap.sse.gwt.client.ErrorReporter;
165 168
import com.sap.sse.gwt.client.Notification;
166 169
import com.sap.sse.gwt.client.Notification.NotificationType;
... ...
@@ -403,7 +406,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
403 406
private final TrueNorthIndicatorPanel trueNorthIndicatorPanel;
404 407
private final FlowPanel topLeftControlsWrapperPanel;
405 408
406
- private final TimeRangeActionsExecutor<CompactBoatPositionsDTO, GPSFixDTOWithSpeedWindTackAndLegTypeIterable, String> timeRangeActionsExecutor;
409
+ private final TimeRangeActionsExecutor<CompactBoatPositionsDTO, GPSFixDTOWithSpeedWindTackAndLegTypeIterable, Pair<String, DetailType>> timeRangeActionsExecutor;
407 410
private final AsyncActionsExecutor asyncActionsExecutor;
408 411
409 412
/**
... ...
@@ -1504,7 +1507,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
1504 1507
timeRangeActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race,
1505 1508
fromTimesForNonOverlappingTailsCall, toTimesForNonOverlappingTailsCall, /* extrapolate */ true,
1506 1509
detailType, leaderboardName, leaderboardGroupName, leaderboardGroupId),
1507
- new GetBoatPositionsCallback(new AsyncCallback<CompactBoatPositionsDTO>() {
1510
+ new GetBoatPositionsCallback(detailType, new AsyncCallback<CompactBoatPositionsDTO>() {
1508 1511
@Override
1509 1512
public void onFailure(Throwable t) {
1510 1513
errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */);
... ...
@@ -2864,12 +2867,17 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
2864 2867
}
2865 2868
2866 2869
private ListBox createDetailTypeDropdown(CompetitorDTO competitor) {
2867
- ListBox lb = new ListBox();
2870
+ final ListBox lb = new ListBox();
2871
+ final NodeList<OptionElement> options = DOMUtils.getOptions(lb);
2868 2872
lb.addItem(stringMessages.none(), "none");
2869 2873
if (sortedAvailableDetailTypes != null) {
2870 2874
for (int i = 0; i < sortedAvailableDetailTypes.size(); i++) {
2871
- DetailType detail = sortedAvailableDetailTypes.get(i);
2875
+ final DetailType detail = sortedAvailableDetailTypes.get(i);
2872 2876
lb.addItem(DetailTypeFormatter.format(detail), detail.name());
2877
+ final String tooltip = DetailTypeFormatter.getTooltip(detail);
2878
+ if (Util.hasLength(tooltip)) {
2879
+ options.getItem(options.getLength()-1).setTitle(tooltip);
2880
+ }
2873 2881
if (detail == selectedDetailType) {
2874 2882
lb.setSelectedIndex(i + 1);
2875 2883
}
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
... ...
@@ -2456,16 +2456,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
2456 2456
2457 2457
@Override
2458 2458
public SwissTimingEventRecordDTO getRacesOfSwissTimingEvent(String eventJsonURL)
2459
- throws UnknownHostException, IOException, InterruptedException, ParseException {
2459
+ throws UnknownHostException, IOException, InterruptedException, ParseException, URISyntaxException {
2460 2460
SwissTimingEventRecordDTO result = null;
2461
- List<SwissTimingRaceRecordDTO> swissTimingRaces = new ArrayList<SwissTimingRaceRecordDTO>();
2462
-
2463
- // TODO: delete getSwissTimingAdapter().getSwissTimingRaceRecords() method
2464
- // TODO: delete SwissTimingDomainFactory.getRaceTypeFromRaceID(String raceID)
2465
- URL url = new URL(eventJsonURL);
2466
- URLConnection eventResultConn = HttpUrlConnectionHelper.redirectConnection(url);
2467
- Manage2SailEventResultsParserImpl parser = new Manage2SailEventResultsParserImpl();
2468
- EventResultDescriptor eventResult = parser.getEventResult((InputStream) eventResultConn.getContent());
2461
+ final List<SwissTimingRaceRecordDTO> swissTimingRaces = new ArrayList<>();
2462
+ final URL url = new URL(eventJsonURL);
2463
+ final Manage2SailEventResultsParserImpl parser = new Manage2SailEventResultsParserImpl();
2464
+ final EventResultDescriptor eventResult = parser.getEventResult(url);
2469 2465
if (eventResult != null) {
2470 2466
for (RegattaResultDescriptor regattaResult : eventResult.getRegattaResults()) {
2471 2467
for (RaceResultDescriptor race : regattaResult.getRaceResults()) {
... ...
@@ -4077,12 +4073,13 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
4077 4073
}
4078 4074
4079 4075
@Override
4080
- public Iterable<RegattaDTO> getRegattas(String manage2SailJsonUrl) {
4081
- StructureImporter structureImporter = new StructureImporter(new SetRacenumberFromSeries(), baseDomainFactory);
4082
- Iterable<RegattaJSON> parsedEvent = structureImporter.parseEvent(manage2SailJsonUrl);
4083
- List<RegattaDTO> regattaDTOs = new ArrayList<RegattaDTO>();
4084
- Iterable<Regatta> regattas = structureImporter.getRegattas(parsedEvent);
4085
- for (Regatta regatta : regattas) {
4076
+ public Iterable<RegattaDTO> getManage2SailRegattas(String manage2SailJsonUrl) throws MalformedURLException, URISyntaxException {
4077
+ final StructureImporter structureImporter = new StructureImporter(new SetRacenumberFromSeries(), baseDomainFactory);
4078
+ final String manage2SailJsonUrlWithAccessToken = com.sap.sailing.manage2sail.Activator.getInstance().addAccessTokenToManage2SailUrl(new URL(manage2SailJsonUrl)).toString();
4079
+ final Iterable<RegattaJSON> parsedEvent = structureImporter.parseEvent(manage2SailJsonUrlWithAccessToken);
4080
+ final List<RegattaDTO> regattaDTOs = new ArrayList<RegattaDTO>();
4081
+ final Iterable<Regatta> regattas = structureImporter.getRegattas(parsedEvent);
4082
+ for (final Regatta regatta : regattas) {
4086 4083
regattaDTOs.add(convertToRegattaDTO(regatta));
4087 4084
}
4088 4085
return regattaDTOs;
java/com.sap.sailing.kiworesultimport/src/com/sap/sailing/kiworesultimport/impl/ScoreCorrectionProviderImpl.java
... ...
@@ -3,6 +3,7 @@ package com.sap.sailing.kiworesultimport.impl;
3 3
import java.io.File;
4 4
import java.io.IOException;
5 5
import java.io.InputStream;
6
+import java.net.URISyntaxException;
6 7
import java.util.ArrayList;
7 8
import java.util.HashMap;
8 9
import java.util.HashSet;
... ...
@@ -40,7 +41,7 @@ public class ScoreCorrectionProviderImpl extends AbstractFileBasedScoreCorrectio
40 41
41 42
@Override
42 43
public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName()
43
- throws IOException, SAXException, ParserConfigurationException {
44
+ throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
44 45
Map<String, Set<Util.Pair<String, TimePoint>>> result = new HashMap<String, Set<Util.Pair<String, TimePoint>>>();
45 46
for (RegattaSummary regattaSummary : getAllRegattaSummaries()) {
46 47
String eventName = regattaSummary.getEventName();
... ...
@@ -71,7 +72,7 @@ public class ScoreCorrectionProviderImpl extends AbstractFileBasedScoreCorrectio
71 72
return result;
72 73
}
73 74
74
- private Iterable<RegattaSummary> getAllRegattaSummaries() throws IOException, SAXException, ParserConfigurationException {
75
+ private Iterable<RegattaSummary> getAllRegattaSummaries() throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
75 76
final List<RegattaSummary> result = new ArrayList<RegattaSummary>();
76 77
for (ResultDocumentDescriptor resultDocDescr : getResultDocumentProvider().getResultDocumentDescriptors()) {
77 78
if (resultDocDescr.getDocumentName().toLowerCase().endsWith(".zip")) {
... ...
@@ -88,7 +89,7 @@ public class ScoreCorrectionProviderImpl extends AbstractFileBasedScoreCorrectio
88 89
}
89 90
@Override
90 91
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName,
91
- TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException {
92
+ TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
92 93
for (RegattaSummary regattaSummary : getAllRegattaSummaries()) {
93 94
if (regattaSummary.getEventName().equals(eventName) && regattaSummary.getBoatClassName().equals(boatClassName) &&
94 95
regattaSummary.getTimePointPublished().equals(timePointPublished)) {
java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/CompetitorImportTest.java
... ...
@@ -8,6 +8,7 @@ import static org.mockito.Mockito.when;
8 8
9 9
import java.io.FileNotFoundException;
10 10
import java.io.IOException;
11
+import java.net.URISyntaxException;
11 12
import java.net.URL;
12 13
import java.util.Arrays;
13 14
... ...
@@ -38,7 +39,7 @@ import com.sap.sse.common.Util;
38 39
*/
39 40
public class CompetitorImportTest extends AbstractEventResultJsonServiceTest {
40 41
@Test
41
- public void simpleCompetitorImportTest() throws FileNotFoundException, IOException, JAXBException {
42
+ public void simpleCompetitorImportTest() throws FileNotFoundException, IOException, JAXBException, URISyntaxException {
42 43
ResultUrlRegistry resultUrlRegistry = mock(ResultUrlRegistry.class);
43 44
when(resultUrlRegistry.getReadableResultUrls(AbstractManage2SailProvider.NAME)).thenReturn(Arrays.asList(getClass().getClassLoader().getResource(EVENT_RESULTS_JSON+".txt")));
44 45
final Manage2SailCompetitorProvider competitorImporter = new Manage2SailCompetitorProvider(ParserFactory.INSTANCE, resultUrlRegistry);
... ...
@@ -53,7 +54,7 @@ public class CompetitorImportTest extends AbstractEventResultJsonServiceTest {
53 54
}
54 55
55 56
@Test
56
- public void simpleCompetitorImportTestNoResultsYet() throws FileNotFoundException, IOException, JAXBException {
57
+ public void simpleCompetitorImportTestNoResultsYet() throws FileNotFoundException, IOException, JAXBException, URISyntaxException {
57 58
ResultUrlRegistry resultUrlRegistry = mock(ResultUrlRegistry.class);
58 59
when(resultUrlRegistry.getReadableResultUrls(AbstractManage2SailProvider.NAME)).thenReturn(Arrays.asList(getClass().getClassLoader().getResource("VSaW_420_Test.json.txt")));
59 60
final CompetitorProvider competitorImporter = new Manage2SailCompetitorProvider(ParserFactory.INSTANCE, resultUrlRegistry) {
... ...
@@ -77,7 +78,7 @@ public class CompetitorImportTest extends AbstractEventResultJsonServiceTest {
77 78
}
78 79
79 80
@Test
80
- public void simpleCompetitorImportTestNoResultsYetAndDivisionEmpty() throws FileNotFoundException, IOException, JAXBException {
81
+ public void simpleCompetitorImportTestNoResultsYetAndDivisionEmpty() throws FileNotFoundException, IOException, JAXBException, URISyntaxException {
81 82
ResultUrlRegistry resultUrlRegistry = mock(ResultUrlRegistry.class);
82 83
when(resultUrlRegistry.getReadableResultUrls(AbstractManage2SailProvider.NAME)).thenReturn(Arrays.asList(getClass().getClassLoader().getResource("VSaW_420_Test_EmptyDivision.json.txt")));
83 84
final CompetitorProvider competitorImporter = new Manage2SailCompetitorProvider(ParserFactory.INSTANCE, resultUrlRegistry) {
java/com.sap.sailing.manage2sail.resultimport.test/src/com/sap/sailing/manage2sail/resultimport/test/ParserTest.java
... ...
@@ -10,6 +10,7 @@ import java.io.FileInputStream;
10 10
import java.io.FileNotFoundException;
11 11
import java.io.IOException;
12 12
import java.io.InputStream;
13
+import java.net.URISyntaxException;
13 14
import java.util.ArrayList;
14 15
import java.util.Arrays;
15 16
import java.util.Date;
... ...
@@ -103,7 +104,7 @@ public class ParserTest {
103 104
104 105
@Test
105 106
public void testScoreCorrectionProviderFeedingAndHasResults() throws IOException, SAXException,
106
- ParserConfigurationException, JAXBException {
107
+ ParserConfigurationException, JAXBException, URISyntaxException {
107 108
ResultUrlRegistry resultUrlRegistry = new ResultUrlRegistryImpl(mock(MongoObjectFactory.class),
108 109
mock(DomainObjectFactory.class));
109 110
ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(
java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/AbstractManage2SailProvider.java
... ...
@@ -14,13 +14,14 @@ public abstract class AbstractManage2SailProvider extends AbstractResultUrlProvi
14 14
public static final String NAME = "Manage2Sail XRR Result Importer";
15 15
16 16
protected static final String EVENT_ID_REGEX = "^[\\da-f]{8}(-[\\da-f]{4}){3}-[\\da-f]{12}$";
17
- protected static final String EVENT_ID_TEMPLATE = "http://manage2sail.com/api/public/links/event/%s?accesstoken=bDAv8CwsTM94ujZ&mediaType=json";
17
+ private static String EVENT_ID_TEMPLATE;
18 18
19 19
private final ParserFactory parserFactory;
20 20
21 21
protected AbstractManage2SailProvider(ParserFactory parserFactory, ResultUrlRegistry resultUrlRegistry) {
22 22
super(resultUrlRegistry);
23 23
this.parserFactory = parserFactory;
24
+ EVENT_ID_TEMPLATE = "https://"+com.sap.sailing.manage2sail.Activator.getInstance().getManage2SailHostname()+"/api/public/links/event/%s?mediaType=json";
24 25
}
25 26
26 27
protected ParserFactory getParserFactory() {
... ...
@@ -31,13 +32,17 @@ public abstract class AbstractManage2SailProvider extends AbstractResultUrlProvi
31 32
public URL resolveUrl(String url) throws MalformedURLException {
32 33
String completedUrl = url;
33 34
if (url.matches(EVENT_ID_REGEX)) {
34
- completedUrl = String.format(EVENT_ID_TEMPLATE, url);
35
+ completedUrl = String.format(getEventIdTemplate(), url);
35 36
}
36
- return new URL(completedUrl); //TODO Find a better way to check if a URL is valid
37
+ return new URL(completedUrl); // TODO Find a better way to check if a URL is valid
37 38
}
38 39
39 40
@Override
40 41
public String getOptionalSampleURL() {
41
- return "http://manage2sail.com/api/public/links/event/d30883d3-2876-4d7e-af49-891af6cbae1b?accesstoken=bDAv8CwsTM94ujZ&mediaType=json";
42
+ return String.format(getEventIdTemplate(), "d30883d3-2876-4d7e-af49-891af6cbae1b");
43
+ }
44
+
45
+ protected static String getEventIdTemplate() {
46
+ return EVENT_ID_TEMPLATE;
42 47
}
43 48
}
java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/AbstractManage2SailResultDocumentProvider.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sailing.manage2sail.resultimport;
2 2
3 3
import java.io.IOException;
4 4
import java.io.InputStream;
5
+import java.net.URISyntaxException;
5 6
import java.net.URL;
6 7
import java.net.URLConnection;
7 8
import java.util.ArrayList;
... ...
@@ -34,12 +35,11 @@ public abstract class AbstractManage2SailResultDocumentProvider implements Resul
34 35
}
35 36
36 37
@Override
37
- public Iterable<ResultDocumentDescriptor> getResultDocumentDescriptors() throws IOException {
38
+ public Iterable<ResultDocumentDescriptor> getResultDocumentDescriptors() throws IOException, URISyntaxException {
38 39
List<ResultDocumentDescriptor> result = new ArrayList<>();
39 40
Manage2SailEventResultsParserImpl parser = new Manage2SailEventResultsParserImpl();
40 41
for (URL url : resultUrlProvider.getReadableUrls()) {
41
- URLConnection eventResultConn = HttpUrlConnectionHelper.redirectConnection(url);
42
- EventResultDescriptor eventResult = parser.getEventResult((InputStream) eventResultConn.getContent());
42
+ EventResultDescriptor eventResult = parser.getEventResult(url);
43 43
addResultsForEvent(result, eventResult);
44 44
}
45 45
return result;
java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/CompetitorDocumentProvider.java
... ...
@@ -1,11 +1,9 @@
1 1
package com.sap.sailing.manage2sail.resultimport;
2 2
3
-import java.io.IOException;
4 3
import java.net.URL;
5 4
6 5
import com.sap.sailing.domain.resultimport.ResultUrlProvider;
7 6
import com.sap.sailing.manage2sail.RegattaResultDescriptor;
8
-import com.sap.sailing.resultimport.ResultDocumentDescriptor;
9 7
10 8
public class CompetitorDocumentProvider extends AbstractManage2SailResultDocumentProvider {
11 9
public CompetitorDocumentProvider(ResultUrlProvider resultUrlProvider) {
... ...
@@ -13,11 +11,6 @@ public class CompetitorDocumentProvider extends AbstractManage2SailResultDocumen
13 11
}
14 12
15 13
@Override
16
- public Iterable<ResultDocumentDescriptor> getResultDocumentDescriptors() throws IOException {
17
- return super.getResultDocumentDescriptors();
18
- }
19
-
20
- @Override
21 14
protected URL getDocumentUrlForRegatta(RegattaResultDescriptor regattaResult) {
22 15
return regattaResult.getXrrEntriesUrl();
23 16
}
java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/Manage2SailCompetitorProvider.java
... ...
@@ -1,6 +1,7 @@
1 1
package com.sap.sailing.manage2sail.resultimport;
2 2
3 3
import java.io.IOException;
4
+import java.net.URISyntaxException;
4 5
import java.util.Locale;
5 6
import java.util.Map;
6 7
import java.util.Set;
... ...
@@ -31,7 +32,7 @@ public class Manage2SailCompetitorProvider extends AbstractManage2SailProvider i
31 32
}
32 33
33 34
@Override
34
- public Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException {
35
+ public Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException, URISyntaxException {
35 36
return competitorResolver.getHasCompetitorsForRegattasInEvent();
36 37
}
37 38
... ...
@@ -41,7 +42,7 @@ public class Manage2SailCompetitorProvider extends AbstractManage2SailProvider i
41 42
}
42 43
43 44
@Override
44
- public Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName) throws JAXBException, IOException {
45
+ public Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName) throws JAXBException, IOException, URISyntaxException {
45 46
return competitorResolver.getCompetitorDescriptors(eventName, regattaName);
46 47
}
47 48
java/com.sap.sailing.manage2sail.resultimport/src/com/sap/sailing/manage2sail/resultimport/ScoreCorrectionProviderImpl.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sailing.manage2sail.resultimport;
2 2
3 3
import java.io.IOException;
4 4
import java.io.InputStream;
5
+import java.net.URISyntaxException;
5 6
import java.util.HashMap;
6 7
import java.util.HashSet;
7 8
import java.util.Map;
... ...
@@ -47,7 +48,7 @@ public class ScoreCorrectionProviderImpl extends AbstractManage2SailProvider imp
47 48
}
48 49
49 50
@Override
50
- public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() {
51
+ public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() throws URISyntaxException {
51 52
Map<String, Set<Util.Pair<String, TimePoint>>> result = new HashMap<String, Set<Util.Pair<String,TimePoint>>>();
52 53
try {
53 54
for (ResultDocumentDescriptor resultDocDescr : documentProvider.getResultDocumentDescriptors()) {
... ...
@@ -73,7 +74,7 @@ public class ScoreCorrectionProviderImpl extends AbstractManage2SailProvider imp
73 74
74 75
@Override
75 76
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName,
76
- TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException {
77
+ TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
77 78
final Parser parser = resolveParser(eventName, boatClassName, timePointPublished);
78 79
try {
79 80
final RegattaResults regattaResults = parser.parse();
... ...
@@ -94,7 +95,7 @@ public class ScoreCorrectionProviderImpl extends AbstractManage2SailProvider imp
94 95
/* boatClassNameFilter */ Optional.empty());
95 96
}
96 97
97
- private Parser resolveParser(String eventName, String boatClassName, TimePoint timePointPublished) throws IOException {
98
+ private Parser resolveParser(String eventName, String boatClassName, TimePoint timePointPublished) throws IOException, URISyntaxException {
98 99
Parser result = null;
99 100
for (ResultDocumentDescriptor resultDocDescr : documentProvider.getResultDocumentDescriptors()) {
100 101
String boatClassAndGenderType = resultDocDescr.getBoatClass();
java/com.sap.sailing.manage2sail/META-INF/MANIFEST.MF
... ...
@@ -6,9 +6,13 @@ Bundle-Version: 1.0.0.qualifier
6 6
Bundle-Vendor: SAP
7 7
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
8 8
Bundle-ActivationPolicy: lazy
9
+Bundle-Activator: com.sap.sailing.manage2sail.Activator
9 10
Require-Bundle: org.json.simple;bundle-version="1.1.0",
10 11
com.sap.sailing.domain.common,
11 12
com.sap.sailing.domain,
12 13
com.sap.sse
13 14
Export-Package: com.sap.sailing.manage2sail
15
+Import-Package: org.apache.http;version="4.4.9",
16
+ org.apache.http.client.utils;version="4.5.5",
17
+ org.osgi.framework
14 18
Automatic-Module-Name: com.sap.sailing.manage2sail
java/com.sap.sailing.manage2sail/src/com/sap/sailing/manage2sail/Activator.java
... ...
@@ -0,0 +1,59 @@
1
+package com.sap.sailing.manage2sail;
2
+
3
+import java.net.MalformedURLException;
4
+import java.net.URISyntaxException;
5
+import java.net.URL;
6
+import org.apache.http.client.utils.URIBuilder;
7
+import org.osgi.framework.BundleActivator;
8
+import org.osgi.framework.BundleContext;
9
+
10
+public class Activator implements BundleActivator {
11
+ private static final String MANAGE2SAIL_HOSTNAME = "www.manage2sail.com";
12
+ private static final String ACCESS_TOKEN_PROPERTY_NAME = "manage2sail.accesstoken";
13
+ private static final String ACCESS_TOKEN_URL_QUERY_PARAMETER_NAME = "accesstoken";
14
+ private static Activator INSTANCE;
15
+ private String accessToken;
16
+
17
+ @Override
18
+ public void start(BundleContext context) throws Exception {
19
+ INSTANCE = this;
20
+ accessToken = System.getProperty(ACCESS_TOKEN_PROPERTY_NAME);
21
+ }
22
+
23
+ @Override
24
+ public void stop(BundleContext context) throws Exception {
25
+ }
26
+
27
+ public static Activator getInstance() {
28
+ if (INSTANCE == null) { // test scenario maybe
29
+ INSTANCE = new Activator();
30
+ }
31
+ return INSTANCE;
32
+ }
33
+
34
+ private String getAccessToken() {
35
+ return accessToken;
36
+ }
37
+
38
+ /**
39
+ * If and only if the {@link URL#getHost() host} part of the URL equals the value of the {@link #getManage2SailHostname()} and
40
+ * there is not yet a query parameter named {@code accesstoken} in the {@code url},
41
+ * a new URL with an "accesstoken=${accessToken}" parameter appended to the {@link URL#getQuery() query} part of the {@code url}
42
+ * will be returned. Otherwise, the {@code url} will be returned unchanged.
43
+ */
44
+ public URL addAccessTokenToManage2SailUrl(URL url) throws URISyntaxException, MalformedURLException {
45
+ final URL result;
46
+ final URIBuilder uriBuilder = new URIBuilder(url.toURI());
47
+ final boolean containsAccessTokenQueryParam = uriBuilder.getQueryParams().stream().anyMatch(nameValuePair -> nameValuePair.getName().equals(ACCESS_TOKEN_URL_QUERY_PARAMETER_NAME));
48
+ if (url.getHost().equals(getManage2SailHostname()) && !containsAccessTokenQueryParam) {
49
+ result = uriBuilder.addParameter(ACCESS_TOKEN_URL_QUERY_PARAMETER_NAME, getAccessToken()).build().toURL();
50
+ } else {
51
+ result = url;
52
+ }
53
+ return result;
54
+ }
55
+
56
+ public String getManage2SailHostname() {
57
+ return MANAGE2SAIL_HOSTNAME;
58
+ }
59
+}
java/com.sap.sailing.manage2sail/src/com/sap/sailing/manage2sail/Manage2SailEventResultsParser.java
... ...
@@ -4,5 +4,5 @@ import java.io.IOException;
4 4
import java.io.InputStream;
5 5
6 6
public interface Manage2SailEventResultsParser {
7
- public EventResultDescriptor getEventResult(InputStream is) throws IOException;
7
+ EventResultDescriptor getEventResult(InputStream is) throws IOException;
8 8
}
java/com.sap.sailing.manage2sail/src/com/sap/sailing/manage2sail/Manage2SailEventResultsParserImpl.java
... ...
@@ -4,6 +4,7 @@ import java.io.IOException;
4 4
import java.io.InputStream;
5 5
import java.io.InputStreamReader;
6 6
import java.net.MalformedURLException;
7
+import java.net.URISyntaxException;
7 8
import java.net.URL;
8 9
import java.util.Date;
9 10
... ...
@@ -15,11 +16,12 @@ import org.json.simple.parser.ParseException;
15 16
import com.sap.sailing.domain.common.CompetitorGenderType;
16 17
import com.sap.sse.InvalidDateException;
17 18
import com.sap.sse.util.DateParser;
19
+import com.sap.sse.util.HttpUrlConnectionHelper;
18 20
19 21
/**
20 22
* URLs should be of the form
21 23
* <pre>
22
- * http://manage2sail.com/api/public/links/event/d30883d3-2876-4d7e-af49-891af6cbae1b?accesstoken=bDAv8CwsTM94ujZ&mediaType=json
24
+ * https://www.manage2sail.com/api/public/links/event/d30883d3-2876-4d7e-af49-891af6cbae1b?accesstoken=bDAv8CwsTM94ujZ&mediaType=json
23 25
* </pre>
24 26
* where the UUID following the <code>event</code> path element represents the event ID. Events can be
25 27
* discovered by the manage2sail.com website.
... ...
@@ -28,7 +30,11 @@ import com.sap.sse.util.DateParser;
28 30
*
29 31
*/
30 32
public class Manage2SailEventResultsParserImpl implements Manage2SailEventResultsParser {
31
-
33
+ public EventResultDescriptor getEventResult(URL eventJsonUrl) throws MalformedURLException, IOException, URISyntaxException {
34
+ return getEventResult((InputStream) HttpUrlConnectionHelper
35
+ .redirectConnection(Activator.getInstance().addAccessTokenToManage2SailUrl(eventJsonUrl)).getContent());
36
+ }
37
+
32 38
/**
33 39
* @param is closed before the method returns, also in case of exception
34 40
*/
java/com.sap.sailing.polars.datamining/resources/stringmessages/Polars_StringMessages_de.properties
... ...
@@ -1,6 +1,6 @@
1 1
PolarChain1=Polare
2 2
PolarChain2=Backend-Polare
3
-LegIndex=Schenkel Index
3
+LegIndex=Bahnschenkel Index
4 4
PolarData=Polardaten
5 5
Polars=Polare
6 6
GPSFix=GPS Fix
java/com.sap.sailing.resultimport/src/com/sap/sailing/resultimport/ResultDocumentProvider.java
... ...
@@ -1,7 +1,8 @@
1 1
package com.sap.sailing.resultimport;
2 2
3 3
import java.io.IOException;
4
+import java.net.URISyntaxException;
4 5
5 6
public interface ResultDocumentProvider {
6
- Iterable<ResultDocumentDescriptor> getResultDocumentDescriptors() throws IOException;
7
+ Iterable<ResultDocumentDescriptor> getResultDocumentDescriptors() throws IOException, URISyntaxException;
7 8
}
java/com.sap.sailing.sailti.resultimport.test/src/com/sap/sailing/sailti/resultimport/test/TrofeoSofia_ParserTest.java
... ...
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertNotNull;
5 5
import static org.mockito.Mockito.mock;
6 6
7 7
import java.io.IOException;
8
+import java.net.URISyntaxException;
8 9
import java.util.Map;
9 10
import java.util.Set;
10 11
... ...
@@ -41,7 +42,7 @@ public class TrofeoSofia_ParserTest extends AbstractTrofeoSofiaTest {
41 42
42 43
@Test
43 44
public void testScoreCorrectionProviderFeedingAndHasResults() throws IOException, SAXException,
44
- ParserConfigurationException, JAXBException {
45
+ ParserConfigurationException, JAXBException, URISyntaxException {
45 46
ResultUrlRegistry resultUrlRegistry = new ResultUrlRegistryImpl(mock(MongoObjectFactory.class),
46 47
mock(DomainObjectFactory.class));
47 48
ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(
java/com.sap.sailing.sailti.resultimport/src/com/sap/sailing/sailti/resultimport/SailtiEventResultsParserImpl.java
... ...
@@ -25,13 +25,6 @@ import com.sap.sse.util.HttpUrlConnectionHelper;
25 25
* for that class can parse the document into an {@link EventResultDescriptor} from which the regatta results can be obtained
26 26
* using {@link EventResultDescriptor#getRegattaResults()}.<p>
27 27
*
28
- * TODO bug5693 Sailti URL format...: URLs should be of the form
29
- * <pre>
30
- * http://manage2sail.com/api/public/links/event/d30883d3-2876-4d7e-af49-891af6cbae1b?accesstoken=bDAv8CwsTM94ujZ&mediaType=json
31
- * </pre>
32
- * where the UUID following the <code>event</code> path element represents the event ID. Events can be
33
- * discovered by the manage2sail.com website.
34
- *
35 28
* @author Axel Uhl (D043530)
36 29
*
37 30
*/
java/com.sap.sailing.sailti.resultimport/src/com/sap/sailing/sailti/resultimport/ScoreCorrectionProviderImpl.java
... ...
@@ -3,6 +3,7 @@ package com.sap.sailing.sailti.resultimport;
3 3
import java.io.IOException;
4 4
import java.io.InputStream;
5 5
import java.net.MalformedURLException;
6
+import java.net.URISyntaxException;
6 7
import java.net.URL;
7 8
import java.util.HashMap;
8 9
import java.util.HashSet;
... ...
@@ -56,7 +57,7 @@ public class ScoreCorrectionProviderImpl extends AbstractResultUrlProvider imple
56 57
}
57 58
58 59
@Override
59
- public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() {
60
+ public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() throws URISyntaxException {
60 61
Map<String, Set<Util.Pair<String, TimePoint>>> result = new HashMap<String, Set<Util.Pair<String,TimePoint>>>();
61 62
try {
62 63
for (ResultDocumentDescriptor resultDocDescr : documentProvider.getResultDocumentDescriptors()) {
... ...
@@ -82,7 +83,7 @@ public class ScoreCorrectionProviderImpl extends AbstractResultUrlProvider imple
82 83
83 84
@Override
84 85
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName,
85
- TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException {
86
+ TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
86 87
Parser parser = resolveParser(eventName, boatClassName);
87 88
try {
88 89
RegattaResults regattaResults = parser.parse();
... ...
@@ -103,7 +104,7 @@ public class ScoreCorrectionProviderImpl extends AbstractResultUrlProvider imple
103 104
/* boatClassNameFilter */ Optional.empty());
104 105
}
105 106
106
- private Parser resolveParser(String eventName, String boatClassName) throws IOException {
107
+ private Parser resolveParser(String eventName, String boatClassName) throws IOException, URISyntaxException {
107 108
Parser result = null;
108 109
for (ResultDocumentDescriptor resultDocDescr : documentProvider.getResultDocumentDescriptors()) {
109 110
if (eventName.equals(resultDocDescr.getEventName()) && boatClassName.equals(resultDocDescr.getBoatClass())) {
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/PageObject.java
... ...
@@ -383,6 +383,7 @@ public class PageObject {
383 383
/**
384 384
* <p>Finds and returns the first element with the specified selenium id in the given search context. If multiple
385 385
* elements exists, the element closest to the context is returned.</p>
386
+ * <p>To be more stable this search is based on a web driver wait. Default lookup timeout is used.
386 387
*
387 388
* @param context
388 389
* The search context to use for the search.
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/AdminConsolePage.java
... ...
@@ -22,6 +22,7 @@ import com.sap.sailing.selenium.pages.adminconsole.event.EventConfigurationPanel
22 22
import com.sap.sailing.selenium.pages.adminconsole.igtimi.IgtimiAccountsManagementPanelPO;
23 23
import com.sap.sailing.selenium.pages.adminconsole.leaderboard.LeaderboardConfigurationPanelPO;
24 24
import com.sap.sailing.selenium.pages.adminconsole.leaderboard.LeaderboardGroupConfigurationPanelPO;
25
+import com.sap.sailing.selenium.pages.adminconsole.racemanagementapp.RaceManagementAppPanelPO;
25 26
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaStructureManagementPanelPO;
26 27
import com.sap.sailing.selenium.pages.adminconsole.roles.RoleDefinitionsPanelPO;
27 28
import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesBoatsPanelPO;
... ...
@@ -49,6 +50,9 @@ public class AdminConsolePage extends HostPageWithAuthentication {
49 50
private static final String EVENTS_TAB_LABEL = "Events"; //$NON-NLS-1$
50 51
private static final String EVENTS_TAB_IDENTIFIER = "EventManagement"; //$NON-NLS-1$
51 52
53
+ private static final String RACE_MANAGER_APP_TAB_LABEL = "Race Manager App"; //$NON-NLS-1$
54
+ private static final String RACE_MANAGER_APP_TAB_IDENTIFIER = "RaceCommiteeAppPanel"; //$NON-NLS-1$
55
+
52 56
private static final String REGATTA_STRUCTURE_TAB_LABEL = "Regattas"; //$NON-NLS-1$
53 57
private static final String REGATTA_STRUCTURE_TAB_IDENTIFIER = "RegattaStructureManagement"; //$NON-NLS-1$
54 58
... ...
@@ -160,6 +164,10 @@ public class AdminConsolePage extends HostPageWithAuthentication {
160 164
return new EventConfigurationPanelPO(this.driver, goToTab(EVENTS_TAB_LABEL, EVENTS_TAB_IDENTIFIER, true));
161 165
}
162 166
167
+ public RaceManagementAppPanelPO goToRaceManagerApp() {
168
+ return new RaceManagementAppPanelPO(this.driver, goToTab(RACE_MANAGER_APP_TAB_LABEL, RACE_MANAGER_APP_TAB_IDENTIFIER, true));
169
+ }
170
+
163 171
public UserManagementPanelPO goToUserManagement() {
164 172
goToTab(ADVANCED_PARENT_LABEL, ADVANCED_TAB_PARENT_IDENTIFIER, true);
165 173
return new UserManagementPanelPO(this.driver,
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/connectors/SmartphoneTrackingEventManagementPanelPO.java
... ...
@@ -11,8 +11,14 @@ import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesListPO;
11 11
import com.sap.sailing.selenium.pages.gwt.CellTablePO;
12 12
import com.sap.sailing.selenium.pages.gwt.DataEntryPO;
13 13
import com.sap.sailing.selenium.pages.gwt.GenericCellTablePO;
14
+import com.sap.sailing.selenium.test.adminconsole.smartphonetracking.MapDevicesDialogPO;
15
+import com.sap.sailing.selenium.test.adminconsole.smartphonetracking.RegisterCompetitorsDialogPO;
14 16
15 17
public class SmartphoneTrackingEventManagementPanelPO extends PageArea {
18
+
19
+ private static final String ACTION_COMPETITOR_REGISTRATIONS = "ACTION_COMPETITOR_REGISTRATIONS";
20
+ private static final String ACTION_MAP_DEVICES = "ACTION_MAP_DEVICES";
21
+
16 22
@FindBy(how = BySeleniumId.class, using = "AvailableLeaderboardsTable")
17 23
private WebElement leaderboardTable;
18 24
... ...
@@ -45,4 +51,14 @@ public class SmartphoneTrackingEventManagementPanelPO extends PageArea {
45 51
leaderboardRefreshButton.click();
46 52
waitForAjaxRequests();
47 53
}
54
+
55
+ public RegisterCompetitorsDialogPO pushCompetitorRegistrationsActionButton(DataEntryPO aLeaderboard) {
56
+ aLeaderboard.clickActionImage(ACTION_COMPETITOR_REGISTRATIONS);
57
+ return this.waitForPO(RegisterCompetitorsDialogPO::new, "registerCompetitorsDialog");
58
+ }
59
+
60
+ public MapDevicesDialogPO pushMapDevicesActionButton(DataEntryPO aLeaderboard) {
61
+ aLeaderboard.clickActionImage(ACTION_MAP_DEVICES);
62
+ return this.waitForPO(MapDevicesDialogPO::new, "regattaLogTrackingDeviceMappingsDialog");
63
+ }
48 64
}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/racemanagementapp/DeviceConfigurationCreateDialogPO.java
... ...
@@ -0,0 +1,23 @@
1
+package com.sap.sailing.selenium.pages.adminconsole.racemanagementapp;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.core.BySeleniumId;
7
+import com.sap.sailing.selenium.core.FindBy;
8
+import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
9
+
10
+public class DeviceConfigurationCreateDialogPO extends DataEntryDialogPO {
11
+
12
+ @FindBy(how = BySeleniumId.class, using = "raceManagerDeviceName")
13
+ private WebElement nameField;
14
+
15
+ protected DeviceConfigurationCreateDialogPO(WebDriver driver, WebElement element) {
16
+ super(driver, element);
17
+ }
18
+
19
+ public void setDeviceName(String name) {
20
+ nameField.sendKeys(name);
21
+ }
22
+
23
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/racemanagementapp/DeviceConfigurationDetailsAreaPO.java
... ...
@@ -0,0 +1,26 @@
1
+package com.sap.sailing.selenium.pages.adminconsole.racemanagementapp;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.core.BySeleniumId;
7
+import com.sap.sailing.selenium.core.FindBy;
8
+import com.sap.sailing.selenium.pages.PageArea;
9
+
10
+public class DeviceConfigurationDetailsAreaPO extends PageArea {
11
+
12
+ public static final String DEVICE_CONFIGURATION_QR_CODE_DIALOG_ID = "DeviceConfigurationQRIdentifierDialog";
13
+
14
+ @FindBy(how = BySeleniumId.class, using = "DeviceConfigurationDetailsQrCodeButton")
15
+ private WebElement deviceConfigurationDetailsQrCodeButton;
16
+
17
+ public DeviceConfigurationDetailsAreaPO(WebDriver driver, WebElement element) {
18
+ super(driver, element);
19
+ }
20
+
21
+ public DeviceConfigurationQRCodeDialogPO openQRCodeDialog() {
22
+ deviceConfigurationDetailsQrCodeButton.click();
23
+ return getPO(DeviceConfigurationQRCodeDialogPO::new, DEVICE_CONFIGURATION_QR_CODE_DIALOG_ID);
24
+ }
25
+
26
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/racemanagementapp/DeviceConfigurationQRCodeDialogPO.java
... ...
@@ -0,0 +1,23 @@
1
+package com.sap.sailing.selenium.pages.adminconsole.racemanagementapp;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.core.BySeleniumId;
7
+import com.sap.sailing.selenium.core.FindBy;
8
+import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
9
+
10
+public class DeviceConfigurationQRCodeDialogPO extends DataEntryDialogPO {
11
+
12
+ @FindBy(how = BySeleniumId.class, using = "QRIdentifierURL")
13
+ private WebElement url;
14
+
15
+ protected DeviceConfigurationQRCodeDialogPO(WebDriver driver, WebElement element) {
16
+ super(driver, element);
17
+ }
18
+
19
+ public String getUrl() {
20
+ return url.getText();
21
+ }
22
+
23
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/racemanagementapp/DeviceConfigurationsTablePO.java
... ...
@@ -0,0 +1,34 @@
1
+package com.sap.sailing.selenium.pages.adminconsole.racemanagementapp;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.pages.gwt.CellTablePO;
7
+import com.sap.sailing.selenium.pages.gwt.DataEntryPO;
8
+import com.sap.sailing.selenium.pages.gwt.GenericCellTablePO;
9
+
10
+public class DeviceConfigurationsTablePO extends GenericCellTablePO<DeviceConfigurationsTablePO.DeviceConfigurationEntryPO> {
11
+
12
+ public static class DeviceConfigurationEntryPO extends DataEntryPO {
13
+
14
+ public DeviceConfigurationEntryPO(CellTablePO<?> table, WebElement element) {
15
+ super(table, element);
16
+ }
17
+
18
+ @Override
19
+ public Object getIdentifier() {
20
+ return getColumnContent("Device");
21
+ }
22
+
23
+ }
24
+
25
+ public DeviceConfigurationsTablePO(WebDriver driver, WebElement element) {
26
+ super(driver, element, DeviceConfigurationEntryPO.class);
27
+ }
28
+
29
+ @Override
30
+ protected DeviceConfigurationEntryPO createDataEntry(WebElement element) {
31
+ return new DeviceConfigurationEntryPO(this, element);
32
+ }
33
+
34
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/racemanagementapp/RaceManagementAppPanelPO.java
... ...
@@ -0,0 +1,44 @@
1
+package com.sap.sailing.selenium.pages.adminconsole.racemanagementapp;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.core.BySeleniumId;
7
+import com.sap.sailing.selenium.core.FindBy;
8
+import com.sap.sailing.selenium.pages.PageArea;
9
+import com.sap.sailing.selenium.pages.adminconsole.racemanagementapp.DeviceConfigurationsTablePO.DeviceConfigurationEntryPO;
10
+import com.sap.sailing.selenium.pages.gwt.CellTablePO;
11
+import com.sap.sailing.selenium.pages.gwt.GenericCellTablePO;
12
+
13
+public class RaceManagementAppPanelPO extends PageArea {
14
+
15
+ private static final String DEVICE_CONGIGURATION_DETAILS_AREA_ID = "DeviceConfigurationDetailComposite";
16
+ private static final String NEW_DEVICE_CONFIGURATION_DIALOG_ID = "SelectNameForNewDeviceConfigurationDialog";
17
+
18
+ @FindBy(how = BySeleniumId.class, using = "addDeviceConfigurationButton")
19
+ private WebElement addDeviceConfigurationButton;
20
+
21
+ @FindBy(how = BySeleniumId.class, using = "DeviceConfigurationList")
22
+ private WebElement deviceConfigurationListTable;
23
+
24
+ public RaceManagementAppPanelPO(WebDriver driver, WebElement element) {
25
+ super(driver, element);
26
+ }
27
+
28
+ public DeviceConfigurationCreateDialogPO createDeviceConfiguration() {
29
+ addDeviceConfigurationButton.click();
30
+ return getPO(DeviceConfigurationCreateDialogPO::new, NEW_DEVICE_CONFIGURATION_DIALOG_ID);
31
+ }
32
+
33
+ public DeviceConfigurationDetailsAreaPO getDeviceConfigurationDetails() {
34
+ DeviceConfigurationEntryPO deviceConfiguration = getLeaderboardTable().getEntries().get(0);
35
+ deviceConfiguration.select();
36
+ return getPO(DeviceConfigurationDetailsAreaPO::new, DEVICE_CONGIGURATION_DETAILS_AREA_ID);
37
+ }
38
+
39
+
40
+ public CellTablePO<DeviceConfigurationEntryPO> getLeaderboardTable() {
41
+ return new GenericCellTablePO<>(this.driver, this.deviceConfigurationListTable, DeviceConfigurationEntryPO.class);
42
+ }
43
+
44
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaDetailsCompositePO.java
... ...
@@ -34,6 +34,9 @@ public class RegattaDetailsCompositePO extends PageArea {
34 34
@FindBy(how = BySeleniumId.class, using = "SeriesCellTable")
35 35
private WebElement seriesTable;
36 36
37
+ @FindBy(how = BySeleniumId.class, using = "registrationLinkWithQRCodeOpenButton")
38
+ private WebElement registrationLinkWithQRCodeOpenButton;
39
+
37 40
public RegattaDetailsCompositePO(WebDriver driver, WebElement element) {
38 41
super(driver, element);
39 42
}
... ...
@@ -107,4 +110,10 @@ public class RegattaDetailsCompositePO extends PageArea {
107 110
waitForAjaxRequests();
108 111
waitUntil(() -> Objects.equal(getRaceNames(series), races));
109 112
}
113
+
114
+ public RegistrationLinkWithQRCodeDialogPO configureRegistrationURL() {
115
+ registrationLinkWithQRCodeOpenButton.click();
116
+ WebElement dialog = findElementBySeleniumId(this.driver, "RegistrationLinkWithQRCodeDialog");
117
+ return new RegistrationLinkWithQRCodeDialogPO(this.driver, dialog);
118
+ }
110 119
}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaEditDialogPO.java
... ...
@@ -7,6 +7,8 @@ import com.sap.sailing.selenium.core.BySeleniumId;
7 7
import com.sap.sailing.selenium.core.FindBy;
8 8
import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
9 9
import com.sap.sailing.selenium.pages.gwt.CheckBoxPO;
10
+import com.sap.sailing.selenium.pages.gwt.ListBoxPO;
11
+import com.sap.sailing.selenium.pages.gwt.TextBoxPO;
10 12
11 13
public class RegattaEditDialogPO extends DataEntryDialogPO {
12 14
@FindBy(how = BySeleniumId.class, using = "RacingProcedureConfigurationCheckBox")
... ...
@@ -30,8 +32,8 @@ public class RegattaEditDialogPO extends DataEntryDialogPO {
30 32
@FindBy(how = BySeleniumId.class, using = "AddSeriesButton")
31 33
private WebElement addSeriesButton;
32 34
33
- @FindBy(how = BySeleniumId.class, using = "registrationLinkWithQRCodeOpenButton")
34
- private WebElement registrationLinkWithQRCodeOpenButton;
35
+ @FindBy(how = BySeleniumId.class, using = "SecretTextBox")
36
+ private WebElement secretTextBox;
35 37
36 38
public RegattaEditDialogPO(WebDriver driver, WebElement element) {
37 39
super(driver, element);
... ...
@@ -53,15 +55,18 @@ public class RegattaEditDialogPO extends DataEntryDialogPO {
53 55
return new SeriesCreateDialogPO(this.driver, dialog);
54 56
}
55 57
56
- public RegistrationLinkWithQRCodeDialogPO configureRegistrationURL() {
57
- registrationLinkWithQRCodeOpenButton.click();
58
- WebElement dialog = findElementBySeleniumId(this.driver, "RegistrationLinkWithQRCodeDialog");
59
- return new RegistrationLinkWithQRCodeDialogPO(this.driver, dialog);
60
- }
61
-
62 58
public void addSeries(String seriesName) {
63 59
SeriesCreateDialogPO seriesCreateDialog = addSeries();
64 60
seriesCreateDialog.setSeriesName(seriesName);
65 61
seriesCreateDialog.pressOk();
66 62
}
63
+
64
+ public String getSecret() {
65
+ return TextBoxPO.create(driver, secretTextBox).getValue();
66
+ }
67
+
68
+ public String getSelectedEventId() {
69
+ return ListBoxPO.create(driver, eventListBox).getSelectedOptionValue();
70
+ }
67 71
}
72
+
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaStructureManagementPanelPO.java
... ...
@@ -6,6 +6,7 @@ import org.openqa.selenium.WebDriver;
6 6
import org.openqa.selenium.WebElement;
7 7
import org.openqa.selenium.support.ui.WebDriverWait;
8 8
9
+import com.sap.sailing.domain.common.CompetitorRegistrationType;
9 10
import com.sap.sailing.selenium.core.BySeleniumId;
10 11
import com.sap.sailing.selenium.core.FindBy;
11 12
import com.sap.sailing.selenium.pages.PageArea;
... ...
@@ -43,7 +44,7 @@ public class RegattaStructureManagementPanelPO extends PageArea {
43 44
*
44 45
* @param regatta
45 46
*/
46
- public void createRegatta(RegattaDescriptor regatta) {
47
+ public void createRegatta(RegattaDescriptor regatta, boolean withDefaultLeaderboard) {
47 48
RegattaCreateDialogPO createRegattaDialog = startRegattaCreation();
48 49
createRegattaDialog.setRegattaName(regatta.getName()+" ("+regatta.getBoatClass()+")");
49 50
createRegattaDialog.setBoatClass(regatta.getBoatClass());
... ...
@@ -54,7 +55,12 @@ public class RegattaStructureManagementPanelPO extends PageArea {
54 55
// QUESTION: How do we handle an error (here or in the dialog)?
55 56
createRegattaDialog.pressOk();
56 57
DefaultRegattaLeaderboardCreateDialogPO createDefaultRegattaLeaderboardDialog = createDefaultRegattaLeaderboard();
57
- createDefaultRegattaLeaderboardDialog.pressCancel();
58
+ if (withDefaultLeaderboard) {
59
+ createDefaultRegattaLeaderboardDialog.pressOk();
60
+ } else {
61
+ createDefaultRegattaLeaderboardDialog.pressCancel();
62
+ }
63
+
58 64
}
59 65
60 66
public void createRegattaAndAddToEvent(RegattaDescriptor regatta, String event, String[] courseAreaNames) {
... ...
@@ -63,6 +69,7 @@ public class RegattaStructureManagementPanelPO extends PageArea {
63 69
createRegattaDialog.setBoatClass(regatta.getBoatClass());
64 70
createRegattaDialog.setCompetitorRegistrationType(regatta.getCompetitorRegistrationType());
65 71
createRegattaDialog.setEventAndCourseArea(event, courseAreaNames);
72
+ createRegattaDialog.setCompetitorRegistrationType(CompetitorRegistrationType.OPEN_UNMODERATED);
66 73
createRegattaDialog.pressOk();
67 74
createDefaultRegattaLeaderboard().pressOk();
68 75
waitForPO(ConfirmDialogPO::new, EventConfigurationPanelPO.ID_LINK_LEADERBORAD_TO_GROUP_DIALOG, 5).pressOk();
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegistrationLinkWithQRCodeDialogPO.java
... ...
@@ -6,6 +6,7 @@ import org.openqa.selenium.WebElement;
6 6
import com.sap.sailing.selenium.core.BySeleniumId;
7 7
import com.sap.sailing.selenium.core.FindBy;
8 8
import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
9
+import com.sap.sailing.selenium.pages.gwt.TextBoxPO;
9 10
10 11
public class RegistrationLinkWithQRCodeDialogPO extends DataEntryDialogPO {
11 12
... ...
@@ -15,8 +16,15 @@ public class RegistrationLinkWithQRCodeDialogPO extends DataEntryDialogPO {
15 16
@FindBy(how = BySeleniumId.class, using = "GenerateSecretButton")
16 17
private WebElement generateSecretButton;
17 18
19
+ @FindBy(how = BySeleniumId.class, using = "RegistrationLinkUrl")
20
+ private WebElement registrationLinkUrl;
21
+
18 22
protected RegistrationLinkWithQRCodeDialogPO(WebDriver driver, WebElement element) {
19 23
super(driver, element);
20 24
}
25
+
26
+ public String getRegistrationLinkUrl() {
27
+ return TextBoxPO.create(driver, registrationLinkUrl).getValue();
28
+ }
21 29
22 30
}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/common/DataEntryDialogPO.java
... ...
@@ -1,13 +1,11 @@
1 1
package com.sap.sailing.selenium.pages.common;
2 2
3
-import java.util.function.BooleanSupplier;
4
-
5 3
import org.openqa.selenium.Alert;
6 4
import org.openqa.selenium.JavascriptExecutor;
7
-import org.openqa.selenium.StaleElementReferenceException;
8 5
import org.openqa.selenium.WebDriver;
9 6
import org.openqa.selenium.WebElement;
10 7
import org.openqa.selenium.support.ui.ExpectedConditions;
8
+import org.openqa.selenium.support.ui.Wait;
11 9
import org.openqa.selenium.support.ui.WebDriverWait;
12 10
13 11
import com.sap.sailing.selenium.core.BySeleniumId;
... ...
@@ -65,35 +63,18 @@ public abstract class DataEntryDialogPO extends PageArea {
65 63
public void pressOk(boolean acceptAlert, boolean waitForAjaxRequests) {
66 64
// This generically triggers revalidation in dialogs to ensure that the ok button gets enabled
67 65
((JavascriptExecutor) driver).executeScript("!!document.activeElement ? document.activeElement.blur() : 0");
68
-
69
- scrollToView(this.okButton);
70
- // Browsers may use smooth scrolling
71
- waitUntil(() -> isElementEntirelyVisible(this.okButton) && this.okButton.isEnabled());
72
- this.okButton.click();
73
-
66
+ Wait<WebDriver> wait = new WebDriverWait(driver, 20);
67
+ // click OK
68
+ wait.until(ExpectedConditions.elementToBeClickable(okButton)).click();
74 69
if (acceptAlert) {
75 70
final Alert alert = new WebDriverWait(driver, DEFAULT_WAIT_TIMEOUT_SECONDS)
76 71
.until(ExpectedConditions.alertIsPresent());
77 72
alert.accept();
78 73
}
79
-
80 74
if (waitForAjaxRequests) {
81 75
// Wait, since we do a callback usually
82 76
waitForAjaxRequests();
83 77
}
84
-
85
- // This waits until the dialog is physically closed to make sure further don't fail because the dialog still covers other elements
86
- waitUntil(new BooleanSupplier() {
87
- @Override
88
- public boolean getAsBoolean() {
89
- try {
90
- return !((WebElement) context).isDisplayed();
91
- } catch (StaleElementReferenceException e) {
92
- // When the element was removed from the DOM, it isn't displayed anymore
93
- return true;
94
- }
95
- }
96
- });
97 78
}
98 79
99 80
public void pressCancel() {
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestLinkCreation.java
... ...
@@ -0,0 +1,232 @@
1
+package com.sap.sailing.selenium.test.adminconsole;
2
+
3
+import java.net.URL;
4
+import java.util.Date;
5
+
6
+import javax.xml.bind.DatatypeConverter;
7
+
8
+import org.hamcrest.Matcher;
9
+import org.hamcrest.MatcherAssert;
10
+import org.hamcrest.Matchers;
11
+import org.junit.Assert;
12
+import org.junit.Assume;
13
+import org.junit.Before;
14
+import org.junit.Test;
15
+import org.openqa.selenium.By;
16
+import org.openqa.selenium.WebDriver;
17
+import org.openqa.selenium.WebElement;
18
+import org.openqa.selenium.support.ui.ExpectedConditions;
19
+import org.openqa.selenium.support.ui.Wait;
20
+import org.openqa.selenium.support.ui.WebDriverWait;
21
+import org.slf4j.Logger;
22
+import org.slf4j.LoggerFactory;
23
+
24
+import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage;
25
+import com.sap.sailing.selenium.pages.adminconsole.connectors.SmartphoneTrackingEventManagementPanelPO;
26
+import com.sap.sailing.selenium.pages.adminconsole.event.EventConfigurationPanelPO;
27
+import com.sap.sailing.selenium.pages.adminconsole.racemanagementapp.DeviceConfigurationCreateDialogPO;
28
+import com.sap.sailing.selenium.pages.adminconsole.racemanagementapp.DeviceConfigurationDetailsAreaPO;
29
+import com.sap.sailing.selenium.pages.adminconsole.racemanagementapp.DeviceConfigurationQRCodeDialogPO;
30
+import com.sap.sailing.selenium.pages.adminconsole.racemanagementapp.RaceManagementAppPanelPO;
31
+import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaDetailsCompositePO;
32
+import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaEditDialogPO;
33
+import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaListCompositePO.RegattaDescriptor;
34
+import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaStructureManagementPanelPO;
35
+import com.sap.sailing.selenium.pages.adminconsole.regatta.RegistrationLinkWithQRCodeDialogPO;
36
+import com.sap.sailing.selenium.pages.gwt.DataEntryPO;
37
+import com.sap.sailing.selenium.pages.home.HomePage;
38
+import com.sap.sailing.selenium.test.AbstractSeleniumTest;
39
+import com.sap.sailing.selenium.test.adminconsole.smartphonetracking.AddCompetitorWithBoatDialogPO;
40
+import com.sap.sailing.selenium.test.adminconsole.smartphonetracking.AddDeviceMappingsDialogPO;
41
+import com.sap.sailing.selenium.test.adminconsole.smartphonetracking.MapDevicesDialogPO;
42
+import com.sap.sailing.selenium.test.adminconsole.smartphonetracking.RegisterCompetitorsDialogPO;
43
+
44
+/**
45
+ * There are various link creation logic in AdminConsole. This test is to cover them.
46
+ */
47
+public class TestLinkCreation extends AbstractSeleniumTest {
48
+
49
+ private static final Logger LOG = LoggerFactory.getLogger(TestLinkCreation.class);
50
+
51
+ private static final String DEVICE_CONFIG_NAME = "Test";
52
+ private static final String BMW_CUP_EVENT = "BMW Cup";
53
+ private static final String BMW_CUP_BOAT_CLASS = "J80";
54
+ private static final String AUDI_CUP_BOAT_CLASS = "J70";
55
+ private static final String BMW_CUP_REGATTA = "BMW Cup (J80)"; //$NON-NLS-1$
56
+ private static final String AUDI_CUP_REGATTA = "Audi Business Cup (J70)"; //$NON-NLS-1$
57
+ private static final String BMW_CUP_EVENTS_DESC = "BMW Cup Description";
58
+ private static final String BMW_VENUE = "Somewhere";
59
+ private static final Date BMW_START_EVENT_TIME = DatatypeConverter.parseDateTime("2012-04-08T10:09:00-05:00")
60
+ .getTime();
61
+ private static final Date BMW_STOP_EVENT_TIME = DatatypeConverter.parseDateTime("2017-04-08T10:50:00-05:00")
62
+ .getTime();
63
+ private static final String CUSTOM_COURSE_AREA = "Custom X";
64
+ private static final String INVITATION_URL_BASE = "https://sailinsight30-app.sapsailing.com/publicInvite?regatta_name=Audi+Business+Cup+(J70)+(J70)";
65
+ private static final String INVITATION_QR_CODE_BASE = "https://sailinsight30-app.sapsailing.com/publicInvite?regatta_name=Audi%20Business%20Cup%20(J70)%20(J70)";
66
+ private static final String EXPECTED_PUPLIC_INVITE_QR_CODE_TITLE = "Welcome to the public regatta Audi Business Cup (J70) (J70)";
67
+ private static final String EXPECTED_RACE_MANAGER_APP_QR_CODE_TITLE = "Welcome to the Race Manager App registration";
68
+ private static final String EXPECTED_DEVICE_REGISTRATION_QR_CODE_TITLE = "Welcome Competitor Test to the regatta Audi Business Cup (J70) (J70)";
69
+ private static final String EXPECTED_QR_LINK_TEXT = "Please scan this QR Code with your mobile device to proceed with the registration";
70
+
71
+ private static final String CHECK_RACE_APP_URL_REGEX = "^https:\\/\\/racemanager-app.sapsailing.com\\/invite\\"
72
+ + "?server_url=http.*localhost.*(\\d{0,5})?" + "&device_config_identifier=" + DEVICE_CONFIG_NAME
73
+ + "&device_config_uuid=(\\w|\\d|-)*";
74
+
75
+ private static final String DEVICE_REGISTRATION_URL_REGEX = ".*https:\\/\\/sailinsight30-app.sapsailing.com\\/invite\\?checkinUrl=.*";
76
+
77
+ @Override
78
+ @Before
79
+ public void setUp() {
80
+ boolean checkUrlIsWorking;
81
+ String prodCheckUrl = "https://www.sapsailing.com/gwt/status";
82
+ try {
83
+ LOG.debug("Testing check URL from productive server");
84
+ (new URL(prodCheckUrl)).openStream().close();
85
+ checkUrlIsWorking = true;
86
+ LOG.info("Productive server {} is accessible.", prodCheckUrl);
87
+ } catch (Exception ex) {
88
+ checkUrlIsWorking = false;
89
+ LOG.warn("Productive server {} is NOT accessible. Skip tests which are requirering live connection.",
90
+ prodCheckUrl);
91
+ }
92
+ Assume.assumeTrue("Execute link creation test only if productive server is online (www.sapsailing.com).",
93
+ checkUrlIsWorking);
94
+ clearState(getContextRoot());
95
+ super.setUp();
96
+ }
97
+
98
+ /**
99
+ * Test the creation of an invitation link.
100
+ * <p>
101
+ * Please notice, that the test checks the created invitation link by calling it and checking over the redirects
102
+ * from branch.io over production environment (my.sapsailing.com) back to localhost. The link back to localhost need
103
+ * an additional confirmation on production server.
104
+ */
105
+ @Test
106
+ public void testRegattaOverviewInvitationLinkCreation() {
107
+ AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
108
+ // create an event and regatta
109
+ EventConfigurationPanelPO events = adminConsole.goToEvents();
110
+ events.createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(BMW_CUP_EVENT, BMW_CUP_EVENTS_DESC,
111
+ BMW_VENUE, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, /* isPublic */ true, BMW_CUP_REGATTA,
112
+ BMW_CUP_BOAT_CLASS, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, /* useOverallLeaderboard */ false,
113
+ CUSTOM_COURSE_AREA);
114
+ RegattaStructureManagementPanelPO regattas = adminConsole.goToRegattaStructure();
115
+ RegattaDescriptor regattaDescriptor = new RegattaDescriptor(AUDI_CUP_REGATTA, AUDI_CUP_BOAT_CLASS);
116
+ regattas.createRegattaAndAddToEvent(regattaDescriptor, BMW_CUP_EVENT, new String[] { CUSTOM_COURSE_AREA });
117
+ // extract secret and event ID
118
+ RegattaEditDialogPO editRegatta = regattas.getRegattaList().editRegatta(regattaDescriptor);
119
+ String secret = editRegatta.getSecret();
120
+ String selectedEventId = editRegatta.getSelectedEventId();
121
+ editRegatta.clickOkButtonOrThrow();
122
+ // create expected link URL based on the secret and event ID
123
+ RegattaDetailsCompositePO regattaDetails = regattas.getRegattaDetails();
124
+ RegistrationLinkWithQRCodeDialogPO registrationLinkWithQRCode = regattaDetails.configureRegistrationURL();
125
+ // check URL
126
+ String createdInvitationUrl = registrationLinkWithQRCode.getRegistrationLinkUrl();
127
+ Assert.assertTrue(createdInvitationUrl.startsWith(INVITATION_URL_BASE));
128
+ Assert.assertTrue(createdInvitationUrl.contains("secret=" + secret));
129
+ Assert.assertTrue(createdInvitationUrl.contains("event_id=" + selectedEventId));
130
+ Assert.assertTrue(createdInvitationUrl.contains("server=http%3A%2F%2Flocalhost%3A"));
131
+ registrationLinkWithQRCode.clickOkButtonOrThrow();
132
+ HomePage.goToHomeUrl(getWebDriver(), createdInvitationUrl);
133
+ Wait<WebDriver> wait = new WebDriverWait(getWebDriver(), 30);
134
+ // confirm dialog on my.sapsailing.com to redirect back to localhost
135
+ wait.until(ExpectedConditions.numberOfElementsToBe(
136
+ By.xpath("//button[contains(text(), 'Yes')] | //button[contains(text(), 'Ja')]"), 1)).get(0).click();
137
+ // here we are back on localhost (Home.html#QRCodePlace)
138
+ wait.until(ExpectedConditions.presenceOfElementLocated(
139
+ By.xpath("//div[contains(text(), '" + EXPECTED_PUPLIC_INVITE_QR_CODE_TITLE + "')]")));
140
+ WebElement qrCodeLink = wait.until(ExpectedConditions
141
+ .presenceOfElementLocated(By.xpath("//a[contains(text(), '" + EXPECTED_QR_LINK_TEXT + "')]")));
142
+ Assert.assertTrue(qrCodeLink.getAttribute("href").startsWith(INVITATION_QR_CODE_BASE));
143
+ Assert.assertTrue(qrCodeLink.getAttribute("href").contains("secret=" + secret));
144
+ Assert.assertTrue(qrCodeLink.getAttribute("href").contains("server=http%3A%2F%2Flocalhost%3A"));
145
+ }
146
+
147
+ /**
148
+ * Testing the generation of an invitation QR code for the Race Manager App.
149
+ */
150
+ @Test
151
+ public void testRaceManagerAppInvitationLink() {
152
+ AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
153
+ RaceManagementAppPanelPO raceManagerApp = adminConsole.goToRaceManagerApp();
154
+ DeviceConfigurationCreateDialogPO createDeviceConfiguration = raceManagerApp.createDeviceConfiguration();
155
+ createDeviceConfiguration.setDeviceName(DEVICE_CONFIG_NAME);
156
+ createDeviceConfiguration.clickOkButtonOrThrow();
157
+ DeviceConfigurationDetailsAreaPO deviceConfigurationDetails = raceManagerApp.getDeviceConfigurationDetails();
158
+ DeviceConfigurationQRCodeDialogPO qrCodeDialog = deviceConfigurationDetails.openQRCodeDialog();
159
+ Matcher<String> matcher = Matchers.matchesRegex(CHECK_RACE_APP_URL_REGEX);
160
+ String createdInvitationUrl = qrCodeDialog.getUrl();
161
+ MatcherAssert.assertThat("Check URL", matcher.matches(createdInvitationUrl));
162
+ HomePage.goToHomeUrl(getWebDriver(), createdInvitationUrl);
163
+ // confirm dialog on my.sapsailing.com to redirect back to localhost
164
+ Wait<WebDriver> wait = new WebDriverWait(getWebDriver(), 30);
165
+ wait.until(ExpectedConditions.numberOfElementsToBe(
166
+ By.xpath("//button[contains(text(), 'Yes')] | //button[contains(text(), 'Ja')]"), 1)).get(0).click();
167
+ // here we are back on localhost (Home.html#QRCodePlace)
168
+ wait.until(ExpectedConditions.presenceOfElementLocated(
169
+ By.xpath("//div[contains(text(), '" + EXPECTED_RACE_MANAGER_APP_QR_CODE_TITLE + "')]")));
170
+ WebElement qrCodeLink = wait.until(ExpectedConditions
171
+ .presenceOfElementLocated(By.xpath("//a[contains(text(), '" + EXPECTED_QR_LINK_TEXT + "')]")));
172
+ MatcherAssert.assertThat("Check URL", matcher.matches(qrCodeLink.getAttribute("href")));
173
+ }
174
+
175
+ /**
176
+ * Device registration via QR code test.
177
+ */
178
+ @Test
179
+ public void testDeviceRegistation() throws InterruptedException {
180
+ AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
181
+ // create an event and regatta
182
+ EventConfigurationPanelPO events = adminConsole.goToEvents();
183
+ events.createEventWithDefaultLeaderboardGroupRegattaAndDefaultLeaderboard(BMW_CUP_EVENT, BMW_CUP_EVENTS_DESC,
184
+ BMW_VENUE, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, /* isPublic */ true, BMW_CUP_REGATTA,
185
+ BMW_CUP_BOAT_CLASS, BMW_START_EVENT_TIME, BMW_STOP_EVENT_TIME, /* useOverallLeaderboard */ false,
186
+ CUSTOM_COURSE_AREA);
187
+ RegattaStructureManagementPanelPO regattas = adminConsole.goToRegattaStructure();
188
+ RegattaDescriptor regattaDescriptor = new RegattaDescriptor(AUDI_CUP_REGATTA, AUDI_CUP_BOAT_CLASS);
189
+ regattas.createRegattaAndAddToEvent(regattaDescriptor, BMW_CUP_EVENT, new String[] { CUSTOM_COURSE_AREA });
190
+ SmartphoneTrackingEventManagementPanelPO smartphoneTrackingPanel = adminConsole.goToSmartphoneTrackingPanel();
191
+ DataEntryPO aLeaderboard = smartphoneTrackingPanel.getLeaderboardTable().getEntries().get(0);
192
+ // Add a competitor with boat as precondition for adding devices
193
+ RegisterCompetitorsDialogPO registerCompetitorsDialogPO = smartphoneTrackingPanel
194
+ .pushCompetitorRegistrationsActionButton(aLeaderboard);
195
+ AddCompetitorWithBoatDialogPO addCompetitorWithBoatDialogPO = registerCompetitorsDialogPO
196
+ .openAddCompetitorWithBoatDialog();
197
+ addCompetitorWithBoatDialogPO.addCompetitorWithBoat();
198
+ Wait<WebDriver> wait = new WebDriverWait(getWebDriver(), 30);
199
+ // wait until competitor is shown in the table. Sometimes test breaks here if dialog was closed to early
200
+ wait.until((s) -> registerCompetitorsDialogPO.getCompetitorTable().getEntries().size() > 0);
201
+ // now close the dialog
202
+ registerCompetitorsDialogPO.clickOkButtonOrThrow();
203
+ // Add device
204
+ aLeaderboard = smartphoneTrackingPanel.getLeaderboardTable().getEntries().get(0);
205
+ MapDevicesDialogPO mapDevicesDialog = smartphoneTrackingPanel.pushMapDevicesActionButton(aLeaderboard);
206
+ AddDeviceMappingsDialogPO addDeviceMappingsDialog = mapDevicesDialog.addMapping();
207
+ // check URL based on boat selection
208
+ addDeviceMappingsDialog.getBoatsTable().getEntries().get(0).select();
209
+ String boatUrlPattern = DEVICE_REGISTRATION_URL_REGEX + "boat_id.*";
210
+ Matcher<String> boatMatcher = Matchers.matchesRegex(boatUrlPattern);
211
+ MatcherAssert.assertThat("Check URL",
212
+ boatMatcher.matches(addDeviceMappingsDialog.getQrCodeUrl(boatUrlPattern)));
213
+ // check URL based on competitor selection
214
+ addDeviceMappingsDialog.getCompetitorTable().getEntries().get(0).select();
215
+ String compatitorUrlPattern = DEVICE_REGISTRATION_URL_REGEX + "competitor_id.*";
216
+ Matcher<String> competitorMater = Matchers.matchesRegex(compatitorUrlPattern);
217
+ String qrCodeUrl = addDeviceMappingsDialog.getQrCodeUrl(compatitorUrlPattern);
218
+ MatcherAssert.assertThat("Check URL", competitorMater.matches(qrCodeUrl));
219
+ // check redirect
220
+ getWebDriver().get(qrCodeUrl);
221
+ // confirm dialog on my.sapsailing.com to redirect back to localhost
222
+ wait.until(ExpectedConditions.numberOfElementsToBe(
223
+ By.xpath("//button[contains(text(), 'Yes')] | //button[contains(text(), 'Ja')]"), 1)).get(0).click();
224
+ // here we are back on localhost (Home.html#QRCodePlace)
225
+ wait.until(ExpectedConditions.presenceOfElementLocated(
226
+ By.xpath("//div[contains(text(), '" + EXPECTED_DEVICE_REGISTRATION_QR_CODE_TITLE + "')]")));
227
+ WebElement qrCodeLink = wait.until(ExpectedConditions
228
+ .presenceOfElementLocated(By.xpath("//a[contains(text(), '" + EXPECTED_QR_LINK_TEXT + "')]")));
229
+ Matcher<String> matcher = Matchers.matchesRegex(compatitorUrlPattern);
230
+ MatcherAssert.assertThat("Check URL", matcher.matches(qrCodeLink.getAttribute("href")));
231
+ }
232
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestOpenRegattaCreation.java
... ...
@@ -35,6 +35,6 @@ public class TestOpenRegattaCreation extends AbstractSeleniumTest {
35 35
private void configureRegattaAndLeaderboard() {
36 36
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
37 37
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
38
- regattaStructure.createRegatta(this.regatta);
38
+ regattaStructure.createRegatta(this.regatta, /* withDefaultLeaderboard */ false);
39 39
}
40 40
}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestRefreshableSelectionModel.java
... ...
@@ -175,7 +175,7 @@ public class TestRefreshableSelectionModel extends AbstractSeleniumTest {
175 175
176 176
// Create a regatta with 1 series and 5 races as well as a leaderborad
177 177
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
178
- regattaStructure.createRegatta(this.regatta);
178
+ regattaStructure.createRegatta(this.regatta, /* withDefaultLeaderboard */ false);
179 179
180 180
RegattaDetailsCompositePO regattaDetails = regattaStructure.getRegattaDetails(this.regatta);
181 181
SeriesEditDialogPO seriesDialog = regattaDetails.editSeries(RegattaStructureManagementPanelPO.DEFAULT_SERIES_NAME);
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestSmartphoneTrackingEventManagementPanel.java
... ...
@@ -117,7 +117,7 @@ public class TestSmartphoneTrackingEventManagementPanel extends AbstractSelenium
117 117
118 118
// Create a regatta with 1 series and 5 races as well as a leaderborad
119 119
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
120
- regattaStructure.createRegatta(this.regatta);
120
+ regattaStructure.createRegatta(this.regatta, /* withDefaultLeaderboard */ false);
121 121
122 122
RegattaDetailsCompositePO regattaDetails = regattaStructure.getRegattaDetails(this.regatta);
123 123
SeriesEditDialogPO seriesDialog = regattaDetails.editSeries(RegattaStructureManagementPanelPO.DEFAULT_SERIES_NAME);
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/TestStartAndStopTrackingForTracTracEvents.java
... ...
@@ -61,7 +61,7 @@ public class TestStartAndStopTrackingForTracTracEvents extends AbstractSeleniumT
61 61
RegattaDescriptor bmwCupDescriptor = new RegattaDescriptor(BMW_CUP_EVENT, BMW_CUP_BOAT_CLASS);
62 62
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
63 63
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
64
- regattaStructure.createRegatta(bmwCupDescriptor);
64
+ regattaStructure.createRegatta(bmwCupDescriptor, /* withDefaultLeaderboard */ false);
65 65
TracTracEventManagementPanelPO tracTracEvents = adminConsole.goToTracTracEvents();
66 66
tracTracEvents.addConnectionAndListTrackableRaces(BMW_CUP_JSON_URL);
67 67
tracTracEvents.setReggataForTracking(bmwCupDescriptor);
... ...
@@ -79,7 +79,7 @@ public class TestStartAndStopTrackingForTracTracEvents extends AbstractSeleniumT
79 79
public void testStartTrackingWithDefaultReggataWhileReggataForBoatClassExists() {
80 80
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
81 81
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
82
- regattaStructure.createRegatta(new RegattaDescriptor(BMW_CUP_EVENT, BMW_CUP_BOAT_CLASS));
82
+ regattaStructure.createRegatta(new RegattaDescriptor(BMW_CUP_EVENT, BMW_CUP_BOAT_CLASS), /* withDefaultLeaderboard */ false);
83 83
TracTracEventManagementPanelPO tracTracEvents = adminConsole.goToTracTracEvents();
84 84
tracTracEvents.addConnectionAndListTrackableRaces(BMW_CUP_JSON_URL);
85 85
tracTracEvents.setReggataForTracking(DEFAULT_REGATTA);
... ...
@@ -93,8 +93,8 @@ public class TestStartAndStopTrackingForTracTracEvents extends AbstractSeleniumT
93 93
RegattaDescriptor idm2013Descriptor = new RegattaDescriptor(IDM_2013_EVENT, IDM_2013_BOAT_CLASS);
94 94
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
95 95
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
96
- regattaStructure.createRegatta(bmwCupDescriptor);
97
- regattaStructure.createRegatta(idm2013Descriptor);
96
+ regattaStructure.createRegatta(bmwCupDescriptor, /* withDefaultLeaderboard */ false);
97
+ regattaStructure.createRegatta(idm2013Descriptor, /* withDefaultLeaderboard */ false);
98 98
TracTracEventManagementPanelPO tracTracEvents = adminConsole.goToTracTracEvents();
99 99
tracTracEvents.addConnectionAndListTrackableRaces(BMW_CUP_JSON_URL);
100 100
tracTracEvents.setReggataForTracking(idm2013Descriptor);
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/smartphonetracking/AddCompetitorWithBoatDialogPO.java
... ...
@@ -0,0 +1,29 @@
1
+package com.sap.sailing.selenium.test.adminconsole.smartphonetracking;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.core.BySeleniumId;
7
+import com.sap.sailing.selenium.core.FindBy;
8
+import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
9
+
10
+public class AddCompetitorWithBoatDialogPO extends DataEntryDialogPO {
11
+
12
+ @FindBy(how = BySeleniumId.class, using = "NameTextBox")
13
+ private WebElement nameTextBox;
14
+
15
+ @FindBy(how = BySeleniumId.class, using = "SailIdTextBox")
16
+ private WebElement sailIdTextBox;
17
+
18
+ public AddCompetitorWithBoatDialogPO(WebDriver driver, WebElement element) {
19
+ super(driver, element);
20
+ }
21
+
22
+ public void addCompetitorWithBoat() {
23
+ nameTextBox.sendKeys("Competitor Test");
24
+ sailIdTextBox.sendKeys("1234");
25
+ clickOkButtonOrThrow();
26
+ }
27
+
28
+
29
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/smartphonetracking/AddDeviceMappingsDialogPO.java
... ...
@@ -0,0 +1,47 @@
1
+package com.sap.sailing.selenium.test.adminconsole.smartphonetracking;
2
+
3
+import java.util.regex.Pattern;
4
+
5
+import org.openqa.selenium.WebDriver;
6
+import org.openqa.selenium.WebElement;
7
+import org.openqa.selenium.support.ui.ExpectedConditions;
8
+import org.openqa.selenium.support.ui.Wait;
9
+import org.openqa.selenium.support.ui.WebDriverWait;
10
+import org.slf4j.Logger;
11
+import org.slf4j.LoggerFactory;
12
+
13
+import com.sap.sailing.selenium.core.BySeleniumId;
14
+import com.sap.sailing.selenium.core.FindBy;
15
+import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesBoatTablePO;
16
+import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesCompetitorTablePO;
17
+import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
18
+
19
+public class AddDeviceMappingsDialogPO extends DataEntryDialogPO {
20
+
21
+ Logger logger = LoggerFactory.getLogger(AddDeviceMappingsDialogPO.class);
22
+
23
+ @FindBy(how = BySeleniumId.class, using = "QRIdentifierURL")
24
+ private WebElement qrCodeUrl;
25
+
26
+ public AddDeviceMappingsDialogPO(WebDriver driver, WebElement element) {
27
+ super(driver, element);
28
+ }
29
+
30
+ public TrackedRacesBoatTablePO getBoatsTable() {
31
+ Wait<WebDriver> wait = new WebDriverWait(driver, 10);
32
+ WebElement boatsTable = wait.until(ExpectedConditions.presenceOfElementLocated(new BySeleniumId("BoatsTable")));
33
+ return new TrackedRacesBoatTablePO(this.driver, boatsTable);
34
+ }
35
+
36
+ public TrackedRacesCompetitorTablePO getCompetitorTable() {
37
+ Wait<WebDriver> wait = new WebDriverWait(driver, 10);
38
+ WebElement competitorTable = wait.until(ExpectedConditions.presenceOfElementLocated(new BySeleniumId("CompetitorsTable")));
39
+ return new TrackedRacesCompetitorTablePO(this.driver, competitorTable);
40
+ }
41
+
42
+ public String getQrCodeUrl(String matcherPattern) {
43
+ waitUntil(webDriver -> Pattern.compile(matcherPattern).matcher(qrCodeUrl.getText()).matches());
44
+ return qrCodeUrl.getText();
45
+ }
46
+
47
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/smartphonetracking/MapDevicesDialogPO.java
... ...
@@ -0,0 +1,24 @@
1
+package com.sap.sailing.selenium.test.adminconsole.smartphonetracking;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+
6
+import com.sap.sailing.selenium.core.BySeleniumId;
7
+import com.sap.sailing.selenium.core.FindBy;
8
+import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
9
+
10
+public class MapDevicesDialogPO extends DataEntryDialogPO {
11
+
12
+ @FindBy(how = BySeleniumId.class, using = "addMappingButton")
13
+ private WebElement addMappingButton;
14
+
15
+ public MapDevicesDialogPO(WebDriver driver, WebElement element) {
16
+ super(driver, element);
17
+ }
18
+
19
+ public AddDeviceMappingsDialogPO addMapping() {
20
+ addMappingButton.click();
21
+ return this.waitForPO(AddDeviceMappingsDialogPO::new, "RegattaLogAddDeviceMappingDialog");
22
+ }
23
+
24
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/adminconsole/smartphonetracking/RegisterCompetitorsDialogPO.java
... ...
@@ -0,0 +1,39 @@
1
+package com.sap.sailing.selenium.test.adminconsole.smartphonetracking;
2
+
3
+import org.openqa.selenium.WebDriver;
4
+import org.openqa.selenium.WebElement;
5
+import org.openqa.selenium.support.ui.ExpectedConditions;
6
+import org.openqa.selenium.support.ui.Wait;
7
+import org.openqa.selenium.support.ui.WebDriverWait;
8
+
9
+import com.sap.sailing.selenium.core.BySeleniumId;
10
+import com.sap.sailing.selenium.core.FindBy;
11
+import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesCompetitorTablePO;
12
+import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
13
+
14
+public class RegisterCompetitorsDialogPO extends DataEntryDialogPO {
15
+
16
+ @FindBy(how = BySeleniumId.class, using = "registerCompetitorsDialog")
17
+ private WebElement dialog;
18
+
19
+ @FindBy(how = BySeleniumId.class, using = "addCompetitorWithBoatButton")
20
+ private WebElement addCompetitorWithBoatButton;
21
+
22
+ public RegisterCompetitorsDialogPO(WebDriver driver, WebElement element) {
23
+ super(driver, element);
24
+
25
+ }
26
+
27
+ public AddCompetitorWithBoatDialogPO openAddCompetitorWithBoatDialog() {
28
+ this.addCompetitorWithBoatButton.click();
29
+ return getPO(AddCompetitorWithBoatDialogPO::new, "CompetitorWithBoatEditDialog");
30
+ }
31
+
32
+ public TrackedRacesCompetitorTablePO getCompetitorTable() {
33
+ Wait<WebDriver> wait = new WebDriverWait(driver, 10);
34
+ WebElement competitorTable = wait
35
+ .until(ExpectedConditions.presenceOfElementLocated(new BySeleniumId("CompetitorsTable")));
36
+ return new TrackedRacesCompetitorTablePO(this.driver, competitorTable);
37
+ }
38
+
39
+}
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/leaderboard/TestColumnsInLeaderboard.java
... ...
@@ -124,7 +124,7 @@ public class TestColumnsInLeaderboard extends AbstractSeleniumTest {
124 124
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
125 125
// Create a regatta with 1 series and 5 races as well as a leaderboard
126 126
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
127
- regattaStructure.createRegatta(this.regatta);
127
+ regattaStructure.createRegatta(this.regatta, /* withDefaultLeaderboard */ false);
128 128
RegattaDetailsCompositePO regattaDetails = regattaStructure.getRegattaDetails(this.regatta);
129 129
SeriesEditDialogPO seriesDialog = regattaDetails.editSeries(RegattaStructureManagementPanelPO.DEFAULT_SERIES_NAME);
130 130
seriesDialog.addRaces(1, 1);
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/leaderboard/TestLeaderboardConfiguration.java
... ...
@@ -187,7 +187,7 @@ public class TestLeaderboardConfiguration extends AbstractSeleniumTest {
187 187
188 188
// Create a regatta with 1 series and 5 races as well as a leaderborad
189 189
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
190
- regattaStructure.createRegatta(this.regatta);
190
+ regattaStructure.createRegatta(this.regatta, /* withDefaultLeaderboard */ false);
191 191
192 192
RegattaDetailsCompositePO regattaDetails = regattaStructure.getRegattaDetails(this.regatta);
193 193
SeriesEditDialogPO seriesDialog = regattaDetails.editSeries(RegattaStructureManagementPanelPO.DEFAULT_SERIES_NAME);
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/test/leaderboard/TestSwitchingOffStartOfRaceInference.java
... ...
@@ -96,7 +96,7 @@ public class TestSwitchingOffStartOfRaceInference extends AbstractSeleniumTest {
96 96
AdminConsolePage adminConsole = AdminConsolePage.goToPage(getWebDriver(), getContextRoot());
97 97
// Create a regatta with 1 series and 5 races as well as a leaderboard
98 98
RegattaStructureManagementPanelPO regattaStructure = adminConsole.goToRegattaStructure();
99
- regattaStructure.createRegatta(this.regatta);
99
+ regattaStructure.createRegatta(this.regatta, /* withDefaultLeaderboard */ false);
100 100
RegattaDetailsCompositePO regattaDetails = regattaStructure.getRegattaDetails(this.regatta);
101 101
SeriesEditDialogPO seriesDialog = regattaDetails.editSeries(RegattaStructureManagementPanelPO.DEFAULT_SERIES_NAME);
102 102
seriesDialog.addRaces(3, 3);
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/RaceWindJsonSerializer.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sailing.server.gateway.serialization.impl;
2 2
3 3
import java.util.Iterator;
4 4
import java.util.List;
5
+import java.util.Set;
5 6
import java.util.stream.Collectors;
6 7
7 8
import org.json.simple.JSONArray;
... ...
@@ -36,12 +37,13 @@ public class RaceWindJsonSerializer extends AbstractTrackedRaceDataJsonSerialize
36 37
37 38
@Override
38 39
public JSONObject serialize(TrackedRace trackedRace) {
39
- final TimePoint finalFrom = trackedRace.getStartOfTracking();
40
- final TimePoint finalTo = Util.getEarliestOfTimePoints(trackedRace.getEndOfTracking(),
41
- trackedRace.getTimePointOfNewestEvent());
40
+ final TimePoint finalFrom = trackedRace.getStartOfRace();
41
+ final TimePoint finalTo = Util.getEarliestOfTimePoints(trackedRace.getEndOfTracking(), trackedRace.getEndOfRace());
42
+ final Set<WindSource> windSourcesToExclude = trackedRace.getWindSourcesToExclude();
42 43
List<WindSource> highQualityWindSources = trackedRace.getWindSources().stream()
43
- .filter(windSource -> windSource.getType() == WindSourceType.EXPEDITION
44
+ .filter(windSource -> (windSource.getType() == WindSourceType.EXPEDITION
44 45
|| windSource.getType() == WindSourceType.RACECOMMITTEE)
46
+ && !windSourcesToExclude.contains(windSource))
45 47
.collect(Collectors.toList());
46 48
JSONArray windSourcesJson = new JSONArray();
47 49
TimePoint earliestTimePoint = null;
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/TrackedRaceJsonSerializer.java
... ...
@@ -35,13 +35,10 @@ public class TrackedRaceJsonSerializer implements JsonSerializer<TrackedRace> {
35 35
36 36
public JSONObject serialize(TrackedRace trackedRace) {
37 37
JSONObject jsonRace = new JSONObject();
38
-
39 38
jsonRace.put(FIELD_NAME, trackedRace.getRace().getName());
40 39
jsonRace.put(FIELD_REGATTA, trackedRace.getRaceIdentifier().getRegattaName());
41
-
42 40
if (windTrackSerializerProducer != null) {
43 41
JSONArray windTracks = new JSONArray();
44
-
45 42
List<WindSource> windSources = getAvailableWindSources(trackedRace);
46 43
JSONArray jsonWindSourcesDisplayed = new JSONArray();
47 44
for (WindSource windSource : windSources) {
java/com.sap.sailing.server/SailingServer (No Proxy).launch
... ...
@@ -24,7 +24,7 @@
24 24
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
25 25
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console 12001 -clean"/>
26 26
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
27
- <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=cp1252 -Dexpedition.udp.port=5010 -Xmx6000m -XX:ThreadPriorityPolicy=2 -XX:+UseG1GC -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -Dpolardata.source.url=https://www.sapsailing.com -Dwindestimation.source.url=https://www.sapsailing.com -Drestore.tracked.races=true -Dorg.eclipse.jetty.server.Request.maxFormContentSize=50000000 -DAnniversaryRaceDeterminator.enabled=true -Djava.naming.factory.url.pkgs=org.eclipse.jetty.jndi -Djava.naming.factory.initial=org.eclipse.jetty.jndi.InitialContextFactory -Dorg.eclipse.jetty.annotations.maxWait=120 -Dchargebee.site=sailytics-test -Dchargebee.apikey=test_G8QP4NUM1f4MCeZeni1VFC04cdIGZSNyp -Dsubscriptions.disableMailVerificationRequirement=true -Dgwt.rpc.version=9"/>
27
+ <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=cp1252 -Dexpedition.udp.port=5010 -Xmx6000m -XX:ThreadPriorityPolicy=2 -XX:+UseG1GC -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -Dpolardata.source.url=https://www.sapsailing.com -Dwindestimation.source.url=https://www.sapsailing.com -Drestore.tracked.races=true -Dorg.eclipse.jetty.server.Request.maxFormContentSize=50000000 -DAnniversaryRaceDeterminator.enabled=true -Djava.naming.factory.url.pkgs=org.eclipse.jetty.jndi -Djava.naming.factory.initial=org.eclipse.jetty.jndi.InitialContextFactory -Dorg.eclipse.jetty.annotations.maxWait=120 -Dchargebee.site=sailytics-test -Dmanage2sail.accesstoken=${MANAGE2SAIL_ACCESS_TOKEN} -Dchargebee.apikey=test_G8QP4NUM1f4MCeZeni1VFC04cdIGZSNyp -Dsubscriptions.disableMailVerificationRequirement=true -Dgwt.rpc.version=9"/>
28 28
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
29 29
<stringAttribute key="pde.version" value="3.3"/>
30 30
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_TENANT_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%"/>
java/com.sap.sailing.windestimation.lab/src/com/sap/sailing/windestimation/data/importer/DistanceBasedTwdTransitionImporter.java
... ...
@@ -62,7 +62,7 @@ public class DistanceBasedTwdTransitionImporter {
62 62
// count progress each time the pair's first element changes:
63 63
if (nextFixPair.getA() != previousFirstElementOfPair) {
64 64
windFixNumber++;
65
- long percent = windFixNumber++ * 100 / numberOfWindFixes;
65
+ final long percent = windFixNumber++ * 100 / numberOfWindFixes;
66 66
if (percent > oldPercent) {
67 67
LoggingUtil.logInfo("Processing wind fix " + windFixNumber + "/" + numberOfWindFixes + " ("
68 68
+ percent + "%) for distance dimension");
... ...
@@ -70,10 +70,10 @@ public class DistanceBasedTwdTransitionImporter {
70 70
}
71 71
previousFirstElementOfPair = nextFixPair.getA();
72 72
}
73
- double meters = nextFixPair.getA().getPosition().getDistance(nextFixPair.getB().getPosition()).getMeters();
73
+ final double meters = nextFixPair.getA().getPosition().getDistance(nextFixPair.getB().getPosition()).getMeters();
74 74
if (meters <= MAX_DISTANCE_METERS && meters >= MIN_DISTANCE_METERS) {
75
- double twdChange = nextFixPair.getA().getBearing().getDifferenceTo(nextFixPair.getB().getBearing()).getDegrees();
76
- SingleDimensionBasedTwdTransition entry = new SingleDimensionBasedTwdTransition(meters, twdChange);
75
+ double twdChangeInDegrees = nextFixPair.getA().getBearing().getDifferenceTo(nextFixPair.getB().getBearing()).getDegrees();
76
+ SingleDimensionBasedTwdTransition entry = new SingleDimensionBasedTwdTransition(meters, twdChangeInDegrees);
77 77
distanceBasedTwdTransitionPersistenceManager.add(entry);
78 78
totalValuesCount++;
79 79
}
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDistanceBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DistanceBasedTwdTransitionRegressorFrom0.0To10.0.clf
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDistanceBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DistanceBasedTwdTransitionRegressorFrom10.0To912.0.clf
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDistanceBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DistanceBasedTwdTransitionRegressorFrom1368.0ToMaximum.clf
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDistanceBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DistanceBasedTwdTransitionRegressorFrom912.0To1368.0.clf
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDurationBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DurationBasedTwdTransitionRegressorFrom0.0To1.0.clf
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDurationBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DurationBasedTwdTransitionRegressorFrom1.0To140.0.clf
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDurationBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DurationBasedTwdTransitionRegressorFrom140.0To5394.0.clf
java/com.sap.sailing.windestimation.test/resources/trained_wind_estimation_models/SERIALIZATION.modelForDurationBasedTwdDeltaStdRegressor.IncrementalSingleDimensionPolynomialRegressor.DurationBasedTwdTransitionRegressorFrom5394.0ToMaximum.clf
java/com.sap.sailing.xrr.resultimport.test/src/com/sap/sailing/xrr/resultimport/test/ParserTest.java
... ...
@@ -10,6 +10,7 @@ import java.io.FileInputStream;
10 10
import java.io.FileNotFoundException;
11 11
import java.io.IOException;
12 12
import java.io.InputStream;
13
+import java.net.URISyntaxException;
13 14
import java.util.ArrayList;
14 15
import java.util.Arrays;
15 16
import java.util.Calendar;
... ...
@@ -87,7 +88,7 @@ public class ParserTest {
87 88
88 89
@Test
89 90
public void testScoreCorrectionProviderFeedingAndHasResults() throws IOException, SAXException,
90
- ParserConfigurationException, JAXBException {
91
+ ParserConfigurationException, JAXBException, URISyntaxException {
91 92
ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(getTestDocumentProvider(),
92 93
ParserFactory.INSTANCE);
93 94
Map<String, Set<com.sap.sse.common.Util.Pair<String, TimePoint>>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName();
... ...
@@ -103,7 +104,7 @@ public class ParserTest {
103 104
104 105
@Test
105 106
public void testScoreCorrectionProvider() throws IOException, SAXException,
106
- ParserConfigurationException, JAXBException {
107
+ ParserConfigurationException, JAXBException, URISyntaxException {
107 108
ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(getTestDocumentProvider(),
108 109
ParserFactory.INSTANCE);
109 110
Map<String, Set<com.sap.sse.common.Util.Pair<String, TimePoint>>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName();
... ...
@@ -140,7 +141,7 @@ public class ParserTest {
140 141
141 142
@Test
142 143
public void testScoreCorrectionProviderForSplitFleet() throws IOException, SAXException,
143
- ParserConfigurationException, JAXBException {
144
+ ParserConfigurationException, JAXBException, URISyntaxException {
144 145
ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(getTestDocumentProvider(),
145 146
ParserFactory.INSTANCE);
146 147
Map<String, Set<com.sap.sse.common.Util.Pair<String, TimePoint>>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName();
java/com.sap.sailing.xrr.resultimport/src/com/sap/sailing/xrr/resultimport/impl/CompetitorResolver.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sailing.xrr.resultimport.impl;
2 2
3 3
import java.io.IOException;
4 4
import java.io.Serializable;
5
+import java.net.URISyntaxException;
5 6
import java.util.ArrayList;
6 7
import java.util.HashMap;
7 8
import java.util.HashSet;
... ...
@@ -58,9 +59,8 @@ public class CompetitorResolver {
58 59
* information about the results..
59 60
*
60 61
* @return A Map, with sets of Regatta names as values, keyed by the events they belong to
61
- * @throws IOException
62 62
*/
63
- public Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException {
63
+ public Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException, URISyntaxException {
64 64
Map<String, Set<String>> result = new HashMap<>();
65 65
for (ResultDocumentDescriptor resultDocDescr : documentProvider.getResultDocumentDescriptors()) {
66 66
final String eventName = resultDocDescr.getEventName();
... ...
@@ -83,11 +83,9 @@ public class CompetitorResolver {
83 83
* @param optional
84 84
* regattaName
85 85
* @return A List of CompetitorDescriptors competing in a specified Event and Regatta
86
- * @throws JAXBException
87
- * @throws IOException
88 86
*/
89 87
public Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName)
90
- throws JAXBException, IOException {
88
+ throws JAXBException, IOException, URISyntaxException {
91 89
final List<CompetitorDescriptor> result = new ArrayList<>();
92 90
final Map<String, CompetitorDescriptor> resultsByTeamID = new HashMap<>();
93 91
final Map<String, CompetitorDescriptor> teamsWithoutRaceAssignments = new HashMap<>(); // keys are the teamID
java/com.sap.sailing.xrr.resultimport/src/com/sap/sailing/xrr/resultimport/impl/ScoreCorrectionProviderImpl.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sailing.xrr.resultimport.impl;
2 2
3 3
import java.io.IOException;
4 4
import java.io.InputStream;
5
+import java.net.URISyntaxException;
5 6
import java.util.ArrayList;
6 7
import java.util.HashMap;
7 8
import java.util.HashSet;
... ...
@@ -48,7 +49,7 @@ public class ScoreCorrectionProviderImpl extends AbstractDocumentBasedScoreCorre
48 49
49 50
@Override
50 51
public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName()
51
- throws IOException, SAXException, ParserConfigurationException {
52
+ throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
52 53
Map<String, Set<Util.Pair<String, TimePoint>>> result = new HashMap<String, Set<Util.Pair<String, TimePoint>>>();
53 54
for (Parser parser : getAllRegattaResults()) {
54 55
try {
... ...
@@ -81,7 +82,7 @@ public class ScoreCorrectionProviderImpl extends AbstractDocumentBasedScoreCorre
81 82
82 83
@Override
83 84
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName,
84
- TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException {
85
+ TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
85 86
for (Parser parser : getAllRegattaResults()) {
86 87
try {
87 88
final RegattaResults regattaResults = parser.parse();
... ...
@@ -108,7 +109,7 @@ public class ScoreCorrectionProviderImpl extends AbstractDocumentBasedScoreCorre
108 109
}
109 110
110 111
private Iterable<Parser> getAllRegattaResults() throws SAXException, IOException,
111
- ParserConfigurationException {
112
+ ParserConfigurationException, URISyntaxException {
112 113
final List<Parser> result = new ArrayList<>();
113 114
for (ResultDocumentDescriptor resultDocDescr : getResultDocumentProvider().getResultDocumentDescriptors()) {
114 115
final Parser parser = parserFactory.createParser(resultDocDescr.getInputStream(), resultDocDescr.getDocumentName());
java/com.sap.sailing.xrr.structureimport/src/com/sapsailing/xrr/structureimport/eventimport/EventImport.java
... ...
@@ -1,8 +1,7 @@
1 1
package com.sapsailing.xrr.structureimport.eventimport;
2 2
3 3
public class EventImport {
4
-
5
- public Iterable<RegattaJSON> getRegattas(String url){
4
+ public Iterable<RegattaJSON> getRegattas(String url) {
6 5
EventResults results = new EventParser().parseEvent(url);
7 6
return results.getRegattas();
8 7
}
java/com.sap.sailing.xrr.structureimport/src/com/sapsailing/xrr/structureimport/eventimport/EventParser.java
... ...
@@ -24,7 +24,6 @@ public class EventParser {
24 24
JSONObject jsonRoot;
25 25
try {
26 26
InputStreamReader streamReader = getStreamReader(url);
27
-
28 27
jsonRoot = (JSONObject) new JSONParser().parse(streamReader);
29 28
String id = (String) jsonRoot.get("Id");
30 29
String name = (String) jsonRoot.get("Name");
java/com.sap.sailing.yachtscoring.resultimport.test/src/com/sap/sailing/yachtscoring/resultimport/test/CharlstonRaceWeek2015_ParserTest.java
... ...
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertNotNull;
5 5
import static org.mockito.Mockito.mock;
6 6
7 7
import java.io.IOException;
8
+import java.net.URISyntaxException;
8 9
import java.util.Map;
9 10
import java.util.Set;
10 11
... ...
@@ -35,16 +36,14 @@ public class CharlstonRaceWeek2015_ParserTest extends AbstractCharlstonRaceWeek2
35 36
36 37
@Test
37 38
public void testScoreCorrectionProviderFeedingAndHasResults() throws IOException, SAXException,
38
- ParserConfigurationException, JAXBException {
39
+ ParserConfigurationException, JAXBException, URISyntaxException {
39 40
ResultUrlRegistry resultUrlRegistry = new ResultUrlRegistryImpl(mock(MongoObjectFactory.class),
40 41
mock(DomainObjectFactory.class));
41 42
ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(
42 43
getTestDocumentProvider(), ParserFactory.INSTANCE, resultUrlRegistry);
43 44
Map<String, Set<com.sap.sse.common.Util.Pair<String, TimePoint>>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName();
44
-
45 45
Set<com.sap.sse.common.Util.Pair<String, TimePoint>> resultsForKeyWestRaceWeek = hasResultsFor.get(CHARLSTONRACEWEEK2015_EVENT_NAME);
46 46
assertNotNull(resultsForKeyWestRaceWeek);
47
-
48 47
assertEquals(2, resultsForKeyWestRaceWeek.size());
49 48
}
50 49
java/com.sap.sailing.yachtscoring.resultimport.test/src/com/sap/sailing/yachtscoring/resultimport/test/CompetitorImportTest.java
... ...
@@ -8,6 +8,7 @@ import static org.mockito.Mockito.when;
8 8
9 9
import java.io.FileNotFoundException;
10 10
import java.io.IOException;
11
+import java.net.URISyntaxException;
11 12
import java.util.Arrays;
12 13
13 14
import javax.xml.bind.JAXBException;
... ...
@@ -23,7 +24,7 @@ import com.sap.sse.common.Util;
23 24
24 25
public class CompetitorImportTest extends AbstractCharlstonRaceWeek2015Test {
25 26
@Test
26
- public void simpleCompetitorImportTest() throws FileNotFoundException, IOException, JAXBException {
27
+ public void simpleCompetitorImportTest() throws FileNotFoundException, IOException, JAXBException, URISyntaxException {
27 28
ResultUrlRegistry resultUrlRegistry = mock(ResultUrlRegistry.class);
28 29
when(resultUrlRegistry.getReadableResultUrls(AbstractYachtScoringProvider.NAME)).thenReturn(Arrays.asList(getClass().getClassLoader().getResource(CHARLSTONRACEWEEK2015_TESTFILE_XRR)));
29 30
final YachtScoringCompetitorProvider competitorProvider = new YachtScoringCompetitorProvider(ParserFactory.INSTANCE, resultUrlRegistry, getTestDocumentProvider());
java/com.sap.sailing.yachtscoring.resultimport.test/src/com/sap/sailing/yachtscoring/resultimport/test/KeyWestRaceWeek2016_ParserTest.java
... ...
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertNotNull;
5 5
import static org.mockito.Mockito.mock;
6 6
7 7
import java.io.IOException;
8
+import java.net.URISyntaxException;
8 9
import java.util.ArrayList;
9 10
import java.util.Date;
10 11
import java.util.List;
... ...
@@ -72,16 +73,14 @@ public class KeyWestRaceWeek2016_ParserTest extends AbstractYachtScoringEventRes
72 73
73 74
@Test
74 75
public void testScoreCorrectionProviderFeedingAndHasResults() throws IOException, SAXException,
75
- ParserConfigurationException, JAXBException {
76
+ ParserConfigurationException, JAXBException, URISyntaxException {
76 77
ResultUrlRegistry resultUrlRegistry = new ResultUrlRegistryImpl(mock(MongoObjectFactory.class),
77 78
mock(DomainObjectFactory.class));
78 79
ScoreCorrectionProviderImpl scoreCorrectionProvider = new ScoreCorrectionProviderImpl(
79 80
getTestDocumentProvider(), ParserFactory.INSTANCE, resultUrlRegistry);
80 81
Map<String, Set<com.sap.sse.common.Util.Pair<String, TimePoint>>> hasResultsFor = scoreCorrectionProvider.getHasResultsForBoatClassFromDateByEventName();
81
-
82 82
Set<com.sap.sse.common.Util.Pair<String, TimePoint>> resultsForKeyWestRaceWeek = hasResultsFor.get(KEYWESTRACEWEEEK2016_EVENT_NAME);
83 83
assertNotNull(resultsForKeyWestRaceWeek);
84
-
85 84
assertEquals(2, resultsForKeyWestRaceWeek.size());
86 85
}
87 86
... ...
@@ -100,7 +99,6 @@ public class KeyWestRaceWeek2016_ParserTest extends AbstractYachtScoringEventRes
100 99
}
101 100
}
102 101
assertNotNull(resultForJ88);
103
-
104 102
RegattaScoreCorrections _J88Result = scoreCorrectionProvider.getScoreCorrections(KEYWESTRACEWEEEK2016_EVENT_NAME, BOAT_CLASS_J88,
105 103
resultForJ88.getB());
106 104
assertNotNull(_J88Result);
... ...
@@ -108,5 +106,4 @@ public class KeyWestRaceWeek2016_ParserTest extends AbstractYachtScoringEventRes
108 106
assertNotNull(scoreCorrectionsForRaces);
109 107
assertEquals(10, Util.size(scoreCorrectionsForRaces));
110 108
}
111
-
112 109
}
java/com.sap.sailing.yachtscoring.resultimport/src/com/sap/sailing/yachtscoring/resultimport/ScoreCorrectionProviderImpl.java
... ...
@@ -2,6 +2,7 @@ package com.sap.sailing.yachtscoring.resultimport;
2 2
3 3
import java.io.IOException;
4 4
import java.io.InputStream;
5
+import java.net.URISyntaxException;
5 6
import java.util.HashMap;
6 7
import java.util.HashSet;
7 8
import java.util.Map;
... ...
@@ -42,7 +43,7 @@ public class ScoreCorrectionProviderImpl extends AbstractYachtScoringProvider im
42 43
}
43 44
44 45
@Override
45
- public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() {
46
+ public Map<String, Set<Util.Pair<String, TimePoint>>> getHasResultsForBoatClassFromDateByEventName() throws URISyntaxException {
46 47
Map<String, Set<Util.Pair<String, TimePoint>>> result = new HashMap<String, Set<Util.Pair<String,TimePoint>>>();
47 48
try {
48 49
for (ResultDocumentDescriptor resultDocDescr : documentProvider.getResultDocumentDescriptors()) {
... ...
@@ -68,7 +69,7 @@ public class ScoreCorrectionProviderImpl extends AbstractYachtScoringProvider im
68 69
69 70
@Override
70 71
public RegattaScoreCorrections getScoreCorrections(String eventName, String boatClassName,
71
- TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException {
72
+ TimePoint timePointPublished) throws IOException, SAXException, ParserConfigurationException, URISyntaxException {
72 73
Parser parser = resolveParser(eventName, boatClassName);
73 74
try {
74 75
RegattaResults regattaResults = parser.parse();
... ...
@@ -89,7 +90,7 @@ public class ScoreCorrectionProviderImpl extends AbstractYachtScoringProvider im
89 90
/* boatClassNameFilter */ Optional.empty());
90 91
}
91 92
92
- private Parser resolveParser(String eventName, String boatClassName) throws IOException {
93
+ private Parser resolveParser(String eventName, String boatClassName) throws IOException, URISyntaxException {
93 94
Parser result = null;
94 95
for (ResultDocumentDescriptor resultDocDescr : documentProvider.getResultDocumentDescriptors()) {
95 96
if (eventName.equals(resultDocDescr.getEventName()) && boatClassName.equals(resultDocDescr.getBoatClass())) {
java/com.sap.sailing.yachtscoring.resultimport/src/com/sap/sailing/yachtscoring/resultimport/YachtScoringCompetitorProvider.java
... ...
@@ -1,6 +1,7 @@
1 1
package com.sap.sailing.yachtscoring.resultimport;
2 2
3 3
import java.io.IOException;
4
+import java.net.URISyntaxException;
4 5
import java.util.Locale;
5 6
import java.util.Map;
6 7
import java.util.Set;
... ...
@@ -40,12 +41,12 @@ public class YachtScoringCompetitorProvider extends AbstractYachtScoringProvider
40 41
}
41 42
42 43
@Override
43
- public Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException {
44
+ public Map<String, Set<String>> getHasCompetitorsForRegattasInEvent() throws IOException, URISyntaxException {
44 45
return competitorResolver.getHasCompetitorsForRegattasInEvent();
45 46
}
46 47
47 48
@Override
48
- public Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName) throws JAXBException, IOException {
49
+ public Iterable<CompetitorDescriptor> getCompetitorDescriptors(String eventName, String regattaName) throws JAXBException, IOException, URISyntaxException {
49 50
return competitorResolver.getCompetitorDescriptors(eventName, regattaName);
50 51
}
51 52
java/com.sap.sse.datamining.test/resources/stringmessages/Test_StringMessages_de.properties
... ...
@@ -6,7 +6,7 @@ regattaName = Regatta Name
6 6
crossSum = Quersumme
7 7
Name=Name
8 8
Year = Jahr
9
-LegNumber=Anzahl der Schenkel
9
+LegNumber=Anzahl der Bahnschenkel
10 10
SailID=Segelnummer
11 11
TestCluster=Test Cluster Deutsch
12 12
increment=Inkrementieren
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/DOMUtils.java
... ...
@@ -2,6 +2,10 @@ package com.sap.sse.gwt.client;
2 2
3 3
import com.google.gwt.dom.client.Document;
4 4
import com.google.gwt.dom.client.Element;
5
+import com.google.gwt.dom.client.NodeList;
6
+import com.google.gwt.dom.client.OptionElement;
7
+import com.google.gwt.dom.client.SelectElement;
8
+import com.google.gwt.user.client.ui.ListBox;
5 9
import com.google.gwt.user.client.ui.Widget;
6 10
7 11
/**
... ...
@@ -37,4 +41,13 @@ public final class DOMUtils {
37 41
return doc.isCSS1Compat() ? doc.getDocumentElement() : doc.getBody();
38 42
}
39 43
44
+ /**
45
+ * Returns a live list of the {@link OptionElement}s constituting the list box contents. Live here means that
46
+ * adding/removing items to the list box reflects in the node list returned. The {@link OptionElement}s in the node
47
+ * list can be used, e.g., to set a tooltip on the elements, using {@link OptionElement#setTitle(String)}.
48
+ */
49
+ public static NodeList<OptionElement> getOptions(ListBox listBox) {
50
+ final SelectElement selectElement = listBox.getElement().cast();
51
+ return selectElement.getOptions();
52
+ }
40 53
}
... ...
\ No newline at end of file
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/async/TimeRangeActionsExecutor.java
... ...
@@ -15,12 +15,12 @@ import com.sap.sse.common.Util.Pair;
15 15
* An Executor for efficient, asynchronous execution of {@link TimeRangeAsyncAction}s. This executor operates on
16 16
* {@link TimeRangeAsyncAction}s which specify the wanted {@link TimeRange} by {@link Key} with
17 17
* {@link TimeRangeAsyncAction#getTimeRanges()} and are expected to return a {@code Result} when their
18
- * {@link TimeRangeAsyncAction#execute(Map<Key,TimeRange>, AsyncCallback)} is invoked. The {@link Key} could, e.g., be a
18
+ * {@link TimeRangeAsyncAction#execute(Map, AsyncCallback)} is invoked. The {@link Key} could, e.g., be a
19 19
* competitor or a boat class.
20 20
* <p>
21 21
*
22 22
* This executor manages and optimizes concurrent requests for equal {@code Key}s that have overlapping time ranges.
23
- * This way, redundant requests for the same data can be avoided. This executor collects the results of the overlapping
23
+ * This way, redundant requests for the same data can thus be avoided. This executor collects the results of the overlapping
24 24
* concurrent requests and uses them to satisfy each individual client request as if they had all been executed in their
25 25
* entirety.
26 26
* <p>
java/com.sap.sse.security.datamining/src/com/sap/sse/security/datamining/components/SecuritySessionsRetrievalProcessor.java
... ...
@@ -43,7 +43,7 @@ public class SecuritySessionsRetrievalProcessor extends AbstractRetrievalProcess
43 43
final PrincipalCollection pc = (PrincipalCollection) attributeValue;
44 44
if (pc != null && pc.getPrimaryPrincipal() != null) {
45 45
final User user = securityService.getUserByName((String) pc.getPrimaryPrincipal());
46
- if (user != null) {
46
+ if (user != null && securityService.hasCurrentUserReadPermission(user)) {
47 47
data.add(new SessionWithContext(securityService, session, user));
48 48
break;
49 49
}
java/target/env-default-rules.sh
... ...
@@ -107,3 +107,12 @@ fi
107 107
if [ -n ${CHARGEBEE_SITE} -a -n ${CHARGEBEE_APIKEY} ]; then
108 108
ADDITIONAL_JAVA_ARGS="${ADDITIONAL_JAVA_ARGS} -Dchargebee.site=${CHARGEBEE_SITE} -Dchargebee.apikey=${CHARGEBEE_APIKEY}"
109 109
fi
110
+if [ -n ${MANAGE2SAIL_ACCESS_TOKEN} ]; then
111
+ ADDITIONAL_JAVA_ARGS="${ADDITIONAL_JAVA_ARGS} -Dmanage2sail.accesstoken=${MANAGE2SAIL_ACCESS_TOKEN}"
112
+fi
113
+if [ -n ${IGTIMI_CLIENT_ID} ]; then
114
+ ADDITIONAL_JAVA_ARGS="${ADDITIONAL_JAVA_ARGS} -Digtimi.client.id=${IGTIMI_CLIENT_ID}"
115
+fi
116
+if [ -n ${IGTIMI_CLIENT_SECRET} ]; then
117
+ ADDITIONAL_JAVA_ARGS="${ADDITIONAL_JAVA_ARGS} -Digtimi.client.secret=${IGTIMI_CLIENT_SECRET}"
118
+fi
wiki/howto/development/gwt-ui-binder.md
... ...
@@ -30,13 +30,13 @@ Ubilabs will deliver two versions of the site: one for use on desktops and table
30 30
31 31
## Internationalization (i18n)
32 32
33
-We're not yet decided on how to implement i18n. GWT offers two mechanisms that seem viable, see http://www.gwtproject.org/doc/latest/DevGuideUiBinderI18n.html. We could use GWT's feature that generates the property files. The upside would be that in case of many static texts we would get a quick start. The generation of UUIDs from the original static texts additionally ensures that changes cause compile errors and hence notify us that re-translation is required, which is good.
33
+GWT offers two mechanisms for i18n, see [https://www.gwtproject.org/doc/latest/DevGuideUiBinderI18n.html](https://www.gwtproject.org/doc/latest/DevGuideUiBinderI18n.html) for the UI Binder approach and [https://www.gwtproject.org/doc/latest/DevGuideI18nMessages.html](https://www.gwtproject.org/doc/latest/DevGuideI18nMessages.html) for explicit message declaration. The upside of GWT's feature that generates the property files would be that in case of many static texts we would get a quick start. The generation of UUIDs from the original static texts additionally ensures that changes cause compile errors and hence notify us that re-translation is required, which is good.
34 34
35 35
The downside is that redundancies across different entry points are not handled well and that the property files may not fit in well with programmatic use of those message strings.
36 36
37
-Alternatively, it is possible to declare the string messages class and the method name that shall be used for the string message property. This way, we could consolidate all texts into a single message properties file which may help avoid redundancies.
37
+Alternatively, it is possible to declare the string messages class and the method name that shall be used for the string message property. This way, we consolidate all texts into a single message properties file which may help avoid redundancies. This is what we have decided for. Find those various ``StringMessages.java`` and corresponding ``StringMessages.properties`` in various places.
38 38
39
-We'll probably try out both approaches in a small scale and then decide.
39
+More details on the i18n process, e.g., how translation is triggered, can be found [here](/wiki/howto/development/i18n).
40 40
41 41
## UI Architecture
42 42
wiki/howto/development/i18n.md
... ...
@@ -0,0 +1,113 @@
1
+# Internationalization (i18n)
2
+
3
+The topic of internationalization deals largely with the translation of UI texts into different languages / locales. In this project we see three different approaches:
4
+
5
+- message strings for web user interfaces built with the Google Web Toolkit (GWT), with i18n happening on the client based, e.g., on the browser's locale
6
+- message strings display in the web user interface, but with the locale and message selection happening on the server side, e.g., for the Data Mining framework
7
+- message strings for mobile apps
8
+
9
+For all these cases, English (en) and German (de) are the development locales where development maintains the message strings. All other languages (at the time of this writing these are zh, ru, ja, es, fr, pt, it, da, sl, and cs) will be handled by SAP Language Labs (LXLabs). The process for this is explained below.
10
+
11
+## GWT UI, Messages
12
+
13
+[https://www.gwtproject.org/doc/latest/DevGuideI18nMessages.html](https://www.gwtproject.org/doc/latest/DevGuideI18nMessages.html) documents the basics. We have several such messages interfaces, such as ``com.sap.sailing.gwt.ui.client.StringMessages`` in the ``com.sap.sailing.gwt.ui`` bundle. These message files contain key/value pairs where the values may contain placeholders for parameters which have to match the corresponding method signature's parameters. The message files use the general Java properties file syntax, in particular the single-quote magic for escaping character sequences which otherwise would have special meaning. Placeholders are written as the zero-based parameter number in curly braces (``{0}``, ``{1}``, etc.).
14
+
15
+For accessing these messages in client-side GWT code, obtain an instance of the messages interface, as so:
16
+
17
+```
18
+ public static final StringMessages INSTANCE = GWT.create(StringMessages.class);
19
+```
20
+
21
+## Backend Messages
22
+
23
+When message strings have to be translated based on a locale already in the back-end, ``com.sap.sse.i18n.ResourceBundleStringMessages`` can be used. Create one instance like this:
24
+
25
+```
26
+ private static final String STRING_MESSAGES_BASE_NAME = "stringmessages/Polars_StringMessages";
27
+
28
+ sailingServerStringMessages = new ResourceBundleStringMessagesImpl(
29
+ STRING_MESSAGES_BASE_NAME, getClassLoader(),
30
+ StandardCharsets.UTF_8.name());
31
+```
32
+
33
+The same quoting, escaping and placeholder rules apply as for standard Java properties files and hence the GWT UI messages files. The difference, when compared to the GWT UI messages, is that there is no Java interface type that models each message as a method. Instead, the default "en" locale is then provided by a file named like the base name provided to the constructor, with the ``.properties`` suffix appended. A good place is a ``resources/`` source folder in your bundle which then contains a ``stringmessages`` sub-folder in which the ``.properties`` files reside. In the example above, the files would have paths relative to the bundle's root folder like this: ``resources/stringmessages/Polars_StringMessages.properties`` for the default "en" locale, or ``resources/stringmessages/Polars_StringMessages_es.properties`` for the Spanish locale.
34
+
35
+The string message values are obtained in the back-end code ``ResourceBundleStringMessages.get`` methods, passing the user's locale that may, e.g., be inferred through ``ProxiedRemoteServiceServlet.getClientLocale()`` when in a GWT RPC service implementation.
36
+
37
+## Mobile Apps
38
+
39
+We can distinguish three types of mobile apps that were created so far over the course of the program. We started by implementing a first native Android app (SAP Sailing Race Manager), using Java as the development language, sharing code also with the back-end logic. Two more apps followed with the same approach: the SAP Sailing Buoy Pinger and the self-tracking SAP Sail InSight app.
40
+
41
+SAP Sail InSight was then also ported to iOS, using Swift / ObjectiveC as languages.
42
+
43
+Later, SAP Sail InSight was developed again from the ground up, this time as a React Native app using TypeScript as the development language. Its naming was slightly altered into "Sail Insight (Powered by SAP)", and it went through different organizations taking care of and publishing it.
44
+
45
+All these approaches differ in how i18n is done for them.
46
+
47
+### Native Android Apps
48
+
49
+These keep their message strings in files called ``strings.xml`` under the ``res/`` folder in the respective app's root folder. They are located in directories named after the locale for which they are used. The default "en" locale uses ``values`` as the directory name, so in total ``mobile/{app-root-folder}/res/values/strings.xml``. Other locales append the locale name to the ``values`` folder name, separated by a dash. Example: ``mobile/{app-root-folder}/res/values-pt/strings.xml`` for the Portugese translations.
50
+
51
+### Old iOS App
52
+
53
+The old iOS version of SAP Sail InSight is still around in our repository. It lives under the ``ios`` root folder in our Git workspace. For each locale there is a ``*.lproj`` sub-folder in ``ios/SAPTracker/src/xcode/SAPTracker`` where the default "en" locale is represented by the ``Base.lproj`` folder, Chinese would then be ``ios/SAPTracker/src/xcode/SAPTracker/zh.lproj``, and so on. In each such ``*.lproj`` folder there are two files: ``InfoPlist.strings`` and ``Localizable.strings`` out of which the app draws the message strings to display for the user.
54
+
55
+### React Native Version of Sail Insight
56
+
57
+The primary repository for the app lives at ``trac@sapsailing.com:/home/trac/sailinsight-git``. A copy exists on Github: ``git@github.com:SailTracks/sailinsight.git``. In order to have these be subject to translation within our existing project with SAP LXLabs, we create a synchronization job that copies the message strings from the ``*.json`` files in the ``src/i18n/translations`` directory of the app repository into the equal-named folder of our default SAP Sailing Analytics repository (currently ``ssh://trac@sapsailing.com/home/trac/git``). When translation is completed, translation results need to be transferred back into the Sail Insight repository.
58
+
59
+## Triggering and Integrating Translations
60
+
61
+We entertain a Git repo clone at ``https://github.tools.sap/SAP-Sailing-Analytics/sapsailing``. When we push new commits to the ``translation`` branch of that repository (see [https://github.tools.sap/SAP-Sailing-Analytics/sapsailing/tree/translation](https://github.tools.sap/SAP-Sailing-Analytics/sapsailing/tree/translation)) then LXLabs will grab these changes in regular intervals (as of this writing typically every Friday evening) and look for changes in files that belong to any message collection.
62
+
63
+Message collections are declared in the file ``translation_v2.json`` in the root of our Git workspace. Adding new collections to this file requires us to inform LXLabs (e.g., vyacheslav.sklyarenko@sap.com, called "Slava"). They need to make the corresponding update in the so-called [Translation Enablement Workbench](https://tew.ingress.prod.lp.shoot.live.k8s-hana.ondemand.com/#/param/WzIwMiw1NiwiLyIsInRhc2tRdWV1ZSJd).
64
+
65
+During the translation process, translators may have questions regarding new or changed message strings. This will results in tasks being assigned by the Translation Enablement Workbench, and an e-mail to the assignee will result. Currently, the default assignee for these tasks is axel.uhl@sap.com. We then need to answer these questions as soon as possible to unblock the translation process so that ideally we get the translation in the same sweep as those for other languages, in the same round.
66
+
67
+When translations are done, they are pushed to the same ``translation`` branch of ``https://github.tools.sap/SAP-Sailing-Analytics/sapsailing`` by LXLabs and then usually contain the translation of all changed or added message strings. LXLabs goes by the default locale ("en") and ignores all we do in the German ("de") locale.
68
+
69
+We have a Github webhook installed (see [https://github.tools.sap/SAP-Sailing-Analytics/sapsailing/settings/hooks/285417](https://github.tools.sap/SAP-Sailing-Analytics/sapsailing/settings/hooks/285417)) which triggers a CGI-BIN script that is installed on sapsailing.com at ``/var/www/cgi-bin/github.cgi`` and is versioned under ``configuration/httpd/cgi-bin/github.cgi`` to which ``/var/www/cgi-bin/github.cgi`` is a symbolic link, and which reacts to pushes from the user with the e-mail address ``tmsatsls+github.tools.sap_service-tip-git@sap.com``, pushing to branch ``translation``. In this case, the Git workspace installed at ``/home/wiki/gitwiki`` for the user ``wiki`` is used to fetch the latest translations, using the Git remote ``sapsailing`` which is expected to use a Personal Access Token (PAT) that is granted read access to the repository. The changes are then pushed to the ``translation`` branch on ``ssh://trac@sapsailing.com/home/trac/git`` which is expected to trigger the [hudson job for the translation branch](https://hudson.sapsailing.com/job/SAPSailingAnalytics-translation/). This, in turn, will run a full build, and if successful, merge the ``translation`` branch into ``master`` and push it back to ``ssh://trac@sapsailing.com/home/trac/git`` which will then trigger the [master's build job](https://hudson.sapsailing.com/job/SAPSailingAnalytics-master/).
70
+
71
+First building in a separate job before merging into master partly goes back to the times when LXLabs had issues exporting Java properties file syntax when placeholders and single-quote characters were used in the translations; a common problem in the French translations. However, this has improved massively, and we haven't seen any translation-incurred build errors in a long time. Yet, we stick to the process.
72
+
73
+Roll-out to the landscape can take place after the master job has built a release successfully.
74
+
75
+The Github webhook CGI script is pointed to by the ``/etc/httpd/conf.d/004-git-ssl.conf`` configuration which has to enable symbolic links for its CGI directory (``Options FollowSymLinks``). The ``ScriptAlias`` in the configuration is defined like this:
76
+
77
+```
78
+ScriptAlias /hooks/ /var/www/cgi-bin/
79
+<Directory "/var/www/cgi-bin">
80
+ SSLOptions +StdEnvVars
81
+ AllowOverride None
82
+ Options FollowSymLinks
83
+ Order allow,deny
84
+ Allow from all
85
+</Directory>
86
+```
87
+
88
+letting the webhook react to the URL ``https://git.sapsailing.com/hooks/github.cgi``. The Github hook configuration must be set to use ``application/json`` as the content type as the CGI script parses pusher and ref from the request body that is expected to be in JSON format.
89
+
90
+## Adding New Collections of Translatable Strings
91
+
92
+Follow the patterns you find in the ``translation_v2.json`` file at the root of our Git repository and make sure to inform LXLabs (currently vyacheslav.sklyarenko@sap.com, called "Slava"). Make sure you have your default ("en") and German ("de") locale-specific files in place and checked into Git. Once LXLabs has confirmed the creation of the new collection, push your changes to the [https://github.tools.sap/SAP-Sailing-Analytics/sapsailing/tree/translation](https://github.tools.sap/SAP-Sailing-Analytics/sapsailing/tree/translation) branch and wait for the translations to come back.
93
+
94
+## Adding Locales
95
+
96
+This comes with two aspects.
97
+
98
+As the first aspect, GWT requires us to "declare" the locales we support. We currently do this in two files which serve as the common ancestor in the GWT module inheritance hierarchy for the two major areas that contribute end user-facing entry points:
99
+
100
+- ``java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/SailingLocalesAllPermutations.gwt.xml``
101
+- ``java/com.sap.sse.security.ui/src/main/resources/com/sap/sse/security/ui/SecurityLocalesAllPermutations.gwt.xml``
102
+
103
+They both have a sibling file ``...SinglePermutation.gwt.xml`` which is used for local / fast builds where only one browser permutation for the default "en" locale is defined.
104
+
105
+The list of locales supported is specified in a line like this:
106
+
107
+```
108
+ <extend-property name="locale" values="en,de,zh,ru,ja,es,fr,pt,it,da,sl,cs" />
109
+```
110
+
111
+Add your locale's two-letter key to the end of this list, separated by a comma.
112
+
113
+The second aspect is to request the translation into the new locale from LXLabs (vyacheslav.sklyarenko@sap.com, called "Slava"). This typically comes at a price tag of around 5k Euro for each new language. If the cost center owner approves, LXLabs bills the language addition to that cost center, and a few weeks later the new translations will be delivered to the ``translation`` branch.
... ...
\ No newline at end of file
wiki/howto/onboarding.md
... ...
@@ -44,11 +44,11 @@ First of all, make sure you've looked at [http://www.amazon.de/Patterns-Elements
44 44
### Installations
45 45
46 46
1. JDK >= 11. No longer required by Eclipse because new Eclipse releases bring their own JDK bundled with the installer. Still, if you want to use Java 11 or Java 17, install any such JDK and set the `JAVA_HOME` variable to it.
47
-2. Eclipse IDE for Eclipse Committers, version 4.26.0 ["2022-12"](https://www.eclipse.org/downloads/packages/release/2022-12/r/eclipse-ide-eclipse-committers)
47
+2. Eclipse IDE for Eclipse Committers, version 4.29.0 ["2023-09"](https://www.eclipse.org/downloads/packages/release/2023-09/r)
48 48
3. JDK 1.8 (Java SE 8), ideal is the SAPJVM 1.8: Go to [https://tools.eu1.hana.ondemand.com/#cloud](https://tools.eu1.hana.ondemand.com/#cloud), scroll down to `SAP JVM` select your operating System, extract the downloaded .zip into desired location (e.g. Windows `C:\Program Files\Java`. If you want to make this your default JDK, set the `JAVA_HOME` variable to it. In any case, set the `JAVA8_HOME` variable to it which is required by a few build scripts where certain steps currently are not yet compatible with newer JDK releases, such as our Android build process, keeping us on Gradle 6.0.1 for the time being which isn't Java 17-compatible.
49 49
4. Git (e.g. Git for Windows v2.18), [http://git-scm.com](http://git-scm.com) / [https://git-for-windows.github.io](https://git-for-windows.github.io)
50 50
5. Configure git (see [Git repository configuration essentials](#onboarding-information_sap-sailing-analytics-development-setup_git-repository-configuration-essentials))
51
-6. MongoDB (at least Release 4.4), download: [https://www.mongodb.com/](https://www.mongodb.com/)
51
+6. MongoDB (at least Release 4.4), download: [https://www.mongodb.com/](https://www.mongodb.com/). You may need to choose the community edition. In addition, install `mongosh`.
52 52
7. RabbitMQ, download from [http://www.rabbitmq.com](http://www.rabbitmq.com). Requires Erlang to be installed. RabbitMQ installer will assist in installing Erlang. Some sources report that there may be trouble with the latest versions of RabbitMQ. In some cases, McAffee seems to block the installation of the latest version on SAP hardware; in other cases connection problems to the newest versions have been reported. We know that version 3.6.8 works well. [https://github.com/rabbitmq/rabbitmq-server/releases/tag/rabbitmq_v3_6_8](https://github.com/rabbitmq/rabbitmq-server/releases/tag/rabbitmq_v3_6_8)
53 53
8. Maven 3.1.1 (or higher), [http://maven.apache.org](http://maven.apache.org)
54 54
A setup guide for windows can be found on this webpage: [https://maven.apache.org/guides/getting-started/windows-prerequisites.html](https://maven.apache.org/guides/getting-started/windows-prerequisites.html)
... ...
@@ -113,6 +113,7 @@ Out of the box, multiple settings in Eclipse need to be changed. Go to Window
113 113
- In "General ⇒ Content Types" select on CSS (Text ⇒ CSS) and add \*.gss in the lower file association list to get limited syntax highlighting and content assist in GSS files
114 114
- In "General ⇒ Editors ⇒ Text Editors" check Insert Spaces for Tabs
115 115
- In "General ⇒ Editors ⇒ Text Editors ⇒ Quick Diff" change the reference source from 'Version on Disk' to 'A Git Revision'. If you like other colours for marking diffs change them here. (Example: Changes = Yellow, Additions = Green, Deletions = Red)
116
+- If you'd like to be able to import official results from the Manage2Sail regatta management system: In Run/Debug ⇒ String Substitution add a variable ``MANAGE2SAIL_ACCESS_TOKEN``. Ask your team lead for the value of such an access token you can uses for testing. The variable is used by the "Sailing Server (No Proxy)" launch configuration. and maybe others.
116 117
- In "GWT ⇒ Errors/Warnings" set "Missing SDK" to "Ignore"
117 118
- In "GWT ⇒ GWT Settings ⇒ Add..." add the GWT SDK you downloaded and unpacked earlier
118 119
- In "Java ⇒ Build Path ⇒ Classpath Variables" create a new classpath variable called `ANDROID_HOME`. Set its value to the installation location of your Android SDK, e.g., `C:\Users\'user'\AppData\Local\Android\Sdk` or `/usr/local/android-sdk-linux`.
... ...
@@ -245,13 +246,14 @@ See [RaceCommittee App](/wiki/info/mobile/racecommittee-app) for more informatio
245 246
246 247
Irritating behavior can occur on ARM-based processors, such as on a MacBook. There are some problems, especially with graphical functions. There are cases where javax.imageio.ImageIO, javax.imageio.ImageReader or java.awt.graphics2D stops responding without error message.
247 248
In such cases it might help to set AWT to headless mode (`-Djava.awt.headless=true`, see [stackoverflow](https://stackoverflow.com/questions/13796611/imageio-read-with-mac for more information)).
249
+Another struggle can be to install the JVM Profiler Plug-in on ARM based Eclipse. It seems to block the necessary Software while the Automatic Eclipse plugin installation. One way is to use the Eclipse x64 Installer. It will take more time for transformation, but you will be able to use the Profiler.
248 250
249 251
### GWT Browser Plugin
250 252
Install the GWT Browser Plugin for the GWT Development mode. As of 2016-08-31 Firefox is the only browser supporting the GWT plugin, you have to download Firefox version 24 for it to work. The Plugin can be found on this page: [https://code.google.com/archive/p/google-web-toolkit/downloads](https://code.google.com/archive/p/google-web-toolkit/downloads)
251 253
252 254
### Create Hudson Job
253 255
If you want a hudson job to run when you push your branch then you can run a script in `configuration` called . Run options for a branch titled `createHudsonJobForBug.sh`. For you bug branch titled `bug<bug number>`, create a build job, which will create a release, by running the script like so: `./createHudsonJobForBug.sh <bug number>`.
254
-If on Windows, you may need to disable any web shields in antivirus software, to allow `curl` to function.
256
+If on Windows, you may need to disable any web shields in antivirus software, to allow `curl` to function. If on Mac, you may need to install gnu-sed.
255 257
256 258
###Issues when playing around with AWS
257 259
- The problem: **aws cli (used for aws ec2 describe-tags) hangs in eu-west-2** in all AZs on new instances I created, using a target group which permitted all outbound connections and inbound https, http and ssh connections. I tried permitting everything but that didn’t work. When I attached (at Axel’s suggestion) the Java Application with Reverse Proxy security group, it worked but — even if I duplicated this security group, and applied that copy instead — it still didn’t work.
wiki/info/landscape/archive-server-upgrade.md
... ...
@@ -2,15 +2,22 @@
2 2
3 3
## TL;DR
4 4
5
-- Launch MongoDB replica for ``archive`` replica set (see [here](https://security-service.sapsailing.com/gwt/AdminConsole.html#LandscapeManagementPlace:))
6
-- Wait until MongoDB replica is in ``SECONDARY`` state
5
+- Optionally, to accelerate DB reads, launch MongoDB replica for ``archive`` replica set (see [here](https://security-service.sapsailing.com/gwt/AdminConsole.html#LandscapeManagementPlace:)); wait until MongoDB replica is in ``SECONDARY`` state
7 6
- Launch "more like this" based on existing primary archive, adjusting the ``INSTALL_FROM_RELEASE`` user data entry to the release of choice and the ``Name`` tag to "SL Archive (New Candidate)"
8
-- Wait until the new instance is done with its background tasks and CPU utilization goes to 0% (approximately 24h)
7
+- Wait until the new instance is done with its background tasks and CPU utilization goes to 0% (approximately 36h)
8
+- Create an entry in the reverse proxy's ``/etc/httpd/conf.d/001-events.conf`` file like this:
9
+```
10
+ Use Plain archive-candidate.sapsailing.com 172.31.46.203 8888
11
+```
12
+with ``172.31.46.203`` being an example of the internal IP address your new archive candidate instance got assigned.
9 13
- Compare server contents, either with ``compareServers`` script or through REST API, and fix any differences
14
+```
15
+ java/target/compareServers -ael https://www.sapsailing.com https://archive-candidate.sapsailing.com
16
+```
10 17
- Do some spot checks on the new instance
11 18
- Switch reverse proxy by adjusting ``ArchiveRewrite`` macro under ``root@sapsailing.com:/etc/httpd/conf.d/000-macros.conf`` followed by ``service httpd reload``
12
-- Terminate old fail-over EC2 instance
13
-- adjust Name tags for what is now the fail-over and what is now the primary archive server in EC2 console
19
+- Terminate old fail-over EC2 instance; you will have to disabel its termination protection first.
20
+- Adjust Name tags for what is now the fail-over and what is now the primary archive server in EC2 console
14 21
15 22
[[_TOC_]]
16 23
... ...
@@ -61,6 +68,16 @@ INFO: Thread[MarkPassingCalculator for race R14 initialization,4,main]: Timeout
61 68
```
62 69
that will keep repeating. Watch out for the ``queued tasks`` count. It should be decreasing, and when done it should go down to 0 eventually.
63 70
71
+### Create a Temporary Mapping in ``/etc/httpd/conf.d/001-events.conf`` to Make New Server Accessible Before Switching
72
+
73
+Grab the internal IP address of your freshly launched archive server candidate (something like 172.31.x.x) and ensure you have a line of the form
74
+
75
+```
76
+ Use Plain archive-candidate.sapsailing.com 172.31.35.213 8888
77
+```
78
+
79
+in the file ``root@sapsailing.com:/etc/httpd/conf.d/001-events.conf``, preferably towards the top of the file where it can be quickly found. Save the changes and check the configuration using the ``apachectl configtest`` command. It should give an output saying ``Syntax OK``. Only in this case reload the configuration by issuing the ``service httpd reload`` command as user ``root``. After this command has completed, you can watch your archive server candidate start up at [https://archive-candidate.sapsailing.com/gwt/status](https://archive-candidate.sapsailing.com/gwt/status) and make any changes necessary when the ``compareServers`` script (see below) notifies you of any differences that need handling.
80
+
64 81
### Comparing Contents with Primary ARCHIVE Server
65 82
66 83
This is when you can move on to comparing the new candidate's contents with the primary archive server. Two ways are currently possible.
... ...
@@ -94,6 +111,8 @@ Differences are reported in a JSON response document. If no differences are foun
94 111
```
95 112
In this example, ``34.242.227.113`` would be the public IP address of the server that responded to the ``www.sapsailing.com`` request (the current primary archive server), and ``1.2.3.4`` would be the public IP address of your new candidate archive server. The response status will be ``200`` if the comparison was ok, and ``409`` otherwise. Handle differences are described for the ``compareServers`` script above.
96 113
114
+You can also trigger the REST API-based comparison by using the ``-a`` option of the ``compareServers`` script.
115
+
97 116
### Manual Spot Checks
98 117
99 118
Following the mandatory automated content comparison you should do a few spot checks on the new archive server candidate. Go to ``http://1.2.3.4:8888/gwt/Home.html`` if ``1.2.3.4`` is the public IP address of your new archive server candidate and browse through a few events. Note that clicking on a link to show all events will get you back to ``www.sapsailing.com``. In this case, replace ``www.sapsailing.com`` by your candidate server's public IP address again and continue browsing.
wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch.md
... ...
@@ -172,28 +172,24 @@ maxretry = 5
172 172
173 173
## Basic setup for reverse proxy instance
174 174
175
-perl
176
-httpd
177
-apachetop
178
-per-CGI
179
-postfix
180
-sendmail-cf
181
- mod_proxy_html
182
-tmux
183
-nfs-utils
184
-amazon linux extras install php7.2 && yum update && yum install php
175
+From a fresh amazon linux 2023 instance (HVM) install perl, httpd, mod_proxy_html, tmux, nfs-utils, git, whois and jq. Then type `amazon-linux-extras install epel`, which adds the epel repo so you can then run install apachetop.
176
+Then you need to remove the automatic ec2 code which disabled root access; reconfigure the sshd_config; setup the keys update script; and initialise the crontab. Store a bearer token in the home dir.
185 177
178
+Rename the welcome.conf. Add a basic web page, as the Apache default page can sometimes return no 2xx codes, which can lead to failing health checks.
186 179
187
-root access
188
-num of connections trackable
189
-keyreader token
180
+Setup fail2ban like above.
190 181
191
-amazon-efs-utils
192
-mounts
182
+Ensure httpd is enabled, so that the server auto starts upon a restart.
183
+Other modules may need to be installed, depending on the httpd config.
193 184
194
-mail -> I don't think anything is needed but I added the stuf to sendmail-cf ///
185
+Configure a startup service (either in /etc/systemd/system or etc/rcX directories) to try to mount an attached nvme as swap space (this step needs to be checked after setup).
186
+Swap space still needs to be fully automated.
187
+
188
+Postmail is useful. The script for this procedure is in configuration and is titled setupDisposableProxy.sh
189
+
190
+Setup the logrotate target.
191
+
192
+Setup the fstab (not automated).
193
+Update amazon cli (because pricing list requires it)
195 194
196
-logrotate target
197 195
198
-chkconfig nfs on (half complete)
199
-fail2ban setup
... ...
\ No newline at end of file
wiki/info/landscape/upgrading-archive-server.md
... ...
@@ -1,113 +0,0 @@
1
-# Upgrading the Archive Server
2
-
3
-[[_TOC_]]
4
-
5
-For the archive server we currently use two EC2 instances: one as the production server, and a second one as a failover instance. The failover server is usually one version "behind" the production server which is meant to allow for recovering fast from regressions introduced accidentally by an upgrade. With startup cycles that take approximately 24h as of this writing it is no option to hope to recover by starting another instance "fast."
6
-
7
-The mapping for which archive server to use is encoded in the central web server's Apache reverse proxy configuration under ``/etc/httpd/conf.d/000-macros.conf``. At its head there is a configuration like this:
8
-
9
-```
10
-<Macro ArchiveRewrite>
11
-# ARCHIVE, based on i3.2xlarge, 64GB RAM and 1.9TB swap
12
- Use Rewrite 172.31.26.254 8888
13
-</Macro>
14
-```
15
-
16
-It defines the ``ArchiveRewrite`` macro which is then used by other macros such as ``Series-ARCHIVE`` and ``Event-ARCHIVE``, used then in ``/etc/httpd/conf.d/001-events.conf``.
17
-
18
-The process of upgrading the archive server works in the following steps:
19
-* Scale out the underlying ``archive`` MongoDB replica set
20
-* Launch a new EC2 instance with the necessary archive server configuration for the new release you'd like to upgrade to
21
-* Create a temporary mapping in ``/etc/httpd/conf.d/001-events.conf`` to make new server accessible before switching
22
-* Wait for the new candidate server to have finished loading
23
-* Compare contents to running archive
24
-* Switch ``ArchiveRewrite`` macro to new instance
25
-* Reload Apache httpd service
26
-* Re-label EC2 instances and shut down old failover server
27
-
28
-The following sections explain the steps in a bit more detail.
29
-
30
-## Scale out the underlying ``archive`` MongoDB replica set
31
-
32
-When not launching an archive server, the ``archive`` replica set runs on a small, inexpensive but slow instance ``dbserver.internal.sapsailing.com:10201`` with which launching a new archive server takes days and weeks. In order to accelerate loading all content, first two or three replicas need to be added. Either use the ``configuration/aws-automation/launch-mongodb-replica.sh`` script from your git workspace as many times as you want to have replicas:
33
-
34
-```
35
- ./configuration/aws-automation/launch-mongodb-replica.sh -r archive -p dbserver.internal.sapsailing.com:10201 -P 0 -t i3.2xlarge -k {your-key-name}
36
-```
37
-
38
-Or use [https://security-service.sapsailing.com/gwt/AdminConsole.html?locale=en#LandscapeManagementPlace:](https://security-service.sapsailing.com/gwt/AdminConsole.html?locale=en#LandscapeManagementPlace:) and log on with your account, make sure you have an EC2 key registered, then select the ``archive`` replica set under "MongoDB Endpoints" and click on the "Scale in/out" action button, then choose the number of replicas you'd like to launch and confirm.
39
-
40
-Once triggered, you can watch the instances start up in the AWS console under EC2 instances. The filling of those replicas may take a few hours; you may check progress by connecting to a host in the landscape with the ``mongo`` client installed and try something like this:
41
-
42
-```
43
- $ mongo "mongodb://dbserver.internal.sapsailing.com:10201/?replicaSet=archive&retryWrites=true&readPreference=nearest"
44
- ...
45
- archive:PRIMARY> rs.status()
46
- archive:PRIMARY> quit()
47
-```
48
-
49
-This will output a JSON document listing the members of the ``archive`` replica set. You should see a ``stateStr`` attribute which usually is one of ``PRIMARY``, ``SECONDARY``, or ``STARTUP2``. Those ``STARTUP2`` instances are in the process of receiving an initial load from the replica set and are not yet ready to receive requests. If you'd like to minimize the actual start-up time of your archive server process later, wait until all replicas have reached the ``SECONDARY`` state. But it's no problem, either, to launch the new archive server before that, only it will take longer to launch, and probably the additional load on the replica set will make the replicas reach the ``SECONDARY`` state even later.
50
-
51
-## Launch a new EC2 instance
52
-
53
-Use an instance of type ``i3.2xlarge`` or larger. The most important thing is to pick an "i" instance type that has fast NVMe disks attached. Those will be configured automatically as swap space. You may select "Launch more like this" based on the current production archive server to start with. Use the following as the user data:
54
-
55
-```
56
-INSTALL_FROM_RELEASE={build-...}
57
-USE_ENVIRONMENT=archive-server
58
-REPLICATE_MASTER_BEARER_TOKEN="***"
59
-```
60
-
61
-Obtain the actual build version, e.g., from looking at [https://releases.sapsailing.com](https://releases.sapsailing.com), picking the newest ``build-...`` release. The Java heap size is currently set at 200GB in [https://releases.sapsailing.com/environments/archive-server](https://releases.sapsailing.com/environments/archive-server). You can adjust this by either adding a ``MEMORY=...`` assignment to your user data, overriding the value coming from the ``archive-server`` environment, or you may consider adjusting the ``archive-server`` environment for good by editing ``trac@sapsailing.com:releases/environments/archive-server``.
62
-
63
-Launching the instance in our default availability zone (AZ) ``eu-west-1c`` may be preferable as it minimizes cross-AZ traffic since the central web server / reverse proxy is also located in that AZ. However, this is not absolutely necessary, and you may also consider it a good choice to launch the new candidate in an AZ explicitly different from the current production archive server's AZ in order to increase chances for high availability should an AZ die.
64
-
65
-Tag the instance with ``Name=SL Archive (New Candidate)`` and ``sailing-analytics-server=ARCHIVE``. You may have inherited these tags (except with the wrong name) in case you used "Launch more like this" before.
66
-
67
-## Create a temporary mapping in ``/etc/httpd/conf.d/001-events.conf`` to make new server accessible before switching
68
-
69
-Grab the internal IP address of your freshly launched archive server candidate (something like 172.31.x.x) and ensure you have a line of the form
70
-
71
-```
72
- Use Plain archive-candidate.sapsailing.com 172.31.35.213 8888
73
-```
74
-
75
-in the file ``root@sapsailing.com:/etc/httpd/conf.d/001-events.conf``, preferably towards the top of the file where it can be quickly found. Save the changes and check the configuration using the ``apachectl configtest`` command. It should give an output saying ``Syntax OK``. Only in this case reload the configuration by issuing the ``service httpd reload`` command as user ``root`. After this command has completed, you can watch your archive server candidate start up at [https://archive-candidate.sapsailing.com/gwt/status](https://archive-candidate.sapsailing.com/gwt/status).
76
-
77
-## Wait for the new candidate server to have finished loading
78
-
79
-At the top of the ``/gwt/status`` output you'll see the fields ``numberofracestorestore`` and the ``numberofracesrestored``. The ``/gwt/status`` health check will respond with a status 200 only after the ``numberofracesrestored`` has reached the ``numberofracestorestore``. But even after that the new archive server candidate will be busy for a few more hours. The ``numberofracesrestored`` count only tells you the loading of how many races has been *triggered*, not necessarily how many of those have already *completed* loading. Furthermore, even when all content has been loaded, calculations will keep going on for a few more hours, re-establishing information about wind estimation, mark passings, and maneuvers. You can follow the CPU load of the candidate server in the AWS EC2 console. Select your new instance and switch to the Monitoring tab, there watch for the "CPU Utilization" and wait for it to consistently drop from approximately 100% down to 0%. This indicates the end of the loading process, and you can now proceed to the next step.
80
-
81
-## Compare contents to running archive
82
-
83
-It is a good idea to ensure all races really have loaded successfully, and all wind data and GPS data is available again, just like in the current production archive server. For this purpose, the ``java/target/compareServers`` script exists in your git workspace. Call like this:
84
-
85
-```
86
- java/target/compareServers -el https://www.sapsailing.com https://archive-candidate.sapsailing.com
87
-```
88
-
89
-This assumes, of course, that you have completed the step explained above for establishing HTTPS access to ``archive-candidate.sapsailing.com``.
90
-
91
-The script will fetch leaderboard group by leaderboard group and compare their contents, race loading state, and presence or absence of tracking and wind data for all races reachable through those leaderboard groups. The names of the leaderboard groups will be printed to the standard output. If a difference is found, the script will exit, showing the difference on the console. You then have a chance to fix the problem by logging on to [https://archive-candidate.sapsailing.com/gwt/AdminConsole.html](https://archive-candidate.sapsailing.com/gwt/AdminConsole.html). Assuming you were able to fix the problem, continue the comparison by calling the ``compareServers`` script again, adding the ``-c`` option ("continue"), like this:
92
-
93
- ```
94
- java/target/compareServers -cel https://www.sapsailing.com https://archive-candidate.sapsailing.com
95
-```
96
-
97
-This will continue with the comparison starting with the leaderboard group where a difference was found in the previous run.
98
-
99
-Should you encounter a situation that you think you cannot or don't want to fix, you have to edit the ``leaderboardgroups.old.sed`` and ``leaderboardgroups.new.sed`` files and remove the leaderboard group(s) you want to exclude from continued comparisons. You find the leaderboard group that produced the last difference usually at the top of the two files. Once saved, continue again with the ``-c`` option as shown above.
100
-
101
-Once the script has completed successfully you may want to do a few spot checks on [https://archive-candidate.sapsailing.com](https://archive-candidate.sapsailing.com). Note, that when you navigate around, some links may throw you back to ``www.sapsailing.com``. There, you manually have to adjust your browser address bar to ``archive-candidate.sapsailing.com`` to continue looking at your new candidate archive server contents.
102
-
103
-## Switch ``ArchiveRewrite`` macro to new instance and reload httpd
104
-
105
-Once you're satisfied with the contents of your new candidate it's time to switch. Log on as ``root@sapsailing.com`` and adjust the ``/etc/httpd/conf.d/000-macros.conf`` file, providing the new internal server IP address in the ``ArchiveRewrite`` macro at the top of the file. Save and check the configuration again using ``apachectl configtest``. If the syntax is OK, reload the configuration with ``service httpd reload`` and inform the community by a corresponding JAM post, ideally providing the release to which you upgraded.
106
-
107
-## Re-label EC2 instances and shut down old failover server
108
-
109
-Finally, it's time to shut down the old failover instance which is now no longer needed. Go to the AWS EC2 console, look at the running instances and identify the one labeled "SL Archive (Failover)". In the "Instance Settings" disable the termination protection for this instance, then terminate.
110
-
111
-Rename the "SL Archive" instance to "SL Archive (Failover)", then rename "SL Archive (New Candidate)" to "SL Archive".
112
-
113
-Then, enable termination protection for your new "SL Archive" server if it isn't already enabled because you used "Launch more like this" to create the instance using the previous production archive server as a template.
... ...
\ No newline at end of file