SEBSERV-347 transmit less data optimization

This commit is contained in:
anhefti 2022-10-31 14:42:40 +01:00
parent 971b130b93
commit 11792be0a9
19 changed files with 512 additions and 244 deletions

View file

@ -12,8 +12,12 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
@ -25,6 +29,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
import ch.ethz.seb.sebserver.gbl.monitoring.SimpleIndicatorValue;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -216,4 +221,59 @@ public class ClientConnectionData implements GrantEntity {
return builder.toString();
}
/** This is a wrapper for the live monitoring data view of this client connection data */
@JsonIgnore
public final ClientMonitoringDataView monitoringDataView = new ClientMonitoringDataView() {
@Override
public Long getId() {
return ClientConnectionData.this.clientConnection.id;
}
@Override
public ConnectionStatus getStatus() {
return ClientConnectionData.this.clientConnection.status;
}
@Override
public String getConnectionToken() {
// TODO Auto-generated method stub
return ClientConnectionData.this.clientConnection.connectionToken;
}
@Override
public String getUserSessionId() {
return ClientConnectionData.this.clientConnection.userSessionId;
}
@Override
public String getInfo() {
return ClientConnectionData.this.clientConnection.info;
}
@Override
public Map<Long, String> getIndicatorValues() {
return ClientConnectionData.this.indicatorValues
.stream()
.collect(Collectors.toMap(
iv -> iv.getIndicatorId(),
iv -> IndicatorValue.getDisplayValue(iv)));
}
@Override
public Set<Long> getGroups() {
return ClientConnectionData.this.groups;
}
@Override
public boolean isMissingPing() {
return BooleanUtils.isTrue(ClientConnectionData.this.missingPing);
}
@Override
public boolean isPendingNotification() {
return BooleanUtils.isTrue(ClientConnectionData.this.pendingNotification);
}
};
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.model.session;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
public class ClientMonitoringData implements ClientMonitoringDataView {
public final Long id;
public final ConnectionStatus status;
public final String connectionToken;
public final String userSessionId;
public final String info;
public final Map<Long, String> indicatorVals;
public final Set<Long> groups;
public final boolean missingPing;
public final boolean pendingNotification;
public ClientMonitoringData(
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) final Long id,
@JsonProperty(ATTR_STATUS) final ConnectionStatus status,
@JsonProperty(ATTR_CONNECTION_TOKEN) final String connectionToken,
@JsonProperty(ATTR_EXAM_USER_SESSION_ID) final String userSessionId,
@JsonProperty(ATTR_INFO) final String info,
@JsonProperty(ATTR_INDICATOR_VALUES) final Map<Long, String> indicatorVals,
@JsonProperty(ATTR_CLIENT_GROUPS) final Set<Long> groups,
@JsonProperty(ATTR_MISSING_PING) final boolean missingPing,
@JsonProperty(ATTR_PENDING_NOTIFICATION) final boolean pendingNotification) {
this.id = id;
this.status = status;
this.connectionToken = connectionToken;
this.userSessionId = userSessionId;
this.info = info;
this.indicatorVals = indicatorVals;
this.groups = groups;
this.missingPing = missingPing;
this.pendingNotification = pendingNotification;
}
@Override
public Long getId() {
return this.id;
}
@Override
public ConnectionStatus getStatus() {
return this.status;
}
@Override
public String getConnectionToken() {
return this.connectionToken;
}
@Override
public String getUserSessionId() {
return this.userSessionId;
}
@Override
public String getInfo() {
return this.info;
}
@Override
public Map<Long, String> getIndicatorValues() {
return this.indicatorVals;
}
@Override
public Set<Long> getGroups() {
return this.groups;
}
@Override
public boolean isMissingPing() {
return this.missingPing;
}
@Override
public boolean isPendingNotification() {
return this.pendingNotification;
}
public boolean dataEquals(final ClientMonitoringData other) {
if (other == null) {
return true;
}
if (this.connectionToken == null) {
if (other.connectionToken != null)
return false;
} else if (!this.connectionToken.equals(other.connectionToken))
return false;
if (this.status != other.status)
return false;
if (this.userSessionId == null) {
if (other.userSessionId != null)
return false;
} else if (!this.userSessionId.equals(other.userSessionId)) {
return false;
}
if (!Objects.equals(this.groups, other.groups)) {
return false;
}
return true;
}
public boolean indicatorValuesEquals(final ClientMonitoringData other) {
return Objects.equals(this.indicatorVals, other.indicatorVals);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.model.session;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
public interface ClientMonitoringDataView {
public static final String ATTR_STATUS = "st";
public static final String ATTR_CONNECTION_TOKEN = "tk";
public static final String ATTR_EXAM_USER_SESSION_ID = "si";
public static final String ATTR_INFO = "in";
public static final String ATTR_INDICATOR_VALUES = "iv";
public static final String ATTR_CLIENT_GROUPS = "cg";
public static final String ATTR_MISSING_PING = "mp";
public static final String ATTR_PENDING_NOTIFICATION = "pn";
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
Long getId();
@JsonProperty(ATTR_STATUS)
ConnectionStatus getStatus();
@JsonProperty(ATTR_CONNECTION_TOKEN)
String getConnectionToken();
@JsonProperty(ATTR_EXAM_USER_SESSION_ID)
String getUserSessionId();
@JsonProperty(ATTR_INFO)
String getInfo();
@JsonProperty(ATTR_INDICATOR_VALUES)
Map<Long, String> getIndicatorValues();
@JsonProperty(ATTR_CLIENT_GROUPS)
Set<Long> getGroups();
@JsonProperty(ATTR_MISSING_PING)
boolean isMissingPing();
@JsonProperty(ATTR_PENDING_NOTIFICATION)
boolean isPendingNotification();
public static Predicate<ClientMonitoringDataView> getStatusPredicate(final ConnectionStatus... status) {
final EnumSet<ConnectionStatus> states = EnumSet.noneOf(ConnectionStatus.class);
if (status != null) {
Collections.addAll(states, status);
}
return connection -> states.contains(connection.getStatus());
}
}

View file

@ -1,108 +0,0 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.gbl.model.session;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.Entity;
@JsonIgnoreProperties(ignoreUnknown = true)
public class StaticClientConnectionData implements Entity {
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
public final Long id;
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN)
public final String connectionToken;
@JsonProperty(ClientConnection.ATTR_INFO)
public final String info;
@JsonProperty(ClientConnectionData.ATTR_CLIENT_GROUPS)
public Set<Long> groups = null;
@JsonCreator
public StaticClientConnectionData(
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) final Long id,
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN) final String connectionToken,
@JsonProperty(ClientConnection.ATTR_INFO) final String info,
@JsonProperty(ClientConnectionData.ATTR_CLIENT_GROUPS) final Set<Long> groups) {
this.id = id;
this.connectionToken = connectionToken;
this.info = info;
this.groups = groups;
}
@Override
public String getModelId() {
return (this.id != null)
? String.valueOf(this.id)
: null;
}
@Override
public EntityType entityType() {
return EntityType.CLIENT_CONNECTION;
}
@Override
public String getName() {
return this.connectionToken;
}
public Set<Long> getGroups() {
return this.groups;
}
public void setGroups(final Set<Long> groups) {
this.groups = groups;
}
public Long getId() {
return this.id;
}
public String getConnectionToken() {
return this.connectionToken;
}
public String getInfo() {
return this.info;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final StaticClientConnectionData other = (StaticClientConnectionData) obj;
if (this.id == null) {
if (other.id != null)
return false;
} else if (!this.id.equals(other.id))
return false;
return true;
}
}

