SEBSERV-347 implementation & testing

This commit is contained in:
anhefti 2022-11-02 12:28:54 +01:00
parent 11792be0a9
commit 95e3a70d34
19 changed files with 478 additions and 300 deletions

View file

@ -193,6 +193,7 @@ public final class API {
public static final String EXAM_MONITORING_ENDPOINT = "/monitoring";
public static final String EXAM_MONITORING_FULLPAGE = "/fullpage";
public static final String EXAM_MONITORING_STATIC_CLIENT_DATA = "/static-client-data";
public static final String EXAM_MONITORING_INSTRUCTION_ENDPOINT = "/instruction";
public static final String EXAM_MONITORING_NOTIFICATION_ENDPOINT = "/notification";
public static final String EXAM_MONITORING_DISABLE_CONNECTION_ENDPOINT = "/disable-connection";

View file

@ -9,15 +9,10 @@
package ch.ethz.seb.sebserver.gbl.model.session;
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;
@ -27,9 +22,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.Constants;
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;
@ -48,7 +41,7 @@ public class ClientConnectionData implements GrantEntity {
@JsonProperty(ATTR_INDICATOR_VALUE)
public final List<? extends IndicatorValue> indicatorValues;
@JsonProperty(ATTR_CLIENT_GROUPS)
public Set<Long> groups = null;
public final Set<Long> groups;
public final Boolean missingPing;
public final Boolean pendingNotification;
@ -58,30 +51,26 @@ public class ClientConnectionData implements GrantEntity {
@JsonProperty(ATTR_MISSING_PING) final Boolean missingPing,
@JsonProperty(ATTR_PENDING_NOTIFICATION) final Boolean pendingNotification,
@JsonProperty(ATTR_CLIENT_CONNECTION) final ClientConnection clientConnection,
@JsonProperty(ATTR_INDICATOR_VALUE) final Collection<? extends SimpleIndicatorValue> indicatorValues) {
@JsonProperty(ATTR_INDICATOR_VALUE) final Collection<? extends SimpleIndicatorValue> indicatorValues,
@JsonProperty(ATTR_CLIENT_GROUPS) final Set<Long> groups) {
this.missingPing = missingPing;
this.pendingNotification = pendingNotification;
this.clientConnection = clientConnection;
this.indicatorValues = Utils.immutableListOf(indicatorValues);
this.groups = (groups == null) ? null : Utils.immutableSetOf(groups);
}
public ClientConnectionData(
final ClientConnection clientConnection,
final List<? extends IndicatorValue> indicatorValues) {
final List<? extends IndicatorValue> indicatorValues,
final Set<Long> groups) {
this.missingPing = null;
this.pendingNotification = Boolean.FALSE;
this.clientConnection = clientConnection;
this.indicatorValues = Utils.immutableListOf(indicatorValues);
}
@JsonIgnore
public void addToClientGroup(final ClientGroup group) {
if (this.groups == null) {
this.groups = new HashSet<>(1);
}
this.groups.add(group.id);
this.groups = (groups == null) ? null : Utils.immutableSetOf(groups);
}
@JsonIgnore
@ -221,59 +210,4 @@ 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

@ -10,43 +10,34 @@ 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.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
@JsonIgnoreProperties(ignoreUnknown = true)
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;
@JsonCreator
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;
}
@ -61,31 +52,11 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
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;
@ -96,33 +67,6 @@ public class ClientMonitoringData implements ClientMonitoringDataView {
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

@ -11,7 +11,6 @@ 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;
@ -36,21 +35,9 @@ public interface ClientMonitoringDataView {
@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();

View file

@ -0,0 +1,76 @@
/*
* 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.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.model.Domain;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ClientStaticData {
public static final ClientStaticData NULL_DATA =
new ClientStaticData(-1L, null, null, null, Collections.emptySet());
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID)
public final Long id;
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN)
public final String connectionToken;
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID)
public final String userSessionId;
@JsonProperty(ClientConnection.ATTR_INFO)
public final String info;
@JsonProperty(ClientConnectionData.ATTR_CLIENT_GROUPS)
public final Set<Long> groups;
@JsonCreator
public ClientStaticData(
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_ID) final Long id,
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_CONNECTION_TOKEN) final String connectionToken,
@JsonProperty(Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID) final String userSessionId,
@JsonProperty(ClientConnection.ATTR_INFO) final String info,
@JsonProperty(ClientConnectionData.ATTR_CLIENT_GROUPS) final Set<Long> groups) {
this.id = id;
this.connectionToken = connectionToken;
this.userSessionId = userSessionId;
this.info = info;
this.groups = groups;
}
public Long getId() {
return this.id;
}
public String getConnectionToken() {
return this.connectionToken;
}
public String getUserSessionId() {
return this.userSessionId;
}
public String getInfo() {
return this.info;
}
public Set<Long> getGroups() {
return this.groups;
}
}

View file

@ -39,7 +39,11 @@ public class IPv4RangeClientGroupMatcher implements ClientGroupConnectionMatcher
return (inputIPAddress >= startIPAddress && inputIPAddress <= endIPAddress);
} catch (final Exception e) {
log.error("Failed to verify IP range for group: {} connection: {}", group, clientConnection, e);
log.error(
"Failed to verify IP range for group: {} connection: {}, error",
group,
clientConnection,
e.getMessage());
return false;
}
}

View file

@ -24,14 +24,16 @@ import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
@JsonIgnoreProperties(ignoreUnknown = true)
public class MonitoringSEBConnectionData {
public static final String ATTR_CONNECTIONS = "connections";
public static final String ATTR_STATUS_MAPPING = "statusMapping";
public static final String ATTR_CLIENT_GROUP_MAPPING = "clientGroupMapping";
public static final String ATTR_CONNECTIONS = "cons";
public static final String ATTR_STATUS_MAPPING = "sm";
public static final String ATTR_CLIENT_GROUP_MAPPING = "cgm";
@JsonProperty(ATTR_CONNECTIONS)
public final Collection<? extends ClientMonitoringDataView> monitoringData;
@JsonProperty(ATTR_STATUS_MAPPING)
public final int[] connectionsPerStatus;
@JsonProperty(ATTR_CLIENT_GROUP_MAPPING)
public final Map<Long, Integer> connectionsPerClientGroup;

View file

@ -0,0 +1,47 @@
/*
* 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.monitoring;
import java.util.Collection;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import ch.ethz.seb.sebserver.gbl.model.session.ClientStaticData;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@JsonIgnoreProperties(ignoreUnknown = true)
public class MonitoringStaticClientData {
public static final String ATTR_STATIC_CONNECTION_DATA = "staticClientConnectionData";
public static final String ATTR_DUPLICATIONS = "duplications";
@JsonProperty(ATTR_STATIC_CONNECTION_DATA)
public final Collection<ClientStaticData> staticClientConnectionData;
@JsonProperty(ATTR_DUPLICATIONS)
public final Set<Long> duplications;
public MonitoringStaticClientData(
@JsonProperty(ATTR_STATIC_CONNECTION_DATA) final Collection<ClientStaticData> staticClientConnectionData,
@JsonProperty(ATTR_DUPLICATIONS) final Set<Long> duplications) {
this.staticClientConnectionData = Utils.immutableCollectionOf(staticClientConnectionData);
this.duplications = Utils.immutableSetOf(duplications);
}
public Collection<ClientStaticData> getStaticClientConnectionData() {
return this.staticClientConnectionData;
}
public Set<Long> getDuplications() {
return this.duplications;
}
}

View file

@ -775,7 +775,7 @@ public final class Utils {
try {
return ipToLong(InetAddress.getByName(ipV4Address));
} catch (final UnknownHostException e) {
log.error("Failed to convert IPv4 address: {}", ipV4Address, e);
log.error("Failed to convert IPv4 address: {}, error: ", ipV4Address, e.getMessage());
return -1L;
}
}
@ -805,4 +805,12 @@ public final class Utils {
return false;
}
public static Long toLong(final String longValue) {
try {
return Long.valueOf(longValue);
} catch (final Exception e) {
return null;
}
}
}

View file

@ -132,8 +132,6 @@ 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

@ -0,0 +1,41 @@
/*
* 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.gui.service.remote.webservice.api.session;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.type.TypeReference;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
@Lazy
@Component
@GuiProfile
public class GetMonitoringStaticClientData extends RestCall<MonitoringStaticClientData> {
public GetMonitoringStaticClientData() {
super(new TypeKey<>(
CallType.GET_LIST,
null,
new TypeReference<MonitoringStaticClientData>() {
}),
HttpMethod.POST,
MediaType.APPLICATION_FORM_URLENCODED,
API.EXAM_MONITORING_ENDPOINT
+ API.PARENT_MODEL_ID_VAR_PATH_SEGMENT
+ API.EXAM_MONITORING_STATIC_CLIENT_DATA);
}
}

View file

@ -15,7 +15,6 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@ -38,30 +37,36 @@ import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
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.ConnectionStatus;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringData;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.model.session.ClientStaticData;
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.gbl.util.Tuple;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetMonitoringStaticClientData;
import ch.ethz.seb.sebserver.gui.service.session.IndicatorData.ThresholdColor;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate {
private static final Logger log = LoggerFactory.getLogger(ClientConnectionTable.class);
private static final int BOTTOM_PADDING = 20;
//private static final int[] TABLE_PROPORTIONS = new int[] { 3, 3, 2, 1 };
//private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3;
@ -100,7 +105,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
private boolean needsSort = false;
private LinkedHashMap<Long, UpdatableTableItem> tableMapping;
private final Set<Long> toDelete = new HashSet<>();
private final MultiValueMap<String, Long> sessionIds;
private final Set<Long> toUpdateStatic = new HashSet<>();
private final Set<Long> duplicates = new HashSet<>();
private final Color darkFontColor;
private final Color lightFontColor;
@ -194,7 +200,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
this.tableMapping = new LinkedHashMap<>();
this.sessionIds = new LinkedMultiValueMap<>();
this.table.layout();
}
@ -244,8 +249,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
final UpdatableTableItem updatableTableItem =
new ArrayList<>(this.tableMapping.values())
.get(selectionIndices[i]);
if (filter.test(updatableTableItem.monitoringData)) {
result.add(updatableTableItem.monitoringData.connectionToken);
if (filter.test(updatableTableItem.monitoringData)
&& updatableTableItem.staticData != ClientStaticData.NULL_DATA) {
result.add(updatableTableItem.staticData.connectionToken);
}
}
return result;
@ -253,9 +259,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
return this.tableMapping
.values()
.stream()
.map(item -> item.monitoringData)
.filter(filter)
.map(ClientMonitoringData::getConnectionToken)
.filter(item -> filter.test(item.monitoringData) && item.staticData != ClientStaticData.NULL_DATA)
.map(item -> item.staticData.connectionToken)
.collect(Collectors.toSet());
}
}
@ -304,7 +309,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
(updatableTableItem.connectionId != null)
? String.valueOf(updatableTableItem.connectionId)
: null,
updatableTableItem.monitoringData.connectionToken);
updatableTableItem.staticData.connectionToken);
}
public void forceUpdateAll() {
@ -329,33 +334,32 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
this.toDelete.addAll(this.tableMapping.keySet());
}
this.toUpdateStatic.clear();
monitoringStatus.getConnectionData()
.forEach(data -> {
final UpdatableTableItem tableItem = this.tableMapping.computeIfAbsent(
data.id,
UpdatableTableItem::new);
tableItem.push(data);
if (tableItem.push(data)) {
this.toUpdateStatic.add(data.id);
}
if (needsSync) {
this.toDelete.remove(data.id);
}
});
if (!this.toUpdateStatic.isEmpty()) {
fetchStaticClientConnectionData();
}
if (!this.toDelete.isEmpty()) {
this.toDelete.forEach(id -> {
final UpdatableTableItem item = this.tableMapping.remove(id);
if (item != null) {
final List<Long> list = this.sessionIds.get(item.monitoringData.userSessionId);
if (list != null) {
list.remove(id);
}
}
});
this.toDelete.forEach(id -> this.tableMapping.remove(id));
monitoringStatus.resetFilterChanged();
this.toDelete.clear();
}
this.forceUpdateAll = false;
this.needsSort = sizeChanged;
this.needsSort = this.needsSort || sizeChanged;
updateGUI();
}
@ -447,9 +451,9 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
private boolean dataChanged = false;
private boolean indicatorValueChanged = false;
private ClientMonitoringData monitoringData;
private ClientStaticData staticData = ClientStaticData.NULL_DATA;
private int thresholdsWeight;
private int[] indicatorWeights = null;
private boolean duplicateChecked = false;
UpdatableTableItem(final Long connectionId) {
this.connectionId = connectionId;
@ -457,6 +461,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
private void update(final TableItem tableItem, final boolean force) {
updateDuplicateColor(tableItem);
if (force || this.dataChanged) {
updateData(tableItem);
}
@ -483,13 +488,26 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
.apply(this.monitoringData));
}
if (this.monitoringData != null) {
updateConnectionStatusColor(tableItem);
updateDuplicateColor(tableItem);
updateNotifications(tableItem);
}
}
private void updateDuplicateColor(final TableItem tableItem) {
//System.out.println("ClientConnectionTable.this.duplicates : " + ClientConnectionTable.this.duplicates);
if (ClientConnectionTable.this.duplicates.contains(this.connectionId) &&
tableItem.getBackground(0) != ClientConnectionTable.this.colorData.color3) {
tableItem.setBackground(0, ClientConnectionTable.this.colorData.color3);
tableItem.setForeground(0, ClientConnectionTable.this.lightFontColor);
} else if (!ClientConnectionTable.this.duplicates.contains(this.connectionId) &&
tableItem.getBackground(0) == ClientConnectionTable.this.colorData.color3) {
tableItem.setBackground(0, null);
tableItem.setForeground(0, ClientConnectionTable.this.darkFontColor);
}
}
private void updateNotifications(final TableItem tableItem) {
if (BooleanUtils.isTrue(this.monitoringData.pendingNotification)) {
tableItem.setImage(0,
@ -509,29 +527,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
tableItem.setForeground(index, statusTextColor);
}
private void updateDuplicateColor(final TableItem tableItem) {
tableItem.setBackground(0, null);
tableItem.setForeground(0, ClientConnectionTable.this.darkFontColor);
if (!this.duplicateChecked) {
return;
}
if (this.monitoringData != null
&& StringUtils.isNotBlank(this.monitoringData.userSessionId)) {
final List<Long> list =
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);
} else {
tableItem.setBackground(0, null);
tableItem.setForeground(0, ClientConnectionTable.this.darkFontColor);
}
}
}
private Consumer<Map.Entry<Long, String>> indicatorUpdate(final TableItem tableItem) {
return entry -> {
final Long id = entry.getKey();
@ -541,10 +536,11 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
return;
}
updateIndicatorWeight(displayValue, indicatorData);
if (!this.monitoringData.status.clientActiveStatus) {
tableItem.setText(indicatorData.tableIndex, displayValue);
final String value = (indicatorData.indicator.type.showOnlyInActiveState)
? Constants.EMPTY_NOTE
: displayValue;
tableItem.setText(indicatorData.tableIndex, value);
tableItem.setBackground(indicatorData.tableIndex, indicatorData.defaultColor);
tableItem.setForeground(indicatorData.tableIndex, indicatorData.defaultTextColor);
} else {
@ -571,38 +567,6 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
.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
@ -650,8 +614,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
String getConnectionInfo() {
if (this.monitoringData != null && this.monitoringData.info != null) {
return this.monitoringData.info;
if (this.staticData != null && this.staticData.info != null) {
return this.staticData.info;
}
return Constants.EMPTY_NOTE;
}
@ -659,7 +623,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
private String getGroupInfo() {
final StringBuilder sb = new StringBuilder();
ClientConnectionTable.this.clientGroupMapping.keySet().stream().forEach(key -> {
if (this.monitoringData.groups != null && this.monitoringData.groups.contains(key)) {
if (this.staticData.groups != null && this.staticData.groups.contains(key)) {
final ClientGroup clientGroup = ClientConnectionTable.this.clientGroupMapping.get(key);
sb.append(WidgetFactory.getTextWithBackgroundHTML(clientGroup.name, clientGroup.color));
}
@ -673,16 +637,17 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
}
String getConnectionIdentifier() {
if (this.monitoringData != null && this.monitoringData.userSessionId != null) {
return this.monitoringData.userSessionId;
if (this.staticData != null && this.staticData.userSessionId != null) {
return this.staticData.userSessionId;
}
return "--";
}
void push(final ClientMonitoringData monitoringData) {
boolean push(final ClientMonitoringData monitoringData) {
this.dataChanged = this.monitoringData == null ||
!this.monitoringData.dataEquals(monitoringData);
this.monitoringData.status != monitoringData.status ||
this.monitoringData.missingPing != monitoringData.missingPing;
this.indicatorValueChanged = this.monitoringData == null ||
(this.monitoringData.status.clientActiveStatus
&& !this.monitoringData.indicatorValuesEquals(monitoringData));
@ -697,55 +662,89 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
if (this.indicatorWeights == null) {
this.indicatorWeights = new int[ClientConnectionTable.this.indicatorMapping.size()];
}
// 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 (this.indicatorValueChanged) {
updateIndicatorWeight();
}
this.monitoringData = monitoringData;
if (!this.duplicateChecked &&
this.monitoringData.status != ConnectionStatus.DISABLED &&
StringUtils.isNotBlank(monitoringData.userSessionId)) {
ClientConnectionTable.this.sessionIds.add(
monitoringData.userSessionId,
this.connectionId);
this.duplicateChecked = true;
}
return this.staticData == null
|| this.staticData == ClientStaticData.NULL_DATA
|| this.dataChanged
|| this.monitoringData.status.connectingStatus
|| StringUtils.isBlank(this.staticData.userSessionId);
}
private void updateIndicatorWeight(
final String indicatorValue,
final IndicatorData indicatorData) {
void push(final ClientStaticData staticData) {
this.staticData = staticData;
this.dataChanged = true;
}
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 void updateIndicatorWeight() {
if (this.monitoringData == null) {
return;
}
this.monitoringData.indicatorVals.entrySet().stream().forEach(entry -> {
final Long id = entry.getKey();
final String displayValue = entry.getValue();
final IndicatorData indicatorData = ClientConnectionTable.this.indicatorMapping.get(id);
if (indicatorData == null) {
return;
}
final int indicatorWeight = IndicatorData.getWeight(
indicatorData,
IndicatorValue.getFromDisplayValue(displayValue));
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;
}
}
private void fetchStaticClientConnectionData() {
final String ids = this.toUpdateStatic
.stream()
.map(String::valueOf)
.reduce("", (acc, str) -> acc + str + Constants.LIST_SEPARATOR, (acc1, acc2) -> acc1 + acc2);
final Result<MonitoringStaticClientData> call = this.pageService
.getRestService()
.getBuilder(GetMonitoringStaticClientData.class)
.withFormParam(API.PARAM_MODEL_ID_LIST, ids)
.withURIVariable(API.PARAM_PARENT_MODEL_ID, this.exam.getModelId())
.call();
if (call.hasError()) {
log.error("Failed to get client connection static data for: {}", ids, call.getError());
} else {
final MonitoringStaticClientData monitoringStaticClientData = call.get();
this.duplicates.clear();
this.duplicates.addAll(monitoringStaticClientData.duplications);
monitoringStaticClientData.staticClientConnectionData
.stream()
.forEach(staticData -> {
final UpdatableTableItem updatableTableItem = this.tableMapping.get(staticData.id);
if (updatableTableItem != null) {
updatableTableItem.push(staticData);
} else {
log.error("Failed to find table entry for static data: {}", staticData);
}
});
}
}
}

View file

@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Set;
import java.util.function.Predicate;
import org.springframework.cache.CacheManager;
@ -20,6 +21,7 @@ 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.monitoring.MonitoringSEBConnectionData;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
@ -184,6 +186,18 @@ public interface ExamSessionService {
final Long examId,
final Predicate<ClientConnectionData> filter);
/** Get SEB client connection statically data for the given exam and list of connection ids.
* This is useful if you have monitoring real-time data as MonitoringSEBConnectionData for example and you need to
* complete this data with the more static client connection data of SEB client, one can reload the static data like
* so.
*
* @param examId the exam identifier
* @param connectionIds Set of client connection identifiers (modelIds)
* @return Result refer to the MonitoringStaticSEBConnectionData or to an error when happened */
Result<MonitoringStaticClientData> getMonitoringSEBConnectionStaticData(
final Long examId,
final Set<Long> connectionIds);
/** Gets all connection tokens of client connection that are in ACTIVE state and related to a specified exam
* from persistence storage without caching involved.
*

View file

@ -13,7 +13,11 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -21,8 +25,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
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.ClientEvent.EventType;
import ch.ethz.seb.sebserver.gbl.model.session.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.model.session.ClientStaticData;
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ClientIndicator;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.PendingNotificationIndication;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.indicator.PingIntervalClientIndicator;
@ -39,9 +47,10 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
protected ClientConnectionDataInternal(
final ClientConnection clientConnection,
final PendingNotificationIndication pendingNotificationIndication,
final List<ClientIndicator> clientIndicators) {
final List<ClientIndicator> clientIndicators,
final Set<Long> groups) {
super(clientConnection, clientIndicators);
super(clientConnection, clientIndicators, groups);
this.pendingNotificationIndication = pendingNotificationIndication;
this.indicatorMapping = new EnumMap<>(EventType.class);
@ -100,4 +109,48 @@ public class ClientConnectionDataInternal extends ClientConnectionData {
.isPresent();
}
/** 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 ClientConnectionDataInternal.this.clientConnection.id;
}
@Override
public ConnectionStatus getStatus() {
return ClientConnectionDataInternal.this.clientConnection.status;
}
@Override
public Map<Long, String> getIndicatorValues() {
return ClientConnectionDataInternal.this.indicatorValues
.stream()
.collect(Collectors.toMap(
iv -> iv.getIndicatorId(),
iv -> IndicatorValue.getDisplayValue(iv)));
}
@Override
public boolean isMissingPing() {
return BooleanUtils.isTrue(getMissingPing());
}
@Override
public boolean isPendingNotification() {
return BooleanUtils.isTrue(pendingNotification());
}
};
/** This is a static monitoring connection data wrapper/holder */
@JsonIgnore
public final ClientStaticData clientStaticData =
new ClientStaticData(
ClientConnectionDataInternal.this.clientConnection.id,
ClientConnectionDataInternal.this.clientConnection.connectionToken,
ClientConnectionDataInternal.this.clientConnection.userSessionId,
ClientConnectionDataInternal.this.clientConnection.info,
this.groups);
}

View file

@ -14,6 +14,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@ -44,6 +45,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.ClientMonitoringDataView;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Result;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
@ -425,7 +427,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
.getConnectionTokens(examId)
.getOrThrow()
.stream()
.map(token -> getConnectionDataInternal(token))
.map(this::getConnectionDataInternal)
.filter(Objects::nonNull)
.map(c -> {
statusMapping[c.clientConnection.status.code]++;
@ -443,6 +445,23 @@ public class ExamSessionServiceImpl implements ExamSessionService {
});
}
@Override
public synchronized Result<MonitoringStaticClientData> getMonitoringSEBConnectionStaticData(
final Long examId,
final Set<Long> connectionIds) {
this.duplicateCheck.clear();
this.duplicates.clear();
return this.clientConnectionDAO
.getConnectionTokens(examId)
.map(tokens -> tokens.stream()
.map(this::getForTokenAndCheckDuplication)
.filter(ccd -> connectionIds.contains(ccd.clientConnection.id))
.map(ccd -> ccd.clientStaticData)
.collect(Collectors.toList()))
.map(staticData -> new MonitoringStaticClientData(staticData, this.duplicates));
}
@Override
public Result<Collection<String>> getActiveConnectionTokens(final Long examId) {
return this.clientConnectionDAO
@ -587,4 +606,21 @@ public class ExamSessionServiceImpl implements ExamSessionService {
});
}
private final Map<String, Long> duplicateCheck = new HashMap<>();
private final Set<Long> duplicates = new HashSet<>();
private ClientConnectionDataInternal getForTokenAndCheckDuplication(final String token) {
final ClientConnectionDataInternal cc = this.examSessionCacheService.getClientConnection(token);
if (cc.clientConnection.status.establishedStatus) {
final Long id = this.duplicateCheck.put(
cc.clientConnection.userSessionId,
cc.getConnectionId());
if (id != null) {
this.duplicates.add(id);
this.duplicates.add(cc.getConnectionId());
}
}
return cc;
}
}

