SEBSERV-418 removed SEB config upload to Moodle and call set_exam_data on change instead

This commit is contained in:
anhefti 2024-06-26 10:57:18 +02:00
parent d291e21543
commit 01bd5c5558
8 changed files with 75 additions and 144 deletions

View file

@ -31,7 +31,7 @@ public interface FullLmsIntegrationAPI {
Result<ExamData> applyExamData(ExamData examData);
Result<Exam> applyConnectionConfiguration(Exam exam, byte[] configData);
//Result<Exam> applyConnectionConfiguration(Exam exam, byte[] configData);
Result<String> deleteConnectionDetails();

View file

@ -8,10 +8,7 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
@ -53,7 +50,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConnectionConfigu
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.ConnectionConfigurationService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamConfigUpdateEvent;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ScreenProctoringService;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -152,11 +148,9 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
return Result.tryCatch(() -> {
if (hasFullIntegration(exam.lmsSetupId)) {
this.applyExamData(exam, !exam.active);
this.applyConnectionConfiguration(exam);
}
return exam;
});
}
@Override
@ -245,7 +239,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
.getOr(Collections.emptyList())
.stream()
.filter(exam -> this.needsConnectionConfigurationChange(exam, event.configId))
.forEach(this::applyConnectionConfiguration);
.forEach(exam -> applyExamData(exam, false));
}
@Override
@ -332,8 +326,7 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
.map(template -> getQuizData(template, courseId, quizId, examData))
.map(createExam(examTemplateId, showQuitLink, quitPassword))
.map(exam -> applyExamData(exam, false))
.map(this::applySEBClientRestrictionIfRunning)
.map(this::applyConnectionConfiguration);
.map(this::applySEBClientRestrictionIfRunning);
}
private Exam applySEBClientRestrictionIfRunning(final Exam exam) {
@ -502,7 +495,6 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
.byModelId(examTemplateId)
.getOrThrow();
// import exam
final POSTMapper post = new POSTMapper(null, null);
post.putIfAbsent(Domain.EXAM.ATTR_EXAM_TEMPLATE_ID, examTemplateId);
@ -526,12 +518,11 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
}
private Exam checkDeletion(final Exam exam) {
// TODO check if Exam can be deleted according to the Spec
if (exam.status != Exam.ExamStatus.RUNNING) {
return exam;
}
// if exam is running and has active SEB client connections, it cannot be deleted
final Integer active = this.clientConnectionDAO
.getAllActiveConnectionTokens(exam.id)
.map(Collection::size)
@ -545,15 +536,6 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
throw new APIMessage.APIMessageException(
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
.of("Exam currently has active SEB Client connections."));
// check if there are no active SEB client connections
// if (this.examSessionService.hasActiveSEBClientConnections(exam.id)) {
// throw new APIMessage.APIMessageException(
// APIMessage.ErrorMessage.INTEGRITY_VALIDATION
// .of("Exam currently has active SEB Client connections."));
// }
//
// return exam;
}
@ -637,38 +619,37 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
}
}
private Exam applyConnectionConfiguration(final Exam exam) {
return lmsAPITemplateCacheService
.getLmsAPITemplate(exam.lmsSetupId)
.flatMap(template -> {
final String connectionConfigId = getConnectionConfigurationId(exam);
final ByteArrayOutputStream out = new ByteArrayOutputStream();
final PipedOutputStream pout;
final PipedInputStream pin;
try {
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
this.connectionConfigurationService
.exportSEBClientConfiguration(pout, connectionConfigId, exam.id);
out.flush();
IOUtils.copyLarge(pin, out);
// TODO check if this works as expected
return template.applyConnectionConfiguration(exam, out.toByteArray());
} catch (final Exception e) {
throw new RuntimeException("Failed to stream output", e);
} finally {
IOUtils.closeQuietly(out);
}
})
.onError(error -> log.error("Failed to apply ConnectionConfiguration for exam: {} error: ", exam, error))
.getOr(exam);
}
// private Exam applyConnectionConfiguration(final Exam exam) {
// return lmsAPITemplateCacheService
// .getLmsAPITemplate(exam.lmsSetupId)
// .flatMap(template -> {
// final String connectionConfigId = getConnectionConfigurationId(exam);
//
// final ByteArrayOutputStream out = new ByteArrayOutputStream();
// final PipedOutputStream pout;
// final PipedInputStream pin;
// try {
// pout = new PipedOutputStream();
// pin = new PipedInputStream(pout);
//
// this.connectionConfigurationService
// .exportSEBClientConfiguration(pout, connectionConfigId, exam.id);
//
// out.flush();
//
// IOUtils.copyLarge(pin, out);
//
// return template.applyConnectionConfiguration(exam, out.toByteArray());
//
// } catch (final Exception e) {
// throw new RuntimeException("Failed to stream output", e);
// } finally {
// IOUtils.closeQuietly(out);
// }
// })
// .onError(error -> log.error("Failed to apply ConnectionConfiguration for exam: {} error: ", exam, error))
// .getOr(exam);
// }
private boolean hasFullIntegration(final Long lmsSetupId) {
// no LMS

View file

@ -546,23 +546,6 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
.getOrThrow());
}
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
if (this.lmsIntegrationAPI == null) {
return Result.ofError(
new UnsupportedOperationException("LMS Integration API Not Supported For: " + getType().name()));
}
if (log.isDebugEnabled()) {
log.debug("Apply Connection Configuration for exam: {} for LMSSetup: {}", exam, lmsSetup());
}
return this.examRequest.protectedRun(() -> this.lmsIntegrationAPI
.applyConnectionConfiguration(exam, configData)
.getOrThrow());
}
@Override
public Result<String> deleteConnectionDetails() {
if (this.lmsIntegrationAPI == null) {

View file

@ -444,11 +444,6 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
return Result.ofRuntimeError("Not Supported");
}
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.ofRuntimeError("Not Supported");
}
@Override
public Result<String> deleteConnectionDetails() {
return Result.ofRuntimeError("Not Supported");

View file

@ -39,11 +39,6 @@ public class MockupFullIntegration implements FullLmsIntegrationAPI {
return Result.ofRuntimeError("Not Supported");
}
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.ofRuntimeError("Not Supported");
}
@Override
public Result<String> deleteConnectionDetails() {
return Result.ofRuntimeError("TODO");

View file

@ -452,32 +452,12 @@ public class MoodleRestTemplateFactoryImpl implements MoodleRestTemplateFactory
new HttpEntity<>(body, headers);
final ResponseEntity<String> exchange = super.exchange(
uri.toString(),
uri.append("?token=").append(this.accessToken).toString(),
HttpMethod.POST,
requestEntity,
String.class);
return exchange.getBody();
// multiPartAttributes.add("token", this.accessToken.toString());
//
// queryAttributes.forEach((key, values) -> {
// if (values.isEmpty()) {
// return;
// }
// if (uri.toString().contains("?")) {
// uri.append("&").append(key).append("=").append(values.get(0));
// } else {
// uri.append("?").append(key).append("=").append(values.get(0));
// }
// });
//
// log.info("Upload to Moodle url: {}", uri.toString());
//
// return super.postForObject(
// uri.toString(),
// multiPartAttributes,
// String.class);
}
private String doRequest(

View file

@ -239,43 +239,43 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
});
}
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.tryCatch(() -> {
final String quizId = MoodleUtils.getQuizId(exam.externalId);
final String fileName = getConnectionConfigFileName(exam);
// final MultiValueMap<String, Object> multiPartAttributes = new LinkedMultiValueMap<>();
// multiPartAttributes.add("quizid", quizId);
// multiPartAttributes.add("name", fileName);
// multiPartAttributes.add("filename", fileName);
// final MultiValueMap<String, String> queryAttributes = new LinkedMultiValueMap<>();
// //queryAttributes.add("quizid", quizId);
// final ByteArrayResource contentsAsResource = new ByteArrayResource(configData) {
// @Override
// public String getFilename() {
// return fileName; // Filename has to be returned in order to be able to post.
// }
// };
// @Override
// public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
// return Result.tryCatch(() -> {
//
// multiPartAttributes.add("file", contentsAsResource);
final MoodleAPIRestTemplate rest = getRestTemplate().getOrThrow();
final String response = rest.uploadMultiPart(
UPLOAD_ENDPOINT,
quizId,
fileName,
configData);
if (response != null) {
log.info("Upload Connection Configuration to Moodle: quizid: {}, fileName: {} response: {}", quizId, fileName, response );
}
return exam;
});
}
// final String quizId = MoodleUtils.getQuizId(exam.externalId);
// final String fileName = getConnectionConfigFileName(exam);
//
//// final MultiValueMap<String, Object> multiPartAttributes = new LinkedMultiValueMap<>();
//// multiPartAttributes.add("quizid", quizId);
//// multiPartAttributes.add("name", fileName);
//// multiPartAttributes.add("filename", fileName);
//
//// final MultiValueMap<String, String> queryAttributes = new LinkedMultiValueMap<>();
//// //queryAttributes.add("quizid", quizId);
//// final ByteArrayResource contentsAsResource = new ByteArrayResource(configData) {
//// @Override
//// public String getFilename() {
//// return fileName; // Filename has to be returned in order to be able to post.
//// }
//// };
////
//// multiPartAttributes.add("file", contentsAsResource);
//
// final MoodleAPIRestTemplate rest = getRestTemplate().getOrThrow();
// final String response = rest.uploadMultiPart(
// UPLOAD_ENDPOINT,
// quizId,
// fileName,
// configData);
//
// if (response != null) {
// log.info("Upload Connection Configuration to Moodle: quizid: {}, fileName: {} response: {}", quizId, fileName, response );
// }
//
// return exam;
// });
// }
private String getConnectionConfigFileName(final Exam exam) {
return "SEBServerConnectionConfiguration-" + exam.id + ".seb";
@ -317,7 +317,9 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
public Result<QuizData> getQuizDataForRemoteImport(final String examData) {
return Result.tryCatch(() -> {
log.info("****** Try to parse import exam data sent by Moodle on Exam import: {}", examData);
if (log.isDebugEnabled()) {
log.debug("Try to parse import exam data sent by Moodle on Exam import: {}", examData);
}
final LmsSetup lmsSetup = this.restTemplateFactory.getApiTemplateDataSupplier().getLmsSetup();
final String urlPrefix = (lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR))

View file

@ -431,11 +431,6 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
return Result.ofRuntimeError("Not Supported");
}
@Override
public Result<Exam> applyConnectionConfiguration(final Exam exam, final byte[] configData) {
return Result.ofRuntimeError("Not Supported");
}
@Override
public Result<String> deleteConnectionDetails() {
return Result.ofRuntimeError("Not Supported");