Merge branch 'master' into polar-api

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
This commit is contained in:
Frederik Petersen committed 2013-12-18 09:51:01 +01:00
commit 64fe82c2d8
222 files changed
+3148 -6309

No files matched your search

+8 -8
View File
@@ -346,7 +346,7 @@ if [[ "$@" == "hot-deploy" ]]; then
echo -n "Connecting to OSGi server..."
NC_CMD="nc -t 127.0.0.1 $OSGI_TELNET_PORT"
echo "OK"
OLD_BUNDLE_INFORMATION=`echo -n ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_`
OLD_BUNDLE_INFORMATION=`echo ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_`
BUNDLE_ID=`echo $OLD_BUNDLE_INFORMATION | cut -d " " -f 1`
OLD_ACTIVATED_NAME=`echo $OLD_BUNDLE_INFORMATION | cut -d " " -f 3`
echo "Could identify bundle-id $BUNDLE_ID for $OLD_ACTIVATED_NAME"
@@ -361,11 +361,11 @@ if [[ "$@" == "hot-deploy" ]]; then
fi
# stop and uninstall
echo -n stop $BUNDLE_ID | $NC_CMD > /dev/null
echo -n uninstall $BUNDLE_ID | $NC_CMD > /dev/null
echo stop $BUNDLE_ID | $NC_CMD > /dev/null
echo uninstall $BUNDLE_ID | $NC_CMD > /dev/null
# make sure bundle is removed
UNINSTALL_INFORMATION=`echo -n ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_`
UNINSTALL_INFORMATION=`echo ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_`
if [[ $UNINSTALL_INFORMATION == "" ]]; then
echo "Uninstall procedure sucessful!"
else
@@ -374,14 +374,14 @@ if [[ "$@" == "hot-deploy" ]]; then
fi
# now reinstall bundle
NEW_BUNDLE_ID=`echo -n install file://$SERVERS_HOME/$active_branch/plugins/deploy/${NEW_BUNDLE_BASENAME}.jar | $NC_CMD`
NEW_BUNDLE_INFORMATION=`echo -n ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_`
NEW_BUNDLE_ID=`echo install file://$SERVERS_HOME/$active_branch/plugins/deploy/${NEW_BUNDLE_BASENAME}.jar | $NC_CMD`
NEW_BUNDLE_INFORMATION=`echo ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_`
NEW_BUNDLE_ID=`echo $NEW_BUNDLE_INFORMATION | cut -d " " -f 1`
echo "Installed new bundle file://$SERVERS_HOME/$active_branch/plugins/deploy/${NEW_BUNDLE_BASENAME}.jar with id $NEW_BUNDLE_ID"
# and start
echo -n start $NEW_BUNDLE_ID | $NC_CMD > /dev/null && sleep 1
NEW_BUNDLE_STATUS=`echo -n ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_ | grep ACTIVE`
echo start $NEW_BUNDLE_ID | $NC_CMD > /dev/null && sleep 1
NEW_BUNDLE_STATUS=`echo ss | $NC_CMD | grep ${OSGI_BUNDLE_NAME}_ | grep ACTIVE`
if [[ $NEW_BUNDLE_STATUS == "" ]]; then
echo "ERROR: Something went wrong with start of bundle. Please check if everything went ok."
exit
+1 -1
View File
@@ -151,7 +151,7 @@ def compute_branch(branch_name)
branch.shared_size += blob.size
branch.shared_packed_size += blob.packed_size
else
if dependable_blob_shas.include?(blob.sha)
if $dependable_blob_shas.include?(blob.sha)
branch.non_shared_dependable_size += blob.size
branch.non_shared_dependable_packed_size += blob.packed_size
else
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.common.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -22,6 +23,12 @@ public class Util {
}
}
public static <T> T[] toArray(Iterable<T> what, T[] arr) {
List<T> list = new ArrayList<T>();
addAll(what, list);
return list.toArray(arr);
}
public static <T> int size(Iterable<T> i) {
if (i instanceof Collection<?>) {
return ((Collection<?>) i).size();
@@ -5,6 +5,7 @@ import static org.junit.Assert.assertFalse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -16,6 +17,7 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.igtimiadapter.IgtimiWindListener;
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
import com.sap.sailing.domain.igtimiadapter.datatypes.COG;
@@ -23,23 +25,18 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong;
import com.sap.sailing.domain.igtimiadapter.datatypes.HDG;
import com.sap.sailing.domain.igtimiadapter.datatypes.SOG;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindListener;
public class WindReceiverTest {
@Test
public void simpleWindReceiverTest() {
final List<Wind> windReceived = new ArrayList<>();
final String deviceSerialNumber = "Non-Existing Test Device";
IgtimiWindReceiver receiver = new IgtimiWindReceiver(deviceSerialNumber);
receiver.addListener(new WindListener() {
IgtimiWindReceiver receiver = new IgtimiWindReceiver(Collections.singleton(deviceSerialNumber));
receiver.addListener(new IgtimiWindListener() {
@Override
public void windDataReceived(Wind wind) {
public void windDataReceived(Wind wind, String deviceSerialNumber) {
windReceived.add(wind);
}
@Override
public void windDataRemoved(Wind wind) {}
@Override
public void windAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) {}
});
TimePoint timePoint = MillisecondsTimePoint.now();
Map<Integer, Object> awaMap = new HashMap<>(); awaMap.put(1, 123. /* degrees from */);
@@ -6,9 +6,11 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -20,11 +22,8 @@ import org.junit.Test;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.igtimiadapter.Account;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.Group;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
import com.sap.sailing.domain.igtimiadapter.Permission;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.Session;
@@ -33,14 +32,17 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.impl.Activator;
public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
@Test
public void testGetUsers() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
Iterable<User> users = connection.getUsers();
assertEquals(1, Util.size(users));
assertEquals(connection.getAccount().getUser().getId(), users.iterator().next().getId());
assertEquals(2, Util.size(users));
Set<Long> userIds = new HashSet<>();
for (User user : users) {
userIds.add(user.getId());
}
assertTrue(userIds.contains(connection.getAccount().getUser().getId()));
}
@Test
@@ -113,9 +115,6 @@ public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX", Locale.GERMAN);
TimePoint start = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:00:00Z"));
TimePoint end = new MillisecondsTimePoint(dateFormat.parse("2013-11-09T07:10:00Z"));
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9");
IgtimiConnection connection = connectionFactory.connect(account);
// URL is https://www.igtimi.com/api/v1/devices/data_access_windows?type=read&start_time=1383811200000&end_time=1383933600000&access_token=3b6cbd0522423bb1ac274ddb9e7e579c4b3be6667622271086c4fdbf30634ba9
Iterable<DataAccessWindow> daws = connection.getDataAccessWindows(Permission.read, start, end, /* deviceSerialNumbers; get all devices available for that time */ null);
Set<String> deviceSerialNumbers = new HashSet<>();
@@ -132,4 +131,17 @@ public class BasicIgtimiAdapterTest extends AbstractTestWithIgtimiConnection {
assertTrue(foundWind);
}
@Test
public void testReadLatestData() throws IllegalStateException, ClientProtocolException, IOException, ParseException {
Iterable<Fix> fixes = connection.getLatestFixes(Arrays.asList(new String[] { "DD-EE-AAHG", "GA-EN-AAEA", "DD-EE-AAGA" }), Type.SOG );
assertEquals(2, Util.size(fixes));
Iterator<Fix> i = fixes.iterator();
Fix fix1 = i.next();
assertEquals("DD-EE-AAGA", fix1.getSensor().getDeviceSerialNumber());
assertEquals(Type.SOG, fix1.getType());
Fix fix2 = i.next();
assertEquals("DD-EE-AAHG", fix2.getSensor().getDeviceSerialNumber());
assertEquals(Type.SOG, fix2.getType());
}
}
@@ -0,0 +1,44 @@
package com.sap.sailing.domain.igtimiadapter.test;
import static org.junit.Assert.assertEquals;
import java.util.Iterator;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.junit.Test;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.impl.FixFactory;
public class LatestDatumTest {
private final String sample1 = "{\"latest_data\":[{\"latest_datum\":{\"serial_number\":\"DD-EE-AAGA\",\"resource_id\":1012353,\"c1\":0.5556,\"timestamp\":1384091813000.0}},{\"latest_datum\":{\"serial_number\":\"DD-EE-AAHG\",\"resource_id\":1012356,\"c1\":0.3704,\"timestamp\":1384078160000.0}}]}";
private final String sample2 = "{\"latest_data\":[{\"latest_datum\":{\"serial_number\":\"DD-EE-AAHG\",\"resource_id\":1012356,\"c1\":4.0744,\"timestamp\":1384078157500.0}}]}";
@Test
public void testFixProductionFromSample1() throws ParseException {
Iterable<Fix> fixes = new FixFactory().createFixesFromLastDatum((JSONObject) new JSONParser().parse(sample1), Type.SOG);
assertEquals(2, Util.size(fixes));
Iterator<Fix> i = fixes.iterator();
Fix fix1 = i.next();
assertEquals("DD-EE-AAGA", fix1.getSensor().getDeviceSerialNumber());
assertEquals(Type.SOG, fix1.getType());
Fix fix2 = i.next();
assertEquals("DD-EE-AAHG", fix2.getSensor().getDeviceSerialNumber());
assertEquals(Type.SOG, fix2.getType());
}
@Test
public void testFixProductionFromSample2() throws ParseException {
Iterable<Fix> fixes = new FixFactory().createFixesFromLastDatum((JSONObject) new JSONParser().parse(sample2), Type.AWS);
assertEquals(1, Util.size(fixes));
Iterator<Fix> i = fixes.iterator();
Fix fix1 = i.next();
assertEquals("DD-EE-AAHG", fix1.getSensor().getDeviceSerialNumber());
assertEquals(Type.AWS, fix1.getType());
}
}
@@ -165,6 +165,6 @@ public class WebSocketTest {
});
assertNotNull(liveDataConnection);
assertTrue("Connection handshake not successful within 5s", liveDataConnection.waitForConnection(5000l));
liveDataConnection.disconnect();
liveDataConnection.stop();
}
}
@@ -1,4 +1,5 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
.,\
WEB-INF/
@@ -10,7 +10,9 @@ import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.TrackedRace;
/**
* A connection to the Igtimi system for one {@link Client} and one {@link Account}.
@@ -92,7 +94,7 @@ public interface IgtimiConnection {
* are forwarded in the batches in which they are received to the listeners that can be added to the live connection
* using {@link LiveDataConnection#addListener(BulkFixReceiver)}.
*
* @return a connection that the caller can use to stop the live feed by calling {@link LiveDataConnection#disconnect()}.
* @return a connection that the caller can use to stop the live feed by calling {@link LiveDataConnection#stop()}.
*/
LiveDataConnection createLiveConnection(Iterable<String> deviceSerialNumbers) throws Exception;
@@ -141,4 +143,22 @@ public interface IgtimiConnection {
Iterable<Group> getGroups() throws IllegalStateException, ClientProtocolException, IOException, ParseException;
Account getAccount();
/**
* Finds all data access windows that have wind data for the time span around the race, loads their wind data and
* {@link DynamicTrackedRace#recordWind(com.sap.sailing.domain.tracking.Wind, com.sap.sailing.domain.common.WindSource)
* records it} in the tracked races.
*
* @return the number of wind fixes imported per tracked race; contains an entry for all elements in
* <code>trackedRaces</code>
*/
Map<TrackedRace, Integer> importWindIntoRace(Iterable<DynamicTrackedRace> trackedRaces) throws IllegalStateException,
ClientProtocolException, IOException, ParseException;
/**
* Returns the latest datum for the specified devices that contains a fix of the <code>type</code> requested. The
* result contains entries only for those devices that have actually produced a fix of the <code>type</code> requested
* that is readable by the {@link #getAccount()} used by this connection.
*/
Iterable<Fix> getLatestFixes(Iterable<String> deviceSerialNumbers, Type type) throws IllegalStateException, ClientProtocolException, IOException, ParseException;
}
@@ -22,6 +22,12 @@ public interface IgtimiConnectionFactory {
IgtimiConnection connect(Account account);
/**
* Removes the account and the credentials associated with it permanently. The account is identified by the e-mail
* address of the owner of the data to which the account granted access. See also {@link #getExistingAccountByEmail(String)}.
*/
void removeAccount(String eMail);
/**
* Obtains a URL that a user agent (e.g., a web browser) can be sent to in order to allow that user to authenticate
* and then authorize this factory's {@link Client} for accessing the user's Igtimi data. The URL is chosen such that
@@ -0,0 +1,7 @@
package com.sap.sailing.domain.igtimiadapter;
import com.sap.sailing.domain.tracking.Wind;
public interface IgtimiWindListener {
void windDataReceived(Wind wind, String deviceSerialNumber);
}
@@ -4,7 +4,7 @@ public interface LiveDataConnection {
/**
* Disconnects this connection. Afterwards, the connection will no longer feed live data to its listener(s).
*/
void disconnect() throws Exception;
void stop() throws Exception;
/**
* Waits for a successful connection for <code>timeoutInMillis</code> milliseconds. If the connection is successful
@@ -1,11 +1,22 @@
package com.sap.sailing.domain.igtimiadapter;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
/**
* You can think of resources really as meta-info - identifying time blocks of data that have some meaning. For an
* Igtimi device it's a power cycle. However data access is not forced to be "through" a resource. If you only wanted to
* get data from a specific resource then of course you would just use the start and end time stamps in the request.
* Resources are meant to be non-overlapping, although this is not strictly enforced.
*
* @author Axel Uhl (d043530)
*
*/
public interface Resource extends HasId, HasPermissions, HasStartAndEndTime {
boolean isBlob();
int[] getDataTypes();
Iterable<Type> getDataTypes();
String getDeviceSerialNumber();
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory;
import com.sap.sailing.domain.tracking.WindTrackerFactory;
/**
* Maintains data about a default {@link Client} that represents this application when interacting with the Igtimi
@@ -33,7 +34,8 @@ public class Activator implements BundleActivator {
private static final String CLIENT_ID_PROPERTY_NAME = "igtimi.client.id";
private static final String CLIENT_SECRET_PROPERTY_NAME = "igtimi.client.secret";
private static final String CLIENT_REDIRECT_URI_PROPERTY_NAME = "igtimi.client.redirecturi";
private IgtimiConnectionFactoryImpl connectionFactory;
private final IgtimiConnectionFactoryImpl connectionFactory;
private final IgtimiWindTrackerFactory windTrackerFactory;
public Activator() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
final String clientId = System.getProperty(CLIENT_ID_PROPERTY_NAME, DEFAULT_CLIENT_ID);
@@ -43,12 +45,15 @@ public class Activator implements BundleActivator {
DomainObjectFactory domainObjectFactory = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory();
MongoObjectFactory mongoObjectFactory = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory();
connectionFactory = new IgtimiConnectionFactoryImpl(client, domainObjectFactory, mongoObjectFactory);
windTrackerFactory = new IgtimiWindTrackerFactory(connectionFactory);
}
@Override
public void start(BundleContext context) throws Exception {
INSTANCE = this;
context.registerService(IgtimiConnectionFactory.class, connectionFactory, /* properties */ null);
context.registerService(WindTrackerFactory.class, windTrackerFactory, /* properties */ null);
context.registerService(IgtimiWindTrackerFactory.class, windTrackerFactory, /* properties */ null);
}
public static Activator getInstance() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
@@ -61,6 +66,10 @@ public class Activator implements BundleActivator {
public IgtimiConnectionFactoryImpl getConnectionFactory() {
return connectionFactory;
}
public IgtimiWindTrackerFactory getWindTrackerFactory() {
return windTrackerFactory;
}
@Override
public void stop(BundleContext context) throws Exception {
@@ -28,7 +28,8 @@ public class FixFactory {
String deviceSerialNumber = (String) e.getKey();
JSONObject typesJson = (JSONObject) e.getValue();
for (Entry<Object, Object> fixTypeAndFixesJson : typesJson.entrySet()) {
final String[] fixTypeAndOptionalColonSeparatedSensorsSubId = ((String) fixTypeAndFixesJson.getKey()).split(":");
final String fixTypeAndOptionalSensorId = (String) fixTypeAndFixesJson.getKey();
final String[] fixTypeAndOptionalColonSeparatedSensorsSubId = fixTypeAndOptionalSensorId.split(":");
int fixType = Integer.valueOf(fixTypeAndOptionalColonSeparatedSensorsSubId[0]);
JSONObject fixesJson = (JSONObject) fixTypeAndFixesJson.getValue();
JSONArray timePointsMillis = (JSONArray) fixesJson.get("t");
@@ -53,6 +54,32 @@ public class FixFactory {
}
return result;
}
/**
* @param lastDataJson expected to use "c1", "c2", ... instead of "1" and "2" for the sensor parameters, and
* "timestamp" instead of "t" for the timestamp. The values are not provided as arrays but as single values.
*/
public Iterable<Fix> createFixesFromLastDatum(JSONObject lastDataJson, Type type) {
List<Fix> result = new ArrayList<>();
JSONArray latestDataArray = (JSONArray) lastDataJson.get("latest_data");
for (Object d : latestDataArray) {
JSONObject latestDatum = (JSONObject) ((JSONObject) d).get("latest_datum");
String serialNumber = (String) latestDatum.get("serial_number");
// there is a field called "resource_id" but we're currently ignoring it because not all fixes have one and we currently
// don't have a use for it
Map<Integer, Object> valuesPerSubindex = new HashMap<>();
int i=1;
Object c_i;
while ((c_i=latestDatum.get("c"+i)) != null) {
valuesPerSubindex.put(i, c_i);
i++;
}
Sensor sensor = new SensorImpl(serialNumber, /* sensor ID */ 0); // Note: the sensor ID isn't obvious from neither the URL nor the response; using 0 as default
TimePoint timestamp = new MillisecondsTimePoint(((Number) latestDatum.get("timestamp")).longValue());
result.add(createFix(sensor, type, timestamp, valuesPerSubindex));
}
return result;
}
private Fix createFix(Sensor sensor, Type fixType, TimePoint timePoint, Map<Integer, Object> valuesPerSubindex) {
try {
@@ -211,6 +211,19 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
return url.toString();
}
public String getLatestDatumUrl(Iterable<String> deviceSerialNumbers, Type type, Account account) {
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
url.append("resources/data/latest?type=");
url.append(type.getCode());
for (String deviceSerialNumber : deviceSerialNumbers) {
url.append("&serial_numbers[]=");
url.append(deviceSerialNumber);
}
url.append("&");
url.append(getAccessTokenUrlParameter(account));
return url.toString();
}
public String getResourceDataUrl(TimePoint startTime, TimePoint endTime, Iterable<String> serialNumbers,
Map<Type, Double> typeAndCompression, Account account) {
StringBuilder url = new StringBuilder(getApiV1BaseUrl());
@@ -293,7 +306,7 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
@Override
public String getAuthorizationUrl() {
return getBaseUrl()+"/oauth";
return getBaseUrl()+"/oauth/authorize?response_type=code&client_id="+getClient().getId()+"&redirect_uri="+client.getRedirectUri();
}
private Client getClient() {
@@ -477,4 +490,15 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
}
return result;
}
@Override
public void removeAccount(String eMail) {
Account account = getExistingAccountByEmail(eMail);
if (account != null) {
String accessToken = accessTokensByAccount.remove(account);
accountsByEmail.remove(eMail);
mongoObjectFactory.removeAccessToken(accessToken);
}
}
}
@@ -2,9 +2,14 @@ package com.sap.sailing.domain.igtimiadapter.impl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
@@ -14,6 +19,8 @@ import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.igtimiadapter.Account;
import com.sap.sailing.domain.igtimiadapter.BulkFixReceiver;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
@@ -29,9 +36,12 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.websocket.WebSocketConnectionManager;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicTrackImpl;
public class IgtimiConnectionImpl implements IgtimiConnection {
private static final Logger logger = Logger.getLogger(IgtimiConnectionImpl.class.getName());
private final Account account;
private final IgtimiConnectionFactoryImpl connectionFactory;
@@ -94,6 +104,14 @@ public class IgtimiConnectionImpl implements IgtimiConnection {
return result;
}
@Override
public Iterable<Fix> getLatestFixes(Iterable<String> deviceSerialNumbers, Type type) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
HttpClient client = connectionFactory.getHttpClient();
HttpGet getLatestData = new HttpGet(connectionFactory.getLatestDatumUrl(deviceSerialNumbers, type, account));
JSONObject latestDataJson = ConnectivityUtils.getJsonFromResponse(client.execute(getLatestData));
return new FixFactory().createFixesFromLastDatum(latestDataJson, type);
}
@Override
public Iterable<Session> getSessions(Iterable<Long> sessionIds, Boolean isPublic, Integer limit,
Boolean includeIncomplete) throws IllegalStateException, ClientProtocolException, IOException,
@@ -122,6 +140,7 @@ public class IgtimiConnectionImpl implements IgtimiConnection {
@Override
public Iterable<Fix> getResourceData(TimePoint startTime, TimePoint endTime,
Iterable<String> deviceSerialNumbers, Map<Type, Double> typeAndCompression) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
logger.info("Obtaining resource data from "+startTime+" to "+endTime+" for devices "+deviceSerialNumbers+" for types "+typeAndCompression);
HttpClient client = connectionFactory.getHttpClient();
HttpGet getResourceData = new HttpGet(connectionFactory.getResourceDataUrl(startTime, endTime, deviceSerialNumbers, typeAndCompression, account));
JSONObject resourceDataJson = ConnectivityUtils.getJsonFromResponse(client.execute(getResourceData));
@@ -143,6 +162,7 @@ public class IgtimiConnectionImpl implements IgtimiConnection {
Iterable<String> deviceSerialNumbers, BulkFixReceiver bulkFixReceiver, Type... types)
throws IllegalStateException, ClientProtocolException, IOException, ParseException {
Iterable<Fix> result = getResourceData(startTime, endTime, deviceSerialNumbers, types);
logger.info("Received "+Util.size(result)+" fixes; will pass them to BulkFixReceiver for further processing now.");
bulkFixReceiver.received(result);
return result;
}
@@ -209,4 +229,62 @@ public class IgtimiConnectionImpl implements IgtimiConnection {
}
return result;
}
}
@Override
public Map<TrackedRace, Integer> importWindIntoRace(Iterable<DynamicTrackedRace> trackedRaces) throws IllegalStateException, ClientProtocolException, IOException, ParseException {
logger.info("Importing Igtimi wind for tracked races "+trackedRaces);
TimePoint startOfWindow = new MillisecondsTimePoint(Long.MAX_VALUE);
TimePoint endOfWindow = new MillisecondsTimePoint(0);
for (DynamicTrackedRace trackedRace : trackedRaces) {
startOfWindow = Collections.min(Arrays.asList(startOfWindow, IgtimiWindTracker.getReceivingStartTime(trackedRace)));
endOfWindow = Collections.max(Arrays.asList(endOfWindow, IgtimiWindTracker.getReceivingEndTime(trackedRace)));
}
Iterable<DataAccessWindow> daws = getDataAccessWindows(Permission.read, startOfWindow, endOfWindow, /* find all deviceSerialNumbers for window */ null);
logger.info("Found "+Util.size(daws)+" data access windows. Analyzing which ones contain wind data...");
Set<String> deviceSerialNumbers = new HashSet<>();
for (DataAccessWindow daw : daws) {
String deviceSerialNumber = daw.getDeviceSerialNumber();
// now filter for the wind-providing devices
Iterable<Resource> resources = getResources(Permission.read, startOfWindow, endOfWindow,
Collections.singleton(deviceSerialNumber), /* streamIds */ null);
if (hasWind(resources)) {
StringBuilder resourceIDs = new StringBuilder();
boolean first = true;
for (Resource resource : resources) {
if (!first) {
resourceIDs.append(", ");
} else {
first = false;
}
resourceIDs.append(resource.getId());
}
logger.info(" Resources ["+resourceIDs+"] for device "+deviceSerialNumber+" contain wind data");
deviceSerialNumbers.add(deviceSerialNumber);
}
}
final Map<TrackedRace, Integer> result;
if (!deviceSerialNumbers.isEmpty()) {
IgtimiWindReceiver windReceiver = new IgtimiWindReceiver(deviceSerialNumbers);
final WindListenerSendingToTrackedRace windListener = new WindListenerSendingToTrackedRace(
trackedRaces, Activator.getInstance().getWindTrackerFactory());
windReceiver.addListener(windListener);
getAndNotifyResourceData(startOfWindow, endOfWindow, deviceSerialNumbers, windReceiver,
windReceiver.getFixTypes());
result = windListener.getFixesAppliedPerTrackedRace();
logger.info("Imported the following number of wind fixes for the following list of races: "+result);
} else {
logger.info("No Igtimi devices that measure wind found for time window "+startOfWindow+".."+endOfWindow);
result = new HashMap<>();
}
return result;
}
private boolean hasWind(Iterable<Resource> resources) {
for (Resource resource : resources) {
if (Util.contains(resource.getDataTypes(), Type.AWS)) {
return true;
}
}
return false;
}
}
@@ -20,10 +20,13 @@ import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.confidence.ScalableValue;
import com.sap.sailing.domain.igtimiadapter.BulkFixReceiver;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.IgtimiFixReceiverAdapter;
import com.sap.sailing.domain.igtimiadapter.IgtimiWindListener;
import com.sap.sailing.domain.igtimiadapter.datatypes.AWA;
import com.sap.sailing.domain.igtimiadapter.datatypes.AWS;
import com.sap.sailing.domain.igtimiadapter.datatypes.COG;
@@ -32,6 +35,8 @@ import com.sap.sailing.domain.igtimiadapter.datatypes.GpsLatLong;
import com.sap.sailing.domain.igtimiadapter.datatypes.HDG;
import com.sap.sailing.domain.igtimiadapter.datatypes.HDGM;
import com.sap.sailing.domain.igtimiadapter.datatypes.SOG;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.igtimiadapter.websocket.WebSocketConnectionManager;
import com.sap.sailing.domain.tracking.DynamicTrack;
import com.sap.sailing.domain.tracking.Track;
import com.sap.sailing.domain.tracking.Wind;
@@ -41,9 +46,14 @@ import com.sap.sailing.domain.tracking.impl.WindImpl;
/**
* Receives Igtimi {@link Fix}es and tries to generate a {@link Wind} object from each {@link AWS} fix. For this to
* work, the time-wise adjacent {@link AWA}, {@link HDG}/{@link HDGM} and {@link GpsLatLong} fixes are used. If only
* a magnetic heading ({@link HDGM}) is available, the {@link DeclinationService} is used to map that to a true heading.
* The true wind direction is determined by adding the boat speed vector onto the apparent wind vector.
* work, the time-wise adjacent {@link AWA}, {@link HDG}/{@link HDGM} and {@link GpsLatLong} fixes are used. If only a
* magnetic heading ({@link HDGM}) is available, the {@link DeclinationService} is used to map that to a true heading.
* The true wind direction is determined by adding the boat speed vector onto the apparent wind vector.
* <p>
*
* Use the class by hooking it up to a {@link WebWocketConnectionManager} using
* {@link WebSocketConnectionManager#addListener(BulkFixReceiver)} and {@link #addListener(WindListener) add} a
* {@link WindListener} to this instance.
*
* @author Axel Uhl (d043530)
*
@@ -59,7 +69,7 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
private final DynamicTrack<HDGM> hdgmTrack;
private final FixReceiver receiver;
private final DeclinationService declinationService;
private final ConcurrentHashMap<WindListener, WindListener> listeners;
private final ConcurrentHashMap<IgtimiWindListener, IgtimiWindListener> listeners;
private class FixReceiver extends IgtimiFixReceiverAdapter {
@Override
@@ -98,17 +108,17 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
}
}
public IgtimiWindReceiver(String deviceSerialNumber) {
public IgtimiWindReceiver(Iterable<String> deviceSerialNumbers) {
receiver = new FixReceiver();
declinationService = DeclinationService.INSTANCE;
listeners = new ConcurrentHashMap<>();
awaTrack = new DynamicTrackImpl<>("AWA Track for Igtimi wind track for device "+deviceSerialNumber);
awsTrack = new DynamicTrackImpl<>("AWS Track for Igtimi wind track for device "+deviceSerialNumber);
gpsTrack = new DynamicTrackImpl<>("GPS Track for Igtimi wind track for device "+deviceSerialNumber);
cogTrack = new DynamicTrackImpl<>("COG Track for Igtimi wind track for device "+deviceSerialNumber);
sogTrack = new DynamicTrackImpl<>("SOG Track for Igtimi wind track for device "+deviceSerialNumber);
hdgTrack = new DynamicTrackImpl<>("HDG Track for Igtimi wind track for device "+deviceSerialNumber);
hdgmTrack = new DynamicTrackImpl<>("HDGM Track for Igtimi wind track for device "+deviceSerialNumber);
awaTrack = new DynamicTrackImpl<>("AWA Track for Igtimi wind track for device "+deviceSerialNumbers);
awsTrack = new DynamicTrackImpl<>("AWS Track for Igtimi wind track for device "+deviceSerialNumbers);
gpsTrack = new DynamicTrackImpl<>("GPS Track for Igtimi wind track for device "+deviceSerialNumbers);
cogTrack = new DynamicTrackImpl<>("COG Track for Igtimi wind track for device "+deviceSerialNumbers);
sogTrack = new DynamicTrackImpl<>("SOG Track for Igtimi wind track for device "+deviceSerialNumbers);
hdgTrack = new DynamicTrackImpl<>("HDG Track for Igtimi wind track for device "+deviceSerialNumbers);
hdgmTrack = new DynamicTrackImpl<>("HDGM Track for Igtimi wind track for device "+deviceSerialNumbers);
}
/**
@@ -128,11 +138,12 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
}
});
}
logger.info("Received "+Util.size(awsFixes)+" wind fixes");
for (AWS aws : awsFixes) {
try {
final Wind wind = getWind(aws.getTimePoint());
if (wind != null) {
notifyListeners(wind);
notifyListeners(wind, aws.getSensor().getDeviceSerialNumber());
}
} catch (ClassNotFoundException | IOException | ParseException e) {
logger.log(Level.INFO, "Exception while trying to construct Wind fix from Igtimi fix " + aws, e);
@@ -140,13 +151,13 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
}
}
public void addListener(WindListener listener) {
public void addListener(IgtimiWindListener listener) {
listeners.put(listener, listener);
}
public void notifyListeners(Wind wind) {
for (WindListener listener : listeners.keySet()) {
listener.windDataReceived(wind);
public void notifyListeners(Wind wind, String deviceSerialNumber) {
for (IgtimiWindListener listener : listeners.keySet()) {
listener.windDataReceived(wind, deviceSerialNumber);
}
}
@@ -399,4 +410,12 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
private DynamicTrack<HDGM> getHdgmTrack() {
return hdgmTrack;
}
/**
* Tells the set of types that this wind receiver is interested in. Can be used to subscribe for fixes from
* resource data. See {@link IgtimiConnection#getResourceData(TimePoint, TimePoint, Iterable, Type...)}.
*/
public Type[] getFixTypes() {
return new Type[] { Type.AWA, Type.AWS, Type.HDG, Type.HDGM, Type.gps_latlong, Type.COG, Type.SOG };
}
}
@@ -0,0 +1,163 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.igtimiadapter.Account;
import com.sap.sailing.domain.igtimiadapter.DataAccessWindow;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
import com.sap.sailing.domain.igtimiadapter.LiveDataConnection;
import com.sap.sailing.domain.igtimiadapter.Permission;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
import com.sap.sailing.domain.tracking.AbstractWindTracker;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.WindTracker;
public class IgtimiWindTracker extends AbstractWindTracker implements WindTracker {
private static final Logger logger = Logger.getLogger(IgtimiWindTracker.class.getName());
private static final int TIME_INTERVAL_TO_TRACK_BEFORE_RACE_START_MILLIS = 10*60*1000; // 10 minutes
private static final long TIME_INTERVAL_TO_TRACK_AFTER_END_OF_RACE_MILLIS = 60*60*1000; // 60 minutes
private final Map<LiveDataConnection, Pair<Set<String>, Account>> liveConnectionsAndDeviceSerialNumber;
private final IgtimiWindTrackerFactory windTrackerFactory;
private boolean stopping;
protected IgtimiWindTracker(final DynamicTrackedRace trackedRace, final IgtimiConnectionFactory connectionFactory,
final IgtimiWindTrackerFactory windTrackerFactory) throws Exception {
super(trackedRace);
this.windTrackerFactory = windTrackerFactory;
liveConnectionsAndDeviceSerialNumber = new HashMap<>();
new Thread("IgtimiWindTracker start-up thread for race " + trackedRace.getRace().getName()) {
public void run() {
logger.info("Starting up Igtimi wind tracker for race "+trackedRace.getRace().getName());
// avoid a race condition with stop() being called while this start-up thread is still running
synchronized (IgtimiWindTracker.this) {
Iterable<Account> accounts = connectionFactory.getAllAccounts();
for (Account account : accounts) {
try {
if (!stopping) {
IgtimiConnection connection = connectionFactory.connect(account);
// find all the devices from which we may read
Iterable<DataAccessWindow> dataAccessWindows = connection.getDataAccessWindows(
Permission.read, /* start time */ null, /* end time */ null,
/* get data for all available deviceSerialNumbers */null);
Set<String> deviceSerialNumbersWeCanRead = new HashSet<>();
for (DataAccessWindow daw : dataAccessWindows) {
deviceSerialNumbersWeCanRead.add(daw.getDeviceSerialNumber());
}
// find all that haven't even sent GPS; those may never have sent ever, so we need to listen to them for new stuff; they could be wind sensors
Iterable<Fix> gpsFixes = connection.getLatestFixes(deviceSerialNumbersWeCanRead, Type.gps_latlong);
Set<String> devicesWithGps = getDeviceSerialNumbers(gpsFixes);
Iterable<Fix> awsFixes = connection.getLatestFixes(deviceSerialNumbersWeCanRead, Type.AWS); // look for latest fixes with apparent wind speed in the fix
Set<String> devicesWithWind = getDeviceSerialNumbers(awsFixes);
Set<String> devicesThatHaveNeverSentGpsNorWind = new HashSet<>(deviceSerialNumbersWeCanRead);
devicesThatHaveNeverSentGpsNorWind.removeAll(devicesWithGps);
devicesThatHaveNeverSentGpsNorWind.removeAll(devicesWithWind);
Set<String> devicesWeShouldListenTo = new HashSet<>();
devicesWeShouldListenTo.addAll(devicesWithWind);
devicesWeShouldListenTo.addAll(devicesThatHaveNeverSentGpsNorWind);
logger.info("Will listen to devices "
+ devicesWeShouldListenTo
+ " because from all devices "+deviceSerialNumbersWeCanRead+" for "
+ devicesThatHaveNeverSentGpsNorWind
+ " we don't know what they are as they never sent anything we can access, and for "
+ devicesWithWind + " we know they sent wind");
if (!stopping) {
LiveDataConnection liveConnection = connection.createLiveConnection(devicesWeShouldListenTo);
IgtimiWindReceiver windReceiver = new IgtimiWindReceiver(devicesWeShouldListenTo);
liveConnection.addListener(windReceiver);
windReceiver.addListener(new WindListenerSendingToTrackedRace(Collections.singleton(getTrackedRace()), windTrackerFactory));
liveConnectionsAndDeviceSerialNumber.put(liveConnection, new Pair<Set<String>, Account>(devicesWeShouldListenTo, account));
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception trying to start Igtimi wind tracker for race "
+ getTrackedRace().getRace().getName() + " for account " + account);
}
}
}
}
}.start();
}
private Set<String> getDeviceSerialNumbers(Iterable<Fix> fixes) {
Set<String> deviceSerialNumbers = new HashSet<>();
for (Fix fix : fixes) {
deviceSerialNumbers.add(fix.getSensor().getDeviceSerialNumber());
}
return deviceSerialNumbers;
}
public static TimePoint getReceivingEndTime(DynamicTrackedRace trackedRace) {
TimePoint endOfRace = trackedRace.getEndOfRace();
TimePoint endOfTracking = trackedRace.getEndOfTracking();
final TimePoint endTime;
if (endOfRace == null) {
if (endOfTracking == null) {
endTime = null;
} else {
endTime = endOfTracking.plus(TIME_INTERVAL_TO_TRACK_AFTER_END_OF_RACE_MILLIS);
}
} else {
if (endOfTracking == null) {
endTime = endOfRace;
} else {
endTime = Collections.max(Arrays.asList(endOfRace, endOfTracking));
}
}
return endTime;
}
/**
* Based on the tracked race's time parameters and the current time, decides for a good start time for receiving
* live wind data. Get all devices that have been sending during the last 10 minutes before start of race, start of
* tracking or now, whichever came first.
*/
public static TimePoint getReceivingStartTime(DynamicTrackedRace trackedRace) {
List<TimePoint> startCandidates = new ArrayList<>();
final TimePoint startOfRace = trackedRace.getStartOfRace();
if (startOfRace != null) {
startCandidates.add(startOfRace);
}
final TimePoint startOfTracking = trackedRace.getStartOfTracking();
if (startOfTracking != null) {
startCandidates.add(startOfTracking);
}
startCandidates.add(MillisecondsTimePoint.now());
return Collections.min(startCandidates).minus(TIME_INTERVAL_TO_TRACK_BEFORE_RACE_START_MILLIS);
}
@Override
public void stop() {
logger.info("Stopping Igtimi wind tracker for race "+getTrackedRace().getRace().getName());
stopping = true; // in case stop() is called while the start-up thread is still running, avoid more connections to be created
// this synchronizes with the start-up thread and waits until that has finished and no longer writes to liveConnectionsAndDeviceSerialNumber
synchronized (this) {
windTrackerFactory.windTrackerStopped(getTrackedRace().getRace(), this);
for (LiveDataConnection ldc : liveConnectionsAndDeviceSerialNumber.keySet()) {
try {
logger.info("Stopping Igtimi live connection "+ldc);
ldc.stop();
} catch (Exception e) {
final Pair<Set<String>, Account> deviceSerialNumberAndAccount = liveConnectionsAndDeviceSerialNumber.get(ldc);
logger.log(Level.INFO,
"Exception trying to stop Igtimi live connection for wind receiver for race "
+ getTrackedRace().getRace() + " and device " + deviceSerialNumberAndAccount.getA()
+ " in account " + deviceSerialNumberAndAccount.getB());
}
}
}
}
}
@@ -0,0 +1,50 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.WindTracker;
import com.sap.sailing.domain.tracking.WindTrackerFactory;
public class IgtimiWindTrackerFactory implements WindTrackerFactory {
private static final Logger logger = Logger.getLogger(IgtimiWindTrackerFactory.class.getName());
private final IgtimiConnectionFactoryImpl connectionFactory;
private final WeakHashMap<RaceDefinition, WindTracker> trackersForRace;
public IgtimiWindTrackerFactory(IgtimiConnectionFactoryImpl connectionFactory) {
trackersForRace = new WeakHashMap<>();
this.connectionFactory = connectionFactory;
}
@Override
public WindTracker createWindTracker(DynamicTrackedRegatta trackedRegatta, RaceDefinition race,
boolean correctByDeclination) throws Exception {
DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(race);
IgtimiWindTracker windTracker = new IgtimiWindTracker(trackedRace, connectionFactory, this);
trackersForRace.put(race, windTracker);
return windTracker;
}
@Override
public WindTracker getExistingWindTracker(RaceDefinition race) {
return trackersForRace.get(race);
}
void windTrackerStopped(RaceDefinition race, IgtimiWindTracker igtimiWindTracker) {
WindTracker removedTracker = trackersForRace.remove(race);
if (removedTracker != igtimiWindTracker) {
logger.warning("Expected to remove wind tracker "+igtimiWindTracker+" but did remove "+removedTracker);
}
}
WindSource getWindSource(String deviceSerialNumber) {
return new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, deviceSerialNumber);
}
}
@@ -1,9 +1,13 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.Permission;
import com.sap.sailing.domain.igtimiadapter.Resource;
import com.sap.sailing.domain.igtimiadapter.datatypes.Type;
public class ResourceImpl implements Resource {
private final long id;
@@ -47,8 +51,12 @@ public class ResourceImpl implements Resource {
}
@Override
public int[] getDataTypes() {
return dataTypes;
public Iterable<Type> getDataTypes() {
List<Type> result = new ArrayList<Type>();
for (int dataType : dataTypes) {
result.add(Type.valueOf(dataType));
}
return result;
}
@Override
@@ -0,0 +1,37 @@
package com.sap.sailing.domain.igtimiadapter.impl;
import java.util.HashMap;
import java.util.Map;
import com.sap.sailing.domain.igtimiadapter.IgtimiWindListener;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
class WindListenerSendingToTrackedRace implements IgtimiWindListener {
private final Iterable<DynamicTrackedRace> trackedRaces;
private final IgtimiWindTrackerFactory windTrackerFactory;
private final Map<TrackedRace, Integer> numberOfFixesAppliedPerTrackedRace;
WindListenerSendingToTrackedRace(Iterable<DynamicTrackedRace> trackedRaces, IgtimiWindTrackerFactory windTrackerFactory) {
this.trackedRaces = trackedRaces;
this.windTrackerFactory = windTrackerFactory;
numberOfFixesAppliedPerTrackedRace = new HashMap<>();
for (TrackedRace trackedRace : trackedRaces) {
numberOfFixesAppliedPerTrackedRace.put(trackedRace, 0);
}
}
@Override
public void windDataReceived(Wind wind, String deviceSerialNumber) {
for (DynamicTrackedRace trackedRace : trackedRaces) {
if (trackedRace.recordWind(wind, windTrackerFactory.getWindSource(deviceSerialNumber))) {
numberOfFixesAppliedPerTrackedRace.put(trackedRace, numberOfFixesAppliedPerTrackedRace.get(trackedRace)+1);
}
}
}
public Map<TrackedRace, Integer> getFixesAppliedPerTrackedRace() {
return numberOfFixesAppliedPerTrackedRace;
}
}
@@ -34,14 +34,14 @@ public class AuthorizationCallback {
}
@GET
@Produces("application/json;charset=UTF-8")
@Produces("text/plain;charset=UTF-8")
@Path(AUTHORIZATIONCALLBACK)
public Response obtainAccessToken(@QueryParam("code") String code) throws ClientProtocolException, IOException, IllegalStateException, ParseException, URISyntaxException {
Response result;
if (code != null) {
try {
connectionFactory.obtainAccessTokenFromAuthorizationCode(code);
result = Response.ok().build();
result = Response.ok().entity("SAP Sailing Analytics authorized successfully to access user's tracks").build();
} catch (Exception e) {
result = Response.status(Status.UNAUTHORIZED).entity(e.getMessage()).build();
logger.log(Level.SEVERE, "Error trying to obtain access token from authentication code " + code, e);
@@ -2,7 +2,6 @@ package com.sap.sailing.domain.igtimiadapter.websocket;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
@@ -58,8 +57,9 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
client = new WebSocketClient();
configurationMessage = connectionFactory.getWebSocketConfigurationMessage(account, deviceSerialNumbers);
request = new ClientUpgradeRequest();
client.start();
reconnect();
startClientHeartbeat();
startListeningForServerHeartbeat();
}
/**
@@ -82,8 +82,9 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
}
}
public void disconnect() throws Exception {
public void stop() throws Exception {
targetState = TargetState.CLOSED;
timer.cancel();
client.stop();
}
@@ -93,8 +94,6 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
logger.info("received connection "+session+" for "+this);
try {
getRemote().sendString(configurationMessage.toString());
startClientHeartbeat();
startListeningForServerHeartbeat();
} catch (IOException e) {
logger.log(Level.SEVERE, "Could not send configuration package to Igtimi web socket server in "+this, e);
throw new RuntimeException(e);
@@ -107,7 +106,7 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
logger.finest("Received "+message+" in "+this);
}
if (message.equals("1")) {
logger.fine("Received server heartbeat");
logger.fine("Received server heartbeat for "+this);
receivedServerHeartbeatInInterval = true;
} else if (message.startsWith("[")) {
List<Fix> fixes = new ArrayList<>();
@@ -117,7 +116,7 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
Util.addAll(fixFactory.createFixes((JSONObject) o), fixes);
}
if (logger.isLoggable(Level.FINEST)) {
logger.finest("Received fixes"+fixes);
logger.finest("Received fixes"+fixes+" for "+this);
}
notifyListeners(fixes);
} catch (ParseException e) {
@@ -176,8 +175,11 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
public void run() {
try {
if (!receivedServerHeartbeatInInterval) {
logger.info("Didn't receive server heartbeat in interval for "+this+". Reconnecting...");
logger.info("Didn't receive server heartbeat in interval for "+WebSocketConnectionManager.this+". Reconnecting...");
client.stop();
reconnect();
} else {
receivedServerHeartbeatInInterval = false;
}
} catch (Exception e) {
logger.log(Level.WARNING, "Error with server heartbeat in "+this, e);
@@ -191,22 +193,25 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
@Override
public void run() {
try {
logger.fine("Sending client heartbeat");
getRemote().sendString("1");
} catch (IOException e) {
logger.log(Level.WARNING, "Couldn't send heartbeat to Igtimi web socket session in "+this, e);
if (getRemote() != null) {
logger.fine("Sending client heartbeat for " + WebSocketConnectionManager.this);
getRemote().sendString("1");
}
} catch (Exception e) {
logger.log(Level.WARNING, "Couldn't send heartbeat to Igtimi web socket session in "+WebSocketConnectionManager.this+". Will continue to try...", e);
}
}
}, /* delay */ 0, /* send ping message every other minute; has to be at least every five minutes, so this should be safe */ 120000l);
}
private void reconnect() throws IOException, IllegalStateException, ParseException, URISyntaxException {
private void reconnect() throws Exception {
IOException lastException = null;
for (URI uri : connectionFactory.getWebsocketServers()) {
try {
if (uri.getScheme().equals("ws")) { // as Jetty 9.0.4 currently doesn't seem to support wss, explicitly
// look for ws connectivity
logger.log(Level.INFO, "Trying to connect to " + uri + " for " + this);
client.start();
client.connect(this, uri, request);
logger.log(Level.INFO, "Successfully connected to " + uri + " for " + this);
lastException = null;
@@ -25,7 +25,7 @@ public class EventImpl extends EventBaseImpl implements Event {
super(name, venue, publicationUrl, isPublic, id);
this.regattas = new HashSet<Regatta>();
}
@Override
public Iterable<Regatta> getRegattas() {
return Collections.unmodifiableSet(regattas);
@@ -0,0 +1,15 @@
package com.sap.sailing.domain.tracking;
public abstract class AbstractWindTracker implements WindTracker {
private final DynamicTrackedRace trackedRace;
protected AbstractWindTracker(DynamicTrackedRace trackedRace) {
super();
this.trackedRace = trackedRace;
}
protected DynamicTrackedRace getTrackedRace() {
return trackedRace;
}
}
@@ -1,7 +1,5 @@
package com.sap.sailing.domain.tracking;
import java.net.SocketException;
import com.sap.sailing.domain.base.RaceDefinition;
/**
@@ -12,7 +10,7 @@ import com.sap.sailing.domain.base.RaceDefinition;
*/
public interface WindTrackerFactory {
WindTracker createWindTracker(DynamicTrackedRegatta trackedRegatta, RaceDefinition race, boolean correctByDeclination)
throws SocketException;
throws Exception;
/**
* Returns a {@link WindTracker} is one has been previously created for <code>race</code> and hasn't been
@@ -98,6 +98,9 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
listeners = new HashSet<RaceChangeListener>();
logListener = new DynamicTrackedRaceLogListener(this);
courseDesignChangedListeners = new HashSet<CourseDesignChangedListener>();
startTimeChangedListeners = new HashSet<StartTimeChangedListener>();
}
/**
@@ -637,24 +640,25 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
@Override
public boolean recordWind(Wind wind, WindSource windSource) {
final boolean result;
// TODO check what a good filter is; remember that start/end of tracking may change over time; what if we have discarded valuable wind fixes?
TimePoint startOfRace = getStartOfRace();
TimePoint startOfTracking = getStartOfTracking();
TimePoint endOfRace = getEndOfRace();
TimePoint endOfTracking = getEndOfTracking();
if ((startOfTracking == null || !startOfTracking.after(wind.getTimePoint()) ||
(startOfRace != null && !startOfRace.after(wind.getTimePoint())))
if ((startOfTracking == null || !startOfTracking.minus(TrackedRaceImpl.TIME_BEFORE_START_TO_TRACK_WIND_MILLIS).after(wind.getTimePoint()) ||
(startOfRace != null && !startOfRace.minus(TrackedRaceImpl.TIME_BEFORE_START_TO_TRACK_WIND_MILLIS).after(wind.getTimePoint())))
&&
(endOfTracking == null || endOfTracking.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).after(wind.getTimePoint()) ||
(endOfRace != null && endOfRace.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS).after(wind.getTimePoint())))) {
getOrCreateWindTrack(windSource).add(wind);
result = getOrCreateWindTrack(windSource).add(wind);
updated(/* time point */null); // wind events shouldn't advance race time
triggerManeuverCacheRecalculationForAllCompetitors();
notifyListeners(wind, windSource);
return true;
} else {
return false;
result = false;
}
return result;
}
@Override
@@ -110,6 +110,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
// TODO make this variable
private static final long DELAY_FOR_CACHE_CLEARING_IN_MILLISECONDS = 7500;
public static final long TIME_BEFORE_START_TO_TRACK_WIND_MILLIS = 4 * 60 * 1000l; // let wind start four minutes before race
private TrackedRaceStatus status;
private final Object statusNotifier;
@@ -130,8 +130,9 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
* returned instead.
*/
protected TimePoint getFrom() {
return from == null ? getTrackedRace().getStartOfRace() == null ? new MillisecondsTimePoint(Long.MAX_VALUE)
: floorToResolution(getTrackedRace().getStartOfRace()) : from;
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
return from == null ? startOfRace == null ? new MillisecondsTimePoint(Long.MAX_VALUE)
: floorToResolution(startOfRace.minus(TrackedRaceImpl.TIME_BEFORE_START_TO_TRACK_WIND_MILLIS)) : from;
}
/**
@@ -57,19 +57,20 @@ public abstract class VirtualWindTrackImpl extends WindTrackImpl {
public WindWithConfidence<Pair<Position, TimePoint>> getAveragedWindWithConfidence(Position p, TimePoint at) {
WindWithConfidence<Pair<Position, TimePoint>> result = null;
TimePoint adjustedAt;
TimePoint raceStartTimePoint = getTrackedRace().getStartOfRace();
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
TimePoint timePointOfNewestEvent = getTrackedRace().getTimePointOfNewestEvent();
if (raceStartTimePoint != null) {
if (startOfRace != null) {
TimePoint fourMinutesBeforeRaceStartTimePoint = startOfRace.minus(TrackedRaceImpl.TIME_BEFORE_START_TO_TRACK_WIND_MILLIS); // let wind tracks start four minutes before start
if (timePointOfNewestEvent != null) {
if (at.compareTo(raceStartTimePoint) < 0) {
adjustedAt = raceStartTimePoint;
if (at.compareTo(fourMinutesBeforeRaceStartTimePoint) < 0) {
adjustedAt = fourMinutesBeforeRaceStartTimePoint;
} else if (at.compareTo(timePointOfNewestEvent) > 0) {
adjustedAt = timePointOfNewestEvent;
} else {
adjustedAt = at;
}
} else {
adjustedAt = raceStartTimePoint;
adjustedAt = fourMinutesBeforeRaceStartTimePoint;
}
} else {
if (timePointOfNewestEvent != null) {
@@ -13,6 +13,7 @@ import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
import com.sap.sailing.domain.tracking.AbstractWindTracker;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTracker;
@@ -25,11 +26,9 @@ import com.sap.sailing.domain.tracking.impl.WindImpl;
* @author Axel Uhl (d043530)
*
*/
public class ExpeditionWindTracker implements ExpeditionListener, WindTracker {
public class ExpeditionWindTracker extends AbstractWindTracker implements ExpeditionListener, WindTracker {
private static final Logger logger = Logger.getLogger(ExpeditionWindTracker.class.getName());
private final DynamicTrackedRace race;
private final DeclinationService declinationService;
private final Map<Integer, Position> lastKnownPositionPerBoatID;
@@ -49,9 +48,8 @@ public class ExpeditionWindTracker implements ExpeditionListener, WindTracker {
*/
public ExpeditionWindTracker(DynamicTrackedRace race, DeclinationService declinationService,
UDPExpeditionReceiver receiver, ExpeditionWindTrackerFactory factory) {
super();
super(race);
this.lastKnownPositionPerBoatID = new HashMap<Integer, Position>();
this.race = race;
this.declinationService = declinationService;
this.receiver = receiver;
this.factory = factory;
@@ -62,7 +60,7 @@ public class ExpeditionWindTracker implements ExpeditionListener, WindTracker {
public void stop() {
synchronized (factory) {
receiver.removeListener(this);
factory.windTrackerStopped(race.getRace(), this);
factory.windTrackerStopped(getTrackedRace().getRace(), this);
}
}
@@ -115,9 +113,13 @@ public class ExpeditionWindTracker implements ExpeditionListener, WindTracker {
if (windSpeed != null && lastKnownPositionPerBoatID.get(message.getBoatID()) != null) {
Wind wind = new WindImpl(lastKnownPositionPerBoatID.get(message.getBoatID()), message.getTimePoint(), windSpeed);
String windSourceID = Integer.valueOf(message.getBoatID()).toString();
race.recordWind(wind, new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, windSourceID));
sendWindToRace(wind, windSourceID);
}
}
}
protected void sendWindToRace(Wind wind, String windSourceID) {
getTrackedRace().recordWind(wind, new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, windSourceID));
}
}
@@ -45,7 +45,7 @@ public class ExpeditionWindTrackerFactory implements WindTrackerFactory {
@Override
public WindTracker createWindTracker(DynamicTrackedRegatta trackedRegatta, RaceDefinition race,
boolean correctByDeclination) throws SocketException {
WindTracker result = windTrackers.get(race);
WindTracker result = getExistingWindTracker(race);
if (result == null) {
DynamicTrackedRace trackedRace = trackedRegatta.getTrackedRace(race);
UDPExpeditionReceiver receiver = getOrCreateWindReceiverOnDefaultPort();
@@ -6,6 +6,9 @@ import java.util.logging.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.sap.sailing.domain.tracking.WindTrackerFactory;
import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory;
public class Activator implements BundleActivator {
private static Logger logger = Logger.getLogger(Activator.class.getName());
@@ -31,6 +34,9 @@ public class Activator implements BundleActivator {
port = Integer.valueOf(context.getProperty(EXPEDITION_UDP_PORT_PROPERTY_NAME));
logger.log(Level.INFO, "found "+EXPEDITION_UDP_PORT_PROPERTY_NAME+"="+port+" in OSGi context");
}
// register the Expedition wind tracker factory as an OSGi service
context.registerService(ExpeditionWindTrackerFactory.class, ExpeditionWindTrackerFactory.getInstance(), /* properties */null);
context.registerService(WindTrackerFactory.class, ExpeditionWindTrackerFactory.getInstance(), /* properties */null);
}
public static Activator getInstance() {
@@ -32,6 +32,7 @@
<configurations>
<plugin id="com.sap.sailing.barbados.resultimport" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.domain.igtimiadapter" autoStart="true" startLevel="3" />
<plugin id="com.sap.sailing.domain.swisstimingadapter" autoStart="false" startLevel="4" />
<plugin id="com.sap.sailing.domain.swisstimingreplayadapter" autoStart="false" startLevel="4" />
<plugin id="com.sap.sailing.domain.tractracadapter" autoStart="false" startLevel="4" />
@@ -27,7 +27,8 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.simulator;bundle-version="1.0.0",
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
de.csenk.gwt.ws,
com.google.gwt.osgi;bundle-version="2.5.1"
com.google.gwt.osgi;bundle-version="2.5.1",
com.sap.sailing.domain.igtimiadapter
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy
Import-Package: com.sap.sailing.polars,
@@ -28,7 +28,6 @@ public abstract class AbstractEventManagementPanel extends AbstractRegattaPanel
// TrackedEventsComposite should exist in every *ManagementPanel.
trackedRacesListComposite = new TrackedRacesListComposite(sailingService, errorReporter, regattaRefresher,
raceSelectionProvider, stringMessages, /* multiselection */ true);
trackedRacesListComposite.ensureDebugId("TrackedRacesListComposite");
}
@Override
@@ -62,7 +62,7 @@ public class AdminConsoleEntryPoint extends AbstractEntryPoint implements Regatt
RegattaStructureManagementPanel eventStructureManagementPanel = new RegattaStructureManagementPanel(
sailingService, this, stringMessages, this);
eventStructureManagementPanel.ensureDebugId("RegattaStructureManagement");
// eventStructureManagementPanel.ensureDebugId("RegattaStructureManagement");
eventStructureManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, eventStructureManagementPanel, stringMessages.regattas());
regattaDisplayers.add(eventStructureManagementPanel);
@@ -91,23 +91,28 @@ public class AdminConsoleEntryPoint extends AbstractEntryPoint implements Regatt
stringMessages);
// createSwissTimingRacePanel.ensureDebugId("CreateSwissTimingRace");
createSwissTimingRacePanel.setSize("90%", "90%");
addScrollableTab(tabPanel, createSwissTimingRacePanel, "Create SwissTiming race");
addScrollableTab(tabPanel, createSwissTimingRacePanel, stringMessages.createSwissTimingRace());
IgtimiAccountsPanel igtimiAccountsPanel = new IgtimiAccountsPanel(sailingService, this, stringMessages);
igtimiAccountsPanel.ensureDebugId("IgtimiAccounts");
igtimiAccountsPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, igtimiAccountsPanel, stringMessages.igtimiAccounts());
TrackedRacesManagementPanel trackedRacesManagementPanel = new TrackedRacesManagementPanel(sailingService, this,
this, stringMessages);
trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
// trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
trackedRacesManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, trackedRacesManagementPanel, stringMessages.trackedRaces());
regattaDisplayers.add(trackedRacesManagementPanel);
final CompetitorPanel competitorPanel = new CompetitorPanel(sailingService, stringMessages, this);
competitorPanel.ensureDebugId("CompetitorManagement");
// trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
trackedRacesManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, competitorPanel, stringMessages.competitors());
RaceCourseManagementPanel raceCourseManagementPanel = new RaceCourseManagementPanel(sailingService, this, this,
stringMessages);
raceCourseManagementPanel.ensureDebugId("RaceCourseManagement");
// raceCourseManagementPanel.ensureDebugId("RaceCourseManagement");
raceCourseManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, raceCourseManagementPanel, stringMessages.courseLayout());
regattaDisplayers.add(raceCourseManagementPanel);
@@ -115,14 +120,14 @@ public class AdminConsoleEntryPoint extends AbstractEntryPoint implements Regatt
final AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
WindPanel windPanel = new WindPanel(sailingService, asyncActionsExecutor, this, this, stringMessages);
windPanel.ensureDebugId("WindManagement");
// windPanel.ensureDebugId("WindManagement");
regattaDisplayers.add(windPanel);
windPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, windPanel, stringMessages.wind());
final LeaderboardGroupConfigPanel leaderboardGroupConfigPanel = new LeaderboardGroupConfigPanel(sailingService,
this, this, stringMessages);
leaderboardGroupConfigPanel.ensureDebugId("LeaderboardGroupConfiguration");
// leaderboardGroupConfigPanel.ensureDebugId("LeaderboardGroupConfiguration");
leaderboardGroupConfigPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, leaderboardGroupConfigPanel, stringMessages.leaderboardGroupConfiguration());
regattaDisplayers.add(leaderboardGroupConfigPanel);
@@ -0,0 +1,287 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.google.gwt.view.client.SingleSelectionModel;
import com.sap.sailing.domain.common.impl.NaturalComparator;
import com.sap.sailing.gwt.ui.client.DataEntryDialog;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.components.ImagesBarCell;
import com.sap.sailing.gwt.ui.client.shared.panels.AbstractFilterablePanel;
public class IgtimiAccountsPanel extends FlowPanel {
private final StringMessages stringMessages;
private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter;
private final CellTable<String> allAccounts;
private final AbstractFilterablePanel<String> filterAccountsPanel;
public static class AccountImagesBarCell extends ImagesBarCell {
public static final String ACTION_REMOVE = "ACTION_REMOVE";
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private final StringMessages stringMessages;
public AccountImagesBarCell(StringMessages stringMessages) {
super();
this.stringMessages = stringMessages;
}
public AccountImagesBarCell(SafeHtmlRenderer<String> renderer, StringMessages stringConstants) {
super();
this.stringMessages = stringConstants;
}
@Override
protected Iterable<ImageSpec> getImageSpecs() {
return Arrays.asList(
new ImageSpec(ACTION_REMOVE, stringMessages.actionRemove(), makeImagePrototype(resources.removeIcon())));
}
}
public IgtimiAccountsPanel(final SailingServiceAsync sailingService, final ErrorReporter errorReporter, final StringMessages stringMessages) {
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
allAccounts = new CellTable<String>(/* pageSize */10000, tableRes);
final SingleSelectionModel<String> accountsSelectionModel = new SingleSelectionModel<String>();
allAccounts.setSelectionModel(accountsSelectionModel);
final ListDataProvider<String> filteredAccounts = new ListDataProvider<String>();
ListHandler<String> accountColumnListHandler = new ListHandler<String>(filteredAccounts.getList());
filteredAccounts.addDataDisplay(allAccounts);
final List<String> emptyList = Collections.emptyList();
filterAccountsPanel = new AbstractFilterablePanel<String>(new Label(stringMessages.igtimiAccounts()), emptyList, allAccounts, filteredAccounts) {
@Override
public Iterable<String> getSearchableStrings(String t) {
Set<String> strings = Collections.singleton(t);
return strings;
}
};
final Panel controlsPanel = new HorizontalPanel();
final Button removeAccountButton = new Button(stringMessages.remove());
removeAccountButton.setEnabled(false);
removeAccountButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (accountsSelectionModel.getSelectedObject() != null) {
if (Window.confirm("Do you really want to remove the leaderboards?")) {
removeAccount(accountsSelectionModel.getSelectedObject(), filteredAccounts);
}
}
}
});
accountsSelectionModel.addSelectionChangeHandler(new Handler() {
@Override
public void onSelectionChange(SelectionChangeEvent event) {
removeAccountButton.setEnabled(accountsSelectionModel.getSelectedObject() != null);
}
});
controlsPanel.add(filterAccountsPanel);
controlsPanel.add(removeAccountButton);
add(controlsPanel);
add(allAccounts);
Column<String, String> accountEmailColumn = new TextColumn<String>() {
@Override
public String getValue(String object) {
return object;
}
};
accountEmailColumn.setSortable(true);
accountColumnListHandler.setComparator(accountEmailColumn, new NaturalComparator(/* caseSensitive */ false));
allAccounts.addColumn(accountEmailColumn, stringMessages.emailAddress());
ImagesBarColumn<String, AccountImagesBarCell> accountActionColumn = new ImagesBarColumn<String, AccountImagesBarCell>(
new AccountImagesBarCell(stringMessages));
accountActionColumn.setFieldUpdater(new FieldUpdater<String, String>() {
@Override
public void update(int index, String accountEmail, String value) {
if (AccountImagesBarCell.ACTION_REMOVE.equals(value)) {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveIgtimiAccount(accountEmail))) {
removeAccount(accountEmail, filteredAccounts);
}
}
}
});
allAccounts.addColumn(accountActionColumn, stringMessages.actions());
updateAllAccounts(sailingService, filterAccountsPanel, stringMessages, errorReporter);
Button addAccountButton = new Button(stringMessages.addIgtimiAccount());
addAccountButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
addAccount();
}
});
add(addAccountButton);
Button refreshButton = new Button(stringMessages.refresh());
refreshButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
updateAllAccounts(sailingService, filterAccountsPanel, stringMessages, errorReporter);
}
});
add(refreshButton);
this.sailingService.getIgtimiAuthorizationUrl(new AsyncCallback<String>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(IgtimiAccountsPanel.this.stringMessages.errorGettingIgtimiAuthorizationUrl(caught.getMessage()),
/* silentMode */ true);
}
@Override
public void onSuccess(String result) {
Anchor addIgtimiUserLink = new Anchor(stringMessages.addIgtimiUser(), result);
controlsPanel.add(addIgtimiUserLink);
}
});
}
private static class UserData {
private final String eMail;
private final String password;
protected UserData(String eMail, String password) {
super();
this.eMail = eMail;
this.password = password;
}
protected String geteMail() {
return eMail;
}
protected String getPassword() {
return password;
}
}
private class AddAccountDialog extends DataEntryDialog<UserData> {
private TextBox eMail;
private PasswordTextBox password;
public AddAccountDialog(final AbstractFilterablePanel<String> filterAccountsPanel, final SailingServiceAsync sailingService, final StringMessages stringMessages, final ErrorReporter errorReporter) {
super(stringMessages.addIgtimiUser(), stringMessages.addIgtimiUser(), stringMessages.ok(), stringMessages.cancel(),
new Validator<UserData>() {
@Override
public String getErrorMessage(UserData valueToValidate) {
final String errorMessage;
if (valueToValidate.geteMail() == null || valueToValidate.geteMail().isEmpty()) {
errorMessage = stringMessages.eMailMustNotBeEmpty();
} else {
errorMessage = null;
}
return errorMessage;
}
}, /* animationEnabled */ true, new DialogCallback<UserData>() {
@Override
public void ok(final UserData editedObject) {
sailingService.authorizeAccessToIgtimiUser(editedObject.geteMail(), editedObject.getPassword(), new AsyncCallback<Boolean>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorAuthorizingAccessToIgtimiUser(editedObject.geteMail(),
caught.getMessage()));
}
@Override
public void onSuccess(Boolean result) {
if (result) {
Window.alert(stringMessages.successfullyAuthorizedAccessToIgtimiUser(editedObject.geteMail()));
updateAllAccounts(sailingService, filterAccountsPanel, stringMessages, errorReporter);
} else {
Window.alert(stringMessages.couldNotAuthorizedAccessToIgtimiUser(editedObject.geteMail()));
}
}
});
}
@Override
public void cancel() {
}
});
}
@Override
protected Widget getAdditionalWidget() {
Grid grid = new Grid(2, 2);
grid.setWidget(0, 0, new Label(stringMessages.emailAddress()));
eMail = createTextBox("");
grid.setWidget(0, 1, eMail);
grid.setWidget(1, 0, new Label(stringMessages.password()));
password = createPasswordTextBox("");
grid.setWidget(1, 1, password);
return grid;
}
@Override
public void show() {
super.show();
eMail.setFocus(true);
}
@Override
protected UserData getResult() {
return new UserData(eMail.getText(), password.getText());
}
}
private static void updateAllAccounts(SailingServiceAsync sailingService, final AbstractFilterablePanel<String> filterAccountsPanel,
final StringMessages stringMessages, final ErrorReporter errorReporter) {
sailingService.getAllIgtimiAccountEmailAddresses(new AsyncCallback<Iterable<String>>() {
@Override
public void onSuccess(Iterable<String> result) {
filterAccountsPanel.updateAll(result);
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorFetchingIgtimiAccounts(caught.getMessage()));
}
});
}
private void addAccount() {
new AddAccountDialog(filterAccountsPanel, sailingService, stringMessages, errorReporter).show();
}
private void removeAccount(final String eMailOfAccountToRemove, final ListDataProvider<String> removeFrom) {
sailingService.removeIgtimiAccount(eMailOfAccountToRemove, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorTryingToRemoveIgtimiAccount(eMailOfAccountToRemove));
}
@Override
public void onSuccess(Void result) {
Window.alert(stringMessages.successfullyRemoveIgtimiAccount(eMailOfAccountToRemove));
removeFrom.getList().remove(eMailOfAccountToRemove);
}
});
}
}
@@ -211,7 +211,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
leaderboardRemoveButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (Window.confirm("Do you really want to remove the leaderboards?")) {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveLeaderboards())) {
removeLeaderboards(leaderboardSelectionModel.getSelectedSet());
}
}
@@ -309,7 +309,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
@Override
public void update(int index, StrippedLeaderboardDTO leaderboardDTO, String value) {
if (LeaderboardConfigImagesBarCell.ACTION_REMOVE.equals(value)) {
if (Window.confirm("Do you really want to remove the leaderboard: '" + leaderboardDTO.name + "' ?")) {
if (Window.confirm(stringMessages.doYouReallyWantToRemoveLeaderboard(leaderboardDTO.name))) {
removeLeaderboard(leaderboardDTO);
}
} else if (LeaderboardConfigImagesBarCell.ACTION_EDIT.equals(value)) {
@@ -318,7 +318,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
otherExistingLeaderboard.addAll(availableLeaderboardList);
otherExistingLeaderboard.remove(leaderboardDTO);
if (leaderboardDTO.type.isMetaLeaderboard()) {
Window.alert("This is a meta leaderboard. It can't be changed here.");
Window.alert(stringMessages.metaLeaderboardCannotBeChanged());
} else {
if (leaderboardDTO.type.isRegattaLeaderboard()) {
LeaderboardDescriptor descriptor = new LeaderboardDescriptor(leaderboardDTO.name,
@@ -736,7 +736,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
@Override
public void onSuccess(Boolean result) {
if (!result) {
Window.alert("Failed to set new start time.");
Window.alert(stringMessages.failedToSetNewStartTime());
}
}
});
@@ -1120,7 +1120,7 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
});
}
});
dialog.ensureDebugId("FlexibleLeaderboardCreateDialog");
dialog.ensureDebugId("CreateFlexibleLeaderboardDialog");
dialog.show();
}
@@ -1273,5 +1273,5 @@ public class LeaderboardConfigPanel extends FormPanel implements SelectedLeaderb
@Override
public StrippedLeaderboardDTO getSelectedLeaderboard() {
return leaderboardSelectionModel.getSelectedSet().isEmpty() ? null : leaderboardSelectionModel.getSelectedSet().iterator().next();
}
}
}
@@ -40,7 +40,6 @@ import com.sap.sailing.gwt.ui.client.AbstractRegattaPanel;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.LeaderboardGroupRefresher;
import com.sap.sailing.gwt.ui.client.MarkedAsyncCallback;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -157,7 +156,6 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
private Widget createLeaderboardsGUI(Resources tableRes) {
CaptionPanel leaderboardsCaptionPanel = new CaptionPanel(stringMessages.leaderboards());
leaderboardsCaptionPanel.ensureDebugId("LeaderboardsCaptionPanel");
leaderboardsCaptionPanel.setWidth("95%");
VerticalPanel leaderboardsPanel = new VerticalPanel();
@@ -173,7 +171,6 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
leaderboardsProvider = new ListDataProvider<StrippedLeaderboardDTO>();
ListHandler<StrippedLeaderboardDTO> leaderboardsListHandler = new ListHandler<StrippedLeaderboardDTO>(leaderboardsProvider.getList());
leaderboardsTable = new CellTable<StrippedLeaderboardDTO>(10000, tableRes);
leaderboardsTable.ensureDebugId("LeaderboardsTable");
leaderboardsFilterablePanel = new AbstractFilterablePanel<StrippedLeaderboardDTO>(filterLeaderboardsLabel, availableLeaderboards, leaderboardsTable, leaderboardsProvider) {
@Override
public Iterable<String> getSearchableStrings(StrippedLeaderboardDTO t) {
@@ -191,7 +188,6 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
refreshLeaderboardsList();
}
});
refreshLeaderboardsButton.ensureDebugId("RefreshLeaderboardsButton");
leaderboardsFunctionPanel.add(refreshLeaderboardsButton);
TextColumn<StrippedLeaderboardDTO> leaderboardsNameColumn = new TextColumn<StrippedLeaderboardDTO>() {
@Override
@@ -396,7 +392,6 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
groupsProvider = new ListDataProvider<LeaderboardGroupDTO>();
ListHandler<LeaderboardGroupDTO> leaderboardGroupsListHandler = new ListHandler<LeaderboardGroupDTO>(groupsProvider.getList());
groupsTable = new CellTable<LeaderboardGroupDTO>(10000, tableRes);
groupsTable.ensureDebugId("LeaderboardGroupsTable");
groupsFilterablePanel = new AbstractFilterablePanel<LeaderboardGroupDTO>(filterLeaderboardGroupsLbl, availableLeaderboardGroups, groupsTable, groupsProvider) {
@Override
public Iterable<String> getSearchableStrings(LeaderboardGroupDTO t) {
@@ -413,7 +408,6 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
addNewGroup();
}
});
createGroupButton.ensureDebugId("CreateLeaderboardGroupButton");
leaderboardGroupsFunctionPanel.add(createGroupButton);
removeButton = new Button(stringMessages.remove());
@@ -606,21 +600,20 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
createNewGroup(newGroup);
}
});
dialog.ensureDebugId("LeaderboardGroupCreateDialog");
dialog.show();
}
private void createNewGroup(final LeaderboardGroupDescriptor newGroup) {
sailingService.createLeaderboardGroup(newGroup.getName(), newGroup.getDescription(), newGroup.isDisplayLeaderboardsInReverseOrder(),
newGroup.getOverallLeaderboardDiscardThresholds(),
newGroup.getOverallLeaderboardScoringSchemeType(), new MarkedAsyncCallback<LeaderboardGroupDTO>() {
newGroup.getOverallLeaderboardScoringSchemeType(), new AsyncCallback<LeaderboardGroupDTO>() {
@Override
public void handleFailure(Throwable t) {
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to create new leaderboard group" + newGroup.getName()
+ ": " + t.getMessage());
}
@Override
public void handleSuccess(LeaderboardGroupDTO newGroup) {
public void onSuccess(LeaderboardGroupDTO newGroup) {
availableLeaderboardGroups.add(newGroup);
groupsProvider.getList().add(newGroup);
groupsSelectionModel.clear();
@@ -12,9 +12,7 @@ public class LeaderboardGroupCreateDialog extends LeaderboardGroupDialog {
super(new LeaderboardGroupDTO(), stringMessages,
callback, existingGroups);
nameEntryField = createTextBox(null, 50);
nameEntryField.ensureDebugId("NameTextBox");
descriptionEntryField = createTextArea(null);
descriptionEntryField.ensureDebugId("DescriptionTextBox");
descriptionEntryField = createTextArea(null);
displayLeaderboardsInReverseOrderCheckBox.setValue(false);
useOverallLeaderboardCheckBox.setValue(false);
}
@@ -69,34 +69,22 @@ public class RegattaDetailsComposite extends Composite {
mainPanel.add(vPanel);
Grid grid = new Grid(5, 2);
vPanel.add(grid);
regattaName = new Label();
regattaName.ensureDebugId("RegattaNameLabel");
grid.setWidget(0 , 0, new Label(stringMessages.regattaName() + ":"));
grid.setWidget(0 , 1, regattaName);
boatClassName = new Label();
boatClassName.ensureDebugId("BoatClassNameLabel");
grid.setWidget(1 , 0, new Label(stringMessages.boatClass() + ":"));
grid.setWidget(1 , 1, boatClassName);
defaultCourseArea = new Label();
defaultCourseArea.ensureDebugId("CourseAreaLabel");
grid.setWidget(2 , 0, new Label(stringMessages.courseArea() + ":"));
grid.setWidget(2 , 1, defaultCourseArea);
configuration = new Label();
configuration.ensureDebugId("RacingProcedureConfigurationLabel");
grid.setWidget(3, 0, new Label(stringMessages.racingProcedureConfiguration() + ":"));
grid.setWidget(3, 1, configuration);
grid.setWidget(3, 1, configuration);;
scoringSystem = new Label();
scoringSystem.ensureDebugId("ScoringSystemLabel");
grid.setWidget(4 , 0, new Label(stringMessages.scoringSystem() + ":"));
grid.setWidget(4 , 1, scoringSystem);
seriesTable = createRegattaSeriesTable();
seriesTable.ensureDebugId("SeriesTable");
seriesSelectionModel = new SingleSelectionModel<SeriesDTO>();
seriesSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
@@ -251,7 +239,6 @@ public class RegattaDetailsComposite extends Composite {
updateRacesOfRegattaSeries(regatta, result);
}
});
raceDialog.ensureDebugId("SeriesEditDialog");
raceDialog.show();
}
@@ -16,17 +16,14 @@ public class RegattaLeaderboardCreateDialog extends RegattaLeaderboardDialog {
super(stringMessages.createRegattaLeaderboard(), new LeaderboardDescriptor(), existingRegattas, stringMessages, errorReporter, new RegattaLeaderboardDialog.LeaderboardParameterValidator(stringMessages, existingLeaderboards), callback);
nameTextBox = createTextBox(null);
nameTextBox.ensureDebugId("LeaderboardNameField");
displayNameTextBox = createTextBox(null);
displayNameTextBox.ensureDebugId("DisplayNameField");
// the name of the regatta leaderboard will be derived from the selected regatta
nameTextBox.setEnabled(false);
nameTextBox.setVisibleLength(50);
displayNameTextBox.setVisibleLength(50);
regattaListBox = createSortedRegattaListBox(existingRegattas, null);
regattaListBox.ensureDebugId("RegattaDropDown");
regattaListBox.addChangeHandler(new ChangeHandler() {
public void onChange(ChangeEvent event) {
int selectedIndex = regattaListBox.getSelectedIndex();
@@ -79,17 +79,12 @@ public class RegattaListComposite extends Composite implements RegattaDisplayer
mainPanel.setWidget(panel);
Label filterRegattasLabel = new Label(stringMessages.filterRegattasByName() + ":");
filterRegattasLabel.setWordWrap(false);
noRegattasLabel = new Label(stringMessages.noRegattasYet());
noRegattasLabel.ensureDebugId("NoRegattasLabel");
noRegattasLabel.setWordWrap(false);
panel.add(noRegattasLabel);
regattaListDataProvider = new ListDataProvider<RegattaDTO>();
regattaTable = createRegattaTable();
regattaTable.ensureDebugId("RegattasTable");
regattaTable.setVisible(false);
filterablePanelRegattas = new AbstractFilterablePanel<RegattaDTO>(filterRegattasLabel, allRegattas,
regattaTable, regattaListDataProvider) {
@Override
@@ -101,7 +96,6 @@ public class RegattaListComposite extends Composite implements RegattaDisplayer
}
};
panel.add(filterablePanelRegattas);
regattaSelectionModel = new MultiSelectionModel<RegattaDTO>();
regattaSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() {
@Override
@@ -119,7 +113,6 @@ public class RegattaListComposite extends Composite implements RegattaDisplayer
panel.add(regattaTable);
initWidget(mainPanel);
}
private CellTable<RegattaDTO> createRegattaTable() {
@@ -179,7 +172,7 @@ public class RegattaListComposite extends Composite implements RegattaDisplayer
return table;
}
private void removeRegatta(final RegattaDTO regatta) {
final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.getName());
sailingService.removeRegatta(regattaIdentifier, new AsyncCallback<Void>() {
@@ -195,7 +188,6 @@ public class RegattaListComposite extends Composite implements RegattaDisplayer
});
}
// TODO [D049941]: Use MarkedAsyncCallback
private void editRegatta(final RegattaDTO toBeEdited) {
final Collection<RegattaDTO> existingRegattas = getAllRegattas();
sailingService.getEvents(new AsyncCallback<List<EventDTO>>() {
@@ -223,11 +215,9 @@ public class RegattaListComposite extends Composite implements RegattaDisplayer
commitEditedRegatta(editedRegatta);
}
});
dialog.ensureDebugId("RegattaEditDialog");
dialog.show();
}
// TODO [D049941]: Use MarkedAsyncCallback
private void commitEditedRegatta(final RegattaDTO editedRegatta) {
final RegattaIdentifier regattaName = new RegattaName(editedRegatta.getName());
@@ -66,7 +66,6 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
HorizontalPanel regattaManagementControlsPanel = new HorizontalPanel();
regattaManagementControlsPanel.setSpacing(5);
Button addRegattaBtn = new Button(stringMessages.addRegatta());
addRegattaBtn.ensureDebugId("AddRegattaButton");
addRegattaBtn.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
@@ -76,7 +75,6 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
regattaManagementControlsPanel.add(addRegattaBtn);
removeRegattaButton = new Button(stringMessages.remove());
removeRegattaButton.ensureDebugId("RemoveRegattaButton");
removeRegattaButton.setEnabled(false);
removeRegattaButton.addClickHandler(new ClickHandler() {
@Override
@@ -102,12 +100,10 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
regattaSelectionProvider.addRegattaSelectionChangeListener(this);
regattaListComposite = new RegattaListComposite(sailingService, regattaSelectionProvider, regattaRefresher, errorReporter, stringMessages);
regattaListComposite.ensureDebugId("RegattaListSection");
grid.setWidget(0, 0, regattaListComposite);
grid.getRowFormatter().setVerticalAlign(0, HasVerticalAlignment.ALIGN_TOP);
grid.getColumnFormatter().getElement(1).getStyle().setPaddingTop(2.0, Unit.EM);
regattaDetailsComposite = new RegattaDetailsComposite(sailingService, regattaRefresher, errorReporter, stringMessages);
regattaDetailsComposite.ensureDebugId("RegattaDetailsSection");
regattaDetailsComposite.setVisible(false);
grid.setWidget(0, 1, regattaDetailsComposite);
}
@@ -127,8 +123,7 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
});
}
}
// TODO [D049941]: Use MarkedAsyncCallback
private void openCreateRegattaDialog() {
final Collection<RegattaDTO> existingRegattas = Collections.unmodifiableCollection(regattaListComposite.getAllRegattas());
@@ -157,7 +152,6 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
createNewRegatta(newRegatta);
}
});
dialog.ensureDebugId("RegattaCreateDialog");
dialog.show();
}
@@ -45,19 +45,16 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
this.existingEvents = existingEvents;
nameEntryField = createTextBox(null);
nameEntryField.ensureDebugId("RegattaNameTextField");
nameEntryField.setVisibleLength(40);
nameEntryField.setText(regatta.getName());
boatClassEntryField = createTextBox(null);
boatClassEntryField.ensureDebugId("BoatClassTextField");
boatClassEntryField.setVisibleLength(20);
if (regatta.boatClass != null) {
boatClassEntryField.setText(regatta.boatClass.getName());
}
scoringSchemeListBox = createListBox(false);
scoringSchemeListBox.ensureDebugId("ScoringSystemDropDown");
for (ScoringSchemeType scoringSchemeType : ScoringSchemeType.values()) {
scoringSchemeListBox.addItem(ScoringSchemeTypeFormatter.format(scoringSchemeType, stringMessages),
String.valueOf(scoringSchemeType.ordinal()));
@@ -67,11 +64,7 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
}
sailingEventsListBox = createListBox(false);
sailingEventsListBox.ensureDebugId("EventDropDown");
courseAreaListBox = createListBox(false);
courseAreaListBox.ensureDebugId("CourseAreaDropDown");
setupEventAndCourseAreaListBoxes(stringMessages);
courseAreaListBox.setEnabled(false);
@@ -1,8 +1,10 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
@@ -17,22 +19,26 @@ import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.sap.sailing.domain.common.impl.NaturalComparator;
import com.sap.sailing.gwt.ui.client.DataEntryDialog.DialogCallback;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.EventRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.URLEncoder;
import com.sap.sailing.gwt.ui.client.shared.panels.AbstractFilterablePanel;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
@@ -50,6 +56,8 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
private CellTable<EventDTO> eventTable;
private MultiSelectionModel<EventDTO> eventSelectionModel;
private ListDataProvider<EventDTO> eventProvider;
private List<EventDTO> allEvents;
private AbstractFilterablePanel<EventDTO> filterTextbox;
private Button removeEventsButton;
public static class AnchorCell extends AbstractCell<SafeHtml> {
@@ -65,7 +73,7 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
}
private static AnchorTemplates ANCHORTEMPLATE = GWT.create(AnchorTemplates.class);
private final AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
public SailingEventManagementPanel(final SailingServiceAsync sailingService, final ErrorReporter errorReporter,
@@ -90,7 +98,7 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
}
});
eventsPanel.add(createEventBtn);
removeEventsButton = new Button(stringMessages.remove());
removeEventsButton.setEnabled(false);
removeEventsButton.addClickHandler(new ClickHandler() {
@@ -169,6 +177,12 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
}
});
eventNameColumn.setSortable(true);
venueNameColumn.setSortable(true);
publicationUrlColumn.setSortable(true);
isPublicColumn.setSortable(true);
courseAreasColumn.setSortable(true);
eventTable = new CellTable<EventDTO>(10000, tableRes);
eventTable.addColumn(eventNameColumn, stringMessages.event());
eventTable.addColumn(venueNameColumn, stringMessages.venue());
@@ -189,10 +203,68 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
eventProvider = new ListDataProvider<EventDTO>();
eventProvider.addDataDisplay(eventTable);
mainPanel.add(eventTable);
allEvents = new ArrayList<EventDTO>();
eventTable.addColumnSortHandler(getEventTableColumnSortHandler(eventProvider.getList(), eventNameColumn,
venueNameColumn, publicationUrlColumn, isPublicColumn, courseAreasColumn));
filterTextbox = new AbstractFilterablePanel<EventDTO>(new Label("Filter events by name: "), allEvents,
eventTable, eventProvider) {
@Override
public Iterable<String> getSearchableStrings(EventDTO t) {
List<String> result = new ArrayList<String>();
result.add(t.getName());
result.add(t.venue.getName());
for (CourseAreaDTO c : t.venue.getCourseAreas()) {
result.add(c.getName());
}
return result;
}
};
eventsPanel.add(filterTextbox);
fillEvents();
}
private ListHandler<EventDTO> getEventTableColumnSortHandler(List<EventDTO> eventRecords,
Column<EventDTO, SafeHtml> eventNameColumn, TextColumn<EventDTO> venueNameColumn,
TextColumn<EventDTO> publicationUrlColumn, TextColumn<EventDTO> isPublicColumn,
Column<EventDTO, SafeHtml> courseAreasColumn) {
ListHandler<EventDTO> result = new ListHandler<EventDTO>(eventRecords);
result.setComparator(eventNameColumn, new Comparator<EventDTO>() {
@Override
public int compare(EventDTO e1, EventDTO e2) {
return new NaturalComparator().compare(e1.getName(), e2.getName());
}
});
result.setComparator(venueNameColumn, new Comparator<EventDTO>() {
@Override
public int compare(EventDTO e1, EventDTO e2) {
return new NaturalComparator().compare(e1.venue.getName(), e2.venue.getName());
}
});
result.setComparator(publicationUrlColumn, new Comparator<EventDTO>() {
@Override
public int compare(EventDTO e1, EventDTO e2) {
return new NaturalComparator().compare(e1.publicationUrl, e2.publicationUrl);
}
});
result.setComparator(isPublicColumn, new Comparator<EventDTO>() {
@Override
public int compare(EventDTO e1, EventDTO e2) {
return e1.isPublic == e2.isPublic ? 0 : e1.isPublic ? 1 : -1;
}
});
result.setComparator(courseAreasColumn, new Comparator<EventDTO>() {
@Override
public int compare(EventDTO e1, EventDTO e2) {
return new NaturalComparator().compare(Arrays.toString(e1.venue.getCourseAreas().toArray()),
Arrays.toString(e2.venue.getCourseAreas().toArray()));
}
});
return result;
}
private void removeEvents(Collection<EventDTO> events) {
if (!events.isEmpty()) {
Collection<String> eventIds = new HashSet<String>();
@@ -261,12 +333,12 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
private void updateEvent(final EventDTO oldEvent, final EventDTO updatedEvent) {
List<String> regattaNames = new ArrayList<String>();
// for(RegattaDTO regatta: updatedEvent.regattas) {
// regattaNames.add(regatta.name);
// }
// for(RegattaDTO regatta: updatedEvent.regattas) {
// regattaNames.add(regatta.name);
// }
List<CourseAreaDTO> courseAreasToAdd = getCourseAreasToAdd(oldEvent, updatedEvent);
sailingService.updateEvent(oldEvent.getName(), oldEvent.id, updatedEvent.venue, updatedEvent.publicationUrl,
sailingService.updateEvent(oldEvent.getName(), oldEvent.id, updatedEvent.venue, updatedEvent.publicationUrl,
updatedEvent.isPublic, regattaNames, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable t) {
@@ -276,15 +348,17 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
@Override
public void onSuccess(Void result) {
fillEvents();
if(!oldEvent.getName().equals(updatedEvent.getName())) {
if (!oldEvent.getName().equals(updatedEvent.getName())) {
sailingService.renameEvent(oldEvent.id, updatedEvent.getName(), new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
fillEvents();
}
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to rename sailing event " + oldEvent.getName() + ": " + t.getMessage());
}
});
}
@@ -315,12 +389,12 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
List<String> courseAreaNamesToAdd = new ArrayList<String>();
List<String> newCourseAreaNames = new ArrayList<String>();
for(CourseAreaDTO courseArea : updatedEvent.venue.getCourseAreas()) {
for (CourseAreaDTO courseArea : updatedEvent.venue.getCourseAreas()) {
newCourseAreaNames.add(courseArea.getName());
}
List<String> oldCourseAreaNames = new ArrayList<String>();
for(CourseAreaDTO courseArea : oldEvent.venue.getCourseAreas()) {
for (CourseAreaDTO courseArea : oldEvent.venue.getCourseAreas()) {
oldCourseAreaNames.add(courseArea.getName());
}
@@ -329,7 +403,7 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
courseAreaNamesToAdd.add(newCourseAreaName);
}
for(CourseAreaDTO courseArea : updatedEvent.venue.getCourseAreas()) {
for (CourseAreaDTO courseArea : updatedEvent.venue.getCourseAreas()) {
if (courseAreaNamesToAdd.contains(courseArea.getName())) {
courseAreasToAdd.add(courseArea);
}
@@ -347,6 +421,7 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error trying to create new event" + newEvent.getName() + ": " + t.getMessage());
}
@Override
@@ -365,8 +440,9 @@ public class SailingEventManagementPanel extends SimplePanel implements EventRef
@Override
public void onSuccess(List<EventDTO> result) {
eventProvider.getList().clear();
eventProvider.getList().addAll(result);
allEvents.clear();
allEvents.addAll(result);
filterTextbox.updateAll(allEvents);
}
});
}
@@ -153,24 +153,16 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
String seriesName = getSelectedSeries().getName();
seriesPanel.add(new Label(stringMessages.series() + ": " + seriesName));
additionalWidgetPanel.add(seriesPanel);
isMedalCheckbox = createCheckbox(stringMessages.medalSeries());
isMedalCheckbox.ensureDebugId("MedalSeriesCheckbox");
isMedalCheckbox.setValue(selectedSeries.isMedal());
additionalWidgetPanel.add(isMedalCheckbox);
startWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
startWithZeroScoreCheckbox.ensureDebugId("StartsWithZeroScoreCheckbox");
startWithZeroScoreCheckbox.setValue(selectedSeries.isStartsWithZeroScore());
additionalWidgetPanel.add(startWithZeroScoreCheckbox);
firstColumnIsNonDiscardableCarryForwardCheckbox = createCheckbox(stringMessages.firstRaceIsNonDiscardableCarryForward());
firstColumnIsNonDiscardableCarryForwardCheckbox.ensureDebugId("StartsWithNonDiscardableCarryForwardCheckbox");
firstColumnIsNonDiscardableCarryForwardCheckbox.setValue(selectedSeries.isFirstColumnIsNonDiscardableCarryForward());
additionalWidgetPanel.add(firstColumnIsNonDiscardableCarryForwardCheckbox);
useSeriesResultDiscardingThresholdsCheckbox = createCheckbox(stringMessages.seriesDefinesResultDiscardingRule());
useSeriesResultDiscardingThresholdsCheckbox.ensureDebugId("DefinesResultDiscardingRulesCheckbox");
useSeriesResultDiscardingThresholdsCheckbox.setValue(selectedSeries.getDiscardThresholds() != null);
useSeriesResultDiscardingThresholdsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
@@ -179,13 +171,10 @@ public class SeriesEditDialog extends DataEntryDialog<SeriesDescriptor> {
}
});
additionalWidgetPanel.add(useSeriesResultDiscardingThresholdsCheckbox);
additionalWidgetPanel.add(discardThresholdBoxes.getWidget());
discardThresholdBoxes.getWidget().setVisible(useSeriesResultDiscardingThresholdsCheckbox.getValue());
raceNamesEditor = new StringListEditorComposite(getExistingRacesOfSeries(), new RaceNamesEditorUi(stringMessages, resources.removeIcon(), seriesName));
additionalWidgetPanel.add(raceNamesEditor);
return additionalWidgetPanel;
}
@@ -101,31 +101,19 @@ public class SeriesWithFleetsCreateDialog extends DataEntryDialog<SeriesDTO> {
this.stringMessages = stringMessages;
this.series = new SeriesDTO();
nameEntryField = createTextBox(null);
nameEntryField.ensureDebugId("NameTextField");
nameEntryField.setVisibleLength(40);
isMedalSeriesCheckbox = createCheckbox(stringMessages.medalSeries());
isMedalSeriesCheckbox.ensureDebugId("MedalSeriesCheckbox");
startsWithZeroScoreCheckbox = createCheckbox(stringMessages.startsWithZeroScore());
startsWithZeroScoreCheckbox.ensureDebugId("StartsWithZeroScoreCheckbox");
firstColumnIsNonDiscardableCarryForwardCheckbox = createCheckbox(stringMessages.firstRaceIsNonDiscardableCarryForward());
firstColumnIsNonDiscardableCarryForwardCheckbox.ensureDebugId("StartsWithNonDiscardableCarryForwardCheckbox");
useSeriesResultDiscardingThresholdsCheckbox = createCheckbox(stringMessages.seriesDefinesResultDiscardingRule());
useSeriesResultDiscardingThresholdsCheckbox.ensureDebugId("DefinesResultDiscardingRulesCheckbox");
useSeriesResultDiscardingThresholdsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
discardThresholdBoxes.getWidget().setVisible(event.getValue());
}
});
discardThresholdBoxes = new DiscardThresholdBoxes(this, stringMessages);
discardThresholdBoxes.getWidget().setVisible(false);
fleetListComposite = FleetListEditorComposite.createExpanded(Arrays.asList(new FleetDTO("Default", 0, null)), stringMessages, resources.removeIcon());
fleetListComposite.addValueChangeHandler(new ValueChangeHandler<List<FleetDTO>>() {
@@ -102,7 +102,7 @@ public class TrackedRacesListComposite extends AbstractTrackedRacesListComposite
@Override
protected void addControlButtons(HorizontalPanel trackedRacesButtonPanel) {
btnRemoveRace = new Button(stringMessages.remove());
btnRemoveRace.ensureDebugId("RemoveButton");
btnRemoveRace.ensureDebugId("RemoveRave");
btnRemoveRace.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
@@ -112,7 +112,7 @@ public class TrackedRacesListComposite extends AbstractTrackedRacesListComposite
btnRemoveRace.setEnabled(false);
trackedRacesButtonPanel.add(btnRemoveRace);
btnUntrack = new Button(stringMessages.stopTracking());
btnUntrack.ensureDebugId("StopTrackingButton");
btnUntrack.ensureDebugId("UntrackRace");
btnUntrack.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent click) {
@@ -124,7 +124,7 @@ public class TrackedRacesListComposite extends AbstractTrackedRacesListComposite
btnSetDelayToLive = new Button(stringMessages.setDelayToLive() + "...");
btnSetDelayToLive.ensureDebugId("SetDelayToLiveButton");
btnSetDelayToLive.ensureDebugId("SetDelayToLive");
btnSetDelayToLive.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
@@ -134,9 +134,7 @@ public class TrackedRacesListComposite extends AbstractTrackedRacesListComposite
trackedRacesButtonPanel.add(btnSetDelayToLive);
exportPopup = new ExportPopup(stringMessages);
exportPopup.ensureDebugId("ExportPopup");
btnExport = new Button(stringMessages.export());
btnExport.ensureDebugId("ExportButton");
btnExport.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
@@ -6,6 +6,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gwt.cell.client.ActionCell;
@@ -21,6 +22,7 @@ import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.ColumnSortEvent.Handler;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
@@ -240,19 +242,65 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower
rawWindFixesTable.getColumnSortList().push(timeColumn);
windFixesDisplayPanel.add(rawWindFixesTable);
createWindImportPanel(mainPanel, stringMessages);
mainPanel.add(createExpeditionWindImportPanel());
mainPanel.add(createIgtimiWindImportPanel(mainPanel));
}
private void createWindImportPanel(Panel mainPanel, final StringMessages stringMessages) {
private CaptionPanel createIgtimiWindImportPanel(VerticalPanel mainPanel) {
CaptionPanel igtimiWindImportRootPanel = new CaptionPanel(stringMessages.igtimiWindImport());
VerticalPanel contentPanel = new VerticalPanel();
igtimiWindImportRootPanel.add(contentPanel);
contentPanel.add(new Label(stringMessages.seeIgtimiTabForAccountSettings()));
final Button importButton = new Button(stringMessages.importWindFromIgtimi());
final HTML resultReport = new HTML();
importButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
final String warningMessage;
if (trackedRacesListComposite.getSelectedRaces().isEmpty()) {
warningMessage = stringMessages.windImport_AllRacesWarning();
} else {
warningMessage = stringMessages.windImport_SelectedRacesWarning(trackedRacesListComposite.getSelectedRaces().size());
}
if (Window.confirm(warningMessage)) {
resultReport.setText(stringMessages.loading());
sailingService.importWindFromIgtimi(trackedRacesListComposite.getSelectedRaces(), new AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorImportingIgtimiWind(caught.getMessage()));
resultReport.setText(stringMessages.errorImportingIgtimiWind(caught.getMessage()));
}
@Override
public void onSuccess(Map<RegattaAndRaceIdentifier, Integer> result) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
for (Entry<RegattaAndRaceIdentifier, Integer> e : result.entrySet()) {
sb.append(e.getKey().getRegattaName());
sb.append('/');
sb.append(e.getKey().getRaceName());
sb.append(": ");
sb.append(e.getValue());
sb.append("\n");
}
resultReport.setHTML(new SafeHtmlBuilder().appendEscapedLines(stringMessages.resultFromIgtimiWindImport(sb.toString())).toSafeHtml());
}
});
}
}
});
contentPanel.add(importButton);
contentPanel.add(resultReport);
return igtimiWindImportRootPanel;
}
private CaptionPanel createExpeditionWindImportPanel() {
/*
* To style the "browse" button of the file upload widget
* see http://www.shauninman.com/archive/2007/09/10/styling_file_inputs_with_css_and_the_dom
*/
CaptionPanel windImportRootPanel = new CaptionPanel(stringMessages.windImport_Title());
mainPanel.add(windImportRootPanel);
VerticalPanel windImportContentPanel = new VerticalPanel();
windImportRootPanel.add(windImportContentPanel);
final FormPanel form = new FormPanel();
@@ -369,7 +417,7 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower
}
}
});
return windImportRootPanel;
}
private void showWindSettingDialog(RaceDTO race, CoursePositionsDTO course) {
@@ -532,7 +580,6 @@ public class WindPanel extends FormPanel implements RegattaDisplayer, WindShower
@Override
public void onSuccess(WindInfoForRaceDTO result) {
windFixPanel.clear();
for (WindSourceType input : new WindSourceType[] { WindSourceType.COMBINED }) {
windFixPanel.add(new HTML("&nbsp;"));
windFixPanel.add(new Label(stringMessages.windFixListingDescription() + " " + input.name()));
@@ -23,6 +23,7 @@ import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.LongBox;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
@@ -182,6 +183,43 @@ public abstract class DataEntryDialog<T> {
return textBox;
}
/**
* Creates a password text box with a key-up listener attached which ensures the value is updated after each
* key-up event and the entire dialog is {@link #validate() validated} in this case.
*
* @param initialValue initial value to show in text box; <code>null</code> is permissible
*/
public PasswordTextBox createPasswordTextBox(String initialValue) {
return createPasswordTextBoxInternal(initialValue, 30);
}
/**
* Creates a password text box with a key-up listener attached which ensures the value is updated after each
* key-up event and the entire dialog is {@link #validate() validated} in this case.
*
* @param initialValue initial value to show in text box; <code>null</code> is permissible
* @param visibleLength the visible length of the text box
*/
public PasswordTextBox createPasswordTextBox(String initialValue, int visibleLength) {
return createPasswordTextBoxInternal(initialValue, visibleLength);
}
private PasswordTextBox createPasswordTextBoxInternal(String initialValue, int visibleLength) {
PasswordTextBox parrwordTextBox = new PasswordTextBox();
parrwordTextBox.setVisibleLength(visibleLength);
parrwordTextBox.setText(initialValue == null ? "" : initialValue);
AbstractEntryPoint.addFocusUponKeyUpToggler(parrwordTextBox);
parrwordTextBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
validate();
}
});
AbstractEntryPoint.linkEnterToButton(getOkButton(), parrwordTextBox);
AbstractEntryPoint.linkEscapeToButton(getCancelButton(), parrwordTextBox);
return parrwordTextBox;
}
/**
* Creates a text area with a key-up listener attached which ensures the value is updated after each
* key-up event and the entire dialog is {@link #validate() validated} in this case.
@@ -4,6 +4,7 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import com.google.gwt.i18n.client.NumberFormat;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.gwt.ui.client.Timer.PlayModes;
@@ -27,6 +28,22 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
autoAdjustPlayMode = true;
}
/**
* If the race's start time is already known and the <code>time</code> is before the start, append a count-down to the label
*/
@Override
protected String getTimeLabelText(Date time) {
final String result;
RaceTimesInfoDTO selectedRaceTimes = raceTimesInfoProvider.getRaceTimesInfo(selectedRace);
if (selectedRaceTimes.startOfRace != null && time.before(selectedRaceTimes.startOfRace)) {
result = super.getTimeLabelText(time) + ", " + stringMessages.countDownInMillis(
NumberFormat.getFormat("0.00").format(((double) selectedRaceTimes.startOfRace.getTime()-time.getTime()) / 1000.));
} else {
result = super.getTimeLabelText(time);
}
return result;
}
@Override
public void updateSettings(RaceTimePanelSettings newSettings) {
super.updateSettings(newSettings);
@@ -1,6 +1,5 @@
package com.sap.sailing.gwt.ui.client;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -309,8 +308,6 @@ public interface SailingService extends RemoteService {
PolarSheetGenerationResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces,
PolarSheetGenerationSettings settings, String name) throws Exception;
EventDTO getEventById(Serializable id);
CourseAreaDTO createCourseArea(String eventIdAsString, String courseAreaName);
EventDTO getEventByIdAsString(String eventIdAsString);
@@ -355,4 +352,14 @@ public interface SailingService extends RemoteService {
boolean setStartTime(RaceLogSetStartTimeDTO dto);
Pair<Date, Integer> getStartTime(String leaderboardName, String raceColumnName, String fleetName);
Iterable<String> getAllIgtimiAccountEmailAddresses();
String getIgtimiAuthorizationUrl();
boolean authorizeAccessToIgtimiUser(String eMailAddress, String password) throws Exception;
void removeIgtimiAccount(String eMailOfAccountToRemove);
Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces) throws Exception;
}
@@ -1,6 +1,5 @@
package com.sap.sailing.gwt.ui.client;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@@ -320,8 +319,6 @@ public interface SailingServiceAsync {
*/
void getEventByName(String eventName, AsyncCallback<EventDTO> callback);
void getEventById(Serializable id, AsyncCallback<EventDTO> callback);
/**
* Renames the event with the name <code>oldName</code> to the <code>newName</code>.<br />
* If there's no event with the name <code>oldName</code> or there's already a event with the name
@@ -478,6 +475,15 @@ public interface SailingServiceAsync {
void getStartTime(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Pair<Date, Integer>> callback);
void getAllIgtimiAccountEmailAddresses(AsyncCallback<Iterable<String>> callback);
void getIgtimiAuthorizationUrl(AsyncCallback<String> callback);
void authorizeAccessToIgtimiUser(String eMailAddress, String password, AsyncCallback<Boolean> callback);
void removeIgtimiAccount(String eMailOfAccountToRemove, AsyncCallback<Void> asyncCallback);
void importWindFromIgtimi(List<RaceDTO> selectedRaces, AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>> asyncCallback);
}
@@ -881,4 +881,29 @@ public interface StringMessages extends Messages {
String hasAdditionalGolfDownTime();
String additionalGolfDownTimeHelpText();
String invalidColor(String message);
String createSwissTimingRace();
String igtimiAccounts();
String errorFetchingIgtimiAccounts(String message);
String addIgtimiAccount();
String errorAuthorizingAccessToIgtimiUser(String text, String message);
String successfullyAuthorizedAccessToIgtimiUser(String username);
String couldNotAuthorizedAccessToIgtimiUser(String text);
String errorGettingIgtimiAuthorizationUrl(String message);
String addIgtimiUser();
String doYouReallyWantToRemoveLeaderboards();
String errorTryingToRemoveIgtimiAccount(String eMailOfAccountToRemove);
String successfullyRemoveIgtimiAccount(String eMailOfAccountToRemove);
String eMailMustNotBeEmpty();
String emailAddress();
String password();
String doYouReallyWantToRemoveLeaderboard(String name);
String metaLeaderboardCannotBeChanged();
String failedToSetNewStartTime();
String doYouReallyWantToRemoveIgtimiAccount(String accountEmail);
String igtimiWindImport();
String seeIgtimiTabForAccountSettings();
String importWindFromIgtimi();
String errorImportingIgtimiWind(String message);
String resultFromIgtimiWindImport(String string);
String countDownInMillis(String timeToStartInFractionalSeconds);
}
@@ -868,4 +868,29 @@ currentPass=Current pass
setStartTimeDescription=Select a new start time for the race
hasAdditionalGolfDownTime=Activate additional golf down time
additionalGolfDownTimeHelpText=Activate additional golf down time
invalidColor=Invalid color: {0}
invalidColor=Invalid color: {0}
createSwissTimingRace=Create SwissTiming race
igtimiAccounts=Igtimi Accounts
errorFetchingIgtimiAccounts=Error fetching all Igtimi accounts: {0}
addIgtimiAccount=Add Igtimi Account
errorAuthorizingAccessToIgtimiUser=Error authorizing access to Igtimi user {0}: {1}
successfullyAuthorizedAccessToIgtimiUser=Successfully authorized access to Igtimi user {0}
couldNotAuthorizedAccessToIgtimiUser=Could not authorize user {0}; invalid user name or password
errorGettingIgtimiAuthorizationUrl=Error getting Igtimi authorization URL: {0}
addIgtimiUser=Add Igtimi User
doYouReallyWantToRemoveLeaderboards=Do you really want to remove the leaderboards?
errorTryingToRemoveIgtimiAccount=Error trying to remove Igtimi account for user {0}
successfullyRemoveIgtimiAccount=Successfully removed Igtimi account for user {0}
eMailMustNotBeEmpty=e-Mail address must not be empty
emailAddress=e-mail address
password=Password
doYouReallyWantToRemoveLeaderboard=Do you really want to remove the leaderboard: ''{0}''?
metaLeaderboardCannotBeChanged=This is a meta leaderboard. It can''t be changed here.
failedToSetNewStartTime=Failed to set new start time.
doYouReallyWantToRemoveIgtimiAccount=Do you really want to remove Igtimi account granting access to data of user {0}?
igtimiWindImport=Import Wind from Igtimi
seeIgtimiTabForAccountSettings=See the ''Igtimi'' tab for account settings determining which data from Igtimi will be considered.
importWindFromIgtimi=Import Wind from Igtimi
errorImportingIgtimiWind=Error importing wind data from Igtimi: {0}
resultFromIgtimiWindImport=The following number of fixes were imported for the listed races: {0}
countDownInMillis={0}s to start
@@ -864,4 +864,29 @@ currentPass=Aktueller Pass
setStartTimeDescription=Wähle eine Startzeit für das Rennen aus
hasAdditionalGolfDownTime=Aktiviere Golf-Flaggen Zusatzzeit
additionalGolfDownTimeHelpText=Aktiviere Golf-Flaggen Zusatzzeit
invalidColor=Ungültige Farbangabe: {0}
invalidColor=Ungültige Farbangabe: {0}
createSwissTimingRace=SwissTiming-Rennen anlegen
igtimiAccounts=Igtimi-Konten
errorFetchingIgtimiAccounts=Fehler beim Laden aller Igtimi-Konten: {0}
addIgtimiAccount=Igtimi-Konto hinzufügen
errorAuthorizingAccessToIgtimiUser=Fehler beim autorisieren des Benutzers mit der e-Mail-Adresse {0}: {1}
successfullyAuthorizedAccessToIgtimiUser=Zugriff auf Daten von Benutzer {0} erfolgreich authorisiert
couldNotAuthorizedAccessToIgtimiUser=Benutzer {0} konnte nicht authentifiziert werden; ungültiger Benutzername oder falsches Passwort
errorGettingIgtimiAuthorizationUrl=Fehler beim Beschaffen der Igtimi Authorisierungs-URL: {0}
addIgtimiUser=Igtimi-Benutzer hinzufügen
doYouReallyWantToRemoveLeaderboards=Möchten Sie wirklich diese Ergebnislisten löschen?
errorTryingToRemoveIgtimiAccount=Fehler beim Löschen des Zugangs zum Benutzerkonto {0}
successfullyRemoveIgtimiAccount=Zugang zum Igtimi-Benutzerkonto {0} erfolgreich gelöscht
eMailMustNotBeEmpty=e-Mailadresse darf nicht leer sein
emailAddress=e-Mailadresse
password=Kennwort
doYouReallyWantToRemoveLeaderboard=Möchten Sie wirklich die Ergebnlisliste ''{0}'' löschen?
metaLeaderboardCannotBeChanged=Dies ist ein Meta-Leaderboard, das hier nicht geändert werden kann.
failedToSetNewStartTime=Neue Startzeit konnte nicht gesetzt werden.
doYouReallyWantToRemoveIgtimiAccount=Möchten Sie wirklich den Zugang zu den Daten des Benutzers {0} löschen?
igtimiWindImport=Wind von Igtimi importieren
seeIgtimiTabForAccountSettings=Siehe den ''Igtimi'' Reiter für die Einstellungen, von welchen Igtimi-Konten Winddaten geladen werden sollen.
importWindFromIgtimi=Wind von Igtimi importieren
errorImportingIgtimiWind=Fehler beim Importieren von Wind-Daten von Igtimi: {0}
resultFromIgtimiWindImport=Die folgenden Anzahlen von Windmessungen wurden für die aufgelisteten Wettfahrten importiert: {0}
countDownInMillis={0}s bis zum Start
@@ -307,12 +307,22 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
timeSlider.setCurrentValue(new Double(time.getTime()), false);
}
}
dateLabel.setText(dateFormatter.format(time));
dateLabel.setText(getDateLabelText(time));
timeLabel.setText(getTimeLabelText(time));
}
protected String getTimeLabelText(Date time) {
final String timeLabelText;
if (lastReceivedDataTimepoint == null) {
timeLabel.setText(timeFormatter.format(time));
timeLabelText = timeFormatter.format(time);
} else {
timeLabel.setText(timeFormatter.format(time) + " (" + timeFormatter.format(lastReceivedDataTimepoint) + ")");
timeLabelText = timeFormatter.format(time) + " (" + timeFormatter.format(lastReceivedDataTimepoint) + ")";
}
return timeLabelText;
}
protected String getDateLabelText(Date time) {
return dateFormatter.format(time);
}
protected Date getFromTime() {
@@ -42,7 +42,6 @@ public abstract class AbstractFilterablePanel<T> extends HorizontalPanel {
this.display = display;
this.filtered = filtered;
this.textBox = new TextBox();
this.textBox.ensureDebugId("FilterTextField");
this.all = all;
add(label);
add(getTextBox());
@@ -78,6 +78,9 @@ public class BoatClassImageData {
public ImageTransformer getBoatImageTransformerByLegTypeAndTack(LegType legType, Tack tack, boolean isSelected) {
ImageTransformer result = null;
if (tack == null) {
tack = Tack.STARBOARD; // TODO this is just a fallback / workaround for not having a good image resource for unknown tack
}
switch (tack) {
case PORT:
switch (legType) {
@@ -135,6 +138,9 @@ public class BoatClassImageData {
public ImageTransformer getBoatImageTransformerByTack(Tack tack, boolean isSelected) {
ImageTransformer result = null;
if (tack == null) {
tack = Tack.STARBOARD; // TODO defaulting to a tack to avoid NPE; need to find a default representation for boats in unknown wind conditions
}
switch (tack) {
case STARBOARD:
if (isSelected) {
@@ -9,6 +9,7 @@ import com.google.gwt.maps.client.geometrylib.SphericalUtils;
import com.sap.sailing.domain.common.dto.BoatClassDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO;
import com.sap.sailing.gwt.ui.shared.racemap.CanvasOverlayV3;
/**
@@ -51,7 +52,11 @@ public class BoatOverlay extends CanvasOverlayV3 {
boatImageTransformer = boatClassImageData.getBoatImageTransformerByTack(boatFix.tack, isSelected());
}
double realBoatSizeScaleFactor = getRealBoatSizeScaleFactor(boatImageTransformer.getImageSize());
double boatDrawingAngle = boatFix.speedWithBearing.bearingInDegrees - ORIGINAL_BOAT_IMAGE_ROTATIION_ANGLE;
SpeedWithBearingDTO speedWithBearing = boatFix.speedWithBearing;
if (speedWithBearing == null) {
speedWithBearing = new SpeedWithBearingDTO(0, 0);
}
double boatDrawingAngle = speedWithBearing.bearingInDegrees - ORIGINAL_BOAT_IMAGE_ROTATIION_ANGLE;
if (boatDrawingAngle < 0) {
boatDrawingAngle += 360;
}
@@ -173,7 +173,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
/**
* html5 canvases used for competitor info display on the map
*/
private final Map<CompetitorDTO, CompetitorInfoOverlay> competitorInfoOverlays;
private final Map<CompetitorDTO, SmallTransparentInfoOverlay> competitorInfoOverlays;
private SmallTransparentInfoOverlay countDownOverlay;
/**
* Map overlays with html5 canvas used to display wind sensors
@@ -268,7 +270,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
markDTOs = new HashMap<String, MarkDTO>();
courseSidelines = new HashMap<SidelineDTO, Polygon>();
boatOverlays = new HashMap<CompetitorDTO, BoatOverlay>();
competitorInfoOverlays = new HashMap<CompetitorDTO, CompetitorInfoOverlay>();
competitorInfoOverlays = new HashMap<CompetitorDTO, SmallTransparentInfoOverlay>();
windSensorOverlays = new HashMap<WindSource, WindSensorOverlay>();
courseMarkOverlays = new HashMap<String, CourseMarkOverlay>();
this.competitorSelection = competitorSelection;
@@ -637,14 +639,14 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
if (fixesAndTails.hasFixesFor(competitorDTO)) {
GPSFixDTO lastBoatFix = getBoatFix(competitorDTO, date);
if (lastBoatFix != null) {
CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO);
SmallTransparentInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO);
if (competitorInfoOverlay == null) {
competitorInfoOverlay = createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.COMPETITOR_INFO_ZINDEX, competitorDTO);
competitorInfoOverlay = createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO);
competitorInfoOverlays.put(competitorDTO, competitorInfoOverlay);
competitorInfoOverlay.setBoatFix(lastBoatFix);
competitorInfoOverlay.setPosition(lastBoatFix.position);
competitorInfoOverlay.addToMap();
} else {
competitorInfoOverlay.setBoatFix(lastBoatFix);
competitorInfoOverlay.setPosition(lastBoatFix.position);
competitorInfoOverlay.draw();
}
toRemoveCompetorInfoOverlays.remove(competitorDTO);
@@ -652,13 +654,13 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
}
for (CompetitorDTO toRemoveCompetorDTO : toRemoveCompetorInfoOverlays) {
CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(toRemoveCompetorDTO);
SmallTransparentInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(toRemoveCompetorDTO);
competitorInfoOverlay.removeFromMap();
competitorInfoOverlays.remove(toRemoveCompetorDTO);
}
} else {
// remove all overlays
for (CompetitorInfoOverlay competitorInfoOverlay : competitorInfoOverlays.values()) {
for (SmallTransparentInfoOverlay competitorInfoOverlay : competitorInfoOverlays.values()) {
competitorInfoOverlay.removeFromMap();
}
competitorInfoOverlays.clear();
@@ -851,6 +853,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
int legOfLeadingCompetitor = leadingVisibleCompetitorInfo == null ? -1 : leadingVisibleCompetitorInfo.getA();
int numberOfLegs = lastRaceTimesInfo.legInfos.size();
// draw the start line
updateCountdownCanvas(courseDTO.startMarkPositions);
if (legOfLeadingCompetitor <= 1 &&
settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) && courseDTO.startMarkPositions != null && courseDTO.startMarkPositions.size() == 2) {
LatLng startLinePoint1 = LatLng.newInstance(courseDTO.startMarkPositions.get(0).latDeg, courseDTO.startMarkPositions.get(0).lngDeg);
@@ -898,8 +901,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
startLine.getPath().insertAt(0, startLinePoint1);
startLine.getPath().insertAt(1, startLinePoint2);
}
}
else {
} else {
if (startLine != null) {
startLine.setMap(null);
startLine = null;
@@ -1011,6 +1013,33 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
}
/**
* If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is
* still in the pre-start phase, show a {@link SmallTransparentInfoOverlay} at the
* start line that shows the count down.
*/
private void updateCountdownCanvas(List<PositionDTO> startMarkPositions) {
if (!settings.isShowSelectedCompetitorsInfo() || startMarkPositions == null || startMarkPositions.isEmpty()
|| lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) {
if (countDownOverlay != null) {
countDownOverlay.removeFromMap();
countDownOverlay = null;
}
} else {
final String countDownText = "-"+NumberFormat.getFormat("0.0").format(
Math.round(((double) lastRaceTimesInfo.startOfRace.getTime() - timer.getTime().getTime()) / 100.)/10.);
if (countDownOverlay == null) {
countDownOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX,
countDownText, raceMapImageManager);
countDownOverlay.addToMap();
} else {
countDownOverlay.setInfoText(countDownText);
}
countDownOverlay.setPosition(startMarkPositions.get(startMarkPositions.size() - 1));
countDownOverlay.draw();
}
}
private void zoomMapToNewBounds(LatLngBounds newBounds) {
if (newBounds != null) {
List<ZoomTypes> oldZoomSettings = settings.getZoomSettings().getTypesToConsiderOnZoom();
@@ -1058,8 +1087,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
return courseMarkOverlay;
}
private CompetitorInfoOverlay createCompetitorInfoOverlay(int zIndex, final CompetitorDTO competitorDTO) {
return new CompetitorInfoOverlay(map, zIndex, competitorDTO, raceMapImageManager);
private SmallTransparentInfoOverlay createCompetitorInfoOverlay(int zIndex, final CompetitorDTO competitorDTO) {
return new SmallTransparentInfoOverlay(map, zIndex,
/* infoText */ (competitorDTO.getSailID() == null || competitorDTO.getSailID().isEmpty()) ? competitorDTO.getName() : competitorDTO.getSailID(),
raceMapImageManager);
}
private BoatOverlay createBoatOverlay(int zIndex, final CompetitorDTO competitorDTO, boolean highlighted) {
@@ -1213,9 +1244,14 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
if (rank != null) {
vPanel.add(createInfoWindowLabelAndValue(stringMessages.rank(), String.valueOf(rank)));
}
SpeedWithBearingDTO speedWithBearing = lastFix.speedWithBearing;
if (speedWithBearing == null) {
// TODO should we show the boat at all?
speedWithBearing = new SpeedWithBearingDTO(0, 0);
}
vPanel.add(createInfoWindowLabelAndValue(stringMessages.speed(),
NumberFormatterFactory.getDecimalFormat(1).format(lastFix.speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit()));
vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), (int) lastFix.speedWithBearing.bearingInDegrees + " "+stringMessages.degreesShort()));
NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit()));
vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), (int) speedWithBearing.bearingInDegrees + " "+stringMessages.degreesShort()));
if (lastFix.degreesBoatToTheWind != null) {
vPanel.add(createInfoWindowLabelAndValue(stringMessages.degreesBoatToTheWind(),
(int) Math.abs(lastFix.degreesBoatToTheWind) + " " + stringMessages.degreesShort()));
@@ -13,7 +13,7 @@ public interface RaceMapOverlaysZIndexes {
public static int COURSEMARK_ZINDEX = BASE_CANVAS_OVERLAY_ZINDEX + 2;
public static int WINDSENSOR_ZINDEX = BASE_CANVAS_OVERLAY_ZINDEX + 3;
public static int BOATS_ZINDEX = BASE_CANVAS_OVERLAY_ZINDEX + 4;
public static int COMPETITOR_INFO_ZINDEX = BASE_CANVAS_OVERLAY_ZINDEX + 5;
public static int INFO_OVERLAY_ZINDEX = BASE_CANVAS_OVERLAY_ZINDEX + 5;
/** the z-index of the info window should always be higher than the rest */
public static int INFO_WINDOW_ZINDEX = BASE_CANVAS_OVERLAY_ZINDEX + 11;
@@ -6,24 +6,27 @@ import com.google.gwt.canvas.dom.client.TextMetrics;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.base.LatLng;
import com.google.gwt.maps.client.base.Point;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.domain.common.dto.PositionDTO;
import com.sap.sailing.gwt.ui.shared.racemap.CanvasOverlayV3;
/**
* A google map overlay based on a HTML5 canvas for drawing competitor information close to the boat
* A google map overlay based on a HTML5 canvas for drawing a short textual information close to some object on a map
* with a given position.
*
* @author Frank Mittag
* @author Axel Uhl
*/
public class CompetitorInfoOverlay extends CanvasOverlayV3 {
public class SmallTransparentInfoOverlay extends CanvasOverlayV3 {
/**
* The competitor.
* The text to display in the canvas
*/
private final CompetitorDTO competitorDTO;
private String infoText;
/**
* The current GPS fix of the boat position of the competitor.
*/
private GPSFixDTO boatFix;
private PositionDTO position;
private int canvasWidth;
private int canvasHeight;
@@ -31,10 +34,9 @@ public class CompetitorInfoOverlay extends CanvasOverlayV3 {
private int infoBoxWidth;
private double cornerRadius;
public CompetitorInfoOverlay(MapWidget map, int zIndex, CompetitorDTO competitorDTO, RaceMapImageManager raceMapImageManager) {
public SmallTransparentInfoOverlay(MapWidget map, int zIndex, String infoText, RaceMapImageManager raceMapImageManager) {
super(map, zIndex);
this.competitorDTO = competitorDTO;
this.infoText = infoText;
canvasWidth = 20;
canvasHeight = 45;
infoBoxWidth = 20;
@@ -51,18 +53,13 @@ public class CompetitorInfoOverlay extends CanvasOverlayV3 {
@Override
protected void draw() {
if (mapProjection != null && boatFix != null) {
LatLng latLngPosition = LatLng.newInstance(boatFix.position.latDeg, boatFix.position.lngDeg);
String infoText = competitorDTO.getSailID();
if (infoText == null || infoText.isEmpty()) {
infoText = competitorDTO.getName();
}
if (mapProjection != null && position != null) {
LatLng latLngPosition = LatLng.newInstance(position.latDeg, position.lngDeg);
Context2d context2d = getCanvas().getContext2d();
CssColor grayTransparentColor = CssColor.make("rgba(255,255,255,0.75)");
context2d.setFont("12px bold Verdana sans-serif");
TextMetrics measureText = context2d.measureText(competitorDTO.getSailID());
TextMetrics measureText = context2d.measureText(infoText);
double textWidth = measureText.getWidth();
canvasWidth = (int) textWidth + 17;
@@ -87,11 +84,11 @@ public class CompetitorInfoOverlay extends CanvasOverlayV3 {
context2d.beginPath();
context2d.setFillStyle("black");
context2d.fillText(competitorDTO.getSailID(), 8, 14);
context2d.fillText(infoText, 8, 14);
context2d.stroke();
Point boatPositionInPx = mapProjection.fromLatLngToDivPixel(latLngPosition);
setCanvasPosition(boatPositionInPx.getX(), boatPositionInPx.getY() - canvasHeight);
Point objectPositionInPx = mapProjection.fromLatLngToDivPixel(latLngPosition);
setCanvasPosition(objectPositionInPx.getX(), objectPositionInPx.getY() - canvasHeight);
}
}
@@ -110,11 +107,15 @@ public class CompetitorInfoOverlay extends CanvasOverlayV3 {
context.fill();
}
public GPSFixDTO getBoatFix() {
return boatFix;
public void setPosition(PositionDTO position) {
this.position = position;
}
public void setBoatFix(GPSFixDTO boatFix) {
this.boatFix = boatFix;
/**
* Updates the text to show and re-draws the canvas
*/
public void setInfoText(String infoText) {
this.infoText = infoText;
draw();
}
}
@@ -168,7 +168,6 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
leaderboardSettings, preselectedRace, leaderboardGroupName, leaderboardName, this, stringMessages, userAgent,
showRaceDetails, hideToolbar, autoExpandLastRaceColumn, showCharts, chartDetailType, showOverallLeaderboard);
}
leaderboardViewer.ensureDebugId("LeaderboardViewerWidget");
contentScrollPanel.setWidget(leaderboardViewer);
mainPanel.add(contentScrollPanel);
}
@@ -1393,7 +1393,6 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
setRaceColumnSelectionToLastNStrategy(settings.getNumberOfLastRacesToShow());
break;
}
totalRankColumn = new TotalRankColumn();
RACE_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableRaceColumnHeader();
LEG_COLUMN_HEADER_STYLE = tableResources.cellTableStyle().cellTableLegColumnHeader();
@@ -1402,7 +1401,9 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
LEG_COLUMN_STYLE = tableResources.cellTableStyle().cellTableLegColumn();
LEG_DETAIL_COLUMN_STYLE = tableResources.cellTableStyle().cellTableLegDetailColumn();
TOTAL_COLUMN_STYLE = tableResources.cellTableStyle().cellTableTotalColumn();
leaderboardTable = new SortedCellTableWithStylableHeaders<LeaderboardRowDTO>(
/* pageSize */10000, tableResources);
getLeaderboardTable().setWidth("100%");
leaderboardSelectionModel = new MultiSelectionModel<LeaderboardRowDTO>();
leaderboardSelectionModel.addSelectionChangeHandler(new Handler() {
@Override
@@ -1415,12 +1416,6 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
updateLeaderboard(getLeaderboard());
}
});
this.leaderboardTable = new SortedCellTableWithStylableHeaders<LeaderboardRowDTO>(
/* pageSize */10000, tableResources);
this.leaderboardTable.ensureDebugId("LeaderboardTable");
this.leaderboardTable.setWidth("100%");
if (userAgent.isMobile() == UserAgentDetails.PlatformTypes.MOBILE) {
//Setting up the toggle selection
leaderboardTable.addCellPreviewHandler(new ToggleSelectionCellPreviewHandler<LeaderboardRowDTO>());
@@ -1429,7 +1424,6 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
} else {
leaderboardTable.setSelectionModel(leaderboardSelectionModel);
}
loadCompleteLeaderboard(getLeaderboardDisplayDate());
if (this.preSelectedRace == null) {
@@ -1455,9 +1449,9 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
liveRaceLabel.setVisible(false);
informationPanel.add(liveRaceLabel);
// the toolbar panel
// the toolbar panel
DockPanel toolbarPanel = new DockPanel();
toolbarPanel.setStyleName(STYLE_LEADERBOARD_TOOLBAR);
toolbarPanel.setStyleName(STYLE_LEADERBOARD_TOOLBAR);
busyIndicator = new SimpleBusyIndicator(false, 0.8f);
if (!isEmbedded) {
toolbarPanel.add(informationPanel, DockPanel.WEST);
@@ -47,6 +47,7 @@ import java.util.zip.GZIPInputStream;
import javax.servlet.ServletContext;
import org.apache.http.client.ClientProtocolException;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
@@ -146,6 +147,9 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.racelog.FlagPole;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.igtimiadapter.Account;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnection;
import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory;
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
@@ -175,6 +179,7 @@ import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayRace;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayServiceFactory;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFix;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
@@ -326,6 +331,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private final MongoObjectFactory mongoObjectFactory;
private final SwissTimingAdapterPersistence swissTimingAdapterPersistence;
private final ServiceTracker<SwissTimingAdapterFactory, SwissTimingAdapterFactory> swissTimingAdapterTracker;
private final ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> tractracAdapterTracker;
private final ServiceTracker<IgtimiConnectionFactory, IgtimiConnectionFactory> igtimiAdapterTracker;
private final com.sap.sailing.domain.tractracadapter.persistence.MongoObjectFactory tractracMongoObjectFactory;
@@ -355,28 +366,29 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
* {@link #LEADERBOARD_DIFFERENCE_CACHE_SIZE}.
*/
private final LinkedHashMap<Pair<String, String>, IncrementalLeaderboardDTO> leaderboardDifferenceCacheByIdPair;
private final TracTracAdapter tracTracAdapter;
private final SwissTimingAdapter swissTimingAdapter;
private final SwissTimingReplayService swissTimingReplayService;
private PolarDataService polarDataService;
private final BundleContext context;
public SailingServiceImpl() {
BundleContext context = Activator.getDefault();
context = Activator.getDefault();
racingEventServiceTracker = createAndOpenRacingEventServiceTracker(context);
replicationServiceTracker = createAndOpenReplicationServiceTracker(context);
swissTimingAdapterTracker = createAndOpenSwissTimingAdapterTracker(context);
tractracAdapterTracker = createAndOpenTracTracAdapterTracker(context);
igtimiAdapterTracker = createAndOpenIgtimiTracker(context);
baseDomainFactory = getService().getBaseDomainFactory();
mongoObjectFactory = getService().getMongoObjectFactory();
domainObjectFactory = getService().getDomainObjectFactory();
tracTracAdapter = getTracTracAdapter(context);
// TODO what about passing on the mongo/domain object factory to obtain an according SwissTimingAdapterPersistence instance similar to how the tractracDomainObjectFactory etc. are created below?
swissTimingAdapterPersistence = SwissTimingAdapterPersistence.INSTANCE;
swissTimingAdapter = getSwissTimingAdapter(context);
swissTimingReplayService = getSwissTimingReplayService(context);
scoreCorrectionProviderServiceTracker = createAndOpenScoreCorrectionProviderServiceTracker(context);
tractracDomainObjectFactory = com.sap.sailing.domain.tractracadapter.persistence.PersistenceFactory.INSTANCE.createDomainObjectFactory(mongoObjectFactory.getDatabase(), tracTracAdapter.getTracTracDomainFactory());
tractracDomainObjectFactory = com.sap.sailing.domain.tractracadapter.persistence.PersistenceFactory.INSTANCE
.createDomainObjectFactory(mongoObjectFactory.getDatabase(), getTracTracAdapter()
.getTracTracDomainFactory());
tractracMongoObjectFactory = com.sap.sailing.domain.tractracadapter.persistence.MongoObjectFactory.INSTANCE;
swissTimingFactory = SwissTimingFactory.INSTANCE;
countryCodeFactory = com.sap.sailing.domain.common.CountryCodeFactory.INSTANCE;
@@ -406,15 +418,19 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
protected SwissTimingReplayService getSwissTimingReplayService(BundleContext context) {
return createAndOpenSwissTimingReplayServiceTracker(context).getService().createSwissTimingReplayService(swissTimingAdapter.getSwissTimingDomainFactory());
return createAndOpenSwissTimingReplayServiceTracker(context).getService().createSwissTimingReplayService(getSwissTimingAdapter().getSwissTimingDomainFactory());
}
protected SwissTimingAdapter getSwissTimingAdapter(BundleContext context) {
return createAndOpenSwissTimingAdapterTracker(context).getService().getOrCreateSwissTimingAdapter(baseDomainFactory, swissTimingAdapterPersistence);
protected SwissTimingAdapter getSwissTimingAdapter() {
return swissTimingAdapterTracker.getService().getOrCreateSwissTimingAdapter(baseDomainFactory, swissTimingAdapterPersistence);
}
protected TracTracAdapter getTracTracAdapter(BundleContext context) {
return createAndOpenTracTracAdapterTracker(context).getService().getOrCreateTracTracAdapter(baseDomainFactory);
protected TracTracAdapter getTracTracAdapter() {
return tractracAdapterTracker.getService().getOrCreateTracTracAdapter(baseDomainFactory);
}
protected IgtimiConnectionFactory getIgtimiConnectionFactory(BundleContext context) {
return createAndOpenIgtimiTracker(context).getService();
}
protected ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> createAndOpenTracTracAdapterTracker(BundleContext context) {
@@ -424,6 +440,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return result;
}
protected ServiceTracker<IgtimiConnectionFactory, IgtimiConnectionFactory> createAndOpenIgtimiTracker(BundleContext context) {
ServiceTracker<IgtimiConnectionFactory, IgtimiConnectionFactory> result = new ServiceTracker<IgtimiConnectionFactory, IgtimiConnectionFactory>(
context, IgtimiConnectionFactory.class.getName(), null);
result.open();
return result;
}
protected ServiceTracker<SwissTimingAdapterFactory, SwissTimingAdapterFactory> createAndOpenSwissTimingAdapterTracker(
BundleContext context) {
ServiceTracker<SwissTimingAdapterFactory, SwissTimingAdapterFactory> result = new ServiceTracker<SwissTimingAdapterFactory, SwissTimingAdapterFactory>(
@@ -895,14 +918,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
}
private TracTracAdapter getTracTracAdapter() {
return tracTracAdapter;
}
private SwissTimingAdapter getSwissTimingAdapter() {
return swissTimingAdapter;
}
private SwissTimingReplayService getSwissTimingReplayService() {
return swissTimingReplayService;
}
@@ -921,7 +936,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public void storeTracTracConfiguration(String name, String jsonURL, String liveDataURI, String storedDataURI, String courseDesignUpdateURI, String tracTracUsername, String tracTracPassword) throws Exception {
tractracMongoObjectFactory.storeTracTracConfiguration(tracTracAdapter.createTracTracConfiguration(name, jsonURL, liveDataURI, storedDataURI,
tractracMongoObjectFactory.storeTracTracConfiguration(getTracTracAdapter().createTracTracConfiguration(name, jsonURL, liveDataURI, storedDataURI,
courseDesignUpdateURI, tracTracUsername, tracTracPassword));
}
@@ -1198,7 +1213,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
if (trackedRace != null) {
TimePoint fromTimePoint = from == null ? trackedRace.getStartOfTracking() : new MillisecondsTimePoint(from);
TimePoint toTimePoint = to == null ? trackedRace.getEndOfRace() : new MillisecondsTimePoint(to);
if(fromTimePoint != null && toTimePoint != null) {
if (fromTimePoint != null && toTimePoint != null) {
int numberOfFixes = (int) ((toTimePoint.asMillis() - fromTimePoint.asMillis())/resolutionInMilliseconds);
result = getAveragedWindInfo(fromTimePoint, resolutionInMilliseconds, numberOfFixes, windSourceTypeNames, trackedRace);
}
@@ -2133,7 +2148,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
List<SwissTimingRaceRecordDTO> result = new ArrayList<SwissTimingRaceRecordDTO>();
for (com.sap.sailing.domain.swisstimingadapter.RaceRecord rr : getSwissTimingAdapter().getSwissTimingRaceRecords(hostname, port, canSendRequests)) {
SwissTimingRaceRecordDTO swissTimingRaceRecordDTO = new SwissTimingRaceRecordDTO(rr.getRaceID(), rr.getDescription(), rr.getStartTime());
BoatClass boatClass = swissTimingAdapter.getSwissTimingDomainFactory().getRaceTypeFromRaceID(rr.getRaceID()).getBoatClass();
BoatClass boatClass = getSwissTimingAdapter().getSwissTimingDomainFactory().getRaceTypeFromRaceID(rr.getRaceID()).getBoatClass();
swissTimingRaceRecordDTO.boatClass = boatClass != null ? boatClass.getName() : null;
swissTimingRaceRecordDTO.discipline = rr.getRaceID().length() >= 3 ? rr.getRaceID().substring(2, 3) : null;
swissTimingRaceRecordDTO.hasCourse = rr.hasCourse();
@@ -2256,51 +2271,61 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
course.lockForRead(); // make sure the tracked leg survives this call even if a course update is pending
try {
TrackedLegOfCompetitor trackedLeg = trackedRace.getTrackedLeg(competitor, timePoint);
if (trackedLeg != null) {
switch (dataType) {
case CURRENT_SPEED_OVER_GROUND_IN_KNOTS:
SpeedWithBearing speedOverGround = trackedLeg.getSpeedOverGround(timePoint);
switch (dataType) {
case CURRENT_SPEED_OVER_GROUND_IN_KNOTS:
final GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
if (track != null) {
SpeedWithBearing speedOverGround = track.getEstimatedSpeed(timePoint);
result = (speedOverGround == null) ? null : speedOverGround.getKnots();
break;
case VELOCITY_MADE_GOOD_IN_KNOTS:
}
break;
case VELOCITY_MADE_GOOD_IN_KNOTS:
if (trackedLeg != null) {
Speed velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint);
result = (velocityMadeGood == null) ? null : velocityMadeGood.getKnots();
break;
case DISTANCE_TRAVELED:
}
break;
case DISTANCE_TRAVELED:
if (trackedLeg != null) {
Distance distanceTraveled = trackedRace.getDistanceTraveled(competitor, timePoint);
result = distanceTraveled == null ? null : distanceTraveled.getMeters();
break;
case GAP_TO_LEADER_IN_SECONDS:
}
break;
case GAP_TO_LEADER_IN_SECONDS:
if (trackedLeg != null) {
result = trackedLeg.getGapToLeaderInSeconds(timePoint);
break;
case WINDWARD_DISTANCE_TO_OVERALL_LEADER:
}
break;
case WINDWARD_DISTANCE_TO_OVERALL_LEADER:
if (trackedLeg != null) {
Distance distanceToLeader = trackedLeg.getWindwardDistanceToOverallLeader(timePoint);
result = (distanceToLeader == null) ? null : distanceToLeader.getMeters();
break;
case RACE_RANK:
result = (double) trackedLeg.getRank(timePoint);
break;
case REGATTA_RANK:
if (leaderboardName == null || leaderboardName.isEmpty()) {
break;
}
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
result = leaderboard == null ? null : (double) leaderboard.getTotalRankOfCompetitor(competitor, timePoint);
break;
case OVERALL_RANK:
if (leaderboardGroupName == null || leaderboardGroupName.isEmpty()) {
break;
}
LeaderboardGroup group = getService().getLeaderboardGroupByName(leaderboardGroupName);
Leaderboard overall = group.getOverallLeaderboard();
result = overall == null ? null : (double) overall.getTotalRankOfCompetitor(competitor, timePoint);
break;
default:
throw new UnsupportedOperationException("Theres currently no support for the enum value '"
+ dataType + "' in this method.");
}
break;
case RACE_RANK:
if (trackedLeg != null) {
result = (double) trackedLeg.getRank(timePoint);
}
break;
case REGATTA_RANK:
if (leaderboardName == null || leaderboardName.isEmpty()) {
break;
}
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
result = leaderboard == null ? null : (double) leaderboard.getTotalRankOfCompetitor(competitor,
timePoint);
break;
case OVERALL_RANK:
if (leaderboardGroupName == null || leaderboardGroupName.isEmpty()) {
break;
}
LeaderboardGroup group = getService().getLeaderboardGroupByName(leaderboardGroupName);
Leaderboard overall = group.getOverallLeaderboard();
result = overall == null ? null : (double) overall.getTotalRankOfCompetitor(competitor, timePoint);
break;
default:
throw new UnsupportedOperationException("Theres currently no support for the enum value '" + dataType
+ "' in this method.");
}
return result;
} finally {
@@ -2576,10 +2601,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
@Override
public TrackedRace getTrackedRace(RegattaAndRaceIdentifier regattaNameAndRaceName) {
public DynamicTrackedRace getTrackedRace(RegattaAndRaceIdentifier regattaNameAndRaceName) {
Regatta regatta = getService().getRegattaByName(regattaNameAndRaceName.getRegattaName());
RaceDefinition race = getRaceByName(regatta, regattaNameAndRaceName.getRaceName());
TrackedRace trackedRace = getService().getOrCreateTrackedRegatta(regatta).getTrackedRace(race);
DynamicTrackedRace trackedRace = getService().getOrCreateTrackedRegatta(regatta).getTrackedRace(race);
return trackedRace;
}
@@ -2784,8 +2809,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return getEventById(eventUuid);
}
@Override
public EventDTO getEventById(Serializable id) {
private EventDTO getEventById(UUID id) {
EventDTO result = null;
Event event = getService().getEvent(id);
if (event != null) {
@@ -3580,4 +3604,78 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return new Pair<Date, Integer>(result.getA().asDate(), result.getB());
}
@Override
public Iterable<String> getAllIgtimiAccountEmailAddresses() {
List<String> result = new ArrayList<String>();
for (Account account : getIgtimiConnectionFactory().getAllAccounts()) {
result.add(account.getUser().getEmail());
}
return result;
}
private IgtimiConnectionFactory getIgtimiConnectionFactory() {
return igtimiAdapterTracker.getService();
}
@Override
public String getIgtimiAuthorizationUrl() {
return getIgtimiConnectionFactory().getAuthorizationUrl();
}
@Override
public boolean authorizeAccessToIgtimiUser(String eMailAddress, String password) throws Exception {
Account account = getIgtimiConnectionFactory().createAccountToAccessUserData(eMailAddress, password);
return account != null;
}
@Override
public void removeIgtimiAccount(String eMailOfAccountToRemove) {
getIgtimiConnectionFactory().removeAccount(eMailOfAccountToRemove);
}
@Override
public Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces) throws IllegalStateException,
ClientProtocolException, IOException, org.json.simple.parser.ParseException {
final IgtimiConnectionFactory igtimiConnectionFactory = getIgtimiConnectionFactory();
final Iterable<DynamicTrackedRace> trackedRaces;
if (selectedRaces != null && !selectedRaces.isEmpty()) {
List<DynamicTrackedRace> myTrackedRaces = new ArrayList<DynamicTrackedRace>();
trackedRaces = myTrackedRaces;
for (RaceDTO raceDTO : selectedRaces) {
DynamicTrackedRace trackedRace = getTrackedRace(raceDTO.getRaceIdentifier());
myTrackedRaces.add(trackedRace);
}
} else {
trackedRaces = getAllTrackedRaces();
}
Map<RegattaAndRaceIdentifier, Integer> numberOfWindFixesImportedPerRace = new HashMap<RegattaAndRaceIdentifier, Integer>();
for (Account account : igtimiConnectionFactory.getAllAccounts()) {
IgtimiConnection conn = igtimiConnectionFactory.connect(account);
Map<TrackedRace, Integer> resultsForAccounts = conn.importWindIntoRace(trackedRaces);
for (Entry<TrackedRace, Integer> resultForAccount : resultsForAccounts.entrySet()) {
RegattaAndRaceIdentifier key = resultForAccount.getKey().getRaceIdentifier();
Integer i = numberOfWindFixesImportedPerRace.get(key);
if (i == null) {
i = 0;
}
numberOfWindFixesImportedPerRace.put(key, i+resultForAccount.getValue());
}
}
return numberOfWindFixesImportedPerRace;
}
private Set<DynamicTrackedRace> getAllTrackedRaces() {
Set<DynamicTrackedRace> result = new HashSet<DynamicTrackedRace>();
Iterable<Regatta> allRegattas = getService().getAllRegattas();
for (Regatta regatta : allRegattas) {
DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta);
if (trackedRegatta != null) {
Iterable<TrackedRace> trackedRaces = trackedRegatta.getTrackedRaces();
for (TrackedRace trackedRace : trackedRaces) {
result.add((DynamicTrackedRace) trackedRace);
}
}
}
return result;
}
}
@@ -7,7 +7,6 @@
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<inherits name='com.google.gwt.debug.Debug'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="lib" path="lib/cglib-nodep-2.2.3.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -9,5 +9,3 @@ Require-Bundle: org.junit4;bundle-version="4.8.2",
org.openqa.selenium.osgi;bundle-version="2.34.0",
com.sap.sailing.mongodb;bundle-version="1.0.0",
com.mongodb.driver;bundle-version="2.6.2"
Bundle-ClassPath: lib/cglib-nodep-2.2.3.jar,
.
@@ -3,5 +3,4 @@ source.. = src/,\
output.. = bin/
bin.includes = META-INF/,\
.,\
test-environment-configuration.xsd,\
lib/cglib-nodep-2.2.3.jar
test-environment-configuration.xsd
View File
File mode changed.
@@ -12,5 +12,5 @@
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.selenium.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dselenium.test.environment.configuration=local-test-environment.xml -Dgwt.codesvr=127.0.0.1:9997"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dselenium.test.environment.configuration=local-test-environment.xml -Dgwt.codesvr=gwt.codesvr=127.0.0.1:9997 -Dwebdriver.firefox.profile=Selenium"/>
</launchConfiguration>
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.selenium.test"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.selenium.test"/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.selenium.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dselenium.test.environment.configuration=local-test-environment.xml -Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -Dgwt.codesvr=127.0.0.1:9997 -Dwebdriver.firefox.profile=Selenium"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.selenium.test"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.selenium.test"/>
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.selenium.test"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dselenium.test.environment.configuration=local-test-environment.xml -Dhttp.proxyHost=proxy -Dhttp.proxyPort=8080 -Dgwt.codesvr=gwt.codesvr=127.0.0.1:9997 -Dwebdriver.firefox.profile=Selenium"/>
</launchConfiguration>
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Configuration file for the test environment. This file is used for the configuration of the environment for the
executions of the integration tests. For the execution in a local environment you should make a copy of this file,
change it accordingly to your local setup (eg. browser(s) to use for the tests) and adjust the property
"parameters.integration-tests" in your user specific settings.xml to point to your local copy of this file.
-->
<test-environment xmlns="http://www.sapsailing.com/test-environment"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sapsailing.com/test-environment ./test-environment-configuration.xsd ">
<!--
The context root (the server) against which the tests should be executed.
-->
<context-root>http://riccardo:riccardo@localhost:8889/</context-root>
<!--
<screenshots-folder>file:///C:/Users/D049941/screenshots/</screenshots-folder>
-->
<!--
In this section necessary system properties for the different web drivers can be provided if needed. For more
information about available properties see the following sides:
- http://code.google.com/p/selenium/wiki/ChromeDriver
- http://code.google.com/p/selenium/wiki/FirefoxDriver
- http://code.google.com/p/selenium/wiki/InternetExplorerDriver
- http://code.google.com/p/selenium/wiki/OperaDriver
- http://code.google.com/p/selenium/wiki/SafariDriver
- http://code.google.com/p/selenium/wiki/RemoteWebDriverServer
NOTE: For the Chrome and the InternetExplorer driver an additional "server", which acts as a bridge between the
browser and the driver, is needed.
-->
<system-properties>
<system-property>
<name>webdriver.chrome.driver</name>
<value>C:\apps\ChromeSeleniumDriver\chromedriver.exe</value>
</system-property>
<!--
<system-property>
<name>webdriver.firefox.bin</name>
<value>D:/Internet/Firefox/firefox.exe</value>
</system-property>
-->
<system-property>
<name>webdriver.firefox.profile</name>
<value>Selenium</value>
</system-property>
</system-properties>
<!--
The driver definitions define all web drivers (browsers) which should be used for the execution of the tests. This
means that each test will be executed once for each driver defined here. This allows to perform the tests with
different browsers to validate the proper functionality of the application across different browsers.
The capabilities section can be used for fine tuning of the driver (e.g. version). For more informations see the
pages mentioned above.
-->
<!-- -->
<driver-definition class="org.openqa.selenium.firefox.FirefoxDriver">
<!-- -->
<!--
<capabilities>
<capability>
<name>version</name>
<value>14.0.0</value>
</capability>
</capabilities>
-->
<!-- -->
</driver-definition>
<!-- -->
<!--
<driver-definition class="org.openqa.selenium.chrome.ChromeDriver" />
-->
</test-environment>
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Configuration file for the test environment. This file is used for the configuration of the environment for the
executions of the integration tests. For the execution in a local environment you should make a copy of this file,
change it accordingly to your local setup (eg. browser(s) to use for the tests) and adjust the property
"parameters.integration-tests" in your user specific settings.xml to point to your local copy of this file.
-->
<test-environment xmlns="http://www.sapsailing.com/test-environment"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sapsailing.com/test-environment ./test-environment-configuration.xsd ">
<!--
The context root (the server) against which the tests should be executed.
-->
<context-root>http://riccardo:riccardo@localhost:8085/</context-root>
<!--
<screenshots-folder>file:///C:/Users/D049941/screenshots/</screenshots-folder>
-->
<!--
In this section necessary system properties for the different web drivers can be provided if needed. For more
information about available properties see the following sides:
- http://code.google.com/p/selenium/wiki/ChromeDriver
- http://code.google.com/p/selenium/wiki/FirefoxDriver
- http://code.google.com/p/selenium/wiki/InternetExplorerDriver
- http://code.google.com/p/selenium/wiki/OperaDriver
- http://code.google.com/p/selenium/wiki/SafariDriver
- http://code.google.com/p/selenium/wiki/RemoteWebDriverServer
NOTE: For the Chrome and the InternetExplorer driver an additional "server", which acts as a bridge between the
browser and the driver, is needed.
-->
<system-properties>
<system-property>
<name>webdriver.chrome.driver</name>
<value>C:\apps\ChromeSeleniumDriver\chromedriver.exe</value>
</system-property>
<system-property>
<name>webdriver.firefox.bin</name>
<value>D:/Internet/Firefox/firefox.exe</value>
</system-property>
<system-property>
<name>webdriver.firefox.profile</name>
<value>Selenium</value>
</system-property>
</system-properties>
<!--
The driver definitions define all web drivers (browsers) which should be used for the execution of the tests. This
means that each test will be executed once for each driver defined here. This allows to perform the tests with
different browsers to validate the proper functionality of the application across different browsers.
The capabilities section can be used for fine tuning of the driver (e.g. version). For more informations see the
pages mentioned above.
-->
<!-- -->
<driver-definition class="org.openqa.selenium.firefox.FirefoxDriver">
<!-- -->
<!--
<capabilities>
<capability>
<name>version</name>
<value>14.0.0</value>
</capability>
</capabilities>
-->
<!-- -->
</driver-definition>
<!-- -->
<!--
<driver-definition class="org.openqa.selenium.chrome.ChromeDriver" />
-->
</test-environment>
+6 -10
View File
@@ -13,7 +13,7 @@
<!--
The context root (the server) against which the tests should be executed.
-->
<context-root>http://riccardo:riccardo@localhost:8889/</context-root>
<context-root>http://riccardo:riccardo@localhost:8888/</context-root>
<!--
<screenshots-folder>file:///C:/Users/D049941/screenshots/</screenshots-folder>
@@ -41,13 +41,9 @@
<!--
<system-property>
<name>webdriver.firefox.bin</name>
<value>D:/Internet/Firefox/firefox.exe</value>
<value>c:/Program Files (x86)/Mozilla Firefox/firefox.exe</value>
</system-property>
-->
<system-property>
<name>webdriver.firefox.profile</name>
<value>Selenium</value>
</system-property>
</system-properties>
@@ -60,8 +56,8 @@
pages mentioned above.
-->
<!-- -->
<driver-definition class="org.openqa.selenium.firefox.FirefoxDriver">
<!-- -->
<driver-definition class="org.openqa.selenium.firefox.FirefoxDriver">
<!-- -->
<!--
<capabilities>
<capability>
@@ -74,7 +70,7 @@
</driver-definition>
<!-- -->
<!--
<!--
<driver-definition class="org.openqa.selenium.chrome.ChromeDriver" />
-->
-->
</test-environment>
+5 -7
View File
@@ -73,13 +73,6 @@
</dependencies>
<bundleStartLevel>
<!-- This bundle should only be deployed in a test environment! -->
<bundle>
<id>com.sap.sailing.server.gatway.test.support</id>
<level>5</level>
<autoStart>true</autoStart>
</bundle>
<bundle>
<id>com.sap.sailing.ess40.resultimport</id>
<level>3</level>
@@ -132,6 +125,11 @@
<level>4</level>
<autoStart>true</autoStart>
</bundle>
<bundle>
<id>com.sap.sailing.domain.igtimiadapter</id>
<level>4</level>
<autoStart>true</autoStart>
</bundle>
<bundle>
<id>com.sap.sailing.xcelsiusadapter</id>
<level>4</level>
@@ -25,7 +25,7 @@ public class BySeleniumId extends By {
private String id;
/**
* <p>Creates a new location mechanism which searches for an element or a list of elements with the given
* <p>Creates a new location mechanism which search for an element or a list of elements with the given
* identifier.</p>
*
* @param id
@@ -6,10 +6,10 @@ import com.google.common.base.Function;
/**
* <p>Models a condition that might reasonably be expected to eventually evaluate to something that is neither
* {@code null} nor {@code false} using a search context. An example would be that an element is visible.</p>
* {@code null} nor {@code false} using a search context.</p>
*
* <p>Note that it is expected that conditions are idempotent. They will be called in a loop by the WebDriverWait
* and any modification of the state of the application under test may have unexpected side-effects.</p>
* <p>Note that it is expected that conditions are idempotent. They will be called in a loop by the and any modification
* of the state of the application under test may have unexpected side-effects.</p>
*
* @param <T>
* The return type
@@ -1,86 +0,0 @@
package com.sap.sailing.selenium.core;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.List;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.internal.Locatable;
import org.openqa.selenium.internal.WrapsElement;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import org.openqa.selenium.support.pagefactory.FieldDecorator;
import org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler;
import org.openqa.selenium.support.pagefactory.internal.LocatingElementListHandler;
public class SeleniumFieldDecorator implements FieldDecorator {
private ElementLocatorFactory factory;
public SeleniumFieldDecorator(ElementLocatorFactory factory) {
this.factory = factory;
}
@Override
public Object decorate(ClassLoader loader, Field field) {
ElementLocator locator = this.factory.createLocator(field);
if (locator == null) {
return null;
}
if (isDecoratableField(field)) {
return proxyForElementLocator(loader, locator);
}
if (isDecoratableList(field)) {
return proxyForElementsLocator(loader, locator);
}
return null;
}
private boolean isDecoratableField(Field field) {
return WebElement.class.isAssignableFrom(field.getType());
}
private boolean isDecoratableList(Field field) {
if (!List.class.isAssignableFrom(field.getType())) {
return false;
}
// Type erasure in Java isn't complete. Attempt to discover the generic type of the list.
Type genericType = field.getGenericType();
if (!(genericType instanceof ParameterizedType)) {
return false;
}
Type listType = ((ParameterizedType) genericType).getActualTypeArguments()[0];
if (!WebElement.class.equals(listType)) {
return false;
}
return true;
}
private Object proxyForElementLocator(ClassLoader loader, ElementLocator locator) {
InvocationHandler handler = new LocatingElementHandler(locator);
Object proxy = Proxy.newProxyInstance( loader, new Class[] {WebElement.class, WrapsElement.class, Locatable.class}, handler);
return proxy;
}
private Object proxyForElementsLocator(ClassLoader loader, ElementLocator locator) {
InvocationHandler handler = new LocatingElementListHandler(locator);
Object proxy = Proxy.newProxyInstance(loader, new Class[] {List.class}, handler);
return proxy;
}
}
@@ -21,15 +21,6 @@ public interface TestEnvironment {
*/
public WebDriver getWebDriver();
/**
* <p>Returns the window manager for the used web driver, which can be used to open new windows and switching
* between multiple windows.</p>
*
* @return
* The window manager for the used web driver.
*/
public WindowManager getWindowManager();
/**
* <p>Returns the context root (base URL) against the tests should be executed. The context root identifies the
* web application and usually consists of a protocol definition, the host and a path.</p>
@@ -2,16 +2,36 @@ package com.sap.sailing.selenium.core;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriver.TargetLocator;
import org.openqa.selenium.WebDriverException;
public class WebDriverWindow {
private static int instanceCount = 0;
private static final String JAVA_SCRIPT = "var anchorTag = document.createElement('a');" +
"anchorTag.setAttribute('id', '%s');" +
"anchorTag.setAttribute('href', '%s');" +
"anchorTag.setAttribute('target', '_blank');" +
"anchorTag.setAttribute('style', 'display:block;');" +
"anchorTag.appendChild(document.createTextNode('nwh'));" +
"document.getElementsByTagName('body')[0].appendChild(anchorTag);";
private static String createUniqueName() {
return "WebDriverWindow_" + instanceCount++;
}
private WebDriver driver;
private String name;
private String handle;
private String parent;
/**
* Creates a new window for given web driver
*
@@ -20,34 +40,80 @@ public class WebDriverWindow {
* @param url
* Initial URL to load
*/
protected WebDriverWindow(WebDriver driver, String handle) {
public WebDriverWindow(WebDriver driver, String url) {
this.driver = driver;
this.handle = handle;
this.name = createUniqueName();
createWindow(url);
// Switch to that window and load the url to wait
switchToWindow();//.get(url);
}
public String getWindowHandle() {
return this.handle;
}
public String getParentHandle() {
return this.parent;
}
public void close() {
WebDriver driver = switchToWindow();
driver.close();
this.handle = null;
switchToWindow().close();
this.handle = "";
// Switch back to the parent window
this.driver.switchTo().window(this.parent);
}
public WebDriver switchToWindow() {
checkForClosed();
TargetLocator locator = this.driver.switchTo();
return switchTo(this.handle);
}
public WebDriver switchToParent() {
checkForClosed();
return locator.window(this.handle);
return switchTo(this.parent);
}
private void createWindow(String url) {
// Record old handles
Set<String> oldHandles = this.driver.getWindowHandles();
this.parent = this.driver.getWindowHandle();
// Inject an anchor element
injectAnchorTag(this.name, url);
// Click on the anchor element
this.driver.findElement(By.id(this.name)).click();
this.handle = getNewHandle(oldHandles);
}
private String getNewHandle(Set<String> oldHandles) {
Set<String> newHandles = this.driver.getWindowHandles();
newHandles.removeAll(oldHandles);
// Find the new window
for(String handle : newHandles)
return handle;
return null;
}
private void checkForClosed() {
Set<String> handles = this.driver.getWindowHandles();
if(this.handle == null || this.handle.equals(""))
throw new WebDriverException("Web Window closed or not initialized");
}
private void injectAnchorTag(String id, String url) {
JavascriptExecutor executor = (JavascriptExecutor) this.driver;
executor.executeScript(String.format(JAVA_SCRIPT, id, url));
}
private WebDriver switchTo(String handle) {
TargetLocator locator = this.driver.switchTo();
if(!handles.contains(this.handle))
throw new WebDriverException("Window closed or not initialized"); //$NON-NLS-1$
return locator.window(handle);
}
}
@@ -1,123 +0,0 @@
package com.sap.sailing.selenium.core;
import java.util.Set;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriver.TargetLocator;
/**
* <p></p>
*
* @author
* Riccardo Nimser (D049941)
*/
public class WindowManager {
private static final String JAVA_SCRIPT = "window.open('%s', '_blank')";
private WebDriver driver;
/**
* <p></p>
*
* @param driver
*
*/
public WindowManager(WebDriver driver) {
this.driver = driver;
}
/**
* <p></p>
*
* @param url
*/
public void switchTo(String url) {
WebDriverWindow window = findWindow(url);
if(window == null)
throw new RuntimeException("Window not found");
window.switchToWindow();
}
/**
* <p>Returns a handle to the window witch is currently active.</p>
*
* @return
*
*/
public WebDriverWindow getCurrentWindow() {
return new WebDriverWindow(this.driver, this.driver.getWindowHandle());
}
/**
* <p></p>
*
* <p>Note: If no window is found with the specified URL, </p>
*
* @param url
*
* @return
*
*/
public WebDriverWindow findWindow(String url) {
if(url == null)
throw new IllegalArgumentException();
TargetLocator locator = this.driver.switchTo();
for(String handle : this.driver.getWindowHandles()) {
locator.window(handle);
if(url.equals(this.driver.getCurrentUrl()))
return new WebDriverWindow(this.driver, handle);
}
return null;
}
/**
* <p></p>
*
* @return
*/
public WebDriverWindow openNewWindow() {
return openNewWindow("");
}
/**
* <p></p>
*
* @param url
* @return
*/
public WebDriverWindow openNewWindow(String url) {
return new WebDriverWindow(this.driver, createWindow(url));
}
private String createWindow(String url) {
// Record old handles
Set<String> oldHandles = this.driver.getWindowHandles();
executeScript(url != null ? url : "");
return getNewHandle(oldHandles);
}
private void executeScript(String url) {
JavascriptExecutor executor = (JavascriptExecutor) this.driver;
executor.executeScript(String.format(JAVA_SCRIPT, url));
}
private String getNewHandle(Set<String> oldHandles) {
Set<String> newHandles = this.driver.getWindowHandles();
newHandles.removeAll(oldHandles);
// Find the new window
for(String handle : newHandles)
return handle;
return null;
}
}
@@ -5,7 +5,6 @@ import java.net.URL;
import org.openqa.selenium.WebDriver;
import com.sap.sailing.selenium.core.TestEnvironment;
import com.sap.sailing.selenium.core.WindowManager;
/**
* <p>Default implementation of the test environment.</p>
@@ -15,7 +14,6 @@ import com.sap.sailing.selenium.core.WindowManager;
*/
public class TestEnvironmentImpl implements TestEnvironment {
private WebDriver driver;
private WindowManager manager;
private String root;
private URL screenshots;
@@ -33,8 +31,6 @@ public class TestEnvironmentImpl implements TestEnvironment {
this.driver = driver;
this.root = root;
this.screenshots = screenshots;
this.manager = new WindowManager(driver);
}
@Override
@@ -42,11 +38,6 @@ public class TestEnvironmentImpl implements TestEnvironment {
return this.driver;
}
@Override
public WindowManager getWindowManager() {
return this.manager;
}
@Override
public String getContextRoot() {
return this.root;
@@ -1,78 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.NoAlertPresentException;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriver.TargetLocator;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.FluentWait;
import com.google.common.base.Function;
import com.sap.sailing.selenium.pages.PageObject;
public class Actions {
private static final String ACTION_XPATH = ".//descendant::div[@name='%s']/img";
public static final String EDIT_ACTION = "ACTION_EDIT";
public static final String REMOVE_ACTION = "ACTION_REMOVE";
public static final String EDIT_SCORES_ACTION = "ACTION_EDIT_SCORES";
public static final String CONFIGURE_URL_ACTION = "ACTION_CONFIGURE_URL";
public static final String UNLINK_RACE_ACTION = "ACTION_UNLINK";
public static final String REFRESH_RACE_ACTION = "ACTION_REFRESH_RACE";
public static WebElement findEditAction(SearchContext context) {
return context.findElement(By.xpath(String.format(ACTION_XPATH, EDIT_ACTION)));
}
public static WebElement findRemoveAction(SearchContext context) {
return context.findElement(By.xpath(String.format(ACTION_XPATH, REMOVE_ACTION)));
}
public static WebElement findEditScoresAction(SearchContext context) {
return context.findElement(By.xpath(String.format(ACTION_XPATH, EDIT_SCORES_ACTION)));
}
public static WebElement findConfigureUrlAction(SearchContext context) {
return context.findElement(By.xpath(String.format(ACTION_XPATH, CONFIGURE_URL_ACTION)));
}
public static WebElement findUnlinkRaceAction(SearchContext context) {
return context.findElement(By.xpath(String.format(ACTION_XPATH, UNLINK_RACE_ACTION)));
}
public static WebElement findRefreshAction(SearchContext context) {
return context.findElement(By.xpath(String.format(ACTION_XPATH, REFRESH_RACE_ACTION)));
}
public static void acceptAlert(WebDriver driver) {
Alert alert = getAlert(driver);
alert.accept();
}
public static void dismissAlert(WebDriver driver) {
Alert alert = getAlert(driver);
alert.dismiss();
}
public static Alert getAlert(WebDriver driver) {
FluentWait<WebDriver> wait = PageObject.createFluentWait(driver, NoAlertPresentException.class);
Alert alert = wait.until(new Function<WebDriver, Alert>() {
@Override
public Alert apply(WebDriver driver) {
TargetLocator locator = driver.switchTo();
return locator.alert();
}
});
return alert;
}
}
@@ -1,24 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.leaderboard;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.common.DataEntryDialog;
public class FlexibleLeaderboardCreateDialog extends DataEntryDialog {
@FindBy(how = BySeleniumId.class, using = "LeaderboardNameField")
private WebElement nameField;
public FlexibleLeaderboardCreateDialog(WebDriver driver, WebElement element) {
super(driver, element);
}
public void setName(String name) {
this.nameField.clear();
this.nameField.sendKeys(name);
}
}
@@ -1,143 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.leaderboard;
import java.util.ArrayList;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.PageArea;
import com.sap.sailing.selenium.pages.adminconsole.Actions;
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaList.RegattaDescriptor;
import com.sap.sailing.selenium.pages.gwt.CellTable;
import com.sap.sailing.selenium.pages.gwt.DataEntry;
import com.sap.sailing.selenium.pages.gwt.GenericCellTable;
public class LeaderboardConfigurationPanel extends PageArea {
@FindBy(how = BySeleniumId.class, using = "CreateFlexibleLeaderboardButton")
private WebElement createFlexibleLeaderboardButton;
@FindBy(how = BySeleniumId.class, using = "CreateRegattaLeaderboardButton")
private WebElement createRegattaLeaderboardButton;
@FindBy(how = BySeleniumId.class, using = "AvailableLeaderboardsTable")
private WebElement availableLeaderboardsTable;
@FindBy(how = BySeleniumId.class, using = "LeaderboardDetails")
private WebElement leaderboardDetails;
@FindBy(how = BySeleniumId.class, using = "TrackedRacesList")
private WebElement trackedRacesList;
public LeaderboardConfigurationPanel(WebDriver driver, WebElement element) {
super(driver, element);
}
public FlexibleLeaderboardCreateDialog startCreatingFlexibleLeaderboard() {
this.createFlexibleLeaderboardButton.click();
// Wait, since we trigger an AJAX-request to get the available events
waitForAjaxRequests();
WebElement dialog = findElementBySeleniumId(this.driver, "FlexibleLeaderboardCreateDialog");
return new FlexibleLeaderboardCreateDialog(this.driver, dialog);
}
// TODO: Extend with display name, scoring system, event, course area and discars
public void createFlexibleLeaderboard(String name) {
FlexibleLeaderboardCreateDialog dialog = startCreatingFlexibleLeaderboard();
dialog.setName(name);
dialog.pressOk();
}
public RegattaLeaderboardCreateDialog startCreatingRegattaLeaderboard() {
this.createRegattaLeaderboardButton.click();
WebElement dialog = findElementBySeleniumId(this.driver, "RegattaLeaderboardCreateDialog");
return new RegattaLeaderboardCreateDialog(this.driver, dialog);
}
public void createRegattaLeaderboard(RegattaDescriptor regatta) {
RegattaLeaderboardCreateDialog dialog = startCreatingRegattaLeaderboard();
dialog.selectRegatta(regatta);
dialog.pressOk();
}
public void deleteLeaderboard(String leaderboard) {
CellTable<DataEntry> table = getLeaderboardTable();
List<DataEntry> entries = table.getEntries();
for(DataEntry entry : entries) {
String name = entry.getColumnContent(0);
if(!leaderboard.equals(name)) {
continue;
}
WebElement removeAction = Actions.findRemoveAction(entry.getWebElement());
removeAction.click();
Actions.acceptAlert(this.driver);
waitForAjaxRequests();
}
}
public String getLeaderboardURL(String leaderboard) {
CellTable<DataEntry> table = getLeaderboardTable();
List<DataEntry> entries = table.getEntries();
for(DataEntry entry : entries) {
WebElement name = entry.getWebElement().findElement(By.xpath(".//td/div/a"));
if(!leaderboard.equals(name.getText()))
continue;
return name.getAttribute("href");
}
return null;
}
public List<String> getAvailableLeaderboards() {
List<String> leaderboards = new ArrayList<>();
CellTable<DataEntry> table = getLeaderboardTable();
List<DataEntry> entries = table.getEntries();
for(DataEntry entry : entries) {
leaderboards.add(entry.getColumnContent(0));
}
return leaderboards;
}
public LeaderboardDetails getLeaderboardDetails(String leaderboard) {
CellTable<DataEntry> table = getLeaderboardTable();
List<DataEntry> entries = table.getEntries();
for(DataEntry entry : entries) {
String name = entry.getColumnContent(0);
if(leaderboard.equals(name)) {
entry.select();
return new LeaderboardDetails(this.driver, this.leaderboardDetails);
}
}
return null;
}
private CellTable<DataEntry> getLeaderboardTable() {
return new GenericCellTable<>(this.driver, this.availableLeaderboardsTable, DataEntry.class);
}
}
@@ -1,202 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.leaderboard;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.openqa.selenium.By.ByXPath;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.PageArea;
import com.sap.sailing.selenium.pages.adminconsole.Actions;
import com.sap.sailing.selenium.pages.adminconsole.tracking.TrackedRacesList.TrackedRaceDescriptor;
import com.sap.sailing.selenium.pages.gwt.CellTable;
import com.sap.sailing.selenium.pages.gwt.DataEntry;
import com.sap.sailing.selenium.pages.gwt.GenericCellTable;
public class LeaderboardDetails extends PageArea {
public static class RaceDescriptor {
private final String name;
private final String fleet;
private final boolean medalRace;
private final boolean linked;
private final double factor;
public RaceDescriptor(String name, String fleet, boolean medalRace, boolean linked, double factor) {
this.name = name;
this.fleet = fleet;
this.medalRace = medalRace;
this.linked = linked;
this.factor = factor;
}
public String getName() {
return this.name;
}
public String getFleet() {
return this.fleet;
}
public boolean isMedalRace() {
return this.medalRace;
}
public boolean isLinked() {
return this.linked;
}
public double getFactor() {
return this.factor;
}
@Override
public int hashCode() {
return Objects.hash(this.name, this.fleet);
}
@Override
public boolean equals(Object object) {
if (this == object)
return true;
if (object == null)
return false;
if (getClass() != object.getClass())
return false;
RaceDescriptor other = (RaceDescriptor) object;
if(!Objects.equals(this.name, other.name))
return false;
if(!Objects.equals(this.fleet, other.fleet))
return false;
return true;
}
}
@FindBy(how = BySeleniumId.class, using = "RacesTable")
private WebElement racesTable;
@FindBy(how = ByXPath.class, using = "./../..//*[@selenium-id='TrackedRacesPanel']")
private WebElement trackedRacesPanel;
public LeaderboardDetails(WebDriver driver, WebElement element) {
super(driver, element);
}
public List<RaceDescriptor> getRaces() {
List<RaceDescriptor> result = new ArrayList<>();
CellTable<DataEntry> racesTable = getRacesTable();
for(DataEntry entry : racesTable.getEntries()) {
String name = entry.getColumnContent(0);
String fleet = entry.getColumnContent(1);
//String medalRace = entry.getColumnContent(2);
//String linked = entry.getColumnContent(3);
//String factor = entry.getColumnContent(4);
result.add(new RaceDescriptor(name, fleet, false, false, 0.0));
}
return result;
}
// public RaceEditDialog editRace(RaceDescriptor race) {
//
// WebElement action = Actions.findEditAction(findRace(race));
//
// action.click();
// }
public void unlinkRace(RaceDescriptor race) {
DataEntry entry = findRace(race);
if(entry != null) {
WebElement action = Actions.findUnlinkRaceAction(entry.getWebElement());
action.click();
}
}
public void refreshRaceLog(RaceDescriptor race) {
DataEntry entry = findRace(race);
if(entry != null) {
WebElement action = Actions.findRefreshAction(entry.getWebElement());
action.click();
// QUESTION [D049941]: What else can happen?
Actions.acceptAlert(this.driver);
}
}
public void linkRace(RaceDescriptor race, TrackedRaceDescriptor tracking) {
DataEntry raceRow = findRace(race);
DataEntry trackingRow = findTracking(tracking);
if(raceRow == null || trackingRow == null) {
}
raceRow.select();
trackingRow.select();
}
private DataEntry findRace(RaceDescriptor race) {
CellTable<DataEntry> racesTable = getRacesTable();
for(DataEntry entry : racesTable.getEntries()) {
String name = entry.getColumnContent(0);
String fleet = entry.getColumnContent(1);
//String medalRace = entry.getColumnContent(2);
//String linked = entry.getColumnContent(3);
//String factor = entry.getColumnContent(4);
RaceDescriptor descriptor = new RaceDescriptor(name, fleet, false, false, 0.0);
if(descriptor.equals(race))
return entry;
}
return null;
}
private DataEntry findTracking(TrackedRaceDescriptor tracking) {
CellTable<DataEntry> trackingTable = getTrackedRacesTable();
for(DataEntry entry : trackingTable.getEntries()) {
String regatta = entry.getColumnContent(0);
String boatClass = entry.getColumnContent(1);
String raceName = entry.getColumnContent(2);
TrackedRaceDescriptor descriptor = new TrackedRaceDescriptor(regatta, boatClass, raceName);
if(descriptor.equals(tracking))
return entry;
}
return null;
}
private CellTable<DataEntry> getRacesTable() {
return new GenericCellTable<>(this.driver, this.racesTable, DataEntry.class);
}
private CellTable<DataEntry> getTrackedRacesTable() {
return new GenericCellTable<>(this.driver, findElementBySeleniumId(this.trackedRacesPanel, "TrackedRacesTable"), DataEntry.class);
}
}
@@ -1,92 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.leaderboard;
import static com.sap.sailing.selenium.pages.gwt.query.Alias.$;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.PageArea;
import com.sap.sailing.selenium.pages.adminconsole.Actions;
import com.sap.sailing.selenium.pages.gwt.CellTable;
import com.sap.sailing.selenium.pages.gwt.DataEntry;
import com.sap.sailing.selenium.pages.gwt.GenericCellTable;
import com.sap.sailing.selenium.pages.gwt.query.Alias;
import com.sap.sailing.selenium.pages.gwt.query.TableQuery;
public class LeaderboardGroupConfigurationPanel extends PageArea {
@FindBy(how = BySeleniumId.class, using = "CreateLeaderboardGroupButton")
private WebElement createLeaderboardGroupButton;
@FindBy(how = BySeleniumId.class, using = "LeaderboardGroupsTable")
private WebElement leaderboardGroupsTable;
@FindBy(how = BySeleniumId.class, using = "LeaderboardsCaptionPanel")
private WebElement leaderboardsCaptionPanel;
@FindBy(how = BySeleniumId.class, using = "LeaderboardsTable")
private WebElement leaderboardsTable;
@FindBy(how = BySeleniumId.class, using = "RefreshLeaderboardsButton")
private WebElement refreshLeaderboardsButton;
public LeaderboardGroupConfigurationPanel(WebDriver driver, WebElement element) {
super(driver, element);
}
public LeaderboardGroupCreateDialog startCreatingLeaderboardGroup() {
this.createLeaderboardGroupButton.click();
WebElement dialog = findElementBySeleniumId(this.driver, "LeaderboardGroupCreateDialog");
return new LeaderboardGroupCreateDialog(this.driver, dialog);
}
public void createLeaderboardGroup(String name, String description) {
LeaderboardGroupCreateDialog dialog = startCreatingLeaderboardGroup();
dialog.setName(name);
dialog.setDescription(description);
dialog.pressOk();
}
public CellTable<DataEntry> getLeaderboardGroupsTable() {
return new GenericCellTable<DataEntry>(driver, leaderboardGroupsTable, DataEntry.class);
}
public void deleteLeaderboardGroup(String name) {
CellTable<DataEntry> table = getLeaderboardGroupsTable();
final DataEntry alias = Alias.alias(DataEntry.class);
TableQuery<DataEntry> query = new TableQuery<>();
query.from(table)
.where(
$(alias.getColumnContent("Name")).eq(name)
);
DataEntry result = query.firstResult();
if (result != null) {
Actions.findRemoveAction(result.getWebElement()).click();
Actions.acceptAlert(this.driver);
}
}
public void refreshLeaderboards() {
refreshLeaderboardsButton.click();
}
public CellTable<DataEntry> getLeaderboardsTable() {
return new GenericCellTable<DataEntry>(driver, leaderboardsTable, DataEntry.class);
}
public DataEntry findLeaderboardEntry(String name) {
CellTable<DataEntry> table = getLeaderboardsTable();
final DataEntry alias = Alias.alias(DataEntry.class);
TableQuery<DataEntry> query = new TableQuery<>();
query.from(table).where($(alias.getColumnContent("Leaderboard Name")).eq(name));
return query.firstResult();
}
public void filterLeaderboards(String filterText) {
WebElement leaderboardFilterTextField = findElementBySeleniumId(leaderboardsCaptionPanel, "FilterTextField");
leaderboardFilterTextField.clear();
leaderboardFilterTextField.sendKeys(filterText);
}
}
@@ -1,32 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.leaderboard;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.common.DataEntryDialog;
public class LeaderboardGroupCreateDialog extends DataEntryDialog {
@FindBy(how = BySeleniumId.class, using = "NameTextBox")
private WebElement nameField;
@FindBy(how = BySeleniumId.class, using = "DescriptionTextBox")
private WebElement descriptionField;
public LeaderboardGroupCreateDialog(WebDriver driver, WebElement element) {
super(driver, element);
}
public void setName(String name) {
this.nameField.clear();
this.nameField.sendKeys(name);
}
public void setDescription(String description) {
this.descriptionField.clear();
this.descriptionField.sendKeys(description);
}
}
@@ -1,30 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.leaderboard;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaList.RegattaDescriptor;
import com.sap.sailing.selenium.pages.common.DataEntryDialog;
public class RegattaLeaderboardCreateDialog extends DataEntryDialog {
@FindBy(how = BySeleniumId.class, using = "LeaderboardNameField")
private WebElement nameField;
@FindBy(how = BySeleniumId.class, using = "RegattaDropDown")
private WebElement regattaDropDown;
public RegattaLeaderboardCreateDialog(WebDriver driver, WebElement element) {
super(driver, element);
}
public void selectRegatta(RegattaDescriptor regatta) {
Select dropDown = new Select(this.regattaDropDown);
dropDown.selectByValue(regatta.toString());
}
}
@@ -1,51 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.regatta;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.common.DataEntryDialog;
public class RegattaCreateDialog extends DataEntryDialog {
@FindBy(how = BySeleniumId.class, using = "RegattaNameTextField")
private WebElement regattaNameTextField;
@FindBy(how = BySeleniumId.class, using = "BoatClassTextField")
private WebElement boatClassTextField;
// @FindBy(how = BySeleniumId.class, using = "ScoringSystemDropDown")
// private WebElement scoringSystemDropDown;
// @FindBy(how = BySeleniumId.class, using = "EventDropDown")
// private WebElement eventDropDown;
// @FindBy(how = BySeleniumId.class, using = "CourseAreaDropDown")
// private WebElement courseAreaDropDown;
@FindBy(how = BySeleniumId.class, using = "AddSeriesButton")
private WebElement addSeriesButton;
public RegattaCreateDialog(WebDriver driver, WebElement element) {
super(driver, element);
}
public void setRegattaName(String name) {
this.regattaNameTextField.clear();
this.regattaNameTextField.sendKeys(name);
}
public void setBoatClass(String boatClass) {
this.boatClassTextField.clear();
this.boatClassTextField.sendKeys(boatClass);
}
// TODO: Scoring System, Event and Course Area
public SeriesCreateDialog addSeries() {
this.addSeriesButton.click();
WebElement dialog = findElementBySeleniumId(this.driver, "SeriesCreateDialog");
return new SeriesCreateDialog(this.driver, dialog);
}
}
@@ -1,102 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.regatta;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.PageArea;
import com.sap.sailing.selenium.pages.adminconsole.Actions;
import com.sap.sailing.selenium.pages.gwt.CellTable;
import com.sap.sailing.selenium.pages.gwt.DataEntry;
import com.sap.sailing.selenium.pages.gwt.GenericCellTable;
// TODO: Implement if needed
public class RegattaDetails extends PageArea {
@FindBy(how = BySeleniumId.class, using = "RegattaNameLabel")
private WebElement regattaNameLabel;
@FindBy(how = BySeleniumId.class, using = "BoatClassLabel")
private WebElement boatClassLabel;
@FindBy(how = BySeleniumId.class, using = "CourseAreaLabel")
private WebElement courseAreaLabel;
@FindBy(how = BySeleniumId.class, using = "ScoringSystemLabel")
private WebElement scoringSystemLabel;
@FindBy(how = BySeleniumId.class, using = "SeriesTable")
private WebElement seriesTable;
public RegattaDetails(WebDriver driver, WebElement element) {
super(driver, element);
}
public String getRegattaName() {
return this.regattaNameLabel.getText();
}
public String getBoatClass() {
return this.boatClassLabel.getText();
}
public String getCouresArea() {
return this.courseAreaLabel.getText();
}
public String getScoringSystem() {
return this.scoringSystemLabel.getText();
}
// public void getSeries() {
//
// }
public SeriesEditDialog editSeries(String series) {
DataEntry entry = findSeries(series);
if(entry != null) {
WebElement action = Actions.findEditAction(entry.getWebElement());
action.click();
WebElement dialog = findElementBySeleniumId(this.driver, "SeriesEditDialog");
return new SeriesEditDialog(this.driver, dialog);
}
return null;
}
public void deleteSeries(String series) {
DataEntry entry = findSeries(series);
if(entry != null) {
WebElement removeAction = Actions.findRemoveAction(entry.getWebElement());
removeAction.click();
Actions.acceptAlert(this.driver);
//waitForAjaxRequests();
}
}
private DataEntry findSeries(String series) {
CellTable<DataEntry> table = getSeriesTable();
for(DataEntry entry : table.getEntries()) {
String name = entry.getColumnContent(0);
if(series.equals(name))
return entry;
}
return null;
}
private CellTable<DataEntry> getSeriesTable() {
return new GenericCellTable<>(this.driver, this.seriesTable, DataEntry.class);
}
}
@@ -1,14 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.regatta;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.pages.PageArea;
public class RegattaEditDialog extends PageArea {
public RegattaEditDialog(WebDriver driver, WebElement element) {
super(driver, element);
}
}
@@ -1,168 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.regatta;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.PageArea;
import com.sap.sailing.selenium.pages.adminconsole.Actions;
import com.sap.sailing.selenium.pages.gwt.CellTable;
import com.sap.sailing.selenium.pages.gwt.CellTable.SortingOrder;
import com.sap.sailing.selenium.pages.gwt.DataEntry;
import com.sap.sailing.selenium.pages.gwt.GenericCellTable;
public class RegattaList extends PageArea {
public static class RegattaDescriptor {
private final String name;
private final String boatClass;
// TODO [D049941]: Mark as a bug and remove this method as soon as possible
public static RegattaDescriptor fromString(String name) {
if(name == null || name.isEmpty())
return null;
int openingBrace = name.lastIndexOf('(');
int closingBrace = name.lastIndexOf(')');
return new RegattaDescriptor(name.substring(0, openingBrace - 1),
name.substring(openingBrace + 1, closingBrace));
}
public RegattaDescriptor(String name, String boatClass) {
this.name = name;
this.boatClass = boatClass;
}
public String getName() {
return this.name;
}
public String getBoatClass() {
return this.boatClass;
}
public String toString() {
return this.name + " (" + this.boatClass + ")";
}
@Override
public int hashCode() {
return Objects.hash(this.name, this.boatClass);
}
@Override
public boolean equals(Object object) {
if (this == object)
return true;
if (object == null)
return false;
if (getClass() != object.getClass())
return false;
RegattaDescriptor other = (RegattaDescriptor) object;
if (!Objects.equals(this.boatClass, other.boatClass))
return false;
if (!Objects.equals(this.name, other.name))
return false;
return true;
}
}
@FindBy(how = BySeleniumId.class, using = "FilterRegattasTextField")
private WebElement filterRegattasTextField;
@FindBy(how = BySeleniumId.class, using = "NoRegattasLabel")
private WebElement noRegattasLabel;
@FindBy(how = BySeleniumId.class, using = "RegattasTable")
private WebElement regattasTable;
public RegattaList(WebDriver driver, WebElement element) {
super(driver, element);
}
public void sortByRegattaName(SortingOrder order) {
sortRegattaList(0, order);
}
public void sortByBoatClass(SortingOrder order) {
sortRegattaList(1, order);
}
public List<RegattaDescriptor> getRegattas() {
List<RegattaDescriptor> descriptors = new LinkedList<>();
CellTable<DataEntry> table = getRegattasTable();
for(DataEntry entry : table.getEntries()) {
String name = entry.getColumnContent(0);
String boatClass = entry.getColumnContent(1);
descriptors.add(new RegattaDescriptor(name, boatClass));
}
return descriptors;
}
public void selectRegatta(RegattaDescriptor regatta) {
DataEntry entry = findRegatta(regatta);
if(entry != null) {
entry.select();
}
}
// public EditRegattaDialog editRegatta(RegattaDescriptor regatta) {
//
// }
public void removeRegatta(RegattaDescriptor regatta) {
DataEntry entry = findRegatta(regatta);
if(entry != null) {
WebElement action = Actions.findRemoveAction(entry.getWebElement());
action.click();
waitForAjaxRequests();
Actions.acceptAlert(this.driver);
}
}
private DataEntry findRegatta(RegattaDescriptor regatta) {
CellTable<DataEntry> table = getRegattasTable();
for(DataEntry entry : table.getEntries()) {
String name = entry.getColumnContent(0);
// String boatClass = columns.getColumnContent(1);
//
// if(regatta.equals(new RegattaDescriptor(name, boatClass)))
// return row;
if(regatta.equals(RegattaDescriptor.fromString(name)))
return entry;
}
return null;
}
private void sortRegattaList(int column, SortingOrder order) {
CellTable<DataEntry> table = getRegattasTable();
table.sortByColumn(column, order);
}
private CellTable<DataEntry> getRegattasTable() {
return new GenericCellTable<>(this.driver, this.regattasTable, DataEntry.class);
}
}
@@ -1,89 +0,0 @@
package com.sap.sailing.selenium.pages.adminconsole.regatta;
import java.util.List;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.PageArea;
import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaList.RegattaDescriptor;
public class RegattaStructureManagementPanel extends PageArea {
public static final String DEFAULT_SERIES_NAME = "Default"; //$NON-NLS-1$
@FindBy(how = BySeleniumId.class, using = "AddRegattaButton")
WebElement addRegattaButton;
@FindBy(how = BySeleniumId.class, using = "RemoveRegattaButton")
WebElement removeRegattaButton;
@FindBy(how = BySeleniumId.class, using = "RegattaListSection")
WebElement regattaList;
@FindBy(how = BySeleniumId.class, using = "RegattaDetailsSection")
WebElement regattaDetails;
public RegattaStructureManagementPanel(WebDriver driver, WebElement element) {
super(driver, element);
}
public RegattaCreateDialog startRegattaCreation() {
this.addRegattaButton.click();
WebElement dialog = findElementBySeleniumId(this.driver, "RegattaCreateDialog"); //$NON-NLS-1$
return new RegattaCreateDialog(this.driver, dialog);
}
/**
* <p>Creates a regatta with 1 series (named "Default").</p>
*
* @param regatta
*/
public void createRegatta(RegattaDescriptor regatta) {
RegattaCreateDialog createRegattaDialog = startRegattaCreation();
createRegattaDialog.setRegattaName(regatta.getName());
createRegattaDialog.setBoatClass(regatta.getBoatClass());
SeriesCreateDialog addSeriesDialog = createRegattaDialog.addSeries();
addSeriesDialog.setSeriesName(DEFAULT_SERIES_NAME);
addSeriesDialog.pressOk();
// QUESTION: How do we handle an error (here or in the dialog)?
createRegattaDialog.pressOk();
}
public void removeRegatta(RegattaDescriptor regatta) {
// Select the regatta and click the remove button
}
public void removeRegattas(List<RegattaDescriptor> regattas) {
// Select all regattas and click the remove button
}
public RegattaList getRegattaList() {
return new RegattaList(this.driver, this.regattaList);
}
public void selectRegatta(RegattaDescriptor regatta) {
}
public RegattaDetails getRegattaDetails(RegattaDescriptor regatta) {
RegattaList regattaList = getRegattaList();
regattaList.selectRegatta(regatta);
return getRegattaDetails();
}
public RegattaDetails getRegattaDetails() {
if(this.regattaDetails.isDisplayed())
return new RegattaDetails(this.driver, this.regattaDetails);
return null;
}
}
Loaded 100 of 222 files, more files were not shown because too many files have changed in this diff. Show more