diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index f38a2d88..733ae229 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -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"; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/TeacherAccountService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/TeacherAccountService.java index e2d12976..15f4807d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/TeacherAccountService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/TeacherAccountService.java @@ -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 diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/TeacherAccountServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/TeacherAccountServiceImpl.java index 6d26cabc..31a82776 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/TeacherAccountServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/authorization/impl/TeacherAccountServiceImpl.java @@ -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 diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationAPI.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationAPI.java index 7e16f245..7f99b148 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationAPI.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationAPI.java @@ -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 deleteConnectionDetails(); + Result getQuizDataForRemoteImport(String examData); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java index af038bd7..4a4ca461 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/FullLmsIntegrationService.java @@ -53,7 +53,8 @@ public interface FullLmsIntegrationService { String quizId, String examTemplateId, String quitPassword, - String quitLink); + String quitLink, + final String examData); Result deleteExam( String lmsUUID, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java index 978656e1..145cffd7 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/FullLmsIntegrationServiceImpl.java @@ -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 findQuizData( final String courseId, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java index 6cd54f8e..cdc07aac 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/LmsAPITemplateAdapter.java @@ -577,4 +577,14 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate { .getOrThrow()); } + @Override + public Result 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); + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java index 8c4b5880..424648ec 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/ans/AnsLmsAPITemplate.java @@ -447,6 +447,11 @@ public class AnsLmsAPITemplate extends AbstractCachedCourseAccess implements Lms return Result.ofRuntimeError("Not Supported"); } + @Override + public Result getQuizDataForRemoteImport(final String examData) { + return Result.ofRuntimeError("Not Supported"); + } + private enum LinkRel { FIRST, LAST, PREV, NEXT } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupFullIntegration.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupFullIntegration.java index a5105c15..1d5abf99 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupFullIntegration.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/mockup/MockupFullIntegration.java @@ -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 deleteConnectionDetails() { return Result.ofRuntimeError("TODO"); } + + @Override + public Result getQuizDataForRemoteImport(final String examData) { + return Result.ofRuntimeError("Not Supported"); + } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java index d4656f7e..852c6885 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginCourseAccess.java @@ -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, diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginFullIntegration.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginFullIntegration.java index 842a057b..7244883e 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginFullIntegration.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MoodlePluginFullIntegration.java @@ -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 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 = MoodleUtils.quizDataOf( + lmsSetup, + courseData, + urlPrefix, + prependShortCourseName + ); + + return quizData.get(0); + }); + } + private Result getRestTemplate() { final Result result = this.restTemplateFactory.getRestTemplate(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java index 83b15985..092012fe 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/moodle/plugin/MooldePluginLmsAPITemplateFactory.java @@ -103,7 +103,8 @@ public class MooldePluginLmsAPITemplateFactory implements LmsAPITemplateFactory final MoodlePluginFullIntegration moodlePluginFullIntegration = new MoodlePluginFullIntegration( this.jsonMapper, - moodleRestTemplateFactory + moodleRestTemplateFactory, + environment ); return new LmsAPITemplateAdapter( diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java index 21b76a4f..6ddc5c8b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/olat/OlatLmsAPITemplate.java @@ -434,6 +434,11 @@ public class OlatLmsAPITemplate extends AbstractCachedCourseAccess implements Lm return Result.ofRuntimeError("Not Supported"); } + @Override + public Result getQuizDataForRemoteImport(final String examData) { + return Result.ofRuntimeError("Not Supported"); + } + private T apiGet(final RestTemplate restTemplate, final String url, final Class type) { final LmsSetup lmsSetup = this.apiTemplateDataSupplier.getLmsSetup(); final ResponseEntity res = restTemplate.exchange( diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java index 1f8ca115..16cec3af 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ScreenProctoringServiceImpl.java @@ -485,11 +485,14 @@ public class ScreenProctoringServiceImpl implements ScreenProctoringService { } private Result 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) { diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java index 0567c615..1e27f63b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/LmsIntegrationController.java @@ -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);