SEBSERV-287 back-end implementation
This commit is contained in:
parent
3d94637300
commit
191f8432de
22 changed files with 907 additions and 273 deletions
|
@ -65,8 +65,8 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckExamCon
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.CheckSEBRestriction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetDefaultExamTemplate;
|
||||
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.GetExamTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.lmssetup.TestLmsSetup;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.quiz.GetQuizData;
|
||||
|
@ -143,7 +143,7 @@ public class ExamForm implements TemplateComposer {
|
|||
private final PageService pageService;
|
||||
private final ResourceService resourceService;
|
||||
private final ExamSEBRestrictionSettings examSEBRestrictionSettings;
|
||||
private final ExamProctoringSettings examProctoringSettings;
|
||||
private final ProctoringSettingsPopup proctoringSettingsPopup;
|
||||
private final WidgetFactory widgetFactory;
|
||||
private final RestService restService;
|
||||
private final ExamDeletePopup examDeletePopup;
|
||||
|
@ -154,7 +154,7 @@ public class ExamForm implements TemplateComposer {
|
|||
protected ExamForm(
|
||||
final PageService pageService,
|
||||
final ExamSEBRestrictionSettings examSEBRestrictionSettings,
|
||||
final ExamProctoringSettings examProctoringSettings,
|
||||
final ProctoringSettingsPopup proctoringSettingsPopup,
|
||||
final ExamToConfigBindingForm examToConfigBindingForm,
|
||||
final DownloadService downloadService,
|
||||
final ExamDeletePopup examDeletePopup,
|
||||
|
@ -165,7 +165,7 @@ public class ExamForm implements TemplateComposer {
|
|||
this.pageService = pageService;
|
||||
this.resourceService = pageService.getResourceService();
|
||||
this.examSEBRestrictionSettings = examSEBRestrictionSettings;
|
||||
this.examProctoringSettings = examProctoringSettings;
|
||||
this.proctoringSettingsPopup = proctoringSettingsPopup;
|
||||
this.widgetFactory = pageService.getWidgetFactory();
|
||||
this.restService = this.resourceService.getRestService();
|
||||
this.examDeletePopup = examDeletePopup;
|
||||
|
@ -390,7 +390,7 @@ public class ExamForm implements TemplateComposer {
|
|||
}
|
||||
|
||||
final boolean proctoringEnabled = importFromQuizData ? false : this.restService
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.getBuilder(GetExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.map(ProctoringServiceSettings::getEnableProctoring)
|
||||
|
@ -455,13 +455,13 @@ public class ExamForm implements TemplateComposer {
|
|||
|
||||
.newAction(ActionDefinition.EXAM_PROCTORING_ON)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant && editable))
|
||||
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
|
||||
.noEventPropagation()
|
||||
.publishIf(() -> proctoringEnabled && readonly)
|
||||
|
||||
.newAction(ActionDefinition.EXAM_PROCTORING_OFF)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(this.examProctoringSettings.settingsFunction(this.pageService, modifyGrant && editable))
|
||||
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
|
||||
.noEventPropagation()
|
||||
.publishIf(() -> !proctoringEnabled && readonly);
|
||||
|
||||
|
|
|
@ -44,15 +44,15 @@ import ch.ethz.seb.sebserver.gui.service.page.event.ActionEvent;
|
|||
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplateProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplateProctoringSettings;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ExamProctoringSettings {
|
||||
public class ExamTemplateProctoringSettingsPopup {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ExamProctoringSettings.class);
|
||||
private static final Logger log = LoggerFactory.getLogger(ExamTemplateProctoringSettingsPopup.class);
|
||||
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_TITLE =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.title");
|
||||
|
@ -178,7 +178,7 @@ public class ExamProctoringSettings {
|
|||
|
||||
final boolean saveOk = !pageService
|
||||
.getRestService()
|
||||
.getBuilder(SaveProctoringSettings.class)
|
||||
.getBuilder(SaveExamTemplateProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.withBody(examProctoring)
|
||||
.call()
|
||||
|
@ -227,7 +227,7 @@ public class ExamProctoringSettings {
|
|||
.createPopupScrollComposite(parent);
|
||||
|
||||
final ProctoringServiceSettings proctoringSettings = restService
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.getBuilder(GetExamTemplateProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
@ -329,24 +329,6 @@ public class ExamProctoringSettings {
|
|||
|
||||
return () -> formHandle;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// private void procServiceSelection(final Form form) {
|
||||
// final ProctoringServerType proctoringServerType = ProctoringServerType
|
||||
// .valueOf(form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_TYPE));
|
||||
// switch (proctoringServerType) {
|
||||
// case ZOOM: {
|
||||
// form.setFieldVisible(true, ProctoringServiceSettings.ATTR_SDK_KEY);
|
||||
// form.setFieldVisible(true, ProctoringServiceSettings.ATTR_SDK_SECRET);
|
||||
// break;
|
||||
// }
|
||||
// default: {
|
||||
// form.setFieldVisible(false, ProctoringServiceSettings.ATTR_SDK_KEY);
|
||||
// form.setFieldVisible(false, ProctoringServiceSettings.ATTR_SDK_SECRET);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.content.exam;
|
||||
|
||||
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;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
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.api.EntityType;
|
||||
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;
|
||||
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
|
||||
import ch.ethz.seb.sebserver.gui.form.Form;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormBuilder;
|
||||
import ch.ethz.seb.sebserver.gui.form.FormHandle;
|
||||
import ch.ethz.seb.sebserver.gui.service.ResourceService;
|
||||
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.ModalInputDialogComposer;
|
||||
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.page.event.ActionEvent;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.ModalInputDialog;
|
||||
import ch.ethz.seb.sebserver.gui.service.page.impl.PageAction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestService;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplateProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplateProctoringSettings;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class ProctoringSettingsPopup {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProctoringSettingsPopup.class);
|
||||
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_TITLE =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.title");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_INFO =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.info");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_INFO_TITLE =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.info.title");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_ENABLE =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.enabled");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_TYPE =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.type");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_URL =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.url");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_ROOM_SIZE =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.collectingRoomSize");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_APPKEY =
|
||||
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_SDKKEY =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.sdkkey");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_SDKSECRET =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.sdksecret");
|
||||
private final static LocTextKey SEB_PROCTORING_FORM_USE_ZOOM_APP_CLIENT =
|
||||
new LocTextKey("sebserver.exam.proctoring.form.useZoomAppClient");
|
||||
|
||||
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) {
|
||||
|
||||
return action -> {
|
||||
|
||||
final PageContext pageContext = action.pageContext()
|
||||
.withAttribute(
|
||||
PageContext.AttributeKeys.FORCE_READ_ONLY,
|
||||
(modifyGrant) ? Constants.FALSE_STRING : Constants.TRUE_STRING);
|
||||
final ModalInputDialog<FormHandle<?>> dialog =
|
||||
new ModalInputDialog<FormHandle<?>>(
|
||||
action.pageContext().getParent().getShell(),
|
||||
pageService.getWidgetFactory())
|
||||
.setDialogWidth(740)
|
||||
.setDialogHeight(400);
|
||||
|
||||
final SEBProctoringPropertiesForm bindFormContext = new SEBProctoringPropertiesForm(
|
||||
pageService,
|
||||
pageContext);
|
||||
|
||||
final Predicate<FormHandle<?>> doBind = formHandle -> doSaveSettings(
|
||||
pageService,
|
||||
pageContext,
|
||||
formHandle);
|
||||
|
||||
if (modifyGrant) {
|
||||
dialog.open(
|
||||
SEB_PROCTORING_FORM_TITLE,
|
||||
doBind,
|
||||
Utils.EMPTY_EXECUTION,
|
||||
bindFormContext);
|
||||
} else {
|
||||
dialog.open(
|
||||
SEB_PROCTORING_FORM_TITLE,
|
||||
pageContext,
|
||||
pc -> bindFormContext.compose(pc.getParent()));
|
||||
}
|
||||
|
||||
return action;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean doSaveSettings(
|
||||
final PageService pageService,
|
||||
final PageContext pageContext,
|
||||
final FormHandle<?> formHandle) {
|
||||
|
||||
final boolean isReadonly = BooleanUtils.toBoolean(
|
||||
pageContext.getAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY));
|
||||
if (isReadonly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final EntityKey entityKey = pageContext.getEntityKey();
|
||||
ProctoringServiceSettings examProctoring = null;
|
||||
try {
|
||||
final Form form = formHandle.getForm();
|
||||
form.clearErrors();
|
||||
|
||||
final boolean enabled = BooleanUtils.toBoolean(
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING));
|
||||
final ProctoringServerType serverType = ProctoringServerType
|
||||
.valueOf(form.getFieldValue(ProctoringServiceSettings.ATTR_SERVER_TYPE));
|
||||
|
||||
final String features = form.getFieldValue(ProctoringServiceSettings.ATTR_ENABLED_FEATURES);
|
||||
final EnumSet<ProctoringFeature> featureFlags = (StringUtils.isNotBlank(features))
|
||||
? EnumSet.copyOf(Arrays.asList(StringUtils.split(features, Constants.LIST_SEPARATOR))
|
||||
.stream()
|
||||
.map(str -> ProctoringFeature.valueOf(str))
|
||||
.collect(Collectors.toSet()))
|
||||
: EnumSet.noneOf(ProctoringFeature.class);
|
||||
|
||||
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),
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_SDK_KEY),
|
||||
form.getFieldValue(ProctoringServiceSettings.ATTR_SDK_SECRET),
|
||||
BooleanUtils.toBoolean(form.getFieldValue(
|
||||
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM)));
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.error("Unexpected error while trying to get settings from form: ", e);
|
||||
}
|
||||
|
||||
if (examProctoring == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean saveOk = !pageService
|
||||
.getRestService()
|
||||
.getBuilder(
|
||||
entityKey.entityType == EntityType.EXAM
|
||||
? SaveExamProctoringSettings.class
|
||||
: SaveExamTemplateProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.withBody(examProctoring)
|
||||
.call()
|
||||
.onError(formHandle::handleError)
|
||||
.hasError();
|
||||
|
||||
if (saveOk) {
|
||||
final PageAction action = pageService.pageActionBuilder(pageContext)
|
||||
.newAction(ActionDefinition.EXAM_VIEW_FROM_LIST)
|
||||
.create();
|
||||
|
||||
pageService.firePageEvent(
|
||||
new ActionEvent(action),
|
||||
action.pageContext());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private final class SEBProctoringPropertiesForm
|
||||
implements ModalInputDialogComposer<FormHandle<?>> {
|
||||
|
||||
private final PageService pageService;
|
||||
private final PageContext pageContext;
|
||||
|
||||
protected SEBProctoringPropertiesForm(
|
||||
final PageService pageService,
|
||||
final PageContext pageContext) {
|
||||
|
||||
this.pageService = pageService;
|
||||
this.pageContext = pageContext;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<FormHandle<?>> compose(final Composite parent) {
|
||||
final RestService restService = this.pageService.getRestService();
|
||||
final ResourceService resourceService = this.pageService.getResourceService();
|
||||
final EntityKey entityKey = this.pageContext.getEntityKey();
|
||||
final boolean isReadonly = BooleanUtils.toBoolean(
|
||||
this.pageContext.getAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY));
|
||||
|
||||
final Composite content = this.pageService
|
||||
.getWidgetFactory()
|
||||
.createPopupScrollComposite(parent);
|
||||
|
||||
final ProctoringServiceSettings proctoringSettings = restService
|
||||
.getBuilder(
|
||||
entityKey.entityType == EntityType.EXAM
|
||||
? GetExamProctoringSettings.class
|
||||
: GetExamTemplateProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
||||
final PageContext formContext = this.pageContext
|
||||
.copyOf(content)
|
||||
.clearEntityKeys();
|
||||
|
||||
final FormHandle<ProctoringServiceSettings> formHandle = this.pageService.formBuilder(
|
||||
formContext)
|
||||
.withDefaultSpanInput(5)
|
||||
.withEmptyCellSeparation(true)
|
||||
.withDefaultSpanEmptyCell(1)
|
||||
.readonly(isReadonly)
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
"Info",
|
||||
SEB_PROCTORING_FORM_INFO_TITLE,
|
||||
this.pageService.getI18nSupport().getText(SEB_PROCTORING_FORM_INFO))
|
||||
.asArea(50)
|
||||
.asHTML()
|
||||
.readonly(true))
|
||||
|
||||
.addField(FormBuilder.checkbox(
|
||||
ProctoringServiceSettings.ATTR_ENABLE_PROCTORING,
|
||||
SEB_PROCTORING_FORM_ENABLE,
|
||||
String.valueOf(proctoringSettings.enableProctoring)))
|
||||
|
||||
.addField(FormBuilder.singleSelection(
|
||||
ProctoringServiceSettings.ATTR_SERVER_TYPE,
|
||||
SEB_PROCTORING_FORM_TYPE,
|
||||
proctoringSettings.serverType.name(),
|
||||
resourceService::examProctoringTypeResources))
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
ProctoringServiceSettings.ATTR_SERVER_URL,
|
||||
SEB_PROCTORING_FORM_URL,
|
||||
proctoringSettings.serverURL)
|
||||
.mandatory())
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
ProctoringServiceSettings.ATTR_APP_KEY,
|
||||
SEB_PROCTORING_FORM_APPKEY,
|
||||
proctoringSettings.appKey)
|
||||
.mandatory())
|
||||
.withEmptyCellSeparation(false)
|
||||
|
||||
.addField(FormBuilder.password(
|
||||
ProctoringServiceSettings.ATTR_APP_SECRET,
|
||||
SEB_PROCTORING_FORM_SECRET,
|
||||
(proctoringSettings.appSecret != null)
|
||||
? String.valueOf(proctoringSettings.appSecret)
|
||||
: null)
|
||||
.mandatory())
|
||||
|
||||
.addField(FormBuilder.text(
|
||||
ProctoringServiceSettings.ATTR_SDK_KEY,
|
||||
SEB_PROCTORING_FORM_SDKKEY,
|
||||
proctoringSettings.sdkKey))
|
||||
.withEmptyCellSeparation(false)
|
||||
|
||||
.addField(FormBuilder.password(
|
||||
ProctoringServiceSettings.ATTR_SDK_SECRET,
|
||||
SEB_PROCTORING_FORM_SDKSECRET,
|
||||
(proctoringSettings.sdkSecret != null)
|
||||
? String.valueOf(proctoringSettings.sdkSecret)
|
||||
: 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.checkbox(
|
||||
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM,
|
||||
SEB_PROCTORING_FORM_USE_ZOOM_APP_CLIENT,
|
||||
String.valueOf(proctoringSettings.useZoomAppClientForCollectingRoom)))
|
||||
.withDefaultSpanInput(5)
|
||||
.withEmptyCellSeparation(true)
|
||||
.withDefaultSpanEmptyCell(1)
|
||||
|
||||
.addField(FormBuilder.multiCheckboxSelection(
|
||||
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
|
||||
SEB_PROCTORING_FORM_FEATURES,
|
||||
StringUtils.join(proctoringSettings.enabledFeatures, Constants.LIST_SEPARATOR),
|
||||
resourceService::examProctoringFeaturesResources))
|
||||
|
||||
.build();
|
||||
|
||||
if (proctoringSettings.serviceInUse) {
|
||||
formHandle.getForm().getFieldInput(ProctoringServiceSettings.ATTR_SERVER_TYPE).setEnabled(false);
|
||||
formHandle.getForm().getFieldInput(ProctoringServiceSettings.ATTR_SERVER_URL).setEnabled(false);
|
||||
}
|
||||
|
||||
return () -> formHandle;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -53,7 +53,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.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.logs.GetExtendedClientEventPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ConfirmPendingClientNotification;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.GetClientConnectionData;
|
||||
|
@ -376,7 +376,7 @@ public class MonitoringClientConnection implements TemplateComposer {
|
|||
|
||||
if (connectionData.clientConnection.status == ConnectionStatus.ACTIVE) {
|
||||
final ProctoringServiceSettings proctoringSettings = restService
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.getBuilder(GetExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, parentEntityKey.modelId)
|
||||
.call()
|
||||
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
|
||||
|
|
|
@ -56,7 +56,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.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.auth.CurrentUser;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.ClientConnectionTable;
|
||||
import ch.ethz.seb.sebserver.gui.service.session.FullPageMonitoringGUIUpdate;
|
||||
|
@ -243,7 +243,7 @@ public class MonitoringRunningExam implements TemplateComposer {
|
|||
isExamSupporter));
|
||||
|
||||
final ProctoringServiceSettings proctoringSettings = this.restService
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.getBuilder(GetExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, entityKey.modelId)
|
||||
.call()
|
||||
.getOr(null);
|
||||
|
|
|
@ -32,7 +32,7 @@ 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.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
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;
|
||||
|
@ -69,7 +69,7 @@ public class JitsiMeetProctoringView extends AbstractProctoringView {
|
|||
.getProctoringGUIService();
|
||||
final ProctoringServiceSettings proctoringSettings = this.pageService
|
||||
.getRestService()
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.getBuilder(GetExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId)
|
||||
.call()
|
||||
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
|
||||
|
|
|
@ -32,7 +32,7 @@ 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.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
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;
|
||||
|
@ -69,7 +69,7 @@ public class ZoomProctoringView extends AbstractProctoringView {
|
|||
.getProctoringGUIService();
|
||||
final ProctoringServiceSettings proctoringSettings = this.pageService
|
||||
.getRestService()
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.getBuilder(GetExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, proctoringWindowData.examId)
|
||||
.call()
|
||||
.onError(error -> log.error("Failed to get ProctoringServiceSettings", error))
|
||||
|
|
|
@ -24,9 +24,9 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
|||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetProctoringSettings extends RestCall<ProctoringServiceSettings> {
|
||||
public class GetExamProctoringSettings extends RestCall<ProctoringServiceSettings> {
|
||||
|
||||
public GetProctoringSettings() {
|
||||
public GetExamProctoringSettings() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_SINGLE,
|
||||
EntityType.EXAM_PROCTOR_DATA,
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.exam;
|
||||
|
||||
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.exam.ProctoringServiceSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class GetExamTemplateProctoringSettings extends RestCall<ProctoringServiceSettings> {
|
||||
|
||||
public GetExamTemplateProctoringSettings() {
|
||||
super(new TypeKey<>(
|
||||
CallType.GET_SINGLE,
|
||||
EntityType.EXAM_PROCTOR_DATA,
|
||||
new TypeReference<ProctoringServiceSettings>() {
|
||||
}),
|
||||
HttpMethod.GET,
|
||||
MediaType.APPLICATION_JSON,
|
||||
API.EXAM_TEMPLATE_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -17,20 +17,20 @@ 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.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SaveProctoringSettings extends RestCall<Exam> {
|
||||
public class SaveExamProctoringSettings extends RestCall<ProctoringServiceSettings> {
|
||||
|
||||
public SaveProctoringSettings() {
|
||||
public SaveExamProctoringSettings() {
|
||||
super(new TypeKey<>(
|
||||
CallType.SAVE,
|
||||
EntityType.EXAM_PROCTOR_DATA,
|
||||
new TypeReference<Exam>() {
|
||||
new TypeReference<ProctoringServiceSettings>() {
|
||||
}),
|
||||
HttpMethod.POST,
|
||||
MediaType.APPLICATION_JSON,
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.exam;
|
||||
|
||||
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.exam.ProctoringServiceSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.RestCall;
|
||||
|
||||
@Lazy
|
||||
@Component
|
||||
@GuiProfile
|
||||
public class SaveExamTemplateProctoringSettings extends RestCall<ProctoringServiceSettings> {
|
||||
|
||||
public SaveExamTemplateProctoringSettings() {
|
||||
super(new TypeKey<>(
|
||||
CallType.SAVE,
|
||||
EntityType.EXAM_PROCTOR_DATA,
|
||||
new TypeReference<ProctoringServiceSettings>() {
|
||||
}),
|
||||
HttpMethod.POST,
|
||||
MediaType.APPLICATION_JSON,
|
||||
API.EXAM_TEMPLATE_ENDPOINT
|
||||
+ API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,6 +13,7 @@ import java.util.Map;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
|
||||
|
||||
|
@ -22,6 +23,18 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttribut
|
|||
* in a separated data-base table. */
|
||||
public interface AdditionalAttributesDAO {
|
||||
|
||||
/** Use this to get all additional attribute records for a specific entity.
|
||||
*
|
||||
* @param type the entity type
|
||||
* @param entityId the entity identifier (primary key)
|
||||
* @return Result refer to the collection of additional attribute records or to an error if happened */
|
||||
default Result<Collection<AdditionalAttributeRecord>> getAdditionalAttributes(final EntityKey entityKey) {
|
||||
return Result.tryCatch(() -> getAdditionalAttributes(
|
||||
entityKey.entityType,
|
||||
Long.valueOf(entityKey.modelId))
|
||||
.getOrThrow());
|
||||
}
|
||||
|
||||
/** Use this to get all additional attribute records for a specific entity.
|
||||
*
|
||||
* @param type the entity type
|
||||
|
|
|
@ -12,7 +12,6 @@ import org.apache.commons.lang3.BooleanUtils;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings.ProctoringServerType;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
||||
|
||||
|
@ -83,19 +82,10 @@ public interface ExamAdminService {
|
|||
* @return Result refer to proctoring is enabled flag or to an error when happened. */
|
||||
Result<Boolean> isProctoringEnabled(final Long examId);
|
||||
|
||||
/** Get the exam proctoring service implementation of specified type.
|
||||
*
|
||||
* @param type exam proctoring service server type
|
||||
* @return ExamProctoringService instance */
|
||||
Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type);
|
||||
|
||||
/** Get the exam proctoring service implementation for specified exam.
|
||||
*
|
||||
* @param examId the exam identifier
|
||||
* @return ExamProctoringService instance */
|
||||
default Result<ExamProctoringService> getExamProctoringService(final Long examId) {
|
||||
return getProctoringServiceSettings(examId)
|
||||
.flatMap(settings -> getExamProctoringService(settings.serverType));
|
||||
}
|
||||
Result<ExamProctoringService> getExamProctoringService(final Long examId);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.webservice.servicelayer.exam;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
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.ProctoringServerType;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
||||
|
||||
public interface ProctoringAdminService {
|
||||
|
||||
EnumSet<EntityType> SUPPORTED_PARENT_ENTITES = EnumSet.of(
|
||||
EntityType.EXAM,
|
||||
EntityType.EXAM_TEMPLATE);
|
||||
|
||||
/** Get proctoring service settings for a certain entity (SUPPORTED_PARENT_ENTITES).
|
||||
*
|
||||
* @param parentEntityKey the entity key of the parent entity to get the proctoring service settings from
|
||||
* @return Result refer to proctoring service settings or to an error when happened. */
|
||||
Result<ProctoringServiceSettings> getProctoringSettings(EntityKey parentEntityKey);
|
||||
|
||||
/** Save the given proctoring service settings for a certain entity (SUPPORTED_PARENT_ENTITES).
|
||||
*
|
||||
* @param parentEntityKey the entity key of the parent entity to save the proctoring service settings to
|
||||
* @param proctoringServiceSettings The proctoring service settings to save
|
||||
* @return Result refer to saved proctoring service settings or to an error when happened. */
|
||||
Result<ProctoringServiceSettings> saveProctoringServiceSettings(
|
||||
EntityKey parentEntityKey,
|
||||
ProctoringServiceSettings proctoringServiceSettings);
|
||||
|
||||
/** Get the exam proctoring service implementation of specified type.
|
||||
*
|
||||
* @param type exam proctoring service server type
|
||||
* @return ExamProctoringService instance */
|
||||
Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type);
|
||||
|
||||
/** Use this to test the proctoring service settings against the remote proctoring server.
|
||||
*
|
||||
* @param proctoringSettings the settings to test
|
||||
* @return Result refer to true if the settings are correct and the proctoring server can be accessed. */
|
||||
default Result<Boolean> testExamProctoring(final ProctoringServiceSettings proctoringSettings) {
|
||||
return getExamProctoringService(proctoringSettings.serverType)
|
||||
.flatMap(service -> service.testExamProctoring(proctoringSettings));
|
||||
}
|
||||
|
||||
}
|
|
@ -9,12 +9,7 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.exam.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
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;
|
||||
|
@ -26,28 +21,23 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
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.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;
|
||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.LmsType;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ExamProctoringServiceFactory;
|
||||
|
||||
@Lazy
|
||||
@Service
|
||||
|
@ -57,26 +47,20 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
private static final Logger log = LoggerFactory.getLogger(ExamAdminServiceImpl.class);
|
||||
|
||||
private final ExamDAO examDAO;
|
||||
private final ProctoringAdminService proctoringServiceSettingsService;
|
||||
private final AdditionalAttributesDAO additionalAttributesDAO;
|
||||
private final LmsAPIService lmsAPIService;
|
||||
private final Cryptor cryptor;
|
||||
private final ExamProctoringServiceFactory examProctoringServiceFactory;
|
||||
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
|
||||
|
||||
protected ExamAdminServiceImpl(
|
||||
final ExamDAO examDAO,
|
||||
final ProctoringAdminService proctoringServiceSettingsService,
|
||||
final AdditionalAttributesDAO additionalAttributesDAO,
|
||||
final LmsAPIService lmsAPIService,
|
||||
final Cryptor cryptor,
|
||||
final ExamProctoringServiceFactory examProctoringServiceFactory,
|
||||
final RemoteProctoringRoomDAO remoteProctoringRoomDAO) {
|
||||
final LmsAPIService lmsAPIService) {
|
||||
|
||||
this.examDAO = examDAO;
|
||||
this.proctoringServiceSettingsService = proctoringServiceSettingsService;
|
||||
this.additionalAttributesDAO = additionalAttributesDAO;
|
||||
this.lmsAPIService = lmsAPIService;
|
||||
this.cryptor = cryptor;
|
||||
this.examProctoringServiceFactory = examProctoringServiceFactory;
|
||||
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -135,26 +119,7 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
|
||||
@Override
|
||||
public Result<ProctoringServiceSettings> getProctoringServiceSettings(final Long examId) {
|
||||
return this.additionalAttributesDAO.getAdditionalAttributes(EntityType.EXAM, examId)
|
||||
.map(attrs -> attrs.stream()
|
||||
.collect(Collectors.toMap(
|
||||
attr -> attr.getName(),
|
||||
Function.identity())))
|
||||
.map(mapping -> {
|
||||
return new ProctoringServiceSettings(
|
||||
examId,
|
||||
getEnabled(mapping),
|
||||
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),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_SDK_KEY),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_SDK_SECRET),
|
||||
getBoolean(mapping, ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM));
|
||||
});
|
||||
return this.proctoringServiceSettingsService.getProctoringSettings(new EntityKey(examId, EntityType.EXAM));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -163,79 +128,14 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
final Long examId,
|
||||
final ProctoringServiceSettings proctoringServiceSettings) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_ENABLE_PROCTORING,
|
||||
String.valueOf(proctoringServiceSettings.enableProctoring));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_SERVER_TYPE,
|
||||
proctoringServiceSettings.serverType.name());
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_SERVER_URL,
|
||||
StringUtils.trim(proctoringServiceSettings.serverURL));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
|
||||
String.valueOf(proctoringServiceSettings.collectingRoomSize));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_APP_KEY,
|
||||
StringUtils.trim(proctoringServiceSettings.appKey));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_APP_SECRET,
|
||||
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.appSecret))
|
||||
.getOrThrow()
|
||||
.toString());
|
||||
|
||||
if (StringUtils.isNotBlank(proctoringServiceSettings.sdkKey)) {
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_SDK_KEY,
|
||||
StringUtils.trim(proctoringServiceSettings.sdkKey));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_SDK_SECRET,
|
||||
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.sdkSecret))
|
||||
.getOrThrow()
|
||||
.toString());
|
||||
}
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
|
||||
StringUtils.join(proctoringServiceSettings.enabledFeatures, Constants.LIST_SEPARATOR));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.EXAM,
|
||||
examId,
|
||||
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM,
|
||||
String.valueOf(proctoringServiceSettings.useZoomAppClientForCollectingRoom));
|
||||
|
||||
// Mark the involved exam as updated to notify changes
|
||||
this.examDAO.setModified(examId);
|
||||
|
||||
return proctoringServiceSettings;
|
||||
});
|
||||
return this.proctoringServiceSettingsService
|
||||
.saveProctoringServiceSettings(
|
||||
new EntityKey(examId, EntityType.EXAM),
|
||||
proctoringServiceSettings)
|
||||
.map(settings -> {
|
||||
this.examDAO.setModified(examId);
|
||||
return settings;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -256,78 +156,10 @@ public class ExamAdminServiceImpl implements ExamAdminService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type) {
|
||||
return this.examProctoringServiceFactory
|
||||
.getExamProctoringService(type);
|
||||
}
|
||||
|
||||
private Boolean getEnabled(final Map<String, AdditionalAttributeRecord> mapping) {
|
||||
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)) {
|
||||
return BooleanUtils.toBoolean(mapping.get(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING).getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private ProctoringServerType getServerType(final Map<String, AdditionalAttributeRecord> mapping) {
|
||||
if (mapping.containsKey(ProctoringServiceSettings.ATTR_SERVER_TYPE)) {
|
||||
return ProctoringServerType.valueOf(mapping.get(ProctoringServiceSettings.ATTR_SERVER_TYPE).getValue());
|
||||
} else {
|
||||
return ProctoringServerType.JITSI_MEET;
|
||||
}
|
||||
}
|
||||
|
||||
private String getString(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
|
||||
if (mapping.containsKey(name)) {
|
||||
return mapping.get(name).getValue();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean getBoolean(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
|
||||
if (mapping.containsKey(name)) {
|
||||
return BooleanUtils.toBooleanObject(mapping.get(name).getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getCollectingRoomSize(final Map<String, AdditionalAttributeRecord> mapping) {
|
||||
if (mapping.containsKey(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)) {
|
||||
return Integer.valueOf(mapping.get(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE).getValue());
|
||||
} else {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
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 StringUtils.isNotBlank(value)
|
||||
? 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()))
|
||||
: EnumSet.noneOf(ProctoringFeature.class);
|
||||
} 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);
|
||||
}
|
||||
public Result<ExamProctoringService> getExamProctoringService(final Long examId) {
|
||||
return getProctoringServiceSettings(examId)
|
||||
.flatMap(settings -> this.proctoringServiceSettingsService
|
||||
.getExamProctoringService(settings.serverType));
|
||||
}
|
||||
|
||||
private Result<Exam> saveAdditionalAttributesForMoodleExams(final Exam exam) {
|
||||
|
|
|
@ -118,7 +118,8 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
|
||||
@Override
|
||||
public Result<Exam> initAdditionalAttributes(final Exam exam) {
|
||||
return this.examAdminService.saveLMSAttributes(exam)
|
||||
return this.examAdminService
|
||||
.saveLMSAttributes(exam)
|
||||
.map(_exam -> {
|
||||
|
||||
if (exam.examTemplateId != null) {
|
||||
|
@ -148,7 +149,8 @@ public class ExamTemplateServiceImpl implements ExamTemplateService {
|
|||
}
|
||||
}
|
||||
return _exam;
|
||||
}).onError(error -> log.error("Failed to create additional attributes defined by template for exam: ",
|
||||
}).onError(error -> log.error(
|
||||
"Failed to create additional attributes defined by template for exam: ",
|
||||
error));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* 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.webservice.servicelayer.exam.impl;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
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.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
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.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Cryptor;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.ExamProctoringServiceFactory;
|
||||
|
||||
@Lazy
|
||||
@Service
|
||||
@WebServiceProfile
|
||||
public class ProctoringAdminServiceImpl implements ProctoringAdminService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProctoringAdminServiceImpl.class);
|
||||
|
||||
private final AdditionalAttributesDAO additionalAttributesDAO;
|
||||
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
|
||||
private final ExamProctoringServiceFactory examProctoringServiceFactory;
|
||||
private final Cryptor cryptor;
|
||||
|
||||
public ProctoringAdminServiceImpl(
|
||||
final AdditionalAttributesDAO additionalAttributesDAO,
|
||||
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
|
||||
final ExamProctoringServiceFactory examProctoringServiceFactory,
|
||||
final Cryptor cryptor) {
|
||||
|
||||
this.additionalAttributesDAO = additionalAttributesDAO;
|
||||
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
|
||||
this.examProctoringServiceFactory = examProctoringServiceFactory;
|
||||
this.cryptor = cryptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Result<ProctoringServiceSettings> getProctoringSettings(final EntityKey parentEntityKey) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final Long entityId = Long.parseLong(parentEntityKey.modelId);
|
||||
checkType(parentEntityKey);
|
||||
|
||||
return this.additionalAttributesDAO
|
||||
.getAdditionalAttributes(parentEntityKey.entityType, entityId)
|
||||
.map(attrs -> attrs.stream()
|
||||
.collect(Collectors.toMap(
|
||||
attr -> attr.getName(),
|
||||
Function.identity())))
|
||||
.map(mapping -> {
|
||||
return new ProctoringServiceSettings(
|
||||
entityId,
|
||||
getEnabled(mapping),
|
||||
getServerType(mapping),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_SERVER_URL),
|
||||
getCollectingRoomSize(mapping),
|
||||
getEnabledFeatures(mapping),
|
||||
parentEntityKey.entityType == EntityType.EXAM
|
||||
? this.remoteProctoringRoomDAO.isServiceInUse(entityId).getOr(true)
|
||||
: false,
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_APP_KEY),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_APP_SECRET),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_SDK_KEY),
|
||||
getString(mapping, ProctoringServiceSettings.ATTR_SDK_SECRET),
|
||||
getBoolean(mapping,
|
||||
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM));
|
||||
})
|
||||
.getOrThrow();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Result<ProctoringServiceSettings> saveProctoringServiceSettings(
|
||||
final EntityKey parentEntityKey,
|
||||
final ProctoringServiceSettings proctoringServiceSettings) {
|
||||
|
||||
return Result.tryCatch(() -> {
|
||||
final Long entityId = Long.parseLong(parentEntityKey.modelId);
|
||||
checkType(parentEntityKey);
|
||||
|
||||
if (StringUtils.isNotBlank(proctoringServiceSettings.serverURL)) {
|
||||
testExamProctoring(proctoringServiceSettings).getOrThrow();
|
||||
}
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_ENABLE_PROCTORING,
|
||||
String.valueOf(proctoringServiceSettings.enableProctoring));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_SERVER_TYPE,
|
||||
proctoringServiceSettings.serverType.name());
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_SERVER_URL,
|
||||
StringUtils.trim(proctoringServiceSettings.serverURL));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE,
|
||||
String.valueOf(proctoringServiceSettings.collectingRoomSize));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_APP_KEY,
|
||||
StringUtils.trim(proctoringServiceSettings.appKey));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_APP_SECRET,
|
||||
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.appSecret))
|
||||
.getOrThrow()
|
||||
.toString());
|
||||
|
||||
if (StringUtils.isNotBlank(proctoringServiceSettings.sdkKey)) {
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_SDK_KEY,
|
||||
StringUtils.trim(proctoringServiceSettings.sdkKey));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_SDK_SECRET,
|
||||
this.cryptor.encrypt(Utils.trim(proctoringServiceSettings.sdkSecret))
|
||||
.getOrThrow()
|
||||
.toString());
|
||||
}
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_ENABLED_FEATURES,
|
||||
StringUtils.join(proctoringServiceSettings.enabledFeatures, Constants.LIST_SEPARATOR));
|
||||
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
parentEntityKey.entityType,
|
||||
entityId,
|
||||
ProctoringServiceSettings.ATTR_USE_ZOOM_APP_CLIENT_COLLECTING_ROOM,
|
||||
String.valueOf(proctoringServiceSettings.useZoomAppClientForCollectingRoom));
|
||||
|
||||
return proctoringServiceSettings;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<ExamProctoringService> getExamProctoringService(final ProctoringServerType type) {
|
||||
return this.examProctoringServiceFactory
|
||||
.getExamProctoringService(type);
|
||||
}
|
||||
|
||||
private void checkType(final EntityKey parentEntityKey) {
|
||||
if (!SUPPORTED_PARENT_ENTITES.contains(parentEntityKey.entityType)) {
|
||||
throw new UnsupportedOperationException(
|
||||
"No proctoring service settings supported for entity: " + parentEntityKey);
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean getEnabled(final Map<String, AdditionalAttributeRecord> mapping) {
|
||||
if (mapping.containsKey(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING)) {
|
||||
return BooleanUtils.toBoolean(mapping.get(ProctoringServiceSettings.ATTR_ENABLE_PROCTORING).getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private ProctoringServerType getServerType(final Map<String, AdditionalAttributeRecord> mapping) {
|
||||
if (mapping.containsKey(ProctoringServiceSettings.ATTR_SERVER_TYPE)) {
|
||||
return ProctoringServerType.valueOf(mapping.get(ProctoringServiceSettings.ATTR_SERVER_TYPE).getValue());
|
||||
} else {
|
||||
return ProctoringServerType.JITSI_MEET;
|
||||
}
|
||||
}
|
||||
|
||||
private String getString(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
|
||||
if (mapping.containsKey(name)) {
|
||||
return mapping.get(name).getValue();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean getBoolean(final Map<String, AdditionalAttributeRecord> mapping, final String name) {
|
||||
if (mapping.containsKey(name)) {
|
||||
return BooleanUtils.toBooleanObject(mapping.get(name).getValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getCollectingRoomSize(final Map<String, AdditionalAttributeRecord> mapping) {
|
||||
if (mapping.containsKey(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE)) {
|
||||
return Integer.valueOf(mapping.get(ProctoringServiceSettings.ATTR_COLLECTING_ROOM_SIZE).getValue());
|
||||
} else {
|
||||
return 20;
|
||||
}
|
||||
}
|
||||
|
||||
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 StringUtils.isNotBlank(value)
|
||||
? 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()))
|
||||
: EnumSet.noneOf(ProctoringFeature.class);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -40,6 +40,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO;
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService;
|
||||
|
@ -58,6 +59,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
private final RemoteProctoringRoomDAO remoteProctoringRoomDAO;
|
||||
private final ClientConnectionDAO clientConnectionDAO;
|
||||
private final ExamAdminService examAdminService;
|
||||
private final ProctoringAdminService proctoringAdminService;
|
||||
private final ExamSessionService examSessionService;
|
||||
private final SEBClientInstructionService sebInstructionService;
|
||||
private final boolean sendBroadcastReset;
|
||||
|
@ -66,6 +68,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
final RemoteProctoringRoomDAO remoteProctoringRoomDAO,
|
||||
final ClientConnectionDAO clientConnectionDAO,
|
||||
final ExamAdminService examAdminService,
|
||||
final ProctoringAdminService proctoringAdminService,
|
||||
final ExamSessionService examSessionService,
|
||||
final SEBClientInstructionService sebInstructionService,
|
||||
@Value("${sebserver.webservice.proctoring.resetBroadcastOnLeav:true}") final boolean sendBroadcastReset) {
|
||||
|
@ -73,6 +76,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
|
||||
this.clientConnectionDAO = clientConnectionDAO;
|
||||
this.examAdminService = examAdminService;
|
||||
this.proctoringAdminService = proctoringAdminService;
|
||||
this.examSessionService = examSessionService;
|
||||
this.sebInstructionService = sebInstructionService;
|
||||
this.sendBroadcastReset = sendBroadcastReset;
|
||||
|
@ -146,7 +150,8 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
event.ids.forEach(examId -> {
|
||||
try {
|
||||
|
||||
this.examAdminService.examForPK(examId)
|
||||
this.examAdminService
|
||||
.examForPK(examId)
|
||||
.flatMap(this::disposeRoomsForExam)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -176,7 +181,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(exam.id)
|
||||
.getOrThrow();
|
||||
|
||||
this.examAdminService
|
||||
this.proctoringAdminService
|
||||
.getExamProctoringService(proctoringSettings.serverType)
|
||||
.flatMap(service -> service.disposeServiceRoomsForExam(exam.id, proctoringSettings))
|
||||
.onError(error -> log.error("Failed to dispose proctoring service rooms for exam: {} / {}",
|
||||
|
@ -204,7 +209,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(examId)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -242,7 +247,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(examId)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -251,7 +256,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.flatMap(room -> this.remoteProctoringRoomDAO.createBreakOutRoom(examId, room, connectionTokens))
|
||||
.getOrThrow();
|
||||
|
||||
return this.examAdminService
|
||||
return this.proctoringAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.map(service -> sendJoinRoomBreakOutInstructions(
|
||||
settings,
|
||||
|
@ -272,7 +277,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(examId)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -357,7 +362,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(examId)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(proctoringSettings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -401,7 +406,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(examId)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(proctoringSettings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -574,7 +579,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(examId)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -635,7 +640,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.getProctoringServiceSettings(examId)
|
||||
.getOrThrow();
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(settings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
@ -689,7 +694,7 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
final String roomName,
|
||||
final String subject) {
|
||||
|
||||
final ExamProctoringService examProctoringService = this.examAdminService
|
||||
final ExamProctoringService examProctoringService = this.proctoringAdminService
|
||||
.getExamProctoringService(proctoringSettings.serverType)
|
||||
.getOrThrow();
|
||||
|
||||
|
|
|
@ -381,11 +381,6 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
|||
.byPK(examId)
|
||||
.flatMap(this.authorization::checkModify)
|
||||
.map(exam -> {
|
||||
if (StringUtils.isNotBlank(proctoringServiceSettings.serverURL)) {
|
||||
this.examAdminService.getExamProctoringService(proctoringServiceSettings.serverType)
|
||||
.flatMap(service -> service.testExamProctoring(proctoringServiceSettings))
|
||||
.getOrThrow();
|
||||
}
|
||||
this.examAdminService.saveProctoringServiceSettings(examId, proctoringServiceSettings);
|
||||
return exam;
|
||||
})
|
||||
|
|
|
@ -39,6 +39,7 @@ import ch.ethz.seb.sebserver.gbl.model.PageSortOrder;
|
|||
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
|
||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||
|
@ -48,6 +49,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionServic
|
|||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ProctoringAdminService;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationService;
|
||||
|
||||
@WebServiceProfile
|
||||
|
@ -56,6 +58,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
|||
public class ExamTemplateController extends EntityController<ExamTemplate, ExamTemplate> {
|
||||
|
||||
private final ExamTemplateDAO examTemplateDAO;
|
||||
private final ProctoringAdminService proctoringServiceSettingsService;
|
||||
|
||||
protected ExamTemplateController(
|
||||
final AuthorizationService authorization,
|
||||
|
@ -63,7 +66,8 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
final ExamTemplateDAO entityDAO,
|
||||
final UserActivityLogDAO userActivityLogDAO,
|
||||
final PaginationService paginationService,
|
||||
final BeanValidationService beanValidationService) {
|
||||
final BeanValidationService beanValidationService,
|
||||
final ProctoringAdminService proctoringServiceSettingsService) {
|
||||
|
||||
super(
|
||||
authorization,
|
||||
|
@ -74,6 +78,7 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
beanValidationService);
|
||||
|
||||
this.examTemplateDAO = entityDAO;
|
||||
this.proctoringServiceSettingsService = proctoringServiceSettingsService;
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
|
@ -93,6 +98,9 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// **** Indicator
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_TEMPLATE_INDICATOR_PATH_SEGMENT,
|
||||
|
@ -225,11 +233,65 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
|||
|
||||
// check write privilege for requested institution and concrete entityType
|
||||
this.checkWritePrivilege(institutionId);
|
||||
return this.examTemplateDAO.deleteIndicatorTemplate(parentModelId, modelId)
|
||||
return this.examTemplateDAO
|
||||
.deleteIndicatorTemplate(parentModelId, modelId)
|
||||
.flatMap(this.userActivityLogDAO::logDelete)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
// **** Indicator
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
// **** Proctoring
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT,
|
||||
method = RequestMethod.GET,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ProctoringServiceSettings getProctoringServiceSettings(
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable final Long modelId) {
|
||||
|
||||
checkReadPrivilege(institutionId);
|
||||
return this.proctoringServiceSettingsService
|
||||
.getProctoringSettings(new EntityKey(modelId, EntityType.EXAM_TEMPLATE))
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
@RequestMapping(
|
||||
path = API.MODEL_ID_VAR_PATH_SEGMENT
|
||||
+ API.EXAM_ADMINISTRATION_PROCTORING_PATH_SEGMENT,
|
||||
method = RequestMethod.POST,
|
||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public ExamTemplate saveProctoringServiceSettings(
|
||||
@RequestParam(
|
||||
name = API.PARAM_INSTITUTION_ID,
|
||||
required = true,
|
||||
defaultValue = UserService.USERS_INSTITUTION_AS_DEFAULT) final Long institutionId,
|
||||
@PathVariable(API.PARAM_MODEL_ID) final Long examId,
|
||||
@Valid @RequestBody final ProctoringServiceSettings proctoringServiceSettings) {
|
||||
|
||||
checkModifyPrivilege(institutionId);
|
||||
return this.entityDAO
|
||||
.byPK(examId)
|
||||
.flatMap(this.authorization::checkModify)
|
||||
.map(exam -> {
|
||||
this.proctoringServiceSettingsService.saveProctoringServiceSettings(
|
||||
new EntityKey(examId, EntityType.EXAM_TEMPLATE),
|
||||
proctoringServiceSettings);
|
||||
return exam;
|
||||
})
|
||||
.flatMap(this.userActivityLogDAO::logModify)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
// **** Proctoring
|
||||
// ****************************************************************************
|
||||
|
||||
@Override
|
||||
protected ExamTemplate createNew(final POSTMapper postParams) {
|
||||
final Long institutionId = postParams.getLong(API.PARAM_INSTITUTION_ID);
|
||||
|
|
|
@ -132,6 +132,7 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfi
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamDependencies;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamNames;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamPage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplatePage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplates;
|
||||
|
@ -140,7 +141,6 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplatePage;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicators;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetSEBRestrictionSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamConfigMapping;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamTemplate;
|
||||
|
@ -148,10 +148,10 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicator
|
|||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicatorTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamConfigMapping;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicator;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicatorTemplate;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveProctoringSettings;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveSEBRestriction;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution;
|
||||
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution;
|
||||
|
@ -3371,15 +3371,15 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
|||
final RestServiceImpl restService = createRestServiceForUser(
|
||||
"admin",
|
||||
"admin",
|
||||
new GetProctoringSettings(),
|
||||
new SaveProctoringSettings());
|
||||
new GetExamProctoringSettings(),
|
||||
new SaveExamProctoringSettings());
|
||||
|
||||
final Exam exam = createTestExam("admin", "admin");
|
||||
assertNotNull(exam);
|
||||
assertEquals("Demo Quiz 6 (MOCKUP)", exam.name);
|
||||
|
||||
final ProctoringServiceSettings settings = restService
|
||||
.getBuilder(GetProctoringSettings.class)
|
||||
.getBuilder(GetExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
|
||||
.call()
|
||||
.getOrThrow();
|
||||
|
@ -3401,16 +3401,16 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
|
|||
"sdkSecret",
|
||||
false);
|
||||
|
||||
final Result<Exam> saveCall = restService
|
||||
.getBuilder(SaveProctoringSettings.class)
|
||||
final Result<ProctoringServiceSettings> saveCall = restService
|
||||
.getBuilder(SaveExamProctoringSettings.class)
|
||||
.withURIVariable(API.PARAM_MODEL_ID, exam.getModelId())
|
||||
.withBody(newSettings)
|
||||
.call();
|
||||
|
||||
if (!saveCall.hasError()) {
|
||||
assertFalse(saveCall.hasError());
|
||||
final Exam exam2 = saveCall.get();
|
||||
assertEquals(exam2.id, exam.id);
|
||||
final ProctoringServiceSettings settings2 = saveCall.get();
|
||||
assertEquals(settings2.examId, exam.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ public class ExamProctoringRoomServiceTest extends AdministrationAPIIntegrationT
|
|||
this.examAdminService.saveProctoringServiceSettings(
|
||||
2L,
|
||||
new ProctoringServiceSettings(
|
||||
2L, true, ProctoringServerType.JITSI_MEET, "http://jitsi.ch", 1, null, false,
|
||||
2L, true, ProctoringServerType.JITSI_MEET, "", 1, null, false,
|
||||
"app-key", "app.secret", "sdk-key", "sdk.secret", false));
|
||||
|
||||
assertTrue(this.examAdminService.isProctoringEnabled(2L).get());
|
||||
|
|
Loading…
Reference in a new issue