SEBSERV-202 implemented

This commit is contained in:
anhefti 2021-06-24 15:51:54 +02:00
parent 3f6bc5098d
commit e49d8291d8
10 changed files with 229 additions and 84 deletions

View file

@ -8,6 +8,8 @@
package ch.ethz.seb.sebserver.gbl.model.exam;
import java.util.EnumSet;
import org.apache.commons.lang3.BooleanUtils;
import org.hibernate.validator.constraints.URL;
@ -29,12 +31,20 @@ public class ProctoringServiceSettings implements Entity {
ZOOM
}
public enum ProctoringFeature {
TOWN_HALL,
ONE_TO_ONE,
BROADCAST,
ENABLE_CHAT
}
public static final String ATTR_ENABLE_PROCTORING = "enableProctoring";
public static final String ATTR_SERVER_TYPE = "serverType";
public static final String ATTR_SERVER_URL = "serverURL";
public static final String ATTR_APP_KEY = "appKey";
public static final String ATTR_APP_SECRET = "appSecret";
public static final String ATTR_COLLECTING_ROOM_SIZE = "collectingRoomSize";
public static final String ATTR_ENABLED_FEATURES = "enabledFeatures";
public static final String ATTR_COLLECT_ALL_ROOM_NAME = "collectAllRoomName";
public static final String ATTR_SERVICE_IN_USE = "serviceInUse";
@ -60,6 +70,9 @@ public class ProctoringServiceSettings implements Entity {
@JsonProperty(ATTR_COLLECTING_ROOM_SIZE)
public final Integer collectingRoomSize;
@JsonProperty(ATTR_ENABLED_FEATURES)
public final EnumSet<ProctoringFeature> enabledFeatures;
@JsonProperty(ATTR_SERVICE_IN_USE)
public final Boolean serviceInUse;
@ -70,6 +83,7 @@ public class ProctoringServiceSettings implements Entity {
@JsonProperty(ATTR_SERVER_TYPE) final ProctoringServerType serverType,
@JsonProperty(ATTR_SERVER_URL) final String serverURL,
@JsonProperty(ATTR_COLLECTING_ROOM_SIZE) final Integer collectingRoomSize,
@JsonProperty(ATTR_ENABLED_FEATURES) final EnumSet<ProctoringFeature> enabledFeatures,
@JsonProperty(ATTR_SERVICE_IN_USE) final Boolean serviceInUse,
@JsonProperty(ATTR_APP_KEY) final String appKey,
@JsonProperty(ATTR_APP_SECRET) final CharSequence appSecret) {
@ -79,9 +93,11 @@ public class ProctoringServiceSettings implements Entity {
this.serverType = (serverType != null) ? serverType : ProctoringServerType.JITSI_MEET;
this.serverURL = serverURL;
this.collectingRoomSize = (collectingRoomSize != null) ? collectingRoomSize : 20;
this.enabledFeatures = enabledFeatures != null ? enabledFeatures : EnumSet.allOf(ProctoringFeature.class);
this.serviceInUse = serviceInUse;
this.appKey = appKey;
this.appSecret = appSecret;
}
@Override
@ -119,6 +135,10 @@ public class ProctoringServiceSettings implements Entity {
return this.collectingRoomSize;
}
public EnumSet<ProctoringFeature> getEnabledFeatures() {
return this.enabledFeatures;
}
public String getAppKey() {
return this.appKey;
}
@ -162,7 +182,7 @@ public class ProctoringServiceSettings implements Entity {
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("ProctoringSettings [examId=");
builder.append("ProctoringServiceSettings [examId=");
builder.append(this.examId);
builder.append(", enableProctoring=");
builder.append(this.enableProctoring);
@ -170,8 +190,16 @@ public class ProctoringServiceSettings implements Entity {
builder.append(this.serverType);
builder.append(", serverURL=");
builder.append(this.serverURL);
builder.append(", appKey=");
builder.append(this.appKey);
builder.append(", appSecret=");
builder.append(this.appSecret);
builder.append(", collectingRoomSize=");
builder.append(this.collectingRoomSize);
builder.append(", enabledFeatures=");
builder.append(this.enabledFeatures);
builder.append(", serviceInUse=");
builder.append(this.serviceInUse);
builder.append("]");
return builder.toString();
}

View file

@ -8,11 +8,15 @@
package ch.ethz.seb.sebserver.gui.content;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.swt.widgets.Composite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -23,6 +27,7 @@ import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
@ -67,6 +72,8 @@ public class ExamProctoringSettings {
new LocTextKey("sebserver.exam.proctoring.form.appkey");
private final static LocTextKey SEB_PROCTORING_FORM_SECRET =
new LocTextKey("sebserver.exam.proctoring.form.secret");
private final static LocTextKey SEB_PROCTORING_FORM_FEATURES =
new LocTextKey("sebserver.exam.proctoring.form.features");
Function<PageAction, PageAction> settingsFunction(final PageService pageService, final boolean modifyGrant) {
@ -131,12 +138,20 @@ public class ExamProctoringSettings {
final ProctoringServerType serverType = ProctoringServerType
.valueOf(form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_TYPE));
final String features = form.getFieldValue(ProctoringServiceSettings.ATTR_ENABLED_FEATURES);
final EnumSet<ProctoringFeature> featureFlags =
EnumSet.copyOf(Arrays.asList(StringUtils.split(features, Constants.LIST_SEPARATOR))
.stream()
.map(str -> ProctoringFeature.valueOf(str))
.collect(Collectors.toSet()));
examProctoring = new ProctoringServiceSettings(
Long.parseLong(entityKey.modelId),
enabled,
serverType,
form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_URL),
Integer.parseInt(form.getFieldValue(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)),
featureFlags,
false,
form.getFieldValue(ProctoringServiceSettings.ATTR_APP_KEY),
form.getFieldValue(ProctoringServiceSettings.ATTR_APP_SECRET));
@ -239,15 +254,6 @@ public class ExamProctoringSettings {
ProctoringServiceSettings.ATTR_SERVER_URL,
SEB_PROCTORING_FORM_URL,
proctoringSettings.serverURL))
.withDefaultSpanInput(1)
.addField(FormBuilder.text(
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
SEB_PROCTORING_FORM_ROOM_SIZE,
String.valueOf(proctoringSettings.getCollectingRoomSize()))
.asNumber(numString -> Long.parseLong(numString)))
.withDefaultSpanInput(5)
.withDefaultSpanEmptyCell(4)
.addField(FormBuilder.text(
ProctoringServiceSettings.ATTR_APP_KEY,
@ -262,6 +268,22 @@ public class ExamProctoringSettings {
? String.valueOf(proctoringSettings.appSecret)
: null))
.withDefaultSpanInput(1)
.addField(FormBuilder.text(
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
SEB_PROCTORING_FORM_ROOM_SIZE,
String.valueOf(proctoringSettings.getCollectingRoomSize()))
.asNumber(numString -> Long.parseLong(numString)))
.withEmptyCellSeparation(true)
.withDefaultSpanEmptyCell(4)
.withDefaultSpanInput(5)
.addField(FormBuilder.multiCheckboxSelection(
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
SEB_PROCTORING_FORM_FEATURES,
StringUtils.join(proctoringSettings.enabledFeatures, Constants.LIST_SEPARATOR),
resourceService::examProctoringFeaturesResources))
.build();
if (proctoringSettings.serviceInUse) {

View file

@ -27,6 +27,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
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;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
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;
@ -367,27 +368,31 @@ public class MonitoringClientConnection implements TemplateComposer {
connectionData.clientConnection.status.indicatorActiveStatus);
if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE) {
final ProctoringServiceSettings procotringSettings = restService
final ProctoringServiceSettings proctoringSettings = restService
.getBuilder(GetProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId)
.call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
.getOr(null);
if (procotringSettings != null && procotringSettings.enableProctoring) {
if (proctoringSettings != null && proctoringSettings.enableProctoring) {
final ProctoringGUIService proctoringGUIService = this.resourceService
.getCurrentUser()
.getProctoringGUIService();
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING)
.withEntityKey(parentEntityKey)
.withExec(action -> this.monitoringProctoringService.openOneToOneRoom(
action,
connectionData,
proctoringGUIService))
.noEventPropagation()
.publish()
if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.ONE_TO_ONE)) {
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_PROCTORING)
.withEntityKey(parentEntityKey)
.withExec(action -> this.monitoringProctoringService.openOneToOneRoom(
action,
connectionData,
proctoringGUIService))
.noEventPropagation()
.publish();
}
actionBuilder
.newAction(ActionDefinition.MONITOR_EXAM_CLIENT_CONNECTION_EXAM_ROOM_PROCTORING)
.withEntityKey(parentEntityKey)
.withExec(action -> this.monitoringProctoringService.openExamCollectionProctorScreen(

View file

@ -34,6 +34,7 @@ import ch.ethz.seb.sebserver.gbl.model.EntityKey;
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;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
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.user.UserRole;
@ -260,22 +261,24 @@ public class MonitoringRunningExam implements TemplateComposer {
.getOr(null);
if (proctoringSettings != null && proctoringSettings.enableProctoring) {
if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.TOWN_HALL)) {
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)
.withEntityKey(entityKey)
.withExec(action -> this.monitoringProctoringService.toggleTownhallRoom(proctoringGUIService,
action))
.noEventPropagation()
.publish();
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM)
.withEntityKey(entityKey)
.withExec(action -> this.monitoringProctoringService.toggleTownhallRoom(proctoringGUIService,
action))
.noEventPropagation()
.publish();
if (this.monitoringProctoringService.isTownhallRoomActive(entityKey.modelId)) {
this.pageService.firePageEvent(
new ActionActivationEvent(
true,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
pageContext);
if (this.monitoringProctoringService.isTownhallRoomActive(entityKey.modelId)) {
this.pageService.firePageEvent(
new ActionActivationEvent(
true,
new Tuple<>(
ActionDefinition.MONITOR_EXAM_OPEN_TOWNHALL_PROCTOR_ROOM,
ActionDefinition.MONITOR_EXAM_CLOSE_TOWNHALL_PROCTOR_ROOM)),
pageContext);
}
}
this.monitoringProctoringService.initCollectingRoomActions(

View file

@ -43,6 +43,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction.PermissionComponent;
import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction.WhiteListPath;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
@ -122,6 +123,7 @@ public class ResourceService {
public static final String SEB_RESTRICTION_PERMISSIONS_PREFIX = "sebserver.exam.form.sebrestriction.permissions.";
public static final String SEB_CLIENT_CONFIG_PURPOSE_PREFIX = "sebserver.clientconfig.config.purpose.";
public static final String EXAM_PROCTORING_TYPE_PREFIX = "sebserver.exam.proctoring.type.servertype.";
public static final String EXAM_PROCTORING_FEATURES_PREFIX = "sebserver.exam.proctoring.form.features.";
public static final String VDI_TYPE_PREFIX = "sebserver.clientconfig.form.vditype.";
private static final String DISABLE_LMS_FLAG = "sebserver.gui.webservice.lms.disable.";
@ -422,6 +424,18 @@ public class ResourceService {
.collect(Collectors.toList());
}
public List<Tuple<String>> examProctoringFeaturesResources() {
return Arrays.stream(ProctoringFeature.values())
.map(type -> new Tuple3<>(
type.name(),
this.i18nSupport.getText(EXAM_PROCTORING_FEATURES_PREFIX + type.name()),
Utils.formatLineBreaks(this.i18nSupport.getText(
EXAM_PROCTORING_FEATURES_PREFIX + type.name() + Constants.TOOLTIP_TEXT_KEY_SUFFIX,
StringUtils.EMPTY))))
.sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList());
}
public List<Tuple<String>> vdiTypeResources() {
return Arrays.stream(VDIType.values())
.map(type -> new Tuple3<>(

View file

@ -23,11 +23,15 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -62,6 +66,13 @@ public class JitsiMeetProctoringView extends AbstractProctoringView {
final ProctoringGUIService proctoringGUIService = this.pageService
.getCurrentUser()
.getProctoringGUIService();
final ProctoringServiceSettings proctoringSettings = this.pageService
.getRestService()
.getBuilder(GetProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId)
.call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
.getOr(null);
content.setLayout(gridLayout);
final GridData headerCell = new GridData(SWT.FILL, SWT.FILL, true, true);
@ -99,31 +110,34 @@ public class JitsiMeetProctoringView extends AbstractProctoringView {
closeAction.addListener(SWT.Selection, event -> closeRoom(proctoringGUIService, proctoringWindowData));
final BroadcastActionState broadcastActionState = new BroadcastActionState();
if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.BROADCAST)) {
final Button broadcastAudioAction = widgetFactory.buttonLocalized(footer, BROADCAST_AUDIO_ON_TEXT_KEY);
broadcastAudioAction.setLayoutData(new RowData());
broadcastAudioAction.addListener(SWT.Selection, event -> toggleBroadcastAudio(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastAudioAction));
broadcastAudioAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
final Button broadcastAudioAction = widgetFactory.buttonLocalized(footer, BROADCAST_AUDIO_ON_TEXT_KEY);
broadcastAudioAction.setLayoutData(new RowData());
broadcastAudioAction.addListener(SWT.Selection, event -> toggleBroadcastAudio(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastAudioAction));
broadcastAudioAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
final Button broadcastVideoAction = widgetFactory.buttonLocalized(footer, BROADCAST_VIDEO_ON_TEXT_KEY);
broadcastVideoAction.setLayoutData(new RowData());
broadcastVideoAction.addListener(SWT.Selection, event -> toggleBroadcastVideo(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastVideoAction,
broadcastAudioAction));
broadcastVideoAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
}
final Button broadcastVideoAction = widgetFactory.buttonLocalized(footer, BROADCAST_VIDEO_ON_TEXT_KEY);
broadcastVideoAction.setLayoutData(new RowData());
broadcastVideoAction.addListener(SWT.Selection, event -> toggleBroadcastVideo(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastVideoAction,
broadcastAudioAction));
broadcastVideoAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
final Button chatAction = widgetFactory.buttonLocalized(footer, CHAT_ON_TEXT_KEY);
chatAction.setLayoutData(new RowData());
chatAction.addListener(SWT.Selection, event -> toggleChat(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
chatAction));
chatAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.ENABLE_CHAT)) {
final Button chatAction = widgetFactory.buttonLocalized(footer, CHAT_ON_TEXT_KEY);
chatAction.setLayoutData(new RowData());
chatAction.addListener(SWT.Selection, event -> toggleChat(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
chatAction));
chatAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
}
}
}

View file

@ -23,11 +23,15 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.api.API;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.GuiServiceInfo;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService;
import ch.ethz.seb.sebserver.gui.service.session.proctoring.ProctoringGUIService.ProctoringWindowData;
import ch.ethz.seb.sebserver.gui.widget.WidgetFactory;
@ -62,6 +66,13 @@ public class ZoomProctoringView extends AbstractProctoringView {
final ProctoringGUIService proctoringGUIService = this.pageService
.getCurrentUser()
.getProctoringGUIService();
final ProctoringServiceSettings proctoringSettings = this.pageService
.getRestService()
.getBuilder(GetProctoringSettings.class)
.withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId)
.call()
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
.getOr(null);
content.setLayout(gridLayout);
final GridData headerCell = new GridData(SWT.FILL, SWT.FILL, true, true);
@ -99,32 +110,33 @@ public class ZoomProctoringView extends AbstractProctoringView {
closeAction.addListener(SWT.Selection, event -> closeRoom(proctoringGUIService, proctoringWindowData));
final BroadcastActionState broadcastActionState = new BroadcastActionState();
if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.BROADCAST)) {
final Button broadcastAudioAction = widgetFactory.buttonLocalized(footer, BROADCAST_AUDIO_ON_TEXT_KEY);
broadcastAudioAction.setLayoutData(new RowData());
broadcastAudioAction.addListener(SWT.Selection, event -> toggleBroadcastAudio(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastAudioAction));
broadcastAudioAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
final Button broadcastAudioAction = widgetFactory.buttonLocalized(footer, BROADCAST_AUDIO_ON_TEXT_KEY);
broadcastAudioAction.setLayoutData(new RowData());
broadcastAudioAction.addListener(SWT.Selection, event -> toggleBroadcastAudio(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastAudioAction));
broadcastAudioAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
final Button broadcastVideoAction = widgetFactory.buttonLocalized(footer, BROADCAST_VIDEO_ON_TEXT_KEY);
broadcastVideoAction.setLayoutData(new RowData());
broadcastVideoAction.addListener(SWT.Selection, event -> toggleBroadcastVideo(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastVideoAction,
broadcastAudioAction));
broadcastVideoAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
final Button chatAction = widgetFactory.buttonLocalized(footer, CHAT_ON_TEXT_KEY);
chatAction.setLayoutData(new RowData());
chatAction.addListener(SWT.Selection, event -> toggleChat(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
chatAction));
chatAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
final Button broadcastVideoAction = widgetFactory.buttonLocalized(footer, BROADCAST_VIDEO_ON_TEXT_KEY);
broadcastVideoAction.setLayoutData(new RowData());
broadcastVideoAction.addListener(SWT.Selection, event -> toggleBroadcastVideo(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
broadcastVideoAction,
broadcastAudioAction));
broadcastVideoAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
}
if (proctoringSettings.enabledFeatures.contains(ProctoringFeature.ENABLE_CHAT)) {
final Button chatAction = widgetFactory.buttonLocalized(footer, CHAT_ON_TEXT_KEY);
chatAction.setLayoutData(new RowData());
chatAction.addListener(SWT.Selection, event -> toggleChat(
proctoringWindowData.examId,
proctoringWindowData.connectionData.roomName,
chatAction));
chatAction.setData(BroadcastActionState.KEY_NAME, broadcastActionState);
}
}
}

View file

@ -10,13 +10,17 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@ -32,6 +36,7 @@ import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSEBRestriction;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringFeature;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
@ -56,6 +61,9 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.Exa
@WebServiceProfile
public class ExamAdminServiceImpl implements ExamAdminService {
private static final Logger log = LoggerFactory.getLogger(ExamAdminServiceImpl.class);
private static final Object ATTR_ENABLE_PROCTORING = null;
private final ExamDAO examDAO;
private final IndicatorDAO indicatorDAO;
private final AdditionalAttributesDAO additionalAttributesDAO;
@ -203,6 +211,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
getServerType(mapping),
getString(mapping, ProctoringServiceSettings.ATTR_SERVER_URL),
getCollectingRoomSize(mapping),
getEnabledFeatures(mapping),
this.remoteProctoringRoomDAO.isServiceInUse(examId).getOr(true),
getString(mapping, ProctoringServiceSettings.ATTR_APP_KEY),
getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET));
@ -255,6 +264,12 @@ public class ExamAdminServiceImpl implements ExamAdminService {
.getOrThrow()
.toString());
this.additionalAttributesDAO.saveAdditionalAttribute(
EntityType.EXAM,
examId,
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
StringUtils.join(proctoringServiceSettings.enabledFeatures, Constants.LIST_SEPARATOR));
return proctoringServiceSettings;
});
}
@ -310,4 +325,31 @@ public class ExamAdminServiceImpl implements ExamAdminService {
}
}
private EnumSet<ProctoringFeature> getEnabledFeatures(final Map<String, AdditionalAttributeRecord> mapping) {
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLED_FEATURES)) {
try {
final String value = mapping.get(ProctoringServiceSettings.ATTR_ENABLED_FEATURES).getValue();
return EnumSet.copyOf(Arrays.asList(StringUtils.split(value, Constants.LIST_SEPARATOR))
.stream()
.map(str -> {
try {
return ProctoringFeature.valueOf(str);
} catch (final Exception e) {
log.error(
"Failed to enabled single features for proctoring settings. Skipping. {}",
e.getMessage());
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toSet()));
} catch (final Exception e) {
log.error("Failed to get enabled features for proctoring settings. Enable all. {}", e.getMessage());
return EnumSet.allOf(ProctoringFeature.class);
}
} else {
return EnumSet.allOf(ProctoringFeature.class);
}
}
}