View file

@ -10,6 +10,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -58,17 +60,24 @@ public class InternalClientConnectionDataFactory {
result = new ClientConnectionDataInternal(
clientConnection,
() -> false,
this.clientIndicatorFactory.createFor(clientConnection));
this.clientIndicatorFactory.createFor(clientConnection),
getGroupIds(clientConnection));
} else {
result = new ClientConnectionDataInternal(
clientConnection,
() -> this.sebClientNotificationService
.hasAnyPendingNotification(clientConnection),
this.clientIndicatorFactory.createFor(clientConnection));
this.clientIndicatorFactory.createFor(clientConnection),
getGroupIds(clientConnection));
}
return result;
}
public Set<Long> getGroupIds(final ClientConnection clientConnection) {
// set client groups for connection
final Set<Long> clientGroupIds = new HashSet<>();
if (clientConnection.examId != null) {
final Collection<ClientGroup> clientGroups = this.clientGroupDAO
.allForExam(clientConnection.examId)
@ -80,13 +89,12 @@ public class InternalClientConnectionDataFactory {
if (!clientGroups.isEmpty()) {
clientGroups.forEach(clientGroup -> {
if (this.clientGroupMatcherService.isInGroup(clientConnection, clientGroup)) {
result.addToClientGroup(clientGroup);
clientGroupIds.add(clientGroup.id);
}
});
}
}
return result;
return clientGroupIds;
}
}