View file

@ -12,6 +12,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
public interface IndicatorValue extends IndicatorValueHolder {
@ -38,6 +39,31 @@ public interface IndicatorValue extends IndicatorValueHolder {
}
}
static String getDisplayValue(final IndicatorValue indicatorValue) {
if (Double.isNaN(indicatorValue.getValue())) {
return Constants.EMPTY_NOTE;
}
if (indicatorValue instanceof ClientIndicator) {
return getDisplayValue(indicatorValue, ((ClientIndicator) indicatorValue).getType());
} else {
return String.valueOf((long) indicatorValue.getValue());
}
}
static double getFromDisplayValue(final String displayValue) {
try {
return Double.parseDouble(displayValue);
} catch (final NumberFormatException nfe) {
return Double.NaN;
}
// if (displayValue == null || displayValue == Constants.EMPTY_NOTE) {
// return Double.NaN;
// }
// return Double.parseDouble(displayValue);
}
default boolean dataEquals(final IndicatorValue other) {
final Long i1 = getIndicatorId();
final Long i2 = other.getIndicatorId();

View file

@ -18,7 +18,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
@JsonIgnoreProperties(ignoreUnknown = true)
public class MonitoringSEBConnectionData {
@ -28,7 +29,7 @@ public class MonitoringSEBConnectionData {
public static final String ATTR_CLIENT_GROUP_MAPPING = "clientGroupMapping";
@JsonProperty(ATTR_CONNECTIONS)
public final Collection<ClientConnectionData> connections;
public final Collection<? extends ClientMonitoringDataView> monitoringData;
@JsonProperty(ATTR_STATUS_MAPPING)
public final int[] connectionsPerStatus;
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING)
@ -36,17 +37,27 @@ public class MonitoringSEBConnectionData {
@JsonCreator
public MonitoringSEBConnectionData(
@JsonProperty(ATTR_CONNECTIONS) final Collection<ClientConnectionData> connections,
@JsonProperty(ATTR_CONNECTIONS) final Collection<ClientMonitoringData> connections,
@JsonProperty(ATTR_STATUS_MAPPING) final int[] connectionsPerStatus,
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING) final Map<Long, Integer> connectionsPerClientGroup) {
this.connections = connections;
this.monitoringData = connections;
this.connectionsPerStatus = connectionsPerStatus;
this.connectionsPerClientGroup = connectionsPerClientGroup;
}
public Collection<ClientConnectionData> getConnections() {
return this.connections;
public MonitoringSEBConnectionData(
final int[] connectionsPerStatus,
final Map<Long, Integer> connectionsPerClientGroup,
final Collection<? extends ClientMonitoringDataView> connections) {
this.monitoringData = connections;
this.connectionsPerStatus = connectionsPerStatus;
this.connectionsPerClientGroup = connectionsPerClientGroup;
}
public Collection<? extends ClientMonitoringDataView> getMonitoringData() {
return this.monitoringData;
}
public int[] getConnectionsPerStatus() {
@ -73,7 +84,7 @@ public class MonitoringSEBConnectionData {
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("MonitoringSEBConnectionData [connections=");
builder.append(this.connections);
builder.append(this.monitoringData);
builder.append(", connectionsPerStatus=");
builder.append(Arrays.toString(this.connectionsPerStatus));
builder.append(", connectionsPerClientGroup=");

View file

@ -598,7 +598,7 @@ public class MonitoringRunningExam implements TemplateComposer {
private Set<EntityKey> selectionForInstruction(final ClientConnectionTable clientTable) {
final Set<String> connectionTokens = clientTable.getConnectionTokens(
cc -> cc.status.clientActiveStatus,
cc -> cc.getStatus().clientActiveStatus,
true);
if (connectionTokens == null || connectionTokens.isEmpty()) {
return Collections.emptySet();

View file

@ -24,6 +24,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
@ -74,12 +75,12 @@ public class SEBSendLockPopup {
public PageAction show(
final PageAction action,
final Function<Predicate<ClientConnection>, Set<String>> selectionFunction) {
final Function<Predicate<ClientMonitoringDataView>, Set<String>> selectionFunction) {
try {
final PageContext pageContext = action.pageContext();
final Set<String> selection = selectionFunction.apply(ClientConnection.getStatusPredicate(
final Set<String> selection = selectionFunction.apply(ClientMonitoringDataView.getStatusPredicate(
ConnectionStatus.CONNECTION_REQUESTED,
ConnectionStatus.ACTIVE));

View file

@ -63,6 +63,7 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent;
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification;
import ch.ethz.seb.sebserver.gbl.model.session.ClientNotification.NotificationType;
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
@ -646,6 +647,28 @@ public class ResourceService {
};
}
public Function<ClientMonitoringData, String> localizedClientMonitoringStatusNameFunction() {
// Memoizing
final String missing = this.i18nSupport.getText(
SEB_CONNECTION_STATUS_KEY_PREFIX + MISSING_CLIENT_PING_NAME_KEY,
MISSING_CLIENT_PING_NAME_KEY);
final EnumMap<ConnectionStatus, String> localizedNames = new EnumMap<>(ConnectionStatus.class);
Arrays.asList(ConnectionStatus.values()).stream().forEach(state -> localizedNames.put(state, this.i18nSupport
.getText(SEB_CONNECTION_STATUS_KEY_PREFIX + state.name(), state.name())));
return monitoringData -> {
if (monitoringData == null) {
localizedNames.get(ConnectionStatus.UNDEFINED);
}
if (monitoringData.missingPing && monitoringData.status.establishedStatus) {
return missing;
} else {
return localizedNames.get(monitoringData.status);
}
};
}
public String localizedClientConnectionStatusName(final ConnectionStatus status) {
String name;
if (status != null) {

View file

@ -132,6 +132,8 @@ public abstract class RestCall<T> {
return Result.ofEmpty();
}
System.out.println("************** size = " + responseEntity.getBody().length());
return Result.of(RestCall.this.jsonMapper.readValue(
responseEntity.getBody(),
RestCall.this.typeKey.typeRef));

View file

@ -48,9 +48,9 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
@ -93,7 +93,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
private final Map<Long, ClientGroup> clientGroupMapping;
private final Table table;
private final ColorData colorData;
private final Function<ClientConnectionData, String> localizedClientConnectionStatusNameFunction;
private final Function<ClientMonitoringData, String> localizedClientConnectionStatusNameFunction;
private Consumer<ClientConnectionTable> selectionListener;
private int tableWidth;
@ -151,7 +151,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
.collect(Collectors.toMap(cg -> cg.id, Function.identity()));
this.localizedClientConnectionStatusNameFunction =
resourceService.localizedClientConnectionStatusNameFunction();
resourceService.localizedClientMonitoringStatusNameFunction();
this.table = widgetFactory.tableLocalized(tableRoot, SWT.MULTI | SWT.V_SCROLL);
final GridLayout gridLayout = new GridLayout(3 + indicators.size(), false);
@ -230,7 +230,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
public Set<String> getConnectionTokens(
final Predicate<ClientConnection> filter,
final Predicate<ClientMonitoringDataView> filter,
final boolean selected) {
if (selected) {
@ -244,8 +244,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
final UpdatableTableItem updatableTableItem =
new ArrayList<>(this.tableMapping.values())
.get(selectionIndices[i]);
if (filter.test(updatableTableItem.connectionData.clientConnection)) {
result.add(updatableTableItem.connectionData.clientConnection.connectionToken);
if (filter.test(updatableTableItem.monitoringData)) {
result.add(updatableTableItem.monitoringData.connectionToken);
}
}
return result;
@ -253,9 +253,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
return this.tableMapping
.values()
.stream()
.map(item -> item.connectionData.clientConnection)
.map(item -> item.monitoringData)
.filter(filter)
.map(ClientConnection::getConnectionToken)
.map(ClientMonitoringData::getConnectionToken)
.collect(Collectors.toSet());
}
}
@ -304,7 +304,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
(updatableTableItem.connectionId != null)
? String.valueOf(updatableTableItem.connectionId)
: null,
updatableTableItem.connectionData.clientConnection.connectionToken);
updatableTableItem.monitoringData.connectionToken);
}
public void forceUpdateAll() {
@ -313,8 +313,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
@Override
public void update(final MonitoringFilter monitoringStatus) {
final Collection<ClientConnectionData> connectionData = monitoringStatus.getConnectionData();
final boolean sizeChanged = connectionData.size() != this.table.getItemCount();
final Collection<ClientMonitoringData> monitoringData = monitoringStatus.getConnectionData();
final boolean sizeChanged = monitoringData.size() != this.table.getItemCount();
final boolean needsSync = monitoringStatus.filterChanged() ||
this.forceUpdateAll ||
sizeChanged ||
@ -332,11 +332,11 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
monitoringStatus.getConnectionData()
.forEach(data -> {
final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent(
data.getConnectionId(),
data.id,
UpdatableTableItem::new);
tableItem.push(data);
if (needsSync) {
this.toDelete.remove(data.getConnectionId());
this.toDelete.remove(data.id);
}
});
@ -344,7 +344,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
this.toDelete.forEach(id -> {
final UpdatableTableItem item = this.tableMapping.remove(id);
if (item != null) {
final List<Long> list = this.sessionIds.get(item.connectionData.clientConnection.userSessionId);
final List<Long> list = this.sessionIds.get(item.monitoringData.userSessionId);
if (list != null) {
list.remove(id);
}
@ -446,7 +446,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
final Long connectionId;
private boolean dataChanged = false;
private boolean indicatorValueChanged = false;
private ClientConnectionData connectionData;
private ClientMonitoringData monitoringData;
private int thresholdsWeight;
private int[] indicatorWeights = null;
private boolean duplicateChecked = false;
@ -475,15 +475,15 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
tableItem.setText(
3,
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
.apply(this.connectionData));
.apply(this.monitoringData));
} else {
tableItem.setText(1, getConnectionInfo());
tableItem.setText(
2,
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
.apply(this.connectionData));
.apply(this.monitoringData));
}
if (this.connectionData != null) {
if (this.monitoringData != null) {
updateConnectionStatusColor(tableItem);
updateDuplicateColor(tableItem);
updateNotifications(tableItem);
@ -491,7 +491,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
private void updateNotifications(final TableItem tableItem) {
if (BooleanUtils.isTrue(this.connectionData.pendingNotification())) {
if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) {
tableItem.setImage(0,
WidgetFactory.ImageIcon.NOTIFICATION.getImage(ClientConnectionTable.this.table.getDisplay()));
} else {
@ -502,7 +502,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
private void updateConnectionStatusColor(final TableItem tableItem) {
final Color statusColor = ClientConnectionTable.this.colorData.getStatusColor(this.connectionData);
final Color statusColor = ClientConnectionTable.this.colorData.getStatusColor(this.monitoringData);
final Color statusTextColor = ClientConnectionTable.this.colorData.getStatusTextColor(statusColor);
final int index = ClientConnectionTable.this.hasClientGroups ? 3 : 2;
tableItem.setBackground(index, statusColor);
@ -518,10 +518,10 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
return;
}
if (this.connectionData != null
&& StringUtils.isNotBlank(this.connectionData.clientConnection.userSessionId)) {
if (this.monitoringData != null
&& StringUtils.isNotBlank(this.monitoringData.userSessionId)) {
final List<Long> list =
ClientConnectionTable.this.sessionIds.get(this.connectionData.clientConnection.userSessionId);
ClientConnectionTable.this.sessionIds.get(this.monitoringData.userSessionId);
if (list != null && list.size() > 1) {
tableItem.setBackground(0, ClientConnectionTable.this.colorData.color3);
tableItem.setForeground(0, ClientConnectionTable.this.lightFontColor);
@ -532,31 +532,23 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
}
private void updateIndicatorValues(final TableItem tableItem) {
if (this.connectionData == null || this.indicatorWeights == null) {
return;
}
for (int i = 0; i < this.connectionData.indicatorValues.size(); i++) {
final IndicatorValue indicatorValue = this.connectionData.indicatorValues.get(i);
final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping
.get(indicatorValue.getIndicatorId());
private Consumer<Map.Entry<Long, String>> indicatorUpdate(final TableItem tableItem) {
return entry -> {
final Long id = entry.getKey();
final String displayValue = entry.getValue();
final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping.get(id);
if (indicatorData == null) {
continue;
return;
}
if (!this.connectionData.clientConnection.status.clientActiveStatus) {
final String value = (indicatorData.indicator.type.showOnlyInActiveState)
? Constants.EMPTY_NOTE
: IndicatorValue.getDisplayValue(indicatorValue, indicatorData.indicator.type);
tableItem.setText(indicatorData.tableIndex, value);
updateIndicatorWeight(displayValue, indicatorData);
if (!this.monitoringData.status.clientActiveStatus) {
tableItem.setText(indicatorData.tableIndex, displayValue);
tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
} else {
tableItem.setText(indicatorData.tableIndex, IndicatorValue.getDisplayValue(
indicatorValue,
indicatorData.indicator.type));
tableItem.setText(indicatorData.tableIndex, displayValue);
final int weight = this.indicatorWeights[indicatorData.index];
if (weight >= 0 && weight < indicatorData.thresholdColor.length) {
final ThresholdColor thresholdColor = indicatorData.thresholdColor[weight];
@ -567,7 +559,50 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
}
}
};
}
private void updateIndicatorValues(final TableItem tableItem) {
if (this.monitoringData == null || this.indicatorWeights == null) {
return;
}
this.monitoringData.indicatorVals
.entrySet()
.stream()
.forEach(indicatorUpdate(tableItem));
// for (int i = 0; i < this.monitoringData.indicatorVals.size(); i++) {
// final IndicatorValue indicatorValue = this.monitoringData.indicatorValues.get(i);
// final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping
// .get(indicatorValue.getIndicatorId());
//
// if (indicatorData == null) {
// continue;
// }
//
// if (!this.connectionData.clientConnection.status.clientActiveStatus) {
// final String value = (indicatorData.indicator.type.showOnlyInActiveState)
// ? Constants.EMPTY_NOTE
// : IndicatorValue.getDisplayValue(indicatorValue, indicatorData.indicator.type);
// tableItem.setText(indicatorData.tableIndex, value);
// tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
// tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
// } else {
// tableItem.setText(indicatorData.tableIndex, IndicatorValue.getDisplayValue(
// indicatorValue,
// indicatorData.indicator.type));
// final int weight = this.indicatorWeights[indicatorData.index];
// if (weight >= 0 && weight < indicatorData.thresholdColor.length) {
// final ThresholdColor thresholdColor = indicatorData.thresholdColor[weight];
// tableItem.setBackground(indicatorData.tableIndex, thresholdColor.color);
// tableItem.setForeground(indicatorData.tableIndex, thresholdColor.textColor);
// } else {
// tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
// tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
// }
// }
// }
}
@Override
@ -603,11 +638,11 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
int notificationWeight() {
return BooleanUtils.isTrue(this.connectionData.pendingNotification) ? -1 : 0;
return BooleanUtils.isTrue(this.monitoringData.pendingNotification) ? -1 : 0;
}
int statusWeight() {
return ClientConnectionTable.this.colorData.statusWeight(this.connectionData);
return ClientConnectionTable.this.colorData.statusWeight(this.monitoringData);
}
int thresholdsWeight() {
@ -615,8 +650,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
String getConnectionInfo() {
if (this.connectionData != null && this.connectionData.clientConnection.info != null) {
return this.connectionData.clientConnection.info;
if (this.monitoringData != null && this.monitoringData.info != null) {
return this.monitoringData.info;
}
return Constants.EMPTY_NOTE;
}
@ -624,7 +659,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
private String getGroupInfo() {
final StringBuilder sb = new StringBuilder();
ClientConnectionTable.this.clientGroupMapping.keySet().stream().forEach(key -> {
if (this.connectionData.groups != null && this.connectionData.groups.contains(key)) {
if (this.monitoringData.groups != null && this.monitoringData.groups.contains(key)) {
final ClientGroup clientGroup = ClientConnectionTable.this.clientGroupMapping.get(key);
sb.append(WidgetFactory.getTextWithBackgroundHTML(clientGroup.name, clientGroup.color));
}
@ -638,22 +673,22 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
String getConnectionIdentifier() {
if (this.connectionData != null && this.connectionData.clientConnection.userSessionId != null) {
return this.connectionData.clientConnection.userSessionId;
if (this.monitoringData != null && this.monitoringData.userSessionId != null) {
return this.monitoringData.userSessionId;
}
return "--";
}
void push(final ClientConnectionData connectionData) {
this.dataChanged = this.connectionData == null ||
!this.connectionData.dataEquals(connectionData);
this.indicatorValueChanged = this.connectionData == null ||
(this.connectionData.clientConnection.status.clientActiveStatus
&& !this.connectionData.indicatorValuesEquals(connectionData));
final boolean notificationChanged = this.connectionData == null ||
BooleanUtils.toBoolean(this.connectionData.pendingNotification) != BooleanUtils
.toBoolean(connectionData.pendingNotification);
void push(final ClientMonitoringData monitoringData) {
this.dataChanged = this.monitoringData == null ||
!this.monitoringData.dataEquals(monitoringData);
this.indicatorValueChanged = this.monitoringData == null ||
(this.monitoringData.status.clientActiveStatus
&& !this.monitoringData.indicatorValuesEquals(monitoringData));
final boolean notificationChanged = this.monitoringData == null ||
BooleanUtils.toBoolean(this.monitoringData.pendingNotification) != BooleanUtils
.toBoolean(monitoringData.pendingNotification);
if (this.dataChanged || notificationChanged) {
ClientConnectionTable.this.needsSort = true;
@ -663,42 +698,50 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
this.indicatorWeights = new int[ClientConnectionTable.this.indicatorMapping.size()];
}
for (int i = 0; i < connectionData.indicatorValues.size(); i++) {
final IndicatorValue indicatorValue = connectionData.indicatorValues.get(i);
final IndicatorData indicatorData =
ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getIndicatorId());
// for (int i = 0; i < monitoringData.indicatorValues.size(); i++) {
// final IndicatorValue indicatorValue = connectionData.indicatorValues.get(i);
// final IndicatorData indicatorData =
// ClientConnectionTable.this.indicatorMapping.get(indicatorValue.getIndicatorId());
//
// if (indicatorData != null) {
// updateIndicatorWeight(indicatorValue, indicatorData);
// }
// }
if (indicatorData != null) {
final double value = indicatorValue.getValue();
final int indicatorWeight = IndicatorData.getWeight(indicatorData, value);
if (this.indicatorWeights[indicatorData.index] != indicatorWeight) {
ClientConnectionTable.this.needsSort = true;
this.thresholdsWeight -= (indicatorData.indicator.type.inverse)
? indicatorData.indicator.thresholds.size()
- this.indicatorWeights[indicatorData.index]
: this.indicatorWeights[indicatorData.index];
this.indicatorWeights[indicatorData.index] = indicatorWeight;
this.thresholdsWeight += (indicatorData.indicator.type.inverse)
? indicatorData.indicator.thresholds.size()
- this.indicatorWeights[indicatorData.index]
: this.indicatorWeights[indicatorData.index];
}
}
}
this.connectionData = connectionData;
this.monitoringData = monitoringData;
if (!this.duplicateChecked &&
this.connectionData.clientConnection.status != ConnectionStatus.DISABLED &&
StringUtils.isNotBlank(connectionData.clientConnection.userSessionId)) {
this.monitoringData.status != ConnectionStatus.DISABLED &&
StringUtils.isNotBlank(monitoringData.userSessionId)) {
ClientConnectionTable.this.sessionIds.add(
connectionData.clientConnection.userSessionId,
monitoringData.userSessionId,
this.connectionId);
this.duplicateChecked = true;
}
}
private void updateIndicatorWeight(
final String indicatorValue,
final IndicatorData indicatorData) {
final int indicatorWeight = IndicatorData.getWeight(
indicatorData,
IndicatorValue.getFromDisplayValue(indicatorValue));
if (this.indicatorWeights[indicatorData.index] != indicatorWeight) {
ClientConnectionTable.this.needsSort = true;
this.thresholdsWeight -= (indicatorData.indicator.type.inverse)
? indicatorData.indicator.thresholds.size()
- this.indicatorWeights[indicatorData.index]
: this.indicatorWeights[indicatorData.index];
this.indicatorWeights[indicatorData.index] = indicatorWeight;
this.thresholdsWeight += (indicatorData.indicator.type.inverse)
? indicatorData.indicator.thresholds.size()
- this.indicatorWeights[indicatorData.index]
: this.indicatorWeights[indicatorData.index];
}
}
private ClientConnectionTable getOuterType() {
return ClientConnectionTable.this;
}

View file

@ -14,6 +14,7 @@ import org.eclipse.swt.widgets.Display;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
import ch.ethz.seb.sebserver.gbl.util.Utils;
public class ColorData {
@ -47,6 +48,19 @@ public class ColorData {
}
}
Color getStatusColor(final ClientMonitoringData monitoringData) {
if (monitoringData == null) {
return this.defaultColor;
}
switch (monitoringData.status) {
case ACTIVE:
return (monitoringData.missingPing) ? this.color2 : this.color1;
default:
return this.defaultColor;
}
}
Color getStatusTextColor(final Color statusColor) {
return Utils.darkColorContrast(statusColor.getRGB()) ? this.darkColor : this.lightColor;
}
@ -69,4 +83,22 @@ public class ColorData {
}
}
int statusWeight(final ClientMonitoringData monitoring) {
if (monitoring == null) {
return 100;
}
switch (monitoring.status) {
case CONNECTION_REQUESTED:
case AUTHENTICATED:
return 1;
case ACTIVE:
return (monitoring.missingPing) ? 0 : 2;
case CLOSED:
return 3;
default:
return 10;
}
}
}

View file

@ -29,10 +29,10 @@ import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction;
import ch.ethz.seb.sebserver.gbl.model.session.ClientInstruction.InstructionType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
@ -70,12 +70,12 @@ public class InstructionProcessor {
public void propagateSEBQuitInstruction(
final String examId,
final Function<Predicate<ClientConnection>, Set<String>> selectionFunction,
final Function<Predicate<ClientMonitoringDataView>, Set<String>> selectionFunction,
final PageContext pageContext) {
try {
final Set<String> connectionTokens = selectionFunction
.apply(ClientConnection.getStatusPredicate(
.apply(ClientMonitoringDataView.getStatusPredicate(
ConnectionStatus.CONNECTION_REQUESTED,
ConnectionStatus.ACTIVE));
@ -156,11 +156,11 @@ public class InstructionProcessor {
public void disableConnection(
final Long examId,
final Function<Predicate<ClientConnection>, Set<String>> selectionFunction,
final Function<Predicate<ClientMonitoringDataView>, Set<String>> selectionFunction,
final PageContext pageContext) {
final Set<String> connectionTokens = selectionFunction
.apply(ClientConnection.getStatusPredicate(
.apply(ClientMonitoringDataView.getStatusPredicate(
ConnectionStatus.CONNECTION_REQUESTED,
ConnectionStatus.UNDEFINED,
ConnectionStatus.CLOSED,

View file

@ -14,7 +14,7 @@ import java.util.EnumSet;
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
import ch.ethz.seb.sebserver.gbl.model.session.RemoteProctoringRoom;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringFullPageData;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
@ -58,10 +58,11 @@ public interface MonitoringFilter {
}
}
default Collection<ClientConnectionData> getConnectionData() {
@SuppressWarnings("unchecked")
default Collection<ClientMonitoringData> getConnectionData() {
final MonitoringSEBConnectionData monitoringSEBConnectionData = getMonitoringSEBConnectionData();
if (monitoringSEBConnectionData != null) {
return monitoringSEBConnectionData.connections;
return (Collection<ClientMonitoringData>) monitoringSEBConnectionData.monitoringData;
} else {
return Collections.emptyList();
}

View file

@ -19,7 +19,6 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.StaticClientConnectionData;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
@ -236,6 +235,4 @@ public interface ExamSessionService {
return connection.clientConnection.status.clientActiveStatus;
}
Result<Collection<StaticClientConnectionData>> getStaticClientConnectionInfo(String connecionTokens);
}

View file

@ -11,7 +11,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -24,7 +23,6 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@ -44,7 +42,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.StaticClientConnectionData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
@ -423,7 +421,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
updateClientConnections(examId);
final List<ClientConnectionData> filteredConnections = this.clientConnectionDAO
final List<? extends ClientMonitoringDataView> filteredConnections = this.clientConnectionDAO
.getConnectionTokens(examId)
.getOrThrow()
.stream()
@ -435,12 +433,13 @@ public class ExamSessionServiceImpl implements ExamSessionService {
return c;
})
.filter(filter)
.map(ccd -> ccd.monitoringDataView)
.collect(Collectors.toList());
return new MonitoringSEBConnectionData(
filteredConnections,
statusMapping,
clientGroupMapping);
clientGroupMapping,
filteredConnections);
});
}
@ -588,23 +587,4 @@ public class ExamSessionServiceImpl implements ExamSessionService {
});
}
@Override
public Result<Collection<StaticClientConnectionData>> getStaticClientConnectionInfo(final String connecionTokens) {
return Result.tryCatch(() -> {
if (StringUtils.isBlank(connecionTokens)) {
return Collections.emptyList();
}
return Arrays.asList(StringUtils.split(connecionTokens, Constants.LIST_SEPARATOR))
.stream()
.map(this.examSessionCacheService::getClientConnection)
.map(cc -> new StaticClientConnectionData(
cc.clientConnection.id,
cc.clientConnection.connectionToken,
cc.clientConnection.info,
cc.groups))
.collect(Collectors.toList());
});
}
}

View file

@ -258,10 +258,8 @@ public class ExamMonitoringController {
checkPrivileges(institutionId, examId);
return this.examSessionService
.getMonitoringSEBConnectionsData(
examId,
createMonitoringFilter(hiddenStates, hiddenClientGroups))
.getOrThrow().connections;
.getConnectionData(examId, createMonitoringFilter(hiddenStates, hiddenClientGroups))
.getOrThrow();
}
@RequestMapping(

View file

@ -287,6 +287,10 @@ public class ModelObjectJSONGenerator {
System.out.println(domainObject.getClass().getSimpleName() + ":");
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
System.out.println("ClientMonitoringData" + ":");
System.out.println(writerWithDefaultPrettyPrinter
.writeValueAsString(((ClientConnectionData) domainObject).monitoringDataView));
domainObject = new ClientEvent(1L, 1L, EventType.WARN_LOG,
System.currentTimeMillis(), System.currentTimeMillis(), 123.0, "text");
System.out.println(domainObject.getClass().getSimpleName() + ":");

View file

@ -2268,7 +2268,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
assertNotNull(fullPageData);
assertFalse(fullPageData.hasError());
final MonitoringSEBConnectionData monitoringConnectionData = fullPageData.get().monitoringConnectionData;
assertTrue(monitoringConnectionData.connections.isEmpty());
assertTrue(monitoringConnectionData.monitoringData.isEmpty());
assertEquals(
"[0, 0, 0, 0, 0, 0]",
Arrays.stream(monitoringConnectionData.connectionsPerStatus).boxed().map(String::valueOf)