SEBSERV-417 added get exam data from Moodle on exam import and fixed teacher account for different exams
This commit is contained in:
parent
b6e3772dcd
commit
6de875b29c
15 changed files with 110 additions and 24 deletions
|
@ -176,6 +176,7 @@ public final class API {
|
|||
public static final String LMS_FULL_INTEGRATION_COURSE_ID = "course_id";
|
||||
public static final String LMS_FULL_INTEGRATION_QUIZ_ID = "quiz_id";
|
||||
public static final String LMS_FULL_INTEGRATION_EXAM_TEMPLATE_ID = "exam_template_id";
|
||||
public static final String LMS_FULL_INTEGRATION_EXAM_DATA = "exam_data";
|
||||
public static final String LMS_FULL_INTEGRATION_QUIT_PASSWORD = "quit_password";
|
||||
public static final String LMS_FULL_INTEGRATION_QUIT_LINK = "quit_link";
|
||||
public static final String LMS_FULL_INTEGRATION_USER_ID = "user_id";
|
||||
|
|
|
@ -38,18 +38,17 @@ public interface TeacherAccountService {
|
|||
final FullLmsIntegrationService.AdHocAccountData adHocAccountData) {
|
||||
|
||||
return getTeacherAccountIdentifier(
|
||||
exam.getModelId(),
|
||||
String.valueOf(exam.lmsSetupId),
|
||||
adHocAccountData.userId);
|
||||
}
|
||||
|
||||
/** Get the identifier for certain Teacher account for specified Exam.
|
||||
*
|
||||
* @param examId The Exam identifier
|
||||
* @param lmsId The Lms identifier
|
||||
* @param userId the account id
|
||||
* @return account identifier
|
||||
*/
|
||||
String getTeacherAccountIdentifier(String examId, String lmsId, String userId);
|
||||
String getTeacherAccountIdentifier(String lmsId, String userId);
|
||||
|
||||
/** Deactivates a certain ad-hoc Teacher account
|
||||
* Usually called when an exam is deleted. Checks if Teacher account for exam
|
||||
|
|
|
@ -78,15 +78,14 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
|
|||
|
||||
@Override
|
||||
public String getTeacherAccountIdentifier(
|
||||
final String examId,
|
||||
final String lmsId,
|
||||
final String userId) {
|
||||
|
||||
if (examId == null || lmsId == null || userId == null) {
|
||||
if (lmsId == null || userId == null) {
|
||||
throw new RuntimeException("examId and/or userId cannot be null");
|
||||
}
|
||||
|
||||
return examId + Constants.UNDERLINE + lmsId + Constants.UNDERLINE + userId;
|
||||
return "TEACHER_" + Constants.UNDERLINE + lmsId + Constants.UNDERLINE + userId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
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.LmsSetupTestResult;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
@ -32,4 +33,5 @@ public interface FullLmsIntegrationAPI {
|
|||
|
||||
Result<String> deleteConnectionDetails();
|
||||
|
||||
Result<QuizData> getQuizDataForRemoteImport(String examData);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,8 @@ public interface FullLmsIntegrationService {
|
|||
String quizId,
|
||||
String examTemplateId,
|
||||
String quitPassword,
|
||||
String quitLink);
|
||||
String quitLink,
|
||||
final String examData);
|
||||
|
||||
Result<EntityKey> deleteExam(
|
||||
String lmsUUID,
|
||||
|
|
|
@ -315,12 +315,14 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
final String quizId,
|
||||
final String examTemplateId,
|
||||
final String quitPassword,
|
||||
final String quitLink) {
|
||||
final String quitLink,
|
||||
final String examData) {
|
||||
|
||||
return lmsSetupDAO
|
||||
.getLmsSetupIdByConnectionId(lmsUUID)
|
||||
.flatMap(lmsAPITemplateCacheService::getLmsAPITemplate)
|
||||
.map(findQuizData(courseId, quizId))
|
||||
.map(template -> getQuizData(template, courseId, quizId, examData))
|
||||
//.map(findQuizData(courseId, quizId))
|
||||
.map(createExam(examTemplateId, quitPassword))
|
||||
.map(exam -> applyExamData(exam, false))
|
||||
.map(this::applyConnectionConfiguration);
|
||||
|
@ -418,6 +420,22 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
|||
.getOneTimeTokenForTeacherAccount(exam, adHocAccountData, true));
|
||||
}
|
||||
|
||||
private QuizData getQuizData(
|
||||
final LmsAPITemplate lmsAPITemplate,
|
||||
final String courseId,
|
||||
final String quizId,
|
||||
final String examData) {
|
||||
|
||||
final String internalQuizId = MoodleUtils.getInternalQuizId(
|
||||
quizId,
|
||||
courseId,
|
||||
null,
|
||||
null);
|
||||
|
||||
return lmsAPITemplate.getQuizDataForRemoteImport(examData)
|
||||
.getOrThrow();
|
||||
}
|
||||
|
||||
|
||||
private Function<LmsAPITemplate, QuizData> findQuizData(
|
||||
final String courseId,
|
||||
|
|
|
@ -577,4 +577,14 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
|
|||
.getOrThrow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<QuizData> getQuizDataForRemoteImport(final String examData) {
|
||||
if (this.lmsIntegrationAPI == null) {
|
||||
return Result.ofError(
|
||||
new UnsupportedOperationException("LMS Integration API Not Supported For: " + getType().name()));
|
||||
}
|
||||
|
||||
return this.lmsIntegrationAPI.getQuizDataForRemoteImport(examData);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -447,6 +447,11 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms
|
|||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<QuizData> getQuizDataForRemoteImport(final String examData) {
|
||||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
private enum LinkRel {
|
||||
FIRST, LAST, PREV, NEXT
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
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.LmsSetupTestResult;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
@ -42,4 +43,9 @@ public class MockupFullIntegration implements FullLmsIntegrationAPI {
|
|||
public Result<String> deleteConnectionDetails() {
|
||||
return Result.ofRuntimeError("TODO");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<QuizData> getQuizDataForRemoteImport(final String examData) {
|
||||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,7 +291,7 @@ public class MoodlePluginCourseAccess extends AbstractCachedCourseAccess impleme
|
|||
|
||||
return this.jsonMapper.readValue(
|
||||
courseJSON,
|
||||
Courses.class).courses
|
||||
CoursesPlugin.class).results
|
||||
.stream()
|
||||
.flatMap(c -> MoodleUtils.quizDataOf(
|
||||
lmsSetup,
|
||||
|
|
|
@ -8,14 +8,13 @@
|
|||
|
||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
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.LmsSetupTestResult;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
|
@ -30,6 +29,7 @@ import org.apache.commons.lang3.BooleanUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
@ -49,12 +49,19 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
|
|||
private final JSONMapper jsonMapper;
|
||||
private final MoodleRestTemplateFactory restTemplateFactory;
|
||||
|
||||
private final boolean prependShortCourseName;
|
||||
|
||||
public MoodlePluginFullIntegration(
|
||||
final JSONMapper jsonMapper,
|
||||
final MoodleRestTemplateFactory restTemplateFactory) {
|
||||
final MoodleRestTemplateFactory restTemplateFactory,
|
||||
final Environment environment) {
|
||||
|
||||
this.jsonMapper = jsonMapper;
|
||||
this.restTemplateFactory = restTemplateFactory;
|
||||
|
||||
this.prependShortCourseName = BooleanUtils.toBoolean(environment.getProperty(
|
||||
"sebserver.webservice.lms.moodle.prependShortCourseName",
|
||||
Constants.TRUE_STRING));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -289,6 +296,33 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
|
||||
final LmsSetup lmsSetup = this.restTemplateFactory.getApiTemplateDataSupplier().getLmsSetup();
|
||||
final String urlPrefix = (lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR))
|
||||
? lmsSetup.lmsApiUrl + MoodlePluginCourseAccess.MOODLE_QUIZ_START_URL_PATH
|
||||
: lmsSetup.lmsApiUrl + Constants.URL_PATH_SEPARATOR + MoodlePluginCourseAccess.MOODLE_QUIZ_START_URL_PATH;
|
||||
MoodleUtils.checkJSONFormat(examData);
|
||||
final MoodleUtils.CoursesPlugin courses = this.jsonMapper.readValue(
|
||||
examData,
|
||||
MoodleUtils.CoursesPlugin.class);
|
||||
|
||||
final MoodleUtils.CourseData courseData = courses.results.iterator().next();
|
||||
final List<QuizData> quizData = MoodleUtils.quizDataOf(
|
||||
lmsSetup,
|
||||
courseData,
|
||||
urlPrefix,
|
||||
prependShortCourseName
|
||||
);
|
||||
|
||||
return quizData.get(0);
|
||||
});
|
||||
}
|
||||
|
||||
private Result<MoodleAPIRestTemplate> getRestTemplate() {
|
||||
|
||||
final Result<MoodleAPIRestTemplate> result = this.restTemplateFactory.getRestTemplate();
|
||||
|
|
|
@ -103,7 +103,8 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory
|
|||
|
||||
final MoodlePluginFullIntegration moodlePluginFullIntegration = new MoodlePluginFullIntegration(
|
||||
this.jsonMapper,
|
||||
moodleRestTemplateFactory
|
||||
moodleRestTemplateFactory,
|
||||
environment
|
||||
);
|
||||
|
||||
return new LmsAPITemplateAdapter(
|
||||
|
|
|
@ -434,6 +434,11 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
|||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<QuizData> getQuizDataForRemoteImport(final String examData) {
|
||||
return Result.ofRuntimeError("Not Supported");
|
||||
}
|
||||
|
||||
private <T> T apiGet(final RestTemplate restTemplate, final String url, final Class<T> type) {
|
||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||
final ResponseEntity<T> res = restTemplate.exchange(
|
||||
|
|
|
@ -485,11 +485,14 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
|||
}
|
||||
|
||||
private Result<Exam> deleteForExam(final Long examId) {
|
||||
return this.examDAO
|
||||
.byPK(examId)
|
||||
.flatMap(this.screenProctoringAPIBinding::deactivateScreenProctoring)
|
||||
.map(this::cleanupAllLocalGroups)
|
||||
.onError(error -> log.error("Failed to delete SPS integration for exam: {}", examId, error));
|
||||
return Result.tryCatch(() -> {
|
||||
final Exam exam = this.examDAO.byPK(examId).getOrThrow();
|
||||
|
||||
this.screenProctoringAPIBinding.deactivateScreenProctoring(exam)
|
||||
.onError(error -> log.error("Failed to deactivate screen proctoring for exam: {}", exam.name, error));
|
||||
|
||||
return this.cleanupAllLocalGroups(exam);
|
||||
});
|
||||
}
|
||||
|
||||
private Exam cleanupAllLocalGroups(final Exam exam) {
|
||||
|
|
|
@ -59,6 +59,7 @@ public class LmsIntegrationController {
|
|||
@RequestParam(name = API.LMS_FULL_INTEGRATION_COURSE_ID) final String courseId,
|
||||
@RequestParam(name = API.LMS_FULL_INTEGRATION_QUIZ_ID) final String quizId,
|
||||
@RequestParam(name = API.LMS_FULL_INTEGRATION_EXAM_TEMPLATE_ID) final String templateId,
|
||||
@RequestParam(name = API.LMS_FULL_INTEGRATION_EXAM_DATA, required = false) final String examData,
|
||||
@RequestParam(name = API.LMS_FULL_INTEGRATION_QUIT_PASSWORD, required = false) final String quitPassword,
|
||||
@RequestParam(name = API.LMS_FULL_INTEGRATION_QUIT_LINK, required = false) final String quitLink,
|
||||
final HttpServletResponse response) {
|
||||
|
@ -69,10 +70,11 @@ public class LmsIntegrationController {
|
|||
quizId,
|
||||
templateId,
|
||||
quitPassword,
|
||||
quitLink)
|
||||
quitLink,
|
||||
examData)
|
||||
.onError(e -> log.error(
|
||||
"Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {}",
|
||||
lmsUUId, courseId, quizId, templateId, e))
|
||||
"Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {} error: {}",
|
||||
lmsUUId, courseId, quizId, templateId, e.getMessage()))
|
||||
.getOrThrow();
|
||||
|
||||
log.info("Auto import of exam successful: {}", exam);
|
||||
|
|
Loading…
Reference in a new issue