Skip to content

Commit

Permalink
SEBSERV-419
Browse files Browse the repository at this point in the history
  • Loading branch information
anhefti committed Jul 2, 2024
1 parent 900bbfe commit c0ead99
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

package ch.ethz.seb.sebserver.gui.content.action;

import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gui.content.activity.PageStateDefinitionImpl;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageStateDefinition;
Expand Down Expand Up @@ -300,12 +299,12 @@ public enum ActionDefinition {
ActionCategory.FORM),
EXAM_TOGGLE_TEST_RUN_ON(
new LocTextKey("sebserver.exam.action.test.run.on"),
ImageIcon.ARCHIVE,
ImageIcon.TEST_RUN_OFF,
PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.FORM),
EXAM_TOGGLE_TEST_RUN_OFF(
new LocTextKey("sebserver.exam.action.test.run.off"),
ImageIcon.ARCHIVE,
ImageIcon.TEST_RUN_ON,
PageStateDefinitionImpl.EXAM_VIEW,
ActionCategory.FORM),
EXAM_ARCHIVE(
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.session.ToggleTestRun;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.joda.time.DateTime;
Expand Down Expand Up @@ -112,6 +113,7 @@ public class ExamForm implements TemplateComposer {
private final static LocTextKey CONSISTENCY_MESSAGE_SEB_RESTRICTION_MISMATCH = new LocTextKey("sebserver.exam.consistencyseb-restriction-mismatch");
private final static LocTextKey AUTO_GEN_CONFIG_ERROR_TITLE = new LocTextKey("sebserver.exam.autogen.error.config.title");
private final static LocTextKey AUTO_GEN_CONFIG_ERROR_TEXT = new LocTextKey("sebserver.exam.autogen.error.config.text");
private final static LocTextKey TEST_RUN_ENABLED_NOTE = new LocTextKey("sebserver.exam.test.run.enabled.note");

private final Map<String, LocTextKey> consistencyMessageMapping;
private final PageService pageService;
Expand Down Expand Up @@ -232,6 +234,9 @@ public void compose(final PageContext pageContext) {
if (sebRestrictionMismatch || (warnings != null && !warnings.isEmpty())) {
showConsistencyChecks(warnings, sebRestrictionMismatch, formContext.getParent());
}
if (exam.status == ExamStatus.TEST_RUN) {
showTestRunMessage(formContext.getParent());
}
}

// the default page layout with title
Expand Down Expand Up @@ -338,26 +343,24 @@ public void compose(final PageContext pageContext) {
.withAttribute(ExamSEBRestrictionSettings.PAGE_CONTEXT_ATTR_LMS_ID, String.valueOf(exam.lmsSetupId))
.withAttribute(PageContext.AttributeKeys.FORCE_READ_ONLY, String.valueOf(!modifyGrant || !editable))
.noEventPropagation()
.publishIf(() -> restrictionEnabled && sebRestrictionAvailable && readonly)
.publishIf(() -> restrictionEnabled && sebRestrictionAvailable)

.newAction(ActionDefinition.EXAM_ENABLE_SEB_RESTRICTION)
.withEntityKey(entityKey)
.withExec(action -> this.examSEBRestrictionSettings.setSEBRestriction(action, true, this.restService))
.publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData
&& BooleanUtils.isFalse(isRestricted))
.publishIf(() -> sebRestrictionAvailable && modifyGrant && !importFromQuizData && BooleanUtils.isFalse(isRestricted))

.newAction(ActionDefinition.EXAM_DISABLE_SEB_RESTRICTION)
.withConfirm(() -> ACTION_MESSAGE_SEB_RESTRICTION_RELEASE)
.withEntityKey(entityKey)
.withExec(action -> this.examSEBRestrictionSettings.setSEBRestriction(action, false, this.restService))
.publishIf(() -> sebRestrictionAvailable && readonly && modifyGrant && !importFromQuizData
&& BooleanUtils.isTrue(isRestricted))
.publishIf(() -> sebRestrictionAvailable && modifyGrant && !importFromQuizData && BooleanUtils.isTrue(isRestricted))

.newAction(ActionDefinition.EXAM_PROCTORING_ON)
.withEntityKey(entityKey)
.withExec(this.proctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation()
.publishIf(() -> lpEnabled && !isLight && proctoringEnabled && readonly)
.publishIf(() -> lpEnabled && !isLight && proctoringEnabled)

.newAction(ActionDefinition.EXAM_PROCTORING_OFF)
.withEntityKey(entityKey)
Expand All @@ -370,7 +373,7 @@ public void compose(final PageContext pageContext) {
.withExec(
this.screenProctoringSettingsPopup.settingsFunction(this.pageService, modifyGrant && editable))
.noEventPropagation()
.publishIf(() -> spsFeatureEnabled && screenProctoringEnabled && readonly)
.publishIf(() -> spsFeatureEnabled && screenProctoringEnabled)

.newAction(ActionDefinition.SCREEN_PROCTORING_OFF)
.withEntityKey(entityKey)
Expand Down Expand Up @@ -773,6 +776,15 @@ private boolean hasSEBRestrictionAPI(final Exam exam) {
return (lmsSetup.getLmsType().features.contains(LmsSetup.Features.SEB_RESTRICTION));
}

private void showTestRunMessage(final Composite parent) {
final Composite notePanel = this.widgetFactory.createWarningPanel(parent);
notePanel.setData(RWT.CUSTOM_VARIANT, CustomVariant.NOTE.key);
this.widgetFactory.labelLocalized(
notePanel,
CustomVariant.TITLE_LABEL,
TEST_RUN_ENABLED_NOTE);
}

private void showConsistencyChecks(
final Collection<APIMessage> result,
final boolean sebRestrictionMismatch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ public enum ImageIcon {
BACK("back.png"),
SCREEN_PROC_ON("screen_proc_on.png"),
SCREEN_PROC_OFF("screen_proc_off.png"),
ADD_EXAM("add_exam.png");
ADD_EXAM("add_exam.png"),
TEST_RUN_ON("testRunOn.png"),
TEST_RUN_OFF("testRunOff.png");

public String fileName;
private ImageData image = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,18 @@ public void notifyLmsSetupChange(final LmsSetupChangeEvent event) {
if (event.activation == Activatable.ActivationAction.NONE) {
if (!lmsSetup.integrationActive) {
applyFullLmsIntegration(lmsSetup.id)
.onError(error -> log.warn("Failed to update LMS integration for: {} error {}", lmsSetup, error.getMessage()))
.onSuccess(data -> log.debug("Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
.onError(error -> log.warn(
"Failed to update LMS integration for: {} error {}", lmsSetup, error.getMessage()))
.onSuccess(data -> log.debug(
"Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
}
} else if (event.activation == Activatable.ActivationAction.ACTIVATE) {
applyFullLmsIntegration(lmsSetup.id)
.map(data -> reapplyExistingExams(data,lmsSetup))
.onError(error -> log.warn("Failed to update LMS integration for: {} error {}", lmsSetup, error.getMessage()))
.onSuccess(data -> log.debug("Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
.onError(error -> log.warn(
"Failed to update LMS integration for: {} error {}", lmsSetup, error.getMessage()))
.onSuccess(data -> log.debug(
"Successfully updated LMS integration for: {} data: {}", lmsSetup, data));
} else if (event.activation == Activatable.ActivationAction.DEACTIVATE) {
// remove all active exam data for involved exams before deactivate them
this.examDAO
Expand Down Expand Up @@ -235,7 +239,9 @@ public void notifyExamTemplateChange(final ExamTemplateChangeEvent event) {
public void notifyConnectionConfigurationChange(final ConnectionConfigurationChangeEvent event) {
lmsSetupDAO.idsOfActiveWithFullIntegration(event.institutionId)
.flatMap(examDAO::allActiveForLMSSetup)
.onError(error -> log.error("Failed to notifyConnectionConfigurationChange: {}", error.getMessage()))
.onError(error -> log.error(
"Failed to notifyConnectionConfigurationChange: {}",
error.getMessage()))
.getOr(Collections.emptyList())
.stream()
.filter(exam -> this.needsConnectionConfigurationChange(exam, event.configId))
Expand Down Expand Up @@ -370,16 +376,22 @@ public Result<Void> streamConnectionConfiguration(
.flatMap(this::findExam);

if (examResult.hasError()) {
log.error("Failed to find exam for SEB Connection Configuration download: ", examResult.getError());
throw new APIMessage.APIMessageException(APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("Exam not found"));
log.error(
"Failed to find exam for SEB Connection Configuration download: ",
examResult.getError());
throw new APIMessage.APIMessageException(
APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("Exam not found"));
}

final Exam exam = examResult.get();

final String connectionConfigId = getConnectionConfigurationId(exam);
if (StringUtils.isBlank(connectionConfigId)) {
log.error("Failed to verify SEB Connection Configuration id for exam: {}", exam.name);
throw new APIMessage.APIMessageException(APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("No active Connection Configuration found"));
log.error(
"Failed to verify SEB Connection Configuration id for exam: {}",
exam.name);
throw new APIMessage.APIMessageException(
APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("No active Connection Configuration found"));
}

this.connectionConfigurationService.exportSEBClientConfiguration(
Expand Down Expand Up @@ -413,7 +425,8 @@ private String getConnectionConfigurationId(final Exam exam) {
.map(all -> all.stream().filter(config -> config.configPurpose == SEBClientConfig.ConfigPurpose.START_EXAM)
.findFirst()
.orElseThrow(() -> new APIMessage.APIMessageException(
APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of("No active Connection Configuration found"))))
APIMessage.ErrorMessage.ILLEGAL_API_ARGUMENT.of(
"No active Connection Configuration found"))))
.map(SEBClientConfig::getModelId)
.getOr(null);
}
Expand Down Expand Up @@ -487,7 +500,6 @@ private Function<QuizData, Exam> createExam(
// check if the exam has already been imported, If so return the existing exam
final Result<Exam> existingExam = findExam(quizData);
if (!existingExam.hasError()) {
// TODO do we need to check if ad-hoc account exists and if not, create one?
return existingExam.get();
}

Expand Down Expand Up @@ -526,7 +538,9 @@ private Exam checkDeletion(final Exam exam) {
final Integer active = this.clientConnectionDAO
.getAllActiveConnectionTokens(exam.id)
.map(Collection::size)
.onError(error -> log.warn("Failed to get active access tokens for exam: {}", error.getMessage()))
.onError(error -> log.warn(
"Failed to get active access tokens for exam: {}",
error.getMessage()))
.getOr(1);

if (active == null || active == 0) {
Expand Down Expand Up @@ -570,9 +584,13 @@ private Exam applyExamData(final Exam exam, final boolean deletion) {
? null
: exam.examTemplateId != null
? String.valueOf(exam.examTemplateId)
: "0";
final String quitPassword = deletion ? null : examConfigurationValueService.getQuitPassword(exam.id);
final String quitLink = deletion ? null : examConfigurationValueService.getQuitLink(exam.id);
: "0"; // no selection on Moodle site
final String quitPassword = deletion
? null
: examConfigurationValueService.getQuitPassword(exam.id);
final String quitLink = deletion
? null
: examConfigurationValueService.getQuitLink(exam.id);

final ExamData examData = new ExamData(
lmsUUID,
Expand Down Expand Up @@ -620,6 +638,10 @@ private Exam applyQuitLinkToSEBConfig(final Exam exam, final boolean showQuitLin
}
}

// Note: We decided that Moodle gets the Connection Configuration from SEB Server instead of SEB Server
// sending the Connection Configuration to Moodle. Code on SEB Server site and Moodle function still
// remains here for the case its need in the future.
//
// private Exam applyConnectionConfiguration(final Exam exam) {
// return lmsAPITemplateCacheService
// .getLmsAPITemplate(exam.lmsSetupId)
Expand Down Expand Up @@ -689,14 +711,18 @@ private String getAutoLoginURL() {
private Exam logExamCreated(final Exam exam) {
this.userActivityLogDAO
.logCreate(exam)
.onError(error -> log.warn("Failed to log exam creation from LMS: {}", error.getMessage()));
.onError(error -> log.warn(
"Failed to log exam creation from LMS: {}",
error.getMessage()));
return exam;
}

private Exam logExamDeleted(final Exam exam) {
this.userActivityLogDAO
.logDelete(exam)
.onError(error -> log.warn("Failed to log exam deletion from LMS: {}", error.getMessage()));
.onError(error -> log.warn(
"Failed to log exam deletion from LMS: {}",
error.getMessage()));
return exam;
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -926,7 +926,7 @@ sebserver.exam.sps.form.collect.strategy=Grouping Strategy
sebserver.exam.sps.form.saveSettings=Save Settings
sebserver.exam.sps.form.saveSettings.error=SEB Server is not able to connect to bundled Screen Proctoring service within the given Service URL.<br/>This probably relies on a incorrect SEB Server setup.<br/><br/>Please make sure your SEB Server setup uses the correct URL mappings or contact a system administrator.


sebserver.exam.test.run.enabled.note=Exam Test Run is currently enabled. To disable please use "Disable Test Run"


sebserver.exam.signaturekey.action.edit=App Signature Key
Expand Down
Binary file added src/main/resources/static/images/testRunOff.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/resources/static/images/testRunOn.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c0ead99

Please sign in to comment.