SEBSERV-121 start implementation
This commit is contained in:
parent
9d614fdf6e
commit
68e48caafb
8 changed files with 158 additions and 10 deletions
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ch.ethz.seb.sebserver.gbl.model.exam;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public final class Chapters {
|
||||
|
||||
public static final String ATTR_CHAPTERS = "chapters";
|
||||
|
||||
public final Collection<Chapter> chapters;
|
||||
|
||||
@JsonCreator
|
||||
public Chapters(@JsonProperty(ATTR_CHAPTERS) final Collection<Chapter> chapters) {
|
||||
this.chapters = chapters;
|
||||
}
|
||||
|
||||
public Collection<Chapter> getChapters() {
|
||||
return this.chapters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Chapters [chapters=");
|
||||
builder.append(this.chapters);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public static final class Chapter {
|
||||
|
||||
public static final String ATTR_NAME = "name";
|
||||
public static final String ATTR_ID = "id";
|
||||
|
||||
public final String name;
|
||||
public final String id;
|
||||
|
||||
@JsonCreator
|
||||
protected Chapter(
|
||||
@JsonProperty(ATTR_NAME) final String name,
|
||||
@JsonProperty(ATTR_ID) final String id) {
|
||||
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Chapter [name=");
|
||||
builder.append(this.name);
|
||||
builder.append(", id=");
|
||||
builder.append(this.id);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@ import java.util.stream.Collectors;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
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.exam.SebRestriction;
|
||||
|
@ -108,6 +109,8 @@ public interface LmsAPITemplate {
|
|||
// examinee identifier received by on SEB-Client connection.
|
||||
//Result<ExamineeAccountDetails> getExamineeAccountDetails(String examineeUserId);
|
||||
|
||||
Result<Chapters> getCourseChapters(String courseId);
|
||||
|
||||
/** Get SEB restriction data form LMS within a SebRestrictionData instance if available
|
||||
* or a ResourceNotFoundException if not yet available or restricted
|
||||
*
|
||||
|
|
|
@ -19,7 +19,9 @@ import java.util.stream.Collectors;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
||||
import ch.ethz.seb.sebserver.gbl.async.CircuitBreaker;
|
||||
import ch.ethz.seb.sebserver.gbl.async.MemoizingCircuitBreaker;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||
|
@ -27,20 +29,26 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
|||
|
||||
public abstract class CourseAccess {
|
||||
|
||||
protected final MemoizingCircuitBreaker<List<QuizData>> allQuizzesSupplier;
|
||||
protected final MemoizingCircuitBreaker<List<QuizData>> allQuizzesRequest;
|
||||
protected final CircuitBreaker<Chapters> chaptersRequest;
|
||||
|
||||
protected CourseAccess(final AsyncService asyncService) {
|
||||
this.allQuizzesSupplier = asyncService.createMemoizingCircuitBreaker(
|
||||
this.allQuizzesRequest = asyncService.createMemoizingCircuitBreaker(
|
||||
allQuizzesSupplier(),
|
||||
3,
|
||||
Constants.MINUTE_IN_MILLIS,
|
||||
Constants.MINUTE_IN_MILLIS,
|
||||
true,
|
||||
Constants.HOUR_IN_MILLIS);
|
||||
|
||||
this.chaptersRequest = asyncService.createCircuitBreaker(
|
||||
3,
|
||||
Constants.MINUTE_IN_MILLIS,
|
||||
Constants.MINUTE_IN_MILLIS);
|
||||
}
|
||||
|
||||
public Result<QuizData> getQuizFromCache(final String id) {
|
||||
return Result.tryCatch(() -> this.allQuizzesSupplier
|
||||
return Result.tryCatch(() -> this.allQuizzesRequest
|
||||
.getCached()
|
||||
.stream()
|
||||
.filter(qd -> id.equals(qd.id))
|
||||
|
@ -50,7 +58,7 @@ public abstract class CourseAccess {
|
|||
|
||||
public Result<Collection<Result<QuizData>>> getQuizzesFromCache(final Set<String> ids) {
|
||||
return Result.tryCatch(() -> {
|
||||
final List<QuizData> cached = this.allQuizzesSupplier.getCached();
|
||||
final List<QuizData> cached = this.allQuizzesRequest.getCached();
|
||||
if (cached == null) {
|
||||
throw new RuntimeException("No cached quizzes");
|
||||
}
|
||||
|
@ -76,10 +84,16 @@ public abstract class CourseAccess {
|
|||
}
|
||||
|
||||
public Result<List<QuizData>> getQuizzes(final FilterMap filterMap) {
|
||||
return this.allQuizzesSupplier.get()
|
||||
return this.allQuizzesRequest.get()
|
||||
.map(LmsAPIService.quizzesFilterFunction(filterMap));
|
||||
}
|
||||
|
||||
protected Result<Chapters> getCourseChapters(final String courseId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract Supplier<List<QuizData>> allQuizzesSupplier();
|
||||
|
||||
protected abstract Supplier<Chapters> getCourseChaptersSupplier(final String courseId);
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory;
|
|||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.api.APIMessage;
|
||||
import ch.ethz.seb.sebserver.gbl.model.Domain.LMS_SETUP;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
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.exam.SebRestriction;
|
||||
|
@ -177,6 +178,11 @@ final class MockupLmsAPITemplate implements LmsAPITemplate {
|
|||
return getQuizzes(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Chapters> getCourseChapters(final String courseId) {
|
||||
return Result.ofError(new UnsupportedOperationException());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SebRestriction> getSebClientRestriction(final Exam exam) {
|
||||
log.info("Apply SEB Client restriction for Exam: {}", exam);
|
||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.edx;
|
|||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -34,6 +35,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
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;
|
||||
|
@ -49,6 +51,8 @@ final class OpenEdxCourseAccess extends CourseAccess {
|
|||
private static final Logger log = LoggerFactory.getLogger(OpenEdxCourseAccess.class);
|
||||
|
||||
private static final String OPEN_EDX_DEFAULT_COURSE_ENDPOINT = "/api/courses/v1/courses/";
|
||||
private static final String OPEN_EDX_DEFAULT_BLOCKS_ENDPOINT =
|
||||
"/api/courses/v1/blocks/?depth=1&all_blocks=true&course_id=";
|
||||
private static final String OPEN_EDX_DEFAULT_COURSE_START_URL_PREFIX = "/courses/";
|
||||
|
||||
private final LmsSetup lmsSetup;
|
||||
|
@ -110,6 +114,11 @@ final class OpenEdxCourseAccess extends CourseAccess {
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<Chapters> getCourseChaptersSupplier(final String courseId) {
|
||||
throw new UnsupportedOperationException("not available yet");
|
||||
}
|
||||
|
||||
private ArrayList<QuizData> collectAllQuizzes(final OAuth2RestTemplate restTemplate) {
|
||||
final String externalStartURI = getExternalLMSServerAddress(this.lmsSetup);
|
||||
return collectAllCourses(
|
||||
|
@ -218,6 +227,18 @@ final class OpenEdxCourseAccess extends CourseAccess {
|
|||
public String end;
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
static final class Blocks {
|
||||
public String root;
|
||||
public Collection<Block> blocks;
|
||||
}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
static final class Block {
|
||||
public String block_id;
|
||||
public String display_name;
|
||||
}
|
||||
|
||||
private static final class EdxOAuth2RequestAuthenticator implements OAuth2RequestAuthenticator {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.Set;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.OpenEdxSebRestriction;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||
|
@ -78,6 +79,13 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
|||
.getOrElse(() -> getQuizzes(ids));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Chapters> getCourseChapters(final String courseId) {
|
||||
return Result.tryCatch(() -> this.openEdxCourseAccess
|
||||
.getCourseChaptersSupplier(courseId)
|
||||
.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SebRestriction> getSebClientRestriction(final Exam exam) {
|
||||
if (log.isDebugEnabled()) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
|
|||
|
||||
import ch.ethz.seb.sebserver.gbl.api.JSONMapper;
|
||||
import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
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;
|
||||
|
@ -102,6 +103,11 @@ public class MoodleCourseAccess extends CourseAccess {
|
|||
.getOrThrow();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Supplier<Chapters> getCourseChaptersSupplier(final String courseId) {
|
||||
throw new UnsupportedOperationException("not available yet");
|
||||
}
|
||||
|
||||
private ArrayList<QuizData> collectAllQuizzes(final MoodleAPIRestTemplate restTemplate) {
|
||||
final String urlPrefix = this.lmsSetup.lmsApiUrl + MOODLE_QUIZ_START_URL_PATH;
|
||||
return collectAllCourses(
|
||||
|
@ -165,6 +171,7 @@ public class MoodleCourseAccess extends CourseAccess {
|
|||
}
|
||||
|
||||
static Map<String, String> additionalAttrs = new HashMap<>();
|
||||
|
||||
private static List<QuizData> quizDataOf(
|
||||
final LmsSetup lmsSetup,
|
||||
final CourseData courseData,
|
||||
|
@ -219,8 +226,8 @@ public class MoodleCourseAccess extends CourseAccess {
|
|||
final String full_name;
|
||||
final String display_name;
|
||||
final String summary;
|
||||
final Long start_date; // unix-time milliseconds UTC
|
||||
final Long end_date; // unix-time milliseconds UTC
|
||||
final Long start_date; // unix-time milliseconds UTC
|
||||
final Long end_date; // unix-time milliseconds UTC
|
||||
final Long time_created; // unix-time milliseconds UTC
|
||||
final Collection<CourseQuiz> quizzes = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import ch.ethz.seb.sebserver.gbl.model.exam.Chapters;
|
||||
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.exam.SebRestriction;
|
||||
|
@ -66,9 +67,16 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
|||
.orElse(() -> getQuiz(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Chapters> getCourseChapters(final String courseId) {
|
||||
return Result.tryCatch(() -> this.moodleCourseAccess
|
||||
.getCourseChaptersSupplier(courseId)
|
||||
.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<SebRestriction> getSebClientRestriction(final Exam exam) {
|
||||
throw new UnsupportedOperationException("SEB Restriction API not available yet");
|
||||
return Result.ofError(new UnsupportedOperationException("SEB Restriction API not available yet"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -76,12 +84,12 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
|||
final String externalExamId,
|
||||
final SebRestriction sebRestrictionData) {
|
||||
|
||||
throw new UnsupportedOperationException("SEB Restriction API not available yet");
|
||||
return Result.ofError(new UnsupportedOperationException("SEB Restriction API not available yet"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<Exam> releaseSebClientRestriction(final Exam exam) {
|
||||
throw new UnsupportedOperationException("SEB Restriction API not available yet");
|
||||
return Result.ofError(new UnsupportedOperationException("SEB Restriction API not available yet"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue