SEBSERV-240 implementation
This commit is contained in:
parent
6d56e71dbe
commit
a35ba48844
15 changed files with 263 additions and 49 deletions
|
@ -213,6 +213,7 @@ public final class API {
|
||||||
public static final String EXAM_PROCTORING_ATTR_ALLOW_CHAT = "allow_chat";
|
public static final String EXAM_PROCTORING_ATTR_ALLOW_CHAT = "allow_chat";
|
||||||
|
|
||||||
public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection";
|
public static final String SEB_CLIENT_CONNECTION_ENDPOINT = "/seb-client-connection";
|
||||||
|
public static final String SEB_CLIENT_CONNECTION_DATA_ENDPOINT = "/data";
|
||||||
|
|
||||||
public static final String SEB_CLIENT_EVENT_ENDPOINT = "/seb-client-event";
|
public static final String SEB_CLIENT_EVENT_ENDPOINT = "/seb-client-event";
|
||||||
public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search";
|
public static final String SEB_CLIENT_EVENT_SEARCH_PATH_SEGMENT = "/search";
|
||||||
|
|
|
@ -17,10 +17,12 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public class ClientConnectionData {
|
public class ClientConnectionData implements GrantEntity {
|
||||||
|
|
||||||
public static final String ATTR_CLIENT_CONNECTION = "cData";
|
public static final String ATTR_CLIENT_CONNECTION = "cData";
|
||||||
public static final String ATTR_INDICATOR_VALUE = "iValues";
|
public static final String ATTR_INDICATOR_VALUE = "iValues";
|
||||||
|
@ -48,7 +50,7 @@ public class ClientConnectionData {
|
||||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ClientConnectionData(
|
public ClientConnectionData(
|
||||||
final ClientConnection clientConnection,
|
final ClientConnection clientConnection,
|
||||||
final List<? extends IndicatorValue> indicatorValues) {
|
final List<? extends IndicatorValue> indicatorValues) {
|
||||||
|
|
||||||
|
@ -58,6 +60,26 @@ public class ClientConnectionData {
|
||||||
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
this.indicatorValues = Utils.immutableListOf(indicatorValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityType entityType() {
|
||||||
|
return this.clientConnection.entityType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.clientConnection.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModelId() {
|
||||||
|
return this.clientConnection.getModelId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getInstitutionId() {
|
||||||
|
return this.clientConnection.getInstitutionId();
|
||||||
|
}
|
||||||
|
|
||||||
@JsonProperty(ATTR_MISSING_PING)
|
@JsonProperty(ATTR_MISSING_PING)
|
||||||
public Boolean getMissingPing() {
|
public Boolean getMissingPing() {
|
||||||
return this.missingPing;
|
return this.missingPing;
|
||||||
|
|
|
@ -9,15 +9,20 @@
|
||||||
package ch.ethz.seb.sebserver.gui.content.monitoring;
|
package ch.ethz.seb.sebserver.gui.content.monitoring;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
import ch.ethz.seb.sebserver.gbl.model.Domain;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
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;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.IndicatorValue;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||||
|
@ -29,7 +34,7 @@ import ch.ethz.seb.sebserver.gui.service.page.TemplateComposer;
|
||||||
import ch.ethz.seb.sebserver.gui.service.push.ServerPushService;
|
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.RestService;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
|
||||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionPage;
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetFinishedExamClientConnectionPage;
|
||||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
|
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition;
|
||||||
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
|
import ch.ethz.seb.sebserver.gui.table.ColumnDefinition.TableFilterAttribute;
|
||||||
import ch.ethz.seb.sebserver.gui.table.TableBuilder;
|
import ch.ethz.seb.sebserver.gui.table.TableBuilder;
|
||||||
|
@ -91,29 +96,33 @@ public class FinishedExam implements TemplateComposer {
|
||||||
.call()
|
.call()
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final TableBuilder<ClientConnection> tableBuilder =
|
final Composite content = this.pageService.getWidgetFactory().defaultPageLayout(
|
||||||
this.pageService.entityTableBuilder(restService.getRestCall(GetClientConnectionPage.class))
|
pageContext.getParent(),
|
||||||
|
TITLE_TEXT_KEY);
|
||||||
|
|
||||||
|
final TableBuilder<ClientConnectionData> tableBuilder =
|
||||||
|
this.pageService.entityTableBuilder(restService.getRestCall(GetFinishedExamClientConnectionPage.class))
|
||||||
.withEmptyMessage(EMPTY_LIST_TEXT_KEY)
|
.withEmptyMessage(EMPTY_LIST_TEXT_KEY)
|
||||||
.withPaging(10)
|
.withPaging(this.pageSize)
|
||||||
.withStaticFilter(ClientConnection.FILTER_ATTR_EXAM_ID, examKey.modelId)
|
.withStaticFilter(ClientConnection.FILTER_ATTR_EXAM_ID, examKey.modelId)
|
||||||
|
|
||||||
.withColumn(new ColumnDefinition<>(
|
.withColumn(new ColumnDefinition<ClientConnectionData>(
|
||||||
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
|
Domain.CLIENT_CONNECTION.ATTR_EXAM_USER_SESSION_ID,
|
||||||
TABLE_COLUMN_NAME,
|
TABLE_COLUMN_NAME,
|
||||||
ClientConnection::getUserSessionId)
|
c -> c.clientConnection.getUserSessionId())
|
||||||
.withFilter(this.nameFilter))
|
.withFilter(this.nameFilter))
|
||||||
|
|
||||||
.withColumn(new ColumnDefinition<>(
|
.withColumn(new ColumnDefinition<ClientConnectionData>(
|
||||||
ClientConnection.ATTR_INFO,
|
ClientConnection.ATTR_INFO,
|
||||||
TABLE_COLUMN_INFO,
|
TABLE_COLUMN_INFO,
|
||||||
ClientConnection::getInfo)
|
c -> c.clientConnection.getInfo())
|
||||||
.withFilter(this.infoFilter))
|
.withFilter(this.infoFilter))
|
||||||
|
|
||||||
.withColumn(new ColumnDefinition<ClientConnection>(
|
.withColumn(new ColumnDefinition<ClientConnectionData>(
|
||||||
Domain.CLIENT_CONNECTION.ATTR_STATUS,
|
Domain.CLIENT_CONNECTION.ATTR_STATUS,
|
||||||
TABLE_COLUMN_STATUS,
|
TABLE_COLUMN_STATUS,
|
||||||
row -> this.pageService.getResourceService()
|
row -> this.pageService.getResourceService()
|
||||||
.localizedClientConnectionStatusName(row.getStatus()))
|
.localizedClientConnectionStatusName(row.clientConnection.getStatus()))
|
||||||
.withFilter(this.statusFilter))
|
.withFilter(this.statusFilter))
|
||||||
|
|
||||||
.withDefaultAction(t -> actionBuilder
|
.withDefaultAction(t -> actionBuilder
|
||||||
|
@ -121,7 +130,25 @@ public class FinishedExam implements TemplateComposer {
|
||||||
.withParentEntityKey(examKey)
|
.withParentEntityKey(examKey)
|
||||||
.create());
|
.create());
|
||||||
|
|
||||||
tableBuilder.compose(pageContext);
|
indicators.stream().forEach(indicator -> {
|
||||||
|
tableBuilder.withColumn(new ColumnDefinition<>(
|
||||||
|
indicator.name,
|
||||||
|
new LocTextKey(indicator.name),
|
||||||
|
indicatorValueFunction(indicator)));
|
||||||
|
});
|
||||||
|
|
||||||
|
tableBuilder.compose(pageContext.copyOf(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function<ClientConnectionData, String> indicatorValueFunction(final Indicator indicator) {
|
||||||
|
return clientConnectionData -> {
|
||||||
|
return clientConnectionData.indicatorValues
|
||||||
|
.stream()
|
||||||
|
.filter(indicatorValue -> indicatorValue.getIndicatorId().equals(indicator.id))
|
||||||
|
.findFirst()
|
||||||
|
.map(iv -> IndicatorValue.getDisplayValue(iv, indicator.type))
|
||||||
|
.orElse(Constants.EMPTY_NOTE);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetFinishedExamClientConnection extends RestCall<ClientConnectionData> {
|
||||||
|
|
||||||
|
public GetFinishedExamClientConnection() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.GET_SINGLE,
|
||||||
|
EntityType.CLIENT_CONNECTION,
|
||||||
|
new TypeReference<ClientConnectionData>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.SEB_CLIENT_CONNECTION_ENDPOINT
|
||||||
|
+ API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT
|
||||||
|
+ API.MODEL_ID_VAR_PATH_SEGMENT);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@GuiProfile
|
||||||
|
public class GetFinishedExamClientConnectionPage extends RestCall<Page<ClientConnectionData>> {
|
||||||
|
|
||||||
|
public GetFinishedExamClientConnectionPage() {
|
||||||
|
super(new TypeKey<>(
|
||||||
|
CallType.GET_PAGE,
|
||||||
|
EntityType.CLIENT_CONNECTION,
|
||||||
|
new TypeReference<Page<ClientConnectionData>>() {
|
||||||
|
}),
|
||||||
|
HttpMethod.GET,
|
||||||
|
MediaType.APPLICATION_FORM_URLENCODED,
|
||||||
|
API.SEB_CLIENT_CONNECTION_ENDPOINT + API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import java.util.Collection;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
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;
|
||||||
|
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;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
|
||||||
|
@ -184,4 +185,11 @@ public interface SEBClientConnectionService {
|
||||||
* @param instructionConfirm the instruction confirm identifier */
|
* @param instructionConfirm the instruction confirm identifier */
|
||||||
void confirmInstructionDone(String connectionToken, String instructionConfirm);
|
void confirmInstructionDone(String connectionToken, String instructionConfirm);
|
||||||
|
|
||||||
|
/** Use this to get the get the specific indicator values for a given client connection.
|
||||||
|
*
|
||||||
|
* @param clientConnection The client connection values
|
||||||
|
* @return Result refer to ClientConnectionData instance containing the given clientConnection plus the indicator
|
||||||
|
* values or to an error when happened */
|
||||||
|
Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,14 @@ public class ClientIndicatorFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ClientIndicator> createFor(final ClientConnection clientConnection) {
|
public List<ClientIndicator> createFor(final ClientConnection clientConnection) {
|
||||||
final List<ClientIndicator> result = new ArrayList<>();
|
return createFor(clientConnection, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ClientIndicator> createFor(
|
||||||
|
final ClientConnection clientConnection,
|
||||||
|
final boolean enableCachingOverride) {
|
||||||
|
|
||||||
|
final List<ClientIndicator> result = new ArrayList<>();
|
||||||
if (clientConnection.examId == null) {
|
if (clientConnection.examId == null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +88,7 @@ public class ClientIndicatorFactory {
|
||||||
indicatorDef,
|
indicatorDef,
|
||||||
clientConnection.id,
|
clientConnection.id,
|
||||||
clientConnection.status.clientActiveStatus,
|
clientConnection.status.clientActiveStatus,
|
||||||
this.enableCaching);
|
this.enableCaching || enableCachingOverride);
|
||||||
|
|
||||||
result.add(indicator);
|
result.add(indicator);
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
|
@ -111,7 +117,7 @@ public class ClientIndicatorFactory {
|
||||||
indicator,
|
indicator,
|
||||||
clientConnection.id,
|
clientConnection.id,
|
||||||
clientConnection.status.clientActiveStatus,
|
clientConnection.status.clientActiveStatus,
|
||||||
this.enableCaching);
|
this.enableCaching || enableCachingOverride);
|
||||||
result.add(pingIndicator);
|
result.add(pingIndicator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
private final SEBClientConfigDAO sebClientConfigDAO;
|
private final SEBClientConfigDAO sebClientConfigDAO;
|
||||||
private final SEBClientInstructionService sebInstructionService;
|
private final SEBClientInstructionService sebInstructionService;
|
||||||
private final ExamAdminService examAdminService;
|
private final ExamAdminService examAdminService;
|
||||||
|
private final ClientIndicatorFactory clientIndicatorFactory;
|
||||||
// TODO get rid of this dependency and use application events for signaling client connection state changes
|
// TODO get rid of this dependency and use application events for signaling client connection state changes
|
||||||
private final DistributedIndicatorValueService distributedPingCache;
|
private final DistributedIndicatorValueService distributedPingCache;
|
||||||
private final boolean isDistributedSetup;
|
private final boolean isDistributedSetup;
|
||||||
|
@ -84,6 +85,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
final SEBClientConfigDAO sebClientConfigDAO,
|
final SEBClientConfigDAO sebClientConfigDAO,
|
||||||
final SEBClientInstructionService sebInstructionService,
|
final SEBClientInstructionService sebInstructionService,
|
||||||
final ExamAdminService examAdminService,
|
final ExamAdminService examAdminService,
|
||||||
|
final ClientIndicatorFactory clientIndicatorFactory,
|
||||||
final DistributedIndicatorValueService distributedPingCache) {
|
final DistributedIndicatorValueService distributedPingCache) {
|
||||||
|
|
||||||
this.examSessionService = examSessionService;
|
this.examSessionService = examSessionService;
|
||||||
|
@ -94,6 +96,7 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
this.sebClientConfigDAO = sebClientConfigDAO;
|
this.sebClientConfigDAO = sebClientConfigDAO;
|
||||||
this.sebInstructionService = sebInstructionService;
|
this.sebInstructionService = sebInstructionService;
|
||||||
this.examAdminService = examAdminService;
|
this.examAdminService = examAdminService;
|
||||||
|
this.clientIndicatorFactory = clientIndicatorFactory;
|
||||||
this.distributedPingCache = distributedPingCache;
|
this.distributedPingCache = distributedPingCache;
|
||||||
this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed();
|
this.isDistributedSetup = sebInstructionService.getWebserviceInfo().isDistributed();
|
||||||
}
|
}
|
||||||
|
@ -702,6 +705,13 @@ public class SEBClientConnectionServiceImpl implements SEBClientConnectionServic
|
||||||
this.sebInstructionService.confirmInstructionDone(connectionToken, instructionConfirm);
|
this.sebInstructionService.confirmInstructionDone(connectionToken, instructionConfirm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<ClientConnectionData> getIndicatorValues(final ClientConnection clientConnection) {
|
||||||
|
return Result.tryCatch(() -> new ClientConnectionData(
|
||||||
|
clientConnection,
|
||||||
|
this.clientIndicatorFactory.createFor(clientConnection, true)));
|
||||||
|
}
|
||||||
|
|
||||||
private void checkExamRunning(final Long examId) {
|
private void checkExamRunning(final Long examId) {
|
||||||
if (examId != null && !this.examSessionService.isExamRunning(examId)) {
|
if (examId != null && !this.examSessionService.isExamRunning(examId)) {
|
||||||
examNotRunningException(examId);
|
examNotRunningException(examId);
|
||||||
|
|
|
@ -21,7 +21,7 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AbstractClientIndicator.class);
|
private static final Logger log = LoggerFactory.getLogger(AbstractClientIndicator.class);
|
||||||
|
|
||||||
protected final DistributedIndicatorValueService distributedPingCache;
|
protected final DistributedIndicatorValueService distributedIndicatorValueService;
|
||||||
|
|
||||||
protected Long indicatorId = -1L;
|
protected Long indicatorId = -1L;
|
||||||
protected Long examId = -1L;
|
protected Long examId = -1L;
|
||||||
|
@ -38,9 +38,9 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
|
||||||
|
|
||||||
protected long lastUpdate = 0;
|
protected long lastUpdate = 0;
|
||||||
|
|
||||||
public AbstractClientIndicator(final DistributedIndicatorValueService distributedPingCache) {
|
public AbstractClientIndicator(final DistributedIndicatorValueService distributedIndicatorValueService) {
|
||||||
super();
|
super();
|
||||||
this.distributedPingCache = distributedPingCache;
|
this.distributedIndicatorValueService = distributedIndicatorValueService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,10 +70,11 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
|
||||||
|
|
||||||
if (!this.cachingEnabled && this.active) {
|
if (!this.cachingEnabled && this.active) {
|
||||||
try {
|
try {
|
||||||
this.ditributedIndicatorValueRecordId = this.distributedPingCache.initIndicatorForConnection(
|
this.ditributedIndicatorValueRecordId =
|
||||||
connectionId,
|
this.distributedIndicatorValueService.initIndicatorForConnection(
|
||||||
getType(),
|
connectionId,
|
||||||
initValue());
|
getType(),
|
||||||
|
initValue());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
tryRecoverIndicatorRecord();
|
tryRecoverIndicatorRecord();
|
||||||
}
|
}
|
||||||
|
@ -94,7 +95,7 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.ditributedIndicatorValueRecordId = this.distributedPingCache.initIndicatorForConnection(
|
this.ditributedIndicatorValueRecordId = this.distributedIndicatorValueService.initIndicatorForConnection(
|
||||||
this.connectionId,
|
this.connectionId,
|
||||||
getType(),
|
getType(),
|
||||||
initValue());
|
initValue());
|
||||||
|
@ -126,18 +127,18 @@ public abstract class AbstractClientIndicator implements ClientIndicator {
|
||||||
public double getValue() {
|
public double getValue() {
|
||||||
|
|
||||||
if (this.initialized && !this.cachingEnabled && this.active
|
if (this.initialized && !this.cachingEnabled && this.active
|
||||||
&& this.lastUpdate != this.distributedPingCache.lastUpdate()) {
|
&& this.lastUpdate != this.distributedIndicatorValueService.lastUpdate()) {
|
||||||
|
|
||||||
if (this.ditributedIndicatorValueRecordId == null) {
|
if (this.ditributedIndicatorValueRecordId == null) {
|
||||||
this.tryRecoverIndicatorRecord();
|
this.tryRecoverIndicatorRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Long indicatorValue = this.distributedPingCache
|
final Long indicatorValue = this.distributedIndicatorValueService
|
||||||
.getIndicatorValue(this.ditributedIndicatorValueRecordId);
|
.getIndicatorValue(this.ditributedIndicatorValueRecordId);
|
||||||
if (indicatorValue != null) {
|
if (indicatorValue != null) {
|
||||||
this.currentValue = indicatorValue.doubleValue();
|
this.currentValue = indicatorValue.doubleValue();
|
||||||
}
|
}
|
||||||
this.lastUpdate = this.distributedPingCache.lastUpdate();
|
this.lastUpdate = this.distributedIndicatorValueService.lastUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.currentValue;
|
return this.currentValue;
|
||||||
|
|
|
@ -75,7 +75,7 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
|
||||||
|
|
||||||
// update active indicator value record on persistent when caching is not enabled
|
// update active indicator value record on persistent when caching is not enabled
|
||||||
if (this.active && this.ditributedIndicatorValueRecordId != null) {
|
if (this.active && this.ditributedIndicatorValueRecordId != null) {
|
||||||
this.distributedPingCache.updateIndicatorValue(
|
this.distributedIndicatorValueService.updateIndicatorValue(
|
||||||
this.ditributedIndicatorValueRecordId,
|
this.ditributedIndicatorValueRecordId,
|
||||||
numberOfLogs.longValue());
|
numberOfLogs.longValue());
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ public abstract class AbstractLogLevelCountIndicator extends AbstractLogIndicato
|
||||||
private void valueChanged(final String eventText) {
|
private void valueChanged(final String eventText) {
|
||||||
if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) {
|
if (this.tags == null || this.tags.length == 0 || hasTag(eventText)) {
|
||||||
if (super.ditributedIndicatorValueRecordId != null) {
|
if (super.ditributedIndicatorValueRecordId != null) {
|
||||||
this.distributedPingCache.incrementIndicatorValue(super.ditributedIndicatorValueRecordId);
|
this.distributedIndicatorValueService.incrementIndicatorValue(super.ditributedIndicatorValueRecordId);
|
||||||
}
|
}
|
||||||
this.currentValue = getValue() + 1d;
|
this.currentValue = getValue() + 1d;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator {
|
||||||
private void valueChanged(final String text, final double value) {
|
private void valueChanged(final String text, final double value) {
|
||||||
if (this.tags == null || this.tags.length == 0 || hasTag(text)) {
|
if (this.tags == null || this.tags.length == 0 || hasTag(text)) {
|
||||||
if (super.ditributedIndicatorValueRecordId != null) {
|
if (super.ditributedIndicatorValueRecordId != null) {
|
||||||
if (!this.distributedPingCache.updateIndicatorValueAsync(
|
if (!this.distributedIndicatorValueService.updateIndicatorValueAsync(
|
||||||
this.ditributedIndicatorValueRecordId,
|
this.ditributedIndicatorValueRecordId,
|
||||||
Double.valueOf(value).longValue())) {
|
Double.valueOf(value).longValue())) {
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ public abstract class AbstractLogNumberIndicator extends AbstractLogIndicator {
|
||||||
|
|
||||||
// update active indicator value record on persistent when caching is not enabled
|
// update active indicator value record on persistent when caching is not enabled
|
||||||
if (this.active && this.ditributedIndicatorValueRecordId != null) {
|
if (this.active && this.ditributedIndicatorValueRecordId != null) {
|
||||||
this.distributedPingCache.updateIndicatorValue(
|
this.distributedIndicatorValueService.updateIndicatorValue(
|
||||||
this.ditributedIndicatorValueRecordId,
|
this.ditributedIndicatorValueRecordId,
|
||||||
numericValue.longValue());
|
numericValue.longValue());
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientEvent.EventType;
|
||||||
|
|
||||||
public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
||||||
|
@ -23,16 +22,6 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
||||||
super(distributedPingCache);
|
super(distributedPingCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(
|
|
||||||
final Indicator indicatorDefinition,
|
|
||||||
final Long connectionId,
|
|
||||||
final boolean active,
|
|
||||||
final boolean cachingEnabled) {
|
|
||||||
|
|
||||||
super.init(indicatorDefinition, connectionId, active, cachingEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<EventType> observedEvents() {
|
public Set<EventType> observedEvents() {
|
||||||
return this.EMPTY_SET;
|
return this.EMPTY_SET;
|
||||||
|
@ -50,7 +39,7 @@ public abstract class AbstractPingIndicator extends AbstractClientIndicator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.distributedPingCache.updatePingAsync(this.ditributedIndicatorValueRecordId);
|
this.distributedIndicatorValueService.updatePingAsync(this.ditributedIndicatorValueRecordId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,8 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
||||||
|
|
||||||
private boolean hidden = false;
|
private boolean hidden = false;
|
||||||
|
|
||||||
public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedPingCache) {
|
public PingIntervalClientIndicator(final DistributedIndicatorValueService distributedIndicatorValueService) {
|
||||||
super(distributedPingCache);
|
super(distributedIndicatorValueService);
|
||||||
this.cachingEnabled = true;
|
this.cachingEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.initialized && !this.cachingEnabled && this.active
|
if (this.initialized && !this.cachingEnabled && this.active
|
||||||
&& this.lastUpdate != this.distributedPingCache.lastUpdate()) {
|
&& this.lastUpdate != this.distributedIndicatorValueService.lastUpdate()) {
|
||||||
|
|
||||||
final long currentTimeMillis = DateTimeUtils.currentTimeMillis();
|
final long currentTimeMillis = DateTimeUtils.currentTimeMillis();
|
||||||
this.currentValue = computeValueAt(currentTimeMillis);
|
this.currentValue = computeValueAt(currentTimeMillis);
|
||||||
|
@ -110,7 +110,7 @@ public final class PingIntervalClientIndicator extends AbstractPingIndicator {
|
||||||
public final double computeValueAt(final long timestamp) {
|
public final double computeValueAt(final long timestamp) {
|
||||||
if (super.ditributedIndicatorValueRecordId != null) {
|
if (super.ditributedIndicatorValueRecordId != null) {
|
||||||
|
|
||||||
final Long lastPing = this.distributedPingCache
|
final Long lastPing = this.distributedIndicatorValueService
|
||||||
.getIndicatorValue(super.ditributedIndicatorValueRecordId);
|
.getIndicatorValue(super.ditributedIndicatorValueRecordId);
|
||||||
|
|
||||||
return (lastPing != null)
|
return (lastPing != null)
|
||||||
|
|
|
@ -12,26 +12,37 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.mybatis.dynamic.sql.SqlTable;
|
import org.mybatis.dynamic.sql.SqlTable;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API;
|
import ch.ethz.seb.sebserver.gbl.api.API;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
|
import ch.ethz.seb.sebserver.gbl.model.EntityDependency;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.Page;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnectionData;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
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.AuthorizationService;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.session.SEBClientConnectionService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||||
|
|
||||||
@WebServiceProfile
|
@WebServiceProfile
|
||||||
|
@ -39,13 +50,16 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
||||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONNECTION_ENDPOINT)
|
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.SEB_CLIENT_CONNECTION_ENDPOINT)
|
||||||
public class ClientConnectionController extends ReadonlyEntityController<ClientConnection, ClientConnection> {
|
public class ClientConnectionController extends ReadonlyEntityController<ClientConnection, ClientConnection> {
|
||||||
|
|
||||||
|
private final SEBClientConnectionService sebClientConnectionService;
|
||||||
|
|
||||||
protected ClientConnectionController(
|
protected ClientConnectionController(
|
||||||
final AuthorizationService authorization,
|
final AuthorizationService authorization,
|
||||||
final BulkActionService bulkActionService,
|
final BulkActionService bulkActionService,
|
||||||
final ClientConnectionDAO clientConnectionDAO,
|
final ClientConnectionDAO clientConnectionDAO,
|
||||||
final UserActivityLogDAO userActivityLogDAO,
|
final UserActivityLogDAO userActivityLogDAO,
|
||||||
final PaginationService paginationService,
|
final PaginationService paginationService,
|
||||||
final BeanValidationService beanValidationService) {
|
final BeanValidationService beanValidationService,
|
||||||
|
final SEBClientConnectionService sebClientConnectionService) {
|
||||||
|
|
||||||
super(authorization,
|
super(authorization,
|
||||||
bulkActionService,
|
bulkActionService,
|
||||||
|
@ -53,6 +67,60 @@ public class ClientConnectionController extends ReadonlyEntityController<ClientC
|
||||||
userActivityLogDAO,
|
userActivityLogDAO,
|
||||||
paginationService,
|
paginationService,
|
||||||
beanValidationService);
|
beanValidationService);
|
||||||
|
|
||||||
|
this.sebClientConnectionService = sebClientConnectionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public Page<ClientConnectionData> getClientConnectionDataPage(
|
||||||
|
@RequestParam(
|
||||||
|
name = API.PARAM_INSTITUTION_ID,
|
||||||
|
required = true,
|
||||||
|
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||||
|
@RequestParam(name = Page.ATTR_PAGE_NUMBER, required = false) final Integer pageNumber,
|
||||||
|
@RequestParam(name = Page.ATTR_PAGE_SIZE, required = false) final Integer pageSize,
|
||||||
|
@RequestParam(name = Page.ATTR_SORT, required = false) final String sort,
|
||||||
|
@RequestParam final MultiValueMap<String, String> allRequestParams,
|
||||||
|
final HttpServletRequest request) {
|
||||||
|
|
||||||
|
// at least current user must have read access for specified entity type within its own institution
|
||||||
|
checkReadPrivilege(institutionId);
|
||||||
|
|
||||||
|
final FilterMap filterMap = new FilterMap(allRequestParams, request.getQueryString());
|
||||||
|
populateFilterMap(filterMap, institutionId, sort);
|
||||||
|
|
||||||
|
final Page<ClientConnectionData> page = this.paginationService.getPage(
|
||||||
|
pageNumber,
|
||||||
|
pageSize,
|
||||||
|
sort,
|
||||||
|
getSQLTableOfEntity().name(),
|
||||||
|
() -> getAllData(filterMap))
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(
|
||||||
|
path = API.SEB_CLIENT_CONNECTION_DATA_ENDPOINT + API.MODEL_ID_VAR_PATH_SEGMENT,
|
||||||
|
method = RequestMethod.GET,
|
||||||
|
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE,
|
||||||
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
public ClientConnectionData getClientConnectionDataBy(@PathVariable final String modelId) {
|
||||||
|
return this.sebClientConnectionService
|
||||||
|
.getIndicatorValues(super.getBy(modelId))
|
||||||
|
.getOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result<Collection<ClientConnectionData>> getAllData(final FilterMap filterMap) {
|
||||||
|
return getAll(filterMap)
|
||||||
|
.map(connection -> connection.stream()
|
||||||
|
.map(this.sebClientConnectionService::getIndicatorValues)
|
||||||
|
.flatMap(Result::onErrorLogAndSkip)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -25,7 +25,7 @@ sebserver.webservice.clean-db-on-startup=false
|
||||||
|
|
||||||
# webservice configuration
|
# webservice configuration
|
||||||
sebserver.init.adminaccount.gen-on-init=false
|
sebserver.init.adminaccount.gen-on-init=false
|
||||||
sebserver.webservice.distributed=false
|
sebserver.webservice.distributed=true
|
||||||
#sebserver.webservice.master.delay.threshold=10000
|
#sebserver.webservice.master.delay.threshold=10000
|
||||||
sebserver.webservice.http.external.scheme=http
|
sebserver.webservice.http.external.scheme=http
|
||||||
sebserver.webservice.http.external.servername=localhost
|
sebserver.webservice.http.external.servername=localhost
|
||||||
|
|
Loading…
Reference in a new issue