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_COURSE_ID = "course_id";
|
||||||
public static final String LMS_FULL_INTEGRATION_QUIZ_ID = "quiz_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_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_PASSWORD = "quit_password";
|
||||||
public static final String LMS_FULL_INTEGRATION_QUIT_LINK = "quit_link";
|
public static final String LMS_FULL_INTEGRATION_QUIT_LINK = "quit_link";
|
||||||
public static final String LMS_FULL_INTEGRATION_USER_ID = "user_id";
|
public static final String LMS_FULL_INTEGRATION_USER_ID = "user_id";
|
||||||
|
|
|
@ -38,18 +38,17 @@ public interface TeacherAccountService {
|
||||||
final FullLmsIntegrationService.AdHocAccountData adHocAccountData) {
|
final FullLmsIntegrationService.AdHocAccountData adHocAccountData) {
|
||||||
|
|
||||||
return getTeacherAccountIdentifier(
|
return getTeacherAccountIdentifier(
|
||||||
exam.getModelId(),
|
|
||||||
String.valueOf(exam.lmsSetupId),
|
String.valueOf(exam.lmsSetupId),
|
||||||
adHocAccountData.userId);
|
adHocAccountData.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the identifier for certain Teacher account for specified Exam.
|
/** 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
|
* @param userId the account id
|
||||||
* @return account identifier
|
* @return account identifier
|
||||||
*/
|
*/
|
||||||
String getTeacherAccountIdentifier(String examId, String lmsId, String userId);
|
String getTeacherAccountIdentifier(String lmsId, String userId);
|
||||||
|
|
||||||
/** Deactivates a certain ad-hoc Teacher account
|
/** Deactivates a certain ad-hoc Teacher account
|
||||||
* Usually called when an exam is deleted. Checks if Teacher account for exam
|
* Usually called when an exam is deleted. Checks if Teacher account for exam
|
||||||
|
|
|
@ -78,15 +78,14 @@ public class TeacherAccountServiceImpl implements TeacherAccountService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getTeacherAccountIdentifier(
|
public String getTeacherAccountIdentifier(
|
||||||
final String examId,
|
|
||||||
final String lmsId,
|
final String lmsId,
|
||||||
final String userId) {
|
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");
|
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
|
@Override
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms;
|
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.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.LmsSetup;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
@ -32,4 +33,5 @@ public interface FullLmsIntegrationAPI {
|
||||||
|
|
||||||
Result<String> deleteConnectionDetails();
|
Result<String> deleteConnectionDetails();
|
||||||
|
|
||||||
|
Result<QuizData> getQuizDataForRemoteImport(String examData);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,8 @@ public interface FullLmsIntegrationService {
|
||||||
String quizId,
|
String quizId,
|
||||||
String examTemplateId,
|
String examTemplateId,
|
||||||
String quitPassword,
|
String quitPassword,
|
||||||
String quitLink);
|
String quitLink,
|
||||||
|
final String examData);
|
||||||
|
|
||||||
Result<EntityKey> deleteExam(
|
Result<EntityKey> deleteExam(
|
||||||
String lmsUUID,
|
String lmsUUID,
|
||||||
|
|
|
@ -315,12 +315,14 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
||||||
final String quizId,
|
final String quizId,
|
||||||
final String examTemplateId,
|
final String examTemplateId,
|
||||||
final String quitPassword,
|
final String quitPassword,
|
||||||
final String quitLink) {
|
final String quitLink,
|
||||||
|
final String examData) {
|
||||||
|
|
||||||
return lmsSetupDAO
|
return lmsSetupDAO
|
||||||
.getLmsSetupIdByConnectionId(lmsUUID)
|
.getLmsSetupIdByConnectionId(lmsUUID)
|
||||||
.flatMap(lmsAPITemplateCacheService::getLmsAPITemplate)
|
.flatMap(lmsAPITemplateCacheService::getLmsAPITemplate)
|
||||||
.map(findQuizData(courseId, quizId))
|
.map(template -> getQuizData(template, courseId, quizId, examData))
|
||||||
|
//.map(findQuizData(courseId, quizId))
|
||||||
.map(createExam(examTemplateId, quitPassword))
|
.map(createExam(examTemplateId, quitPassword))
|
||||||
.map(exam -> applyExamData(exam, false))
|
.map(exam -> applyExamData(exam, false))
|
||||||
.map(this::applyConnectionConfiguration);
|
.map(this::applyConnectionConfiguration);
|
||||||
|
@ -418,6 +420,22 @@ public class FullLmsIntegrationServiceImpl implements FullLmsIntegrationService
|
||||||
.getOneTimeTokenForTeacherAccount(exam, adHocAccountData, true));
|
.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(
|
private Function<LmsAPITemplate, QuizData> findQuizData(
|
||||||
final String courseId,
|
final String courseId,
|
||||||
|
|
|
@ -577,4 +577,14 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
|
||||||
.getOrThrow());
|
.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");
|
return Result.ofRuntimeError("Not Supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<QuizData> getQuizDataForRemoteImport(final String examData) {
|
||||||
|
return Result.ofRuntimeError("Not Supported");
|
||||||
|
}
|
||||||
|
|
||||||
private enum LinkRel {
|
private enum LinkRel {
|
||||||
FIRST, LAST, PREV, NEXT
|
FIRST, LAST, PREV, NEXT
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup;
|
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.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.LmsSetup;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
@ -42,4 +43,9 @@ public class MockupFullIntegration implements FullLmsIntegrationAPI {
|
||||||
public Result<String> deleteConnectionDetails() {
|
public Result<String> deleteConnectionDetails() {
|
||||||
return Result.ofRuntimeError("TODO");
|
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(
|
return this.jsonMapper.readValue(
|
||||||
courseJSON,
|
courseJSON,
|
||||||
Courses.class).courses
|
CoursesPlugin.class).results
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(c -> MoodleUtils.quizDataOf(
|
.flatMap(c -> MoodleUtils.quizDataOf(
|
||||||
lmsSetup,
|
lmsSetup,
|
||||||
|
|
|
@ -8,14 +8,13 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.plugin;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
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.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.LmsSetup;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
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.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
@ -49,12 +49,19 @@ public class MoodlePluginFullIntegration implements FullLmsIntegrationAPI {
|
||||||
private final JSONMapper jsonMapper;
|
private final JSONMapper jsonMapper;
|
||||||
private final MoodleRestTemplateFactory restTemplateFactory;
|
private final MoodleRestTemplateFactory restTemplateFactory;
|
||||||
|
|
||||||
|
private final boolean prependShortCourseName;
|
||||||
|
|
||||||
public MoodlePluginFullIntegration(
|
public MoodlePluginFullIntegration(
|
||||||
final JSONMapper jsonMapper,
|
final JSONMapper jsonMapper,
|
||||||
final MoodleRestTemplateFactory restTemplateFactory) {
|
final MoodleRestTemplateFactory restTemplateFactory,
|
||||||
|
final Environment environment) {
|
||||||
|
|
||||||
this.jsonMapper = jsonMapper;
|
this.jsonMapper = jsonMapper;
|
||||||
this.restTemplateFactory = restTemplateFactory;
|
this.restTemplateFactory = restTemplateFactory;
|
||||||
|
|
||||||
|
this.prependShortCourseName = BooleanUtils.toBoolean(environment.getProperty(
|
||||||
|
"sebserver.webservice.lms.moodle.prependShortCourseName",
|
||||||
|
Constants.TRUE_STRING));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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() {
|
private Result<MoodleAPIRestTemplate> getRestTemplate() {
|
||||||
|
|
||||||
final Result<MoodleAPIRestTemplate> result = this.restTemplateFactory.getRestTemplate();
|
final Result<MoodleAPIRestTemplate> result = this.restTemplateFactory.getRestTemplate();
|
||||||
|
|
|
@ -103,7 +103,8 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory
|
||||||
|
|
||||||
final MoodlePluginFullIntegration moodlePluginFullIntegration = new MoodlePluginFullIntegration(
|
final MoodlePluginFullIntegration moodlePluginFullIntegration = new MoodlePluginFullIntegration(
|
||||||
this.jsonMapper,
|
this.jsonMapper,
|
||||||
moodleRestTemplateFactory
|
moodleRestTemplateFactory,
|
||||||
|
environment
|
||||||
);
|
);
|
||||||
|
|
||||||
return new LmsAPITemplateAdapter(
|
return new LmsAPITemplateAdapter(
|
||||||
|
|
|
@ -434,6 +434,11 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm
|
||||||
return Result.ofRuntimeError("Not Supported");
|
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) {
|
private <T> T apiGet(final RestTemplate restTemplate, final String url, final Class<T> type) {
|
||||||
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup();
|
||||||
final ResponseEntity<T> res = restTemplate.exchange(
|
final ResponseEntity<T> res = restTemplate.exchange(
|
||||||
|
|
|
@ -485,11 +485,14 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<Exam> deleteForExam(final Long examId) {
|
private Result<Exam> deleteForExam(final Long examId) {
|
||||||
return this.examDAO
|
return Result.tryCatch(() -> {
|
||||||
.byPK(examId)
|
final Exam exam = this.examDAO.byPK(examId).getOrThrow();
|
||||||
.flatMap(this.screenProctoringAPIBinding::deactivateScreenProctoring)
|
|
||||||
.map(this::cleanupAllLocalGroups)
|
this.screenProctoringAPIBinding.deactivateScreenProctoring(exam)
|
||||||
.onError(error -> log.error("Failed to delete SPS integration for exam: {}", examId, error));
|
.onError(error -> log.error("Failed to deactivate screen proctoring for exam: {}", exam.name, error));
|
||||||
|
|
||||||
|
return this.cleanupAllLocalGroups(exam);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Exam cleanupAllLocalGroups(final Exam 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_COURSE_ID) final String courseId,
|
||||||
@RequestParam(name = API.LMS_FULL_INTEGRATION_QUIZ_ID) final String quizId,
|
@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_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_PASSWORD, required = false) final String quitPassword,
|
||||||
@RequestParam(name = API.LMS_FULL_INTEGRATION_QUIT_LINK, required = false) final String quitLink,
|
@RequestParam(name = API.LMS_FULL_INTEGRATION_QUIT_LINK, required = false) final String quitLink,
|
||||||
final HttpServletResponse response) {
|
final HttpServletResponse response) {
|
||||||
|
@ -69,10 +70,11 @@ public class LmsIntegrationController {
|
||||||
quizId,
|
quizId,
|
||||||
templateId,
|
templateId,
|
||||||
quitPassword,
|
quitPassword,
|
||||||
quitLink)
|
quitLink,
|
||||||
|
examData)
|
||||||
.onError(e -> log.error(
|
.onError(e -> log.error(
|
||||||
"Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {}",
|
"Failed to create/import exam: lmsId:{}, courseId: {}, quizId: {}, templateId: {} error: {}",
|
||||||
lmsUUId, courseId, quizId, templateId, e))
|
lmsUUId, courseId, quizId, templateId, e.getMessage()))
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
log.info("Auto import of exam successful: {}", exam);
|
log.info("Auto import of exam successful: {}", exam);
|
||||||
|
|
Loading…
Reference in a new issue