SEBSERV-163 front-end implementation
This commit is contained in:
parent
4f2586e799
commit
5703f1cb43
13 changed files with 276 additions and 61 deletions
|
@ -28,7 +28,7 @@ public interface ClientGroupData extends Entity {
|
|||
MAC_OS("TODO"),
|
||||
I_OS("TODO");
|
||||
|
||||
final String queryString;
|
||||
public final String queryString;
|
||||
|
||||
private ClientOS(final String queryString) {
|
||||
this.queryString = queryString;
|
||||
|
|
|
@ -332,8 +332,10 @@ public final class ClientConnection implements GrantEntity {
|
|||
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;
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Collection;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
@ -156,6 +157,14 @@ public class ClientConnectionData implements GrantEntity {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(this.groups, other.groups)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean indicatorValuesEquals(final ClientConnectionData other) {
|
||||
if (this.indicatorValues.size() != other.indicatorValues.size()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroup;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientGroupType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ClientGroupData.ClientOS;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
public class ClientOSGroupMatcher implements ClientGroupConnectionMatcher {
|
||||
|
||||
@Override
|
||||
public ClientGroupType matcherType() {
|
||||
return ClientGroupType.CLIENT_OS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInGroup(final ClientConnection clientConnection, final ClientGroup group) {
|
||||
if (group == null
|
||||
|| group.type != ClientGroupType.CLIENT_OS
|
||||
|| clientConnection == null
|
||||
|| clientConnection.info == null) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final ClientOS osType = ClientOS.valueOf(group.getData());
|
||||
return clientConnection.info.contains(osType.queryString);
|
||||
} catch (final Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ 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.exam.ProctoringServiceSettings;
|
||||
|
@ -55,6 +56,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.GetClientGroups;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.indicator.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ConfirmPendingClientNotification;
|
||||
|
@ -191,6 +193,11 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final Collection<ClientGroup> clientGroups = restService.getBuilder(GetClientGroups.class)
|
||||
.withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, parentEntityKey.modelId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final RestCall<ClientConnectionData>.RestCallBuilder getConnectionData =
|
||||
restService.getBuilder(GetClientConnectionData.class)
|
||||
.withURIVariable(API.PARAM_PARENT_MODEL_ID, exam.getModelId())
|
||||
|
@ -205,7 +212,8 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
pageContext.copyOf(content),
|
||||
exam,
|
||||
getConnectionData,
|
||||
indicators);
|
||||
indicators,
|
||||
clientGroups);
|
||||
|
||||
// NOTIFICATIONS
|
||||
Supplier<EntityTable<ClientNotification>> _notificationTableSupplier = () -> null;
|
||||
|
|
|
@ -30,6 +30,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
|||
import ch.ethz.seb.sebserver.gbl.async.AsyncRunner;
|
||||
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.exam.ProctoringServiceSettings;
|
||||
|
@ -56,6 +57,7 @@ import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.clientgroup.GetClientGroups;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.indicator.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable;
|
||||
|
@ -140,6 +142,11 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final Collection<ClientGroup> clientGroups = this.restService.getBuilder(GetClientGroups.class)
|
||||
.withQueryParam(Indicator.FILTER_ATTR_EXAM_ID, entityKey.modelId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final Composite content = this.pageService.getWidgetFactory().defaultPageLayout(
|
||||
pageContext.getParent(),
|
||||
new LocTextKey("sebserver.monitoring.exam", exam.name));
|
||||
|
@ -166,6 +173,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
tablePane,
|
||||
exam,
|
||||
indicators,
|
||||
clientGroups,
|
||||
this.distributedSetup);
|
||||
guiUpdates.add(clientTable);
|
||||
|
||||
|
|
|
@ -128,6 +128,11 @@ public final class Form implements FormBinding {
|
|||
return this;
|
||||
}
|
||||
|
||||
Form putReadonlyField(final String name, final Control label, final Label field) {
|
||||
this.formFields.add(name, createReadonlyAccessor(label, field));
|
||||
return this;
|
||||
}
|
||||
|
||||
Form putReadonlyField(final String name, final Control label, final Browser field) {
|
||||
this.formFields.add(name, createReadonlyAccessor(label, field));
|
||||
return this;
|
||||
|
@ -308,7 +313,13 @@ public final class Form implements FormBinding {
|
|||
//@formatter:off
|
||||
private FormFieldAccessor createReadonlyAccessor(final Control label, final Text field) {
|
||||
return new FormFieldAccessor(label, field, null) {
|
||||
@Override public String getStringValue() { return null; }
|
||||
@Override public String getStringValue() { return field.getText(); }
|
||||
@Override public void setStringValue(final String value) { field.setText( (value == null) ? StringUtils.EMPTY : value); }
|
||||
};
|
||||
}
|
||||
private FormFieldAccessor createReadonlyAccessor(final Control label, final Label field) {
|
||||
return new FormFieldAccessor(label, field, null) {
|
||||
@Override public String getStringValue() { return field.getText(); }
|
||||
@Override public void setStringValue(final String value) { field.setText( (value == null) ? StringUtils.EMPTY : value); }
|
||||
};
|
||||
}
|
||||
|
@ -320,7 +331,7 @@ public final class Form implements FormBinding {
|
|||
}
|
||||
private FormFieldAccessor createAccessor(final Control label, final Text text, final Label errorLabel) {
|
||||
return new FormFieldAccessor(label, text, errorLabel) {
|
||||
@Override public String getStringValue() {return text.getText();}
|
||||
@Override public String getStringValue() { return text.getText(); }
|
||||
@Override public void setStringValue(final String value) {text.setText(value);}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
|
|||
int areaMinHeight = WidgetFactory.TEXT_AREA_INPUT_MIN_HEIGHT;
|
||||
boolean isColorBox = false;
|
||||
boolean isHTML = false;
|
||||
boolean isMarkup = false;
|
||||
|
||||
TextFieldBuilder(final String name, final LocTextKey label, final String value) {
|
||||
super(name, label, value);
|
||||
|
@ -76,6 +77,11 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TextFieldBuilder asMarkup() {
|
||||
this.isMarkup = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TextFieldBuilder asHTML(final int minHeight) {
|
||||
this.isHTML = true;
|
||||
this.areaMinHeight = minHeight;
|
||||
|
@ -118,6 +124,15 @@ public final class TextFieldBuilder extends FieldBuilder<String> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (readonly && this.isMarkup) {
|
||||
final Label label = new Label(fieldGrid, SWT.NONE);
|
||||
label.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
|
||||
label.setText(this.value);
|
||||
label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
|
||||
builder.form.putReadonlyField(this.name, titleLabel, label);
|
||||
return;
|
||||
}
|
||||
|
||||
final String testKey = (this.label != null) ? this.label.name : this.name;
|
||||
final LocTextKey label = getARIALabel(builder);
|
||||
final Text textInput = (this.isNumber)
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.Map;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
|
@ -22,6 +23,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||
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.exam.QuizData;
|
||||
|
@ -43,6 +45,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.DisposedOAuth2RestTemplateException;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.IndicatorData.ThresholdColor;
|
||||
import ch.ethz.seb.sebserver.gui.table.EntityTable;
|
||||
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
||||
|
||||
public class ClientConnectionDetails {
|
||||
|
||||
|
@ -52,6 +55,8 @@ public class ClientConnectionDetails {
|
|||
new LocTextKey("sebserver.monitoring.connection.form.exam");
|
||||
private final static LocTextKey CONNECTION_ID_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.form.id");
|
||||
private final static LocTextKey CONNECTION_GROUP_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.form.group");
|
||||
private final static LocTextKey CONNECTION_INFO_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.form.info");
|
||||
private final static LocTextKey CONNECTION_STATUS_TEXT_KEY =
|
||||
|
@ -62,6 +67,8 @@ public class ClientConnectionDetails {
|
|||
private final PageService pageService;
|
||||
private final ResourceService resourceService;
|
||||
private final Map<Long, IndicatorData> indicatorMapping;
|
||||
private final Map<Long, ClientGroup> clientGroupMapping;
|
||||
private final boolean hasClientGroups;
|
||||
private final RestCall<ClientConnectionData>.RestCallBuilder restCallBuilder;
|
||||
private final FormHandle<?> formHandle;
|
||||
private final ColorData colorData;
|
||||
|
@ -78,10 +85,11 @@ public class ClientConnectionDetails {
|
|||
final PageContext pageContext,
|
||||
final Exam exam,
|
||||
final RestCall<ClientConnectionData>.RestCallBuilder restCallBuilder,
|
||||
final Collection<Indicator> indicators) {
|
||||
final Collection<Indicator> indicators,
|
||||
final Collection<ClientGroup> clientGroups) {
|
||||
|
||||
final Display display = pageContext.getRoot().getDisplay();
|
||||
|
||||
this.hasClientGroups = clientGroups != null && !clientGroups.isEmpty();
|
||||
this.pageService = pageService;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.restCallBuilder = restCallBuilder;
|
||||
|
@ -92,6 +100,12 @@ public class ClientConnectionDetails {
|
|||
this.colorData,
|
||||
NUMBER_OF_NONE_INDICATOR_ROWS);
|
||||
|
||||
this.clientGroupMapping = clientGroups == null || clientGroups.isEmpty()
|
||||
? null
|
||||
: clientGroups
|
||||
.stream()
|
||||
.collect(Collectors.toMap(cg -> cg.id, Function.identity()));
|
||||
|
||||
final FormBuilder formBuilder = pageService.formBuilder(pageContext)
|
||||
.readonly(true)
|
||||
.addField(FormBuilder.text(
|
||||
|
@ -102,6 +116,12 @@ public class ClientConnectionDetails {
|
|||
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
|
||||
CONNECTION_ID_TEXT_KEY,
|
||||
Constants.EMPTY_NOTE))
|
||||
.addFieldIf(() -> this.hasClientGroups,
|
||||
() -> FormBuilder.text(
|
||||
ClientConnectionData.ATTR_CLIENT_GROUPS,
|
||||
CONNECTION_GROUP_TEXT_KEY,
|
||||
Constants.EMPTY_NOTE)
|
||||
.asMarkup())
|
||||
.addField(FormBuilder.text(
|
||||
ClientConnection.ATTR_INFO,
|
||||
CONNECTION_INFO_TEXT_KEY,
|
||||
|
@ -179,6 +199,13 @@ public class ClientConnectionDetails {
|
|||
ClientConnection.ATTR_INFO,
|
||||
this.connectionData.clientConnection.info);
|
||||
|
||||
if (this.hasClientGroups
|
||||
&& Constants.EMPTY_NOTE.equals(form.getFieldValue(ClientConnectionData.ATTR_CLIENT_GROUPS))) {
|
||||
form.setFieldValue(
|
||||
ClientConnectionData.ATTR_CLIENT_GROUPS,
|
||||
getGroupInfo());
|
||||
}
|
||||
|
||||
if (this.missingChanged) {
|
||||
// update status
|
||||
form.setFieldValue(
|
||||
|
@ -252,4 +279,20 @@ public class ClientConnectionDetails {
|
|||
}
|
||||
}
|
||||
|
||||
private String getGroupInfo() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
this.clientGroupMapping.keySet().stream().forEach(key -> {
|
||||
if (this.connectionData.groups.contains(key)) {
|
||||
final ClientGroup clientGroup = this.clientGroupMapping.get(key);
|
||||
sb.append(WidgetFactory.getTextWithBackgroundHTML(clientGroup.name, clientGroup.color));
|
||||
}
|
||||
});
|
||||
|
||||
if (sb.length() <= 0) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
} else {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.rap.rwt.RWT;
|
||||
import org.eclipse.swt.SWT;
|
||||
import org.eclipse.swt.graphics.Color;
|
||||
import org.eclipse.swt.graphics.Rectangle;
|
||||
|
@ -44,12 +45,13 @@ import ch.ethz.seb.sebserver.gbl.Constants;
|
|||
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;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection.ConnectionStatus;
|
||||
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||
import ch.ethz.seb.sebserver.gbl.monitoring.IndicatorValue;
|
||||
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;
|
||||
|
@ -60,31 +62,35 @@ import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
|
|||
|
||||
public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate {
|
||||
|
||||
private static final int[] TABLE_PROPORTIONS = new int[] { 3, 3, 2, 1 };
|
||||
|
||||
private static final int BOTTOM_PADDING = 20;
|
||||
private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3;
|
||||
//private static final int[] TABLE_PROPORTIONS = new int[] { 3, 3, 2, 1 };
|
||||
//private static final int NUMBER_OF_NONE_INDICATOR_COLUMNS = 3;
|
||||
|
||||
private static final String INDICATOR_NAME_TEXT_KEY_PREFIX =
|
||||
"sebserver.exam.indicator.type.description.";
|
||||
private final static LocTextKey CONNECTION_ID_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.id");
|
||||
private final static LocTextKey CONNECTION_ID_TOOLTIP_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.id" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
new LocTextKey(CONNECTION_ID_TEXT_KEY + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
private final static LocTextKey CONNECTION_GROUP_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.group");
|
||||
private final static LocTextKey CONNECTION_GROUP_TOOLTIP_TEXT_KEY =
|
||||
new LocTextKey(CONNECTION_GROUP_TEXT_KEY + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
private final static LocTextKey CONNECTION_INFO_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.info");
|
||||
private final static LocTextKey CONNECTION_INFO_TOOLTIP_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.info" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
new LocTextKey(CONNECTION_INFO_TEXT_KEY + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
private final static LocTextKey CONNECTION_STATUS_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.status");
|
||||
private final static LocTextKey CONNECTION_STATUS_TOOLTIP_TEXT_KEY =
|
||||
new LocTextKey("sebserver.monitoring.connection.list.column.status" + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
new LocTextKey(CONNECTION_STATUS_TEXT_KEY + Constants.TOOLTIP_TEXT_KEY_SUFFIX);
|
||||
|
||||
private final PageService pageService;
|
||||
private final Exam exam;
|
||||
private final boolean distributedSetup;
|
||||
|
||||
private final Map<Long, IndicatorData> indicatorMapping;
|
||||
private final Map<Long, ClientGroup> clientGroupMapping;
|
||||
private final Table table;
|
||||
private final ColorData colorData;
|
||||
private final Function<ClientConnectionData, String> localizedClientConnectionStatusNameFunction;
|
||||
|
@ -99,6 +105,10 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
private final Color darkFontColor;
|
||||
private final Color lightFontColor;
|
||||
|
||||
private final boolean hasClientGroups;
|
||||
private final int numberOfNoneIndicatorColumns;
|
||||
private final int[] tableProportions;
|
||||
|
||||
private boolean forceUpdateAll = false;
|
||||
|
||||
public ClientConnectionTable(
|
||||
|
@ -106,6 +116,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
final Composite tableRoot,
|
||||
final Exam exam,
|
||||
final Collection<Indicator> indicators,
|
||||
final Collection<ClientGroup> clientGroups,
|
||||
final boolean distributedSetup) {
|
||||
|
||||
this.pageService = pageService;
|
||||
|
@ -121,11 +132,23 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
this.darkFontColor = new Color(display, Constants.BLACK_RGB);
|
||||
this.lightFontColor = new Color(display, Constants.WHITE_RGB);
|
||||
|
||||
this.hasClientGroups = clientGroups != null && !clientGroups.isEmpty();
|
||||
this.numberOfNoneIndicatorColumns = this.hasClientGroups ? 4 : 3;
|
||||
this.tableProportions = this.hasClientGroups
|
||||
? new int[] { 3, 2, 3, 2, 1 }
|
||||
: new int[] { 3, 3, 2, 1 };
|
||||
|
||||
this.indicatorMapping = IndicatorData.createFormIndicators(
|
||||
indicators,
|
||||
display,
|
||||
this.colorData,
|
||||
NUMBER_OF_NONE_INDICATOR_COLUMNS);
|
||||
this.numberOfNoneIndicatorColumns);
|
||||
|
||||
this.clientGroupMapping = clientGroups == null || clientGroups.isEmpty()
|
||||
? null
|
||||
: clientGroups
|
||||
.stream()
|
||||
.collect(Collectors.toMap(cg -> cg.id, Function.identity()));
|
||||
|
||||
this.localizedClientConnectionStatusNameFunction =
|
||||
resourceService.localizedClientConnectionStatusNameFunction();
|
||||
|
@ -136,11 +159,11 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
gridLayout.marginWidth = 100;
|
||||
gridLayout.marginRight = 100;
|
||||
this.table.setLayout(gridLayout);
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
|
||||
final GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
|
||||
this.table.setLayoutData(gridData);
|
||||
this.table.setHeaderVisible(true);
|
||||
this.table.setLinesVisible(true);
|
||||
|
||||
this.table.setData(RWT.MARKUP_ENABLED, Boolean.TRUE);
|
||||
this.table.addListener(SWT.Selection, event -> this.notifySelectionChange());
|
||||
this.table.addListener(SWT.MouseUp, this::notifyTableInfoClick);
|
||||
|
||||
|
@ -148,6 +171,12 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
this.table,
|
||||
CONNECTION_ID_TEXT_KEY,
|
||||
CONNECTION_ID_TOOLTIP_TEXT_KEY);
|
||||
if (this.clientGroupMapping != null && !this.clientGroupMapping.isEmpty()) {
|
||||
widgetFactory.tableColumnLocalized(
|
||||
this.table,
|
||||
CONNECTION_GROUP_TEXT_KEY,
|
||||
CONNECTION_GROUP_TOOLTIP_TEXT_KEY);
|
||||
}
|
||||
widgetFactory.tableColumnLocalized(
|
||||
this.table,
|
||||
CONNECTION_INFO_TEXT_KEY,
|
||||
|
@ -285,10 +314,10 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
@Override
|
||||
public void update(final MonitoringStatus monitoringStatus) {
|
||||
final Collection<ClientConnectionData> connectionData = monitoringStatus.getConnectionData();
|
||||
|
||||
final boolean sizeChanged = connectionData.size() != this.table.getItemCount();
|
||||
final boolean needsSync = monitoringStatus.statusFilterChanged() ||
|
||||
this.forceUpdateAll ||
|
||||
connectionData.size() != this.table.getItemCount() ||
|
||||
sizeChanged ||
|
||||
(this.tableMapping != null &&
|
||||
this.table != null &&
|
||||
this.tableMapping.size() != this.table.getItemCount())
|
||||
|
@ -326,6 +355,7 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
|
||||
this.forceUpdateAll = false;
|
||||
this.needsSort = sizeChanged;
|
||||
updateGUI();
|
||||
}
|
||||
|
||||
|
@ -352,19 +382,21 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
if (this.tableWidth != area.width) {
|
||||
|
||||
// proportions size
|
||||
final int pSize = TABLE_PROPORTIONS[0] +
|
||||
TABLE_PROPORTIONS[1] +
|
||||
TABLE_PROPORTIONS[2] +
|
||||
TABLE_PROPORTIONS[3] * this.indicatorMapping.size();
|
||||
final int pSize = this.tableProportions[0] +
|
||||
this.tableProportions[1] +
|
||||
this.tableProportions[2] +
|
||||
this.tableProportions[3] +
|
||||
(this.hasClientGroups ? this.tableProportions[4] : 0)
|
||||
* this.indicatorMapping.size();
|
||||
final int columnUnitSize = (pSize > 0)
|
||||
? area.width / pSize
|
||||
: area.width / TABLE_PROPORTIONS.length - 1 + this.indicatorMapping.size();
|
||||
: area.width / this.tableProportions.length - 1 + this.indicatorMapping.size();
|
||||
|
||||
final TableColumn[] columns = this.table.getColumns();
|
||||
for (int i = 0; i < columns.length; i++) {
|
||||
final int proportionFactor = (i < TABLE_PROPORTIONS.length)
|
||||
? TABLE_PROPORTIONS[i]
|
||||
: TABLE_PROPORTIONS[TABLE_PROPORTIONS.length - 1];
|
||||
final int proportionFactor = (i < this.tableProportions.length)
|
||||
? this.tableProportions[i]
|
||||
: this.tableProportions[this.tableProportions.length - 1];
|
||||
columns[i].setWidth(proportionFactor * columnUnitSize);
|
||||
}
|
||||
this.tableWidth = area.width;
|
||||
|
@ -412,7 +444,8 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
private final class UpdatableTableItem implements Comparable<UpdatableTableItem> {
|
||||
|
||||
final Long connectionId;
|
||||
private boolean changed = false;
|
||||
private boolean dataChanged = false;
|
||||
private boolean indicatorValueChanged = false;
|
||||
private ClientConnectionData connectionData;
|
||||
private int thresholdsWeight;
|
||||
private int[] indicatorWeights = null;
|
||||
|
@ -424,17 +457,34 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
|
||||
private void update(final TableItem tableItem, final boolean force) {
|
||||
if (force || this.changed) {
|
||||
update(tableItem);
|
||||
if (force || this.dataChanged) {
|
||||
updateData(tableItem);
|
||||
}
|
||||
this.changed = false;
|
||||
if (force || this.indicatorValueChanged) {
|
||||
updateIndicatorValues(tableItem);
|
||||
}
|
||||
this.dataChanged = false;
|
||||
this.indicatorValueChanged = false;
|
||||
}
|
||||
|
||||
private void update(final TableItem tableItem) {
|
||||
updateData(tableItem);
|
||||
private void updateData(final TableItem tableItem) {
|
||||
tableItem.setText(0, getConnectionIdentifier());
|
||||
if (ClientConnectionTable.this.hasClientGroups) {
|
||||
tableItem.setText(1, getGroupInfo());
|
||||
tableItem.setText(2, getConnectionInfo());
|
||||
tableItem.setText(
|
||||
3,
|
||||
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
|
||||
.apply(this.connectionData));
|
||||
} else {
|
||||
tableItem.setText(1, getConnectionInfo());
|
||||
tableItem.setText(
|
||||
2,
|
||||
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction
|
||||
.apply(this.connectionData));
|
||||
}
|
||||
if (this.connectionData != null) {
|
||||
updateConnectionStatusColor(tableItem);
|
||||
updateIndicatorValues(tableItem);
|
||||
updateDuplicateColor(tableItem);
|
||||
updateNotifications(tableItem);
|
||||
}
|
||||
|
@ -451,19 +501,12 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
}
|
||||
|
||||
private void updateData(final TableItem tableItem) {
|
||||
tableItem.setText(0, getConnectionIdentifier());
|
||||
tableItem.setText(1, getConnectionInfo());
|
||||
tableItem.setText(
|
||||
2,
|
||||
ClientConnectionTable.this.localizedClientConnectionStatusNameFunction.apply(this.connectionData));
|
||||
}
|
||||
|
||||
private void updateConnectionStatusColor(final TableItem tableItem) {
|
||||
final Color statusColor = ClientConnectionTable.this.colorData.getStatusColor(this.connectionData);
|
||||
final Color statusTextColor = ClientConnectionTable.this.colorData.getStatusTextColor(statusColor);
|
||||
tableItem.setBackground(2, statusColor);
|
||||
tableItem.setForeground(2, statusTextColor);
|
||||
final int index = ClientConnectionTable.this.hasClientGroups ? 3 : 2;
|
||||
tableItem.setBackground(index, statusColor);
|
||||
tableItem.setForeground(index, statusTextColor);
|
||||
}
|
||||
|
||||
private void updateDuplicateColor(final TableItem tableItem) {
|
||||
|
@ -578,6 +621,22 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
||||
private String getGroupInfo() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
ClientConnectionTable.this.clientGroupMapping.keySet().stream().forEach(key -> {
|
||||
if (this.connectionData.groups.contains(key)) {
|
||||
final ClientGroup clientGroup = ClientConnectionTable.this.clientGroupMapping.get(key);
|
||||
sb.append(WidgetFactory.getTextWithBackgroundHTML(clientGroup.name, clientGroup.color));
|
||||
}
|
||||
});
|
||||
|
||||
if (sb.length() <= 0) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
} else {
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
String getConnectionIdentifier() {
|
||||
if (this.connectionData != null && this.connectionData.clientConnection.userSessionId != null) {
|
||||
return this.connectionData.clientConnection.userSessionId;
|
||||
|
@ -587,15 +646,16 @@ public final class ClientConnectionTable implements FullPageMonitoringGUIUpdate
|
|||
}
|
||||
|
||||
void push(final ClientConnectionData connectionData) {
|
||||
this.changed = this.connectionData == null ||
|
||||
this.dataChanged = this.connectionData == null ||
|
||||
!this.connectionData.dataEquals(connectionData);
|
||||
final boolean statusChanged = this.connectionData == null ||
|
||||
this.connectionData.clientConnection.status != connectionData.clientConnection.status;
|
||||
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);
|
||||
|
||||
if (statusChanged || notificationChanged) {
|
||||
if (this.dataChanged || notificationChanged) {
|
||||
ClientConnectionTable.this.needsSort = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1053,25 +1053,27 @@ public class WidgetFactory {
|
|||
}
|
||||
}
|
||||
|
||||
public String getColorValueHTML(final ClientGroupData data) {
|
||||
public static String getTextWithBackgroundHTML(final String text, final String color) {
|
||||
return new StringBuilder().append("<span style='padding: 5px 5px 5px 5px; background-color: #")
|
||||
.append(color)
|
||||
.append(";")
|
||||
.append((Utils.darkColorContrast(Utils.parseRGB(color)))
|
||||
? "color: #4a4a4a; "
|
||||
: "color: #FFFFFF;")
|
||||
.append("'>")
|
||||
.append(text)
|
||||
// .append(" ")
|
||||
.append("</span>")
|
||||
.toString();
|
||||
}
|
||||
|
||||
public static String getColorValueHTML(final ClientGroupData data) {
|
||||
final String color = data.getColor();
|
||||
if (StringUtils.isBlank(color)) {
|
||||
return Constants.EMPTY_NOTE;
|
||||
}
|
||||
|
||||
return new StringBuilder().append("<span style='padding: 2px 5px 2px 5px; background-color: #")
|
||||
.append(color)
|
||||
.append("; ")
|
||||
.append((Utils.darkColorContrast(Utils.parseRGB(color)))
|
||||
? "color: #4a4a4a; "
|
||||
: "color: #FFFFFF;")
|
||||
.append("'> ")
|
||||
.append(" (#")
|
||||
.append(color)
|
||||
.append(") ")
|
||||
.append("</span>")
|
||||
.toString();
|
||||
|
||||
return getTextWithBackgroundHTML(color, color);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1918,6 +1918,8 @@ sebserver.monitoring.exam=Monitoring Exam: {0}
|
|||
|
||||
sebserver.monitoring.connection.list.column.id=User Name or Session
|
||||
sebserver.monitoring.connection.list.column.id.tooltip=The user session identifier or username sent by the SEB client after LMS login
|
||||
sebserver.monitoring.connection.list.column.group=Client Group(s)
|
||||
sebserver.monitoring.connection.list.column.group.tooltip=The client group(s) the connection belongs to
|
||||
sebserver.monitoring.connection.list.column.info=Connection Info
|
||||
sebserver.monitoring.connection.list.column.info.tooltip=Format: IP Address,SEB Version, OSName
|
||||
sebserver.monitoring.connection.list.column.status=Status
|
||||
|
@ -1925,6 +1927,8 @@ sebserver.monitoring.connection.list.column.status.tooltip=The current connectio
|
|||
|
||||
sebserver.monitoring.connection.form.id=User Name or Session
|
||||
sebserver.monitoring.connection.form.id.tooltip=The user session identifier or username sent by the SEB client after LMS login
|
||||
sebserver.monitoring.connection.form.group=Client Group(s)
|
||||
sebserver.monitoring.connection.form.group.tooltip=The client groups this SEB client belongs to
|
||||
sebserver.monitoring.connection.form.info=Connection Info
|
||||
sebserver.monitoring.connection.form.info.tooltip=Format: IP Address,SEB Version, OSName
|
||||
sebserver.monitoring.connection.form.status=Status
|
||||
|
|
|
@ -25,6 +25,7 @@ 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;
|
||||
|
@ -279,6 +280,13 @@ public class ModelObjectJSONGenerator {
|
|||
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));
|
||||
|
||||
domainObject = new ClientEvent(1L, 1L, EventType.WARN_LOG,
|
||||
System.currentTimeMillis(), System.currentTimeMillis(), 123.0, "text");
|
||||
System.out.println(domainObject.getClass().getSimpleName() + ":");
|
||||
|
|
Loading…
Reference in a new issue