View file

@ -75,6 +75,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
private final ExamAdminService examAdminService;
private final DistributedIndicatorValueService distributedPingCache;
private final ClientIndicatorFactory clientIndicatorFactory;
private final InternalClientConnectionDataFactory internalClientConnectionDataFactory;
private final boolean isDistributedSetup;
protected SEBClientConnectionServiceImpl(
@ -84,7 +85,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
final SEBClientInstructionService sebInstructionService,
final ExamAdminService examAdminService,
final DistributedIndicatorValueService distributedPingCache,
final ClientIndicatorFactory clientIndicatorFactory) {
final ClientIndicatorFactory clientIndicatorFactory,
final InternalClientConnectionDataFactory internalClientConnectionDataFactory) {
this.examSessionService = examSessionService;
this.examSessionCacheService = examSessionService.getExamSessionCacheService();
@ -95,6 +97,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
this.examAdminService = examAdminService;
this.clientIndicatorFactory = clientIndicatorFactory;
this.distributedPingCache = distributedPingCache;
this.internalClientConnectionDataFactory = internalClientConnectionDataFactory;
this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed();
}
@ -740,7 +743,8 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
public Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection) {
return Result.tryCatch(() -> new ClientConnectionData(
clientConnection,
this.clientIndicatorFactory.getIndicatorValues(clientConnection)));
this.clientIndicatorFactory.getIndicatorValues(clientConnection),
this.internalClientConnectionDataFactory.getGroupIds(clientConnection)));
}
private void checkExamRunning(final Long examId, final String user, final String address) {

View file

@ -16,6 +16,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
@ -53,7 +55,9 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserInfo;
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringFullPageData;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringSEBConnectionData;
import ch.ethz.seb.sebserver.gbl.monitoring.MonitoringStaticClientData;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.PermissionDeniedException;
@ -262,6 +266,32 @@ public class ExamMonitoringController {
.getOrThrow();
}
@RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
API.EXAM_MONITORING_STATIC_CLIENT_DATA,
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public MonitoringStaticClientData getMonitoringStaticClientData(
@RequestParam(
name = API.PARAM_INSTITUTION_ID,
required = true,
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
@PathVariable(name = API.PARAM_PARENT_MODEL_ID, required = true) final Long examId,
@RequestParam(name = API.PARAM_MODEL_ID_LIST, required = true) final String clientConnectionIds) {
final Exam runningExam = checkPrivileges(institutionId, examId);
final Set<Long> ids = Stream.of(StringUtils.split(clientConnectionIds, Constants.LIST_SEPARATOR))
.map(Utils::toLong)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
return this.examSessionService
.getMonitoringSEBConnectionStaticData(runningExam.id, ids)
.getOrThrow();
}
@RequestMapping(
path = API.PARENT_MODEL_ID_VAR_PATH_SEGMENT +
API.EXAM_MONITORING_FULLPAGE,

View file

@ -25,7 +25,6 @@ import ch.ethz.seb.sebserver.gbl.api.APIMessage;
import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
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.Exam.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
@ -276,21 +275,14 @@ public class ModelObjectJSONGenerator {
Arrays.asList(
new SimpleIndicatorValue(1L, 1.0),
new SimpleIndicatorValue(2L, 2.0),
new SimpleIndicatorValue(3L, 3.0)));
new SimpleIndicatorValue(3L, 3.0)),
new HashSet<>(Arrays.asList(1L, 2L)));
System.out.println(domainObject.getClass().getSimpleName() + ":");
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
((ClientConnectionData) domainObject)
.addToClientGroup(new ClientGroup(1L, 1L, "group1", null, null, null, null));
((ClientConnectionData) domainObject)
.addToClientGroup(new ClientGroup(2L, 1L, "group2", null, null, null, null));
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() + ":");