View file

@ -661,6 +661,11 @@ sebserver.exam.proctoring.form.appkey=Application Key
sebserver.exam.proctoring.form.appkey.tooltip=The application key of the proctoring service server
sebserver.exam.proctoring.form.secret=Secret
sebserver.exam.proctoring.form.secret.tooltip=The secret used to access the proctoring service
sebserver.exam.proctoring.form.features=Enabled Features
sebserver.exam.proctoring.form.features.TOWN_HALL=Town-Hall Room
sebserver.exam.proctoring.form.features.ONE_TO_ONE=One to One Room
sebserver.exam.proctoring.form.features.BROADCAST=Broadcasting Feature
sebserver.exam.proctoring.form.features.ENABLE_CHAT=Chat Feature
sebserver.exam.proctoring.type.servertype.JITSI_MEET=Jitsi Meet Server
sebserver.exam.proctoring.type.servertype.JITSI_MEET.tooltip=Use a Jitsi Meet server for proctoring

View file

@ -62,7 +62,7 @@ public class ExamProctoringRoomServiceTest extends AdministrationAPIIntegrationT
this.examAdminService.saveProctoringServiceSettings(
2L,
new ProctoringServiceSettings(
2L, true, ProctoringServerType.JITSI_MEET, "http://jitsi.ch", 1, false,
2L, true, ProctoringServerType.JITSI_MEET, "http://jitsi.ch", 1, null, false,
"app-key", "app.secret"));
assertTrue(this.examAdminService.isProctoringEnabled(2L).